summaryrefslogtreecommitdiffstats
path: root/source/slang/check.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/check.cpp')
-rw-r--r--source/slang/check.cpp263
1 files changed, 205 insertions, 58 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);
+ }
}
}