diff options
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/check.cpp | 263 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 68 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 68 | ||||
| -rw-r--r-- | source/slang/decl-defs.h | 7 | ||||
| -rw-r--r-- | source/slang/diagnostic-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 48 | ||||
| -rw-r--r-- | source/slang/lookup.cpp | 27 | ||||
| -rw-r--r-- | source/slang/lookup.h | 9 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 18 | ||||
| -rw-r--r-- | source/slang/modifier-defs.h | 72 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 131 | ||||
| -rw-r--r-- | source/slang/reflection.cpp | 2 | ||||
| -rw-r--r-- | source/slang/syntax-base-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/syntax.h | 5 |
14 files changed, 591 insertions, 134 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index f7cb3b575..1af91e9fd 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1412,6 +1412,11 @@ namespace Slang // These are only used in the stdlib, so no checking is needed } + void visitAttributeDecl(AttributeDecl*) + { + // These are only used in the stdlib, so no checking is needed + } + void visitGenericTypeParamDecl(GenericTypeParamDecl*) { // These are only used in the stdlib, so no checking is needed for now @@ -1427,71 +1432,209 @@ namespace Slang // Do nothing with modifiers for now } - RefPtr<Modifier> checkModifier( - RefPtr<Modifier> m, - Decl* /*decl*/) + AttributeDecl* lookUpAttributeDecl(Name* attributeName, Scope* scope) { - if(auto hlslUncheckedAttribute = m.As<HLSLUncheckedAttribute>()) + // Look up the name and see what we find. + // + // TODO: This needs to have some special filtering or naming + // rules to keep us from seeing shadowing variable declarations. + auto lookupResult = lookUp(getSession(), this, attributeName, scope, LookupMask::Attribute); + + // If we didn't find anything, or the result was overloaded, + // then we aren't going to be able to extract a single decl. + if(!lookupResult.isValid() || lookupResult.isOverloaded()) + return nullptr; + + auto decl = lookupResult.item.declRef.getDecl(); + if( auto attributeDecl = dynamic_cast<AttributeDecl*>(decl) ) { - // We have an HLSL `[name(arg,...)]` attribute, and we'd like - // to check that it is provides all the expected arguments - // - // For now we will do this in a completely ad hoc fashion, - // but it would be nice to have some generic routine to - // do the needed type checking/coercion. - auto attribText = getText(hlslUncheckedAttribute->getName()); - - if(attribText == "numthreads") - { - if(hlslUncheckedAttribute->args.Count() != 3) - return m; + return attributeDecl; + } + else + { + return nullptr; + } + } - auto xVal = checkConstantIntVal(hlslUncheckedAttribute->args[0]); - auto yVal = checkConstantIntVal(hlslUncheckedAttribute->args[1]); - auto zVal = checkConstantIntVal(hlslUncheckedAttribute->args[2]); + bool validateAttribute(RefPtr<Attribute> attr) + { + if(auto numThreadsAttr = attr.As<NumThreadsAttribute>()) + { + SLANG_ASSERT(attr->args.Count() == 3); + auto xVal = checkConstantIntVal(attr->args[0]); + auto yVal = checkConstantIntVal(attr->args[1]); + auto zVal = checkConstantIntVal(attr->args[2]); - if(!xVal) return m; - if(!yVal) return m; - if(!zVal) return m; + if(!xVal) return false; + if(!yVal) return false; + if(!zVal) return false; - auto hlslNumThreadsAttribute = new HLSLNumThreadsAttribute(); + numThreadsAttr->x = (int32_t) xVal->value; + numThreadsAttr->y = (int32_t) yVal->value; + numThreadsAttr->z = (int32_t) zVal->value; + } + else if (auto maxVertexCountAttr = attr.As<MaxVertexCountAttribute>()) + { + SLANG_ASSERT(attr->args.Count() == 1); + auto val = checkConstantIntVal(attr->args[0]); - hlslNumThreadsAttribute->loc = hlslUncheckedAttribute->loc; - hlslNumThreadsAttribute->name = hlslUncheckedAttribute->getName(); - hlslNumThreadsAttribute->args = hlslUncheckedAttribute->args; - hlslNumThreadsAttribute->x = (int32_t) xVal->value; - hlslNumThreadsAttribute->y = (int32_t) yVal->value; - hlslNumThreadsAttribute->z = (int32_t) zVal->value; + maxVertexCountAttr->value = (int32_t)val->value; + } + else if(auto instanceAttr = attr.As<InstanceAttribute>()) + { + SLANG_ASSERT(attr->args.Count() == 1); + auto val = checkConstantIntVal(attr->args[0]); - return hlslNumThreadsAttribute; + instanceAttr->value = (int32_t)val->value; + } + else + { + if(attr->args.Count() == 0) + { + // If the attribute took no arguments, then we will + // assume it is valid as written. + } + else + { + // We should be special-casing the checking of any attribute + // with a non-zero number of arguments. + SLANG_DIAGNOSE_UNEXPECTED(getSink(), attr, "unhandled attribute"); + return false; + } } - else if (attribText == "maxvertexcount") + + return true; + } + + RefPtr<AttributeBase> checkAttribute( + UncheckedAttribute* uncheckedAttr, + ModifiableSyntaxNode* attrTarget) + { + auto attrName = uncheckedAttr->getName(); + auto attrDecl = lookUpAttributeDecl( + attrName, + uncheckedAttr->scope); + + if(!attrDecl) + { + getSink()->diagnose(uncheckedAttr, Diagnostics::unknownAttributeName, attrName); + return uncheckedAttr; + } + + if(!attrDecl->syntaxClass.isSubClassOf<Attribute>()) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), attrDecl, "attribute declaration does not reference an attribute class"); + return uncheckedAttr; + } + + RefPtr<RefObject> attrObj = attrDecl->syntaxClass.createInstance(); + auto attr = attrObj.As<Attribute>(); + if(!attr) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), attrDecl, "attribute class did not yield an attribute object"); + return uncheckedAttr; + } + + // We are going to replace the unchecked attribute with the checked one. + + // First copy all of the state over from the original attribute. + attr->name = uncheckedAttr->name; + attr->args = uncheckedAttr->args; + attr->loc = uncheckedAttr->loc; + + // We will start with checking steps that can be applied independent + // of the concrete attribute type that was selected. These only need + // us to look at the attribute declaration itself. + // + // Start by doing argument/parameter matching + UInt argCount = attr->args.Count(); + UInt paramCounter = 0; + bool mismatch = false; + for(auto paramDecl : attrDecl->getMembersOfType<ParamDecl>()) + { + UInt paramIndex = paramCounter++; + if( paramIndex < argCount ) { - if (hlslUncheckedAttribute->args.Count() != 1) - return m; - auto val = checkConstantIntVal(hlslUncheckedAttribute->args[0]); - auto hlslMaxVertexCountAttrib = new HLSLMaxVertexCountAttribute(); + auto arg = attr->args[paramIndex]; + + // TODO: support checking the argument against the declared + // type for the parameter. - hlslMaxVertexCountAttrib->loc = hlslUncheckedAttribute->loc; - hlslMaxVertexCountAttrib->name = hlslUncheckedAttribute->getName(); - hlslMaxVertexCountAttrib->args = hlslUncheckedAttribute->args; - hlslMaxVertexCountAttrib->value = (int32_t)val->value; - return hlslMaxVertexCountAttrib; } - else if (attribText == "instance") + else { - if (hlslUncheckedAttribute->args.Count() != 1) - return m; - auto val = checkConstantIntVal(hlslUncheckedAttribute->args[0]); - auto attrib = new HLSLInstanceAttribute(); + // We didn't have enough arguments for the + // number of parameters declared. + if(auto defaultArg = paramDecl->initExpr) + { + // The attribute declaration provided a default, + // so we should use that. + // + // TODO: we need to figure out how to hook up + // default arguments as needed. + } + else + { + mismatch = true; + } + } + } + UInt paramCount = paramCounter; + + if(mismatch) + { + getSink()->diagnose(attr, Diagnostics::attributeArgumentCountMismatch, attrName, paramCount, argCount); + return uncheckedAttr; + } - attrib->loc = hlslUncheckedAttribute->loc; - attrib->name = hlslUncheckedAttribute->getName(); - attrib->args = hlslUncheckedAttribute->args; - attrib->value = (int32_t)val->value; - return attrib; + // The next bit of validation that we can apply semi-generically + // is to validate that the target for this attribute is a valid + // one for the chosen attribute. + // + // The attribute declaration will have one or more `AttributeTargetModifier`s + // that each specify a syntax class that the attribute can be applied to. + // If any of these match `attrTarget`, then we are good. + // + bool validTarget = false; + for(auto attrTargetMod : attrDecl->GetModifiersOfType<AttributeTargetModifier>()) + { + if(attrTarget->getClass().isSubClassOf(attrTargetMod->syntaxClass)) + { + validTarget = true; + break; } } + if(!validTarget) + { + getSink()->diagnose(attr, Diagnostics::attributeNotApplicable, attrName); + return uncheckedAttr; + } + + // Now apply type-specific validation to the attribute. + if(!validateAttribute(attr)) + { + return uncheckedAttr; + } + + + return attr; + } + + RefPtr<Modifier> checkModifier( + RefPtr<Modifier> m, + ModifiableSyntaxNode* syntaxNode) + { + if(auto hlslUncheckedAttribute = m.As<UncheckedAttribute>()) + { + // We have an HLSL `[name(arg,...)]` attribute, and we'd like + // to check that it is provides all the expected arguments + // + // First, look up the attribute name in the current scope to find + // the right syntax class to instantiate. + // + + return checkAttribute(hlslUncheckedAttribute, syntaxNode); + } // Default behavior is to leave things as they are, // and assume that modifiers are mostly already checked. // @@ -1505,7 +1648,7 @@ namespace Slang } - void checkModifiers(Decl* decl) + void checkModifiers(ModifiableSyntaxNode* syntaxNode) { // TODO(tfoley): need to make sure this only // performs semantic checks on a `SharedModifier` once... @@ -1516,7 +1659,7 @@ namespace Slang RefPtr<Modifier> resultModifiers; RefPtr<Modifier>* resultModifierLink = &resultModifiers; - RefPtr<Modifier> modifier = decl->modifiers.first; + RefPtr<Modifier> modifier = syntaxNode->modifiers.first; while(modifier) { // Because we are rewriting the list in place, we need to extract @@ -1528,7 +1671,7 @@ namespace Slang // be to return a single unlinked modifier. modifier->next = nullptr; - auto checkedModifier = checkModifier(modifier, decl); + auto checkedModifier = checkModifier(modifier, syntaxNode); if(checkedModifier) { // If checking gave us a modifier to add, then we @@ -1552,7 +1695,7 @@ namespace Slang // Whether we actually re-wrote anything or note, lets // install the new list of modifiers on the declaration - decl->modifiers.first = resultModifiers; + syntaxNode->modifiers.first = resultModifiers; } void visitModuleDecl(ModuleDecl* programNode) @@ -2182,6 +2325,7 @@ namespace Slang { if (!stmt) return; StmtVisitor::dispatch(stmt); + checkModifiers(stmt); } void visitFuncDecl(FuncDecl *functionNode) @@ -2610,11 +2754,14 @@ namespace Slang { // TODO: This needs to bottleneck through the common variable checks - para->type = CheckUsableType(para->type); - - if (para->type.Equals(getSession()->getVoidType())) + if(para->type.exp) { - getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid); + para->type = CheckUsableType(para->type); + + if (para->type.Equals(getSession()->getVoidType())) + { + getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid); + } } } diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index b0b233b1d..f105b98aa 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1028,3 +1028,71 @@ for (auto op : binaryOps) } } }}}} + +// Statement Attributes + +__attributeTarget(LoopStmt) +attribute_syntax [unroll(count: int = 0)] : UnrollAttribute; + +__attributeTarget(LoopStmt) +attribute_syntax [loop] : LoopAttribute; + +__attributeTarget(LoopStmt) +attribute_syntax [fastopt] : FastOptAttribute; + +__attributeTarget(LoopStmt) +attribute_syntax [allow_uav_condition] : AllowUAVConditionAttribute; + +__attributeTarget(IfStmt) +attribute_syntax [flatten] : FlattenAttribute; + +__attributeTarget(IfStmt) +__attributeTarget(SwitchStmt) +attribute_syntax [branch] : BranchAttribute; + +__attributeTarget(SwitchStmt) +attribute_syntax [forcecase] : ForceCaseAttribute; + +__attributeTarget(SwitchStmt) +attribute_syntax [call] : CallAttribute; + +// Entry-point Attributes + +// All Stages +__attributeTarget(FuncDecl) +attribute_syntax [shader(stage)] : EntryPointAttribute; + +// Hull Shader +__attributeTarget(FuncDecl) +attribute_syntax [maxtessfactor(factor: float)] : MaxTessFactorAttribute; + +__attributeTarget(FuncDecl) +attribute_syntax [outputcontrolpoints(count: int)] : OutputControlPointsAttribute; + +__attributeTarget(FuncDecl) +attribute_syntax [outputtopology(topology)] : OuptutTopologyAttribute; + +__attributeTarget(FuncDecl) +attribute_syntax [partitioning(mode)] : PartitioningAttribute; + +__attributeTarget(FuncDecl) +attribute_syntax [patchconstantfunc(name)] : PatchConstantFuncAttribute; + +// Hull/Domain Shader +__attributeTarget(FuncDecl) +attribute_syntax [domain(domain)] : DomainAttribute; + +// Geometry Shader +__attributeTarget(FuncDecl) +attribute_syntax [maxvertexcount(count: int)] : MaxVertexCountAttribute; + +__attributeTarget(FuncDecl) +attribute_syntax [instance(count: int)] : InstanceAttribute; + +// Fragment ("Pixel") Shader +__attributeTarget(FuncDecl) +attribute_syntax [earlydepthstencil] : EarlyDepthStencilAttribute; + +// Compute Shader +__attributeTarget(FuncDecl) +attribute_syntax [numthreads(x: int, y: int = 1, z: int = 1)] : NumThreadsAttribute; diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h index 974607a75..5e1f2ec89 100644 --- a/source/slang/core.meta.slang.h +++ b/source/slang/core.meta.slang.h @@ -1028,3 +1028,71 @@ for (auto op : binaryOps) } } SLANG_RAW("\n") +SLANG_RAW("\n") +SLANG_RAW("// Statement Attributes\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(LoopStmt)\n") +SLANG_RAW("attribute_syntax [unroll(count: int = 0)] : UnrollAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(LoopStmt)\n") +SLANG_RAW("attribute_syntax [loop] : LoopAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(LoopStmt)\n") +SLANG_RAW("attribute_syntax [fastopt] : FastOptAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(LoopStmt)\n") +SLANG_RAW("attribute_syntax [allow_uav_condition] : AllowUAVConditionAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(IfStmt)\n") +SLANG_RAW("attribute_syntax [flatten] : FlattenAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(IfStmt)\n") +SLANG_RAW("__attributeTarget(SwitchStmt)\n") +SLANG_RAW("attribute_syntax [branch] : BranchAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(SwitchStmt)\n") +SLANG_RAW("attribute_syntax [forcecase] : ForceCaseAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(SwitchStmt)\n") +SLANG_RAW("attribute_syntax [call] : CallAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("// Entry-point Attributes\n") +SLANG_RAW("\n") +SLANG_RAW("// All Stages\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [shader(stage)] : EntryPointAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("// Hull Shader\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [maxtessfactor(factor: float)] : MaxTessFactorAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [outputcontrolpoints(count: int)] : OutputControlPointsAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [outputtopology(topology)] : OuptutTopologyAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [partitioning(mode)] : PartitioningAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [patchconstantfunc(name)] : PatchConstantFuncAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("// Hull/Domain Shader\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [domain(domain)] : DomainAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("// Geometry Shader\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [maxvertexcount(count: int)] : MaxVertexCountAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [instance(count: int)] : InstanceAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("// Fragment (\"Pixel\") Shader\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [earlydepthstencil] : EarlyDepthStencilAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("// Compute Shader\n") +SLANG_RAW("__attributeTarget(FuncDecl)\n") +SLANG_RAW("attribute_syntax [numthreads(x: int, y: int = 1, z: int = 1)] : NumThreadsAttribute;\n") diff --git a/source/slang/decl-defs.h b/source/slang/decl-defs.h index 8e1985e3f..76480e64b 100644 --- a/source/slang/decl-defs.h +++ b/source/slang/decl-defs.h @@ -266,3 +266,10 @@ SYNTAX_CLASS(SyntaxDecl, Decl) FIELD(SyntaxParseCallback, parseCallback) FIELD(void*, parseUserData) END_SYNTAX_CLASS() + +// A declaration of an attribute to be used with `[name(...)]` syntax. +// +SYNTAX_CLASS(AttributeDecl, ContainerDecl) + // What type of syntax node will be produced to represent this attribute. + FIELD(SyntaxClass<RefObject>, syntaxClass) +END_SYNTAX_CLASS() diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h index 220ad44ff..ae815cd40 100644 --- a/source/slang/diagnostic-defs.h +++ b/source/slang/diagnostic-defs.h @@ -197,6 +197,11 @@ DIAGNOSTIC(30051, Error, invalidValueForArgument, "invalid value for argument '$ DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on type '$1'") DIAGNOSTIC(33070, Error, expectedFunction, "expression preceding parenthesis of apparent call must have function type.") +// Attributes +DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'") +DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)") +DIAGNOSTIC(31001, Error, attributeNotApplicable, "attribute '$0' is not valid here") + // 303xx: interfaces and associated types DIAGNOSTIC(30300, Error, assocTypeInInterfaceOnly, "'associatedtype' can only be defined in an 'interface'.") DIAGNOSTIC(30301, Error, globalGenParamInGlobalScopeOnly, "'__generic_param' can only be defined global scope.") diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 58c917fe7..aee19018a 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -2809,23 +2809,7 @@ struct EmitVisitor void EmitLoopAttributes(RefPtr<Stmt> decl) { - // Don't emit these attributes for GLSL, because it doesn't understand them - if (context->shared->target == CodeGenTarget::GLSL) - return; - - // TODO(tfoley): There really ought to be a semantic checking step for attributes, - // that turns abstract syntax into a concrete hierarchy of attribute types (e.g., - // a specific `LoopModifier` or `UnrollModifier`). - - for(auto attr : decl->GetModifiersOfType<HLSLUncheckedAttribute>()) - { - // Emit whatever attributes the user might have attached, - // whether or not we think they make semantic sense. - // - Emit("["); - emit(attr->getName()); - Emit("]"); - } + // NOTE: Emit-from-AST is gone, so this code is doing nothing. } void EmitUnparsedStmt(RefPtr<UnparsedStmt> stmt) @@ -3134,6 +3118,7 @@ struct EmitVisitor // Only used by stdlib IGNORED(SyntaxDecl) + IGNORED(AttributeDecl) // Don't emit generic decls directly; we will only // ever emit particular instantiations of them. @@ -3346,27 +3331,6 @@ struct EmitVisitor } } - // TODO: eventually we should be checked these modifiers, but for - // now we can emit them unchecked, I guess - else if (auto uncheckedAttr = mod.As<HLSLAttribute>()) - { - Emit("["); - emit(uncheckedAttr->getNameAndLoc()); - auto& args = uncheckedAttr->args; - auto argCount = args.Count(); - if (argCount != 0) - { - Emit("("); - for (UInt aa = 0; aa < argCount; ++aa) - { - if (aa != 0) Emit(", "); - EmitExpr(args[aa]); - } - Emit(")"); - } - Emit("]"); - } - else if(auto simpleModifier = mod.As<SimpleModifier>()) { emit(simpleModifier->getNameAndLoc()); @@ -6756,13 +6720,13 @@ emitDeclImpl(decl, nullptr); break; case Stage::Geometry: { - if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLMaxVertexCountAttribute>()) + if (auto attrib = entryPointLayout->entryPoint->FindModifier<MaxVertexCountAttribute>()) { emit("[maxvertexcount("); Emit(attrib->value); emit(")]\n"); } - if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLInstanceAttribute>()) + if (auto attrib = entryPointLayout->entryPoint->FindModifier<InstanceAttribute>()) { emit("[instance("); Emit(attrib->value); @@ -6813,13 +6777,13 @@ emitDeclImpl(decl, nullptr); break; case Stage::Geometry: { - if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLMaxVertexCountAttribute>()) + if (auto attrib = entryPointLayout->entryPoint->FindModifier<MaxVertexCountAttribute>()) { emit("layout(max_vertices = "); Emit(attrib->value); emit(") out;\n"); } - if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLInstanceAttribute>()) + if (auto attrib = entryPointLayout->entryPoint->FindModifier<InstanceAttribute>()) { emit("layout(invocations = "); Emit(attrib->value); diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp index e5ffa00f7..eebef6503 100644 --- a/source/slang/lookup.cpp +++ b/source/slang/lookup.cpp @@ -96,6 +96,11 @@ bool DeclPassesLookupMask(Decl* decl, LookupMask mask) { return (int(mask) & int(LookupMask::Function)) != 0; } + // attribute declaration + else if( auto attrDecl = dynamic_cast<AttributeDecl*>(decl) ) + { + return (int(mask) & int(LookupMask::Attribute)) != 0; + } // default behavior is to assume a value declaration // (no overloading allowed) @@ -423,11 +428,13 @@ LookupResult lookUp( Session* session, SemanticsVisitor* semantics, Name* name, - RefPtr<Scope> scope) + RefPtr<Scope> scope, + LookupMask mask) { LookupRequest request; request.semantics = semantics; request.scope = scope; + request.mask = mask; return DoLookup(session, name, request); } @@ -437,10 +444,12 @@ LookupResult lookUpLocal( Session* session, SemanticsVisitor* semantics, Name* name, - DeclRef<ContainerDecl> containerDeclRef) + DeclRef<ContainerDecl> containerDeclRef, + LookupMask mask) { LookupRequest request; request.semantics = semantics; + request.mask = mask; LookupResult result; DoLocalLookupImpl(session, name, containerDeclRef, request, result, nullptr); @@ -448,12 +457,13 @@ LookupResult lookUpLocal( } void lookUpMemberImpl( - Session* session, + Session* session, SemanticsVisitor* semantics, Name* name, Type* type, LookupResult& ioResult, - BreadcrumbInfo* inBreadcrumbs) + BreadcrumbInfo* inBreadcrumbs, + LookupMask mask) { if (auto declRefType = type->As<DeclRefType>()) { @@ -475,7 +485,7 @@ void lookUpMemberImpl( breadcrumb.declRef = constraintDeclRef; // TODO: Need to consider case where this might recurse infinitely. - lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb); + lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb, mask); } } else if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>()) @@ -517,7 +527,7 @@ void lookUpMemberImpl( breadcrumb.declRef = constraintDeclRef; // TODO: Need to consider case where this might recurse infinitely. - lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb); + lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb, mask); } } @@ -529,10 +539,11 @@ LookupResult lookUpMember( Session* session, SemanticsVisitor* semantics, Name* name, - Type* type) + Type* type, + LookupMask mask) { LookupResult result; - lookUpMemberImpl(session, semantics, name, type, result, nullptr); + lookUpMemberImpl(session, semantics, name, type, result, nullptr, mask); return result; } diff --git a/source/slang/lookup.h b/source/slang/lookup.h index 473ecaf96..37ab5cf06 100644 --- a/source/slang/lookup.h +++ b/source/slang/lookup.h @@ -21,7 +21,8 @@ LookupResult lookUp( Session* session, SemanticsVisitor* semantics, Name* name, - RefPtr<Scope> scope); + RefPtr<Scope> scope, + LookupMask mask = LookupMask::Default); // perform lookup within the context of a particular container declaration, // and do *not* look further up the chain @@ -29,14 +30,16 @@ LookupResult lookUpLocal( Session* session, SemanticsVisitor* semantics, Name* name, - DeclRef<ContainerDecl> containerDeclRef); + DeclRef<ContainerDecl> containerDeclRef, + LookupMask mask = LookupMask::Default); // Perform member lookup in the context of a type LookupResult lookUpMember( Session* session, SemanticsVisitor* semantics, Name* name, - Type* type); + Type* type, + LookupMask mask = LookupMask::Default); // TODO: this belongs somewhere else diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index cfd1c89a4..428044830 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -2004,17 +2004,12 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor> IRInst* inst, Stmt* stmt) { - for(auto attr : stmt->GetModifiersOfType<HLSLUncheckedAttribute>()) + if( stmt->FindModifier<UnrollAttribute>() ) { - // TODO: We should actually catch these attributes during - // semantic checking, so that they have a strongly-typed - // representation in the AST. - if(getText(attr->getName()) == "unroll") - { - auto decoration = getBuilder()->addDecoration<IRLoopControlDecoration>(inst); - decoration->mode = kIRLoopControl_Unroll; - } + auto decoration = getBuilder()->addDecoration<IRLoopControlDecoration>(inst); + decoration->mode = kIRLoopControl_Unroll; } + // TODO: handle other cases here } void visitForStmt(ForStmt* stmt) @@ -2794,6 +2789,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> return LoweredValInfo(); } + LoweredValInfo visitAttributeDecl(AttributeDecl* /*decl*/) + { + return LoweredValInfo(); + } + LoweredValInfo visitTypeDefDecl(TypeDefDecl * decl) { return LoweredValInfo::simple(context->irBuilder->getTypeVal(decl->type.type)); diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index 725629d35..fdbb05748 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -288,34 +288,88 @@ SIMPLE_SYNTAX_CLASS(HLSLUniformModifier, Modifier) // HLSL `volatile` modifier (ignored) SIMPLE_SYNTAX_CLASS(HLSLVolatileModifier, Modifier) -// An HLSL `[name(arg0, ...)]` style attribute. -SYNTAX_CLASS(HLSLAttribute, Modifier) +SYNTAX_CLASS(AttributeTargetModifier, Modifier) + // A class to which the declared attribute type is applicable + FIELD(SyntaxClass<RefObject>, syntaxClass) +END_SYNTAX_CLASS() + +// Base class for checked and unchecked `[name(arg0, ...)]` style attribute. +SYNTAX_CLASS(AttributeBase, Modifier) SYNTAX_FIELD(List<RefPtr<Expr>>, args) END_SYNTAX_CLASS() -// An HLSL `[name(...)]` attribute that hasn't undergone -// any semantic analysis. -// After analysis, this might be transformed into a more specific case. -SIMPLE_SYNTAX_CLASS(HLSLUncheckedAttribute, HLSLAttribute) +// A `[name(...)]` attribute that hasn't undergone any semantic analysis. +// After analysis, this will be transformed into a more specific case. +SYNTAX_CLASS(UncheckedAttribute, AttributeBase) + FIELD(RefPtr<Scope>, scope) +END_SYNTAX_CLASS() + +// A `[name(arg0, ...)]` style attribute that has been validated. +SYNTAX_CLASS(Attribute, AttributeBase) +END_SYNTAX_CLASS() + +// An `[unroll]` or `[unroll(count)]` attribute +SYNTAX_CLASS(UnrollAttribute, Attribute) + RAW(IntegerLiteralValue getCount();) +END_SYNTAX_CLASS() + +SIMPLE_SYNTAX_CLASS(LoopAttribute, Attribute) // `[loop]` +SIMPLE_SYNTAX_CLASS(FastOptAttribute, Attribute) // `[fastopt]` +SIMPLE_SYNTAX_CLASS(AllowUAVConditionAttribute, Attribute) // `[allow_uav_condition]` +SIMPLE_SYNTAX_CLASS(BranchAttribute, Attribute) // `[branch]` +SIMPLE_SYNTAX_CLASS(FlattenAttribute, Attribute) // `[flatten]` +SIMPLE_SYNTAX_CLASS(ForceCaseAttribute, Attribute) // `[forcecase]` +SIMPLE_SYNTAX_CLASS(CallAttribute, Attribute) // `[call]` + +// TODO: for attributes that take arguments, the syntax node +// classes should provide accessors for the values of those arguments. + +SIMPLE_SYNTAX_CLASS(MaxTessFactorAttribute, Attribute) +SIMPLE_SYNTAX_CLASS(OutputControlPointsAttribute, Attribute) +SIMPLE_SYNTAX_CLASS(OuptutTopologyAttribute, Attribute) +SIMPLE_SYNTAX_CLASS(PartitioningAttribute, Attribute) +SIMPLE_SYNTAX_CLASS(PatchConstantFuncAttribute, Attribute) +SIMPLE_SYNTAX_CLASS(DomainAttribute, Attribute) + +SIMPLE_SYNTAX_CLASS(EarlyDepthStencilAttribute, Attribute) // An HLSL `[numthreads(x,y,z)]` attribute -SYNTAX_CLASS(HLSLNumThreadsAttribute, HLSLAttribute) +SYNTAX_CLASS(NumThreadsAttribute, Attribute) // The number of threads to use along each axis + // + // TODO: These should be accessors that use the + // ordinary `args` list, rather than side data. FIELD(int32_t, x) FIELD(int32_t, y) FIELD(int32_t, z) END_SYNTAX_CLASS() -SYNTAX_CLASS(HLSLMaxVertexCountAttribute, HLSLAttribute) +SYNTAX_CLASS(MaxVertexCountAttribute, Attribute) // The number of max vertex count for geometry shader + // + // TODO: This should be an accessor that uses the + // ordinary `args` list, rather than side data. FIELD(int32_t, value) END_SYNTAX_CLASS() -SYNTAX_CLASS(HLSLInstanceAttribute, HLSLAttribute) +SYNTAX_CLASS(InstanceAttribute, Attribute) // The number of instances to run for geometry shader + // + // TODO: This should be an accessor that uses the + // ordinary `args` list, rather than side data. FIELD(int32_t, value) END_SYNTAX_CLASS() +// A `[shader("stageName")]` attribute, which marks an entry point +// to be compiled, and specifies the stage for that entry point +SYNTAX_CLASS(EntryPointAttribute, Attribute) + // The resolved stage that the entry point is targetting. + // + // TODO: This should be an accessor that uses the + // ordinary `args` list, rather than side data. + FIELD(Stage, stage); +END_SYNTAX_CLASS() + // HLSL modifiers for geometry shader input topology SIMPLE_SYNTAX_CLASS(HLSLGeometryShaderInputPrimitiveTypeModifier, Modifier) SIMPLE_SYNTAX_CLASS(HLSLPointModifier , HLSLGeometryShaderInputPrimitiveTypeModifier) diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 82f79af17..5fb7445e9 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -602,10 +602,20 @@ namespace Slang parser->ReadToken(TokenType::LBracket); for(;;) { + // Note: When parsing we just construct an AST node for an + // "unchecked" attribute, and defer all detailed semantic + // checking until later. + // + // An alternative would be to perform lookup of an `AttributeDecl` + // at this point, similar to what we do for `SyntaxDecl`, but it + // seems better to not complicate the parsing process any more. + // + auto nameToken = parser->ReadToken(TokenType::Identifier); - RefPtr<HLSLUncheckedAttribute> modifier = new HLSLUncheckedAttribute(); + RefPtr<UncheckedAttribute> modifier = new UncheckedAttribute(); modifier->name = nameToken.getName(); modifier->loc = nameToken.getLoc(); + modifier->scope = parser->currentScope; if (AdvanceIf(parser, TokenType::LParent)) { @@ -2500,6 +2510,107 @@ namespace Slang return syntaxDecl; } + // A parameter declaration in an attribute declaration. + // + // We are going to use `name: type` syntax just for simplicty, and let the type + // be optional, because we don't actually need it in all cases. + // + static RefPtr<ParamDecl> parseAttributeParamDecl(Parser* parser) + { + auto nameAndLoc = expectIdentifier(parser); + + RefPtr<ParamDecl> paramDecl = new ParamDecl(); + paramDecl->nameAndLoc = nameAndLoc; + + if(AdvanceIf(parser, TokenType::Colon)) + { + paramDecl->type = parser->ParseTypeExp(); + } + + if(AdvanceIf(parser, TokenType::OpAssign)) + { + paramDecl->initExpr = parser->ParseInitExpr(); + } + + return paramDecl; + } + + // Parse declaration of a name to be used for resolving `[attribute(...)]` style modifiers. + // + // These are distinct from `syntax` declarations, because their names don't get added + // to the current scope using their default name. + // + // Also, attribute-specific code doesn't get invokved during parsing. We always parse + // using the default attribute-parsing logic and then all specialized behavior takes + // place during semantic checking. + // + static RefPtr<RefObject> parseAttributeSyntaxDecl(Parser* parser, void* /*userData*/) + { + // Right now the basic form is: + // + // attribute_syntax <name:id> : <syntaxClass:id>; + // + // - `name` gives the name of the attribute to define. + // - `syntaxClass` is the name of an AST node class that we expect + // this attribute to create when checked. + // - `existingKeyword` is the name of an existing keyword that + // the new syntax should be an alias for. + + expect(parser, TokenType::LBracket); + + // First we parse the attribute name. + auto nameAndLoc = expectIdentifier(parser); + + RefPtr<AttributeDecl> attrDecl = new AttributeDecl(); + if(AdvanceIf(parser, TokenType::LParent)) + { + while(!AdvanceIfMatch(parser, TokenType::RParent)) + { + auto param = parseAttributeParamDecl(parser); + + AddMember(attrDecl, param); + + if(AdvanceIfMatch(parser, TokenType::RParent)) + break; + + expect(parser, TokenType::Comma); + } + } + + expect(parser, TokenType::RBracket); + + // TODO: we should allow parameters to be specified here, to cut down + // on the amount of per-attribute-type logic that has to occur later. + + // Next we look for a clause that specified the AST node class. + SyntaxClass<RefObject> syntaxClass; + if (AdvanceIf(parser, TokenType::Colon)) + { + // User is specifying the class that should be construted + auto classNameAndLoc = expectIdentifier(parser); + + syntaxClass = parser->getSession()->findSyntaxClass(classNameAndLoc.name); + } + else + { + // For now we don't support the alternative approach where + // an existing piece of syntax is named to provide the parsing + // support. + + // TODO: diagnose: a syntax class must be specified. + } + + expect(parser, TokenType::Semicolon); + + // TODO: skip creating the declaration if anything failed, just to not screw things + // up for downstream code? + + attrDecl->nameAndLoc = nameAndLoc; + attrDecl->loc = nameAndLoc.loc; + attrDecl->syntaxClass = syntaxClass; + return attrDecl; + } + // Finish up work on a declaration that was parsed static void CompleteDecl( Parser* /*parser*/, @@ -4182,6 +4293,20 @@ namespace Slang return modifier; } + static RefPtr<RefObject> parseAttributeTargetModifier(Parser* parser, void* /*userData*/) + { + expect(parser, TokenType::LParent); + auto syntaxClassNameAndLoc = expectIdentifier(parser); + expect(parser, TokenType::RParent); + + auto syntaxClass = parser->getSession()->findSyntaxClass(syntaxClassNameAndLoc.name); + + RefPtr<AttributeTargetModifier> modifier = new AttributeTargetModifier(); + modifier->syntaxClass = syntaxClass; + + return modifier; + } + RefPtr<ModuleDecl> populateBaseLanguageModule( Session* session, RefPtr<Scope> scope) @@ -4205,6 +4330,7 @@ namespace Slang DECL(__subscript, ParseSubscriptDecl); DECL(interface, parseInterfaceDecl); DECL(syntax, parseSyntaxDecl); + DECL(attribute_syntax,parseAttributeSyntaxDecl); DECL(__import, parseImportDecl); DECL(import, parseImportDecl); @@ -4280,6 +4406,9 @@ namespace Slang MODIFIER(__intrinsic_type, parseIntrinsicTypeModifier); MODIFIER(__implicit_conversion, parseImplicitConversionModifier); + MODIFIER(__attributeTarget, parseAttributeTargetModifier); + + #undef MODIFIER // Add syntax for expression keywords diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp index 708b98f2b..65901d6af 100644 --- a/source/slang/reflection.cpp +++ b/source/slang/reflection.cpp @@ -961,7 +961,7 @@ SLANG_API void spReflectionEntryPoint_getComputeThreadGroupSize( SlangUInt sizeAlongAxis[3] = { 1, 1, 1 }; // First look for the HLSL case, where we have an attribute attached to the entry point function - auto numThreadsAttribute = entryPointFunc->FindModifier<HLSLNumThreadsAttribute>(); + auto numThreadsAttribute = entryPointFunc->FindModifier<NumThreadsAttribute>(); if (numThreadsAttribute) { sizeAlongAxis[0] = numThreadsAttribute->x; diff --git a/source/slang/syntax-base-defs.h b/source/slang/syntax-base-defs.h index 2c15f7215..4fded014e 100644 --- a/source/slang/syntax-base-defs.h +++ b/source/slang/syntax-base-defs.h @@ -240,7 +240,7 @@ END_SYNTAX_CLASS() // (that is, we don't use a bitfield, even for simple/common flags). // This ensures that we can track source locations for all modifiers. // -ABSTRACT_SYNTAX_CLASS(Modifier, SyntaxNodeBase) +ABSTRACT_SYNTAX_CLASS(Modifier, SyntaxNode) RAW(typedef IModifierVisitor Visitor;) RAW(virtual void accept(IModifierVisitor* visitor, void* extra) = 0;) diff --git a/source/slang/syntax.h b/source/slang/syntax.h index 0dbdd8238..0f23492d6 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -805,8 +805,9 @@ namespace Slang type = 0x1, Function = 0x2, Value = 0x4, + Attribute = 0x8, - All = type | Function | Value, + Default = type | Function | Value, }; // Represents one item found during lookup @@ -1000,7 +1001,7 @@ namespace Slang RefPtr<Scope> scope = nullptr; RefPtr<Scope> endScope = nullptr; - LookupMask mask = LookupMask::All; + LookupMask mask = LookupMask::Default; }; // Generate class definition for all syntax classes |
