diff options
Diffstat (limited to 'source/slang/check.cpp')
| -rw-r--r-- | source/slang/check.cpp | 176 |
1 files changed, 168 insertions, 8 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 046587d0f..1fa7f9b01 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1845,6 +1845,38 @@ namespace Slang return Stage::Unknown; } + bool hasIntArgs(Attribute* attr, int numArgs) + { + if (int(attr->args.Count()) != numArgs) + { + return false; + } + for (int i = 0; i < numArgs; ++i) + { + if (!attr->args[i]->As<IntegerLiteralExpr>()) + { + return false; + } + } + return true; + } + + bool hasStringArgs(Attribute* attr, int numArgs) + { + if (int(attr->args.Count()) != numArgs) + { + return false; + } + for (int i = 0; i < numArgs; ++i) + { + if (!attr->args[i]->As<StringLiteralExpr>()) + { + return false; + } + } + return true; + } + bool validateAttribute(RefPtr<Attribute> attr) { if(auto numThreadsAttr = attr.As<NumThreadsAttribute>()) @@ -1898,7 +1930,27 @@ namespace Slang entryPointAttr->stage = stage; } - else + else if ((attr.As<DomainAttribute>()) || + (attr.As<MaxTessFactorAttribute>()) || + (attr.As<OutputTopologyAttribute>()) || + (attr.As<PartitioningAttribute>()) || + (attr.As<PatchConstantFuncAttribute>())) + { + // Let it go thru iff single string attribute + if (!hasStringArgs(attr, 1)) + { + getSink()->diagnose(attr, Diagnostics::expectedSingleStringArg, attr->name); + } + } + else if (attr.As<OutputControlPointsAttribute>()) + { + // Let it go thru iff single integral attribute + if (!hasIntArgs(attr, 1)) + { + getSink()->diagnose(attr, Diagnostics::expectedSingleIntArg, attr->name); + } + } + else { if(attr->args.Count() == 0) { @@ -8231,18 +8283,89 @@ namespace Slang return type; } + + FuncDecl* findFunctionDeclByName(EntryPointRequest* entryPoint, Name* name) + { + auto translationUnit = entryPoint->getTranslationUnit(); + auto sink = &entryPoint->compileRequest->mSink; + auto translationUnitSyntax = translationUnit->SyntaxNode; + + // Make sure we've got a query-able member dictionary + buildMemberDictionary(translationUnitSyntax); + + // We will look up any global-scope declarations in the translation + // unit that match the name of our entry point. + Decl* firstDeclWithName = nullptr; + if (!translationUnitSyntax->memberDictionary.TryGetValue(name, firstDeclWithName)) + { + // If there doesn't appear to be any such declaration, then we are done. + + sink->diagnose(translationUnitSyntax, Diagnostics::entryPointFunctionNotFound, name); + + return nullptr; + } + + // We found at least one global-scope declaration with the right name, + // but (1) it might not be a function, and (2) there might be + // more than one function. + // + // We'll walk the linked list of declarations with the same name, + // to see what we find. Along the way we'll keep track of the + // first function declaration we find, if any: + FuncDecl* entryPointFuncDecl = nullptr; + for (auto ee = firstDeclWithName; ee; ee = ee->nextInContainerWithSameName) + { + // Is this declaration a function? + if (auto funcDecl = dynamic_cast<FuncDecl*>(ee)) + { + // Skip non-primary declarations, so that + // we don't give an error when an entry + // point is forward-declared. + if (!isPrimaryDecl(funcDecl)) + continue; + + // is this the first one we've seen? + if (!entryPointFuncDecl) + { + // If so, this is a candidate to be + // the entry point function. + entryPointFuncDecl = funcDecl; + } + else + { + // Uh-oh! We've already seen a function declaration with this + // name before, so the whole thing is ambiguous. We need + // to diagnose and bail out. + + sink->diagnose(translationUnitSyntax, Diagnostics::ambiguousEntryPoint, name); + + // List all of the declarations that the user *might* mean + for (auto ff = firstDeclWithName; ff; ff = ff->nextInContainerWithSameName) + { + if (auto candidate = dynamic_cast<FuncDecl*>(ff)) + { + sink->diagnose(candidate, Diagnostics::entryPointCandidate, candidate->getName()); + } + } + + // Bail out. + return nullptr; + } + } + } + + return entryPointFuncDecl; + } + // Validate that an entry point function conforms to any additional // constraints based on the stage (and profile?) it specifies. void validateEntryPoint( - EntryPointRequest* /*entryPoint*/) + EntryPointRequest* entryPoint) { - // TODO: We currently don't do any checking here, but this is the + // TODO: We currently do minimal checking here, but this is the // right place to perform the following validation checks: // - // * Does the entry point specify all of the attributes required - // by the chosen stage (e.g., a `[domain(...)]` attribute for] - // a hull shader. - // + // * Are the function input/output parameters and result type // all valid for the chosen stage? (e.g., there shouldn't be // an `OutputStream<X>` type in a vertex shader signature) @@ -8263,6 +8386,43 @@ namespace Slang // `Texture2D.Sample`, then that should produce an error because // that function is specific to the fragment profile/stage. // + + if (entryPoint->getStage() == Stage::Hull) + { + auto translationUnit = entryPoint->getTranslationUnit(); + auto sink = &entryPoint->compileRequest->mSink; + auto translationUnitSyntax = translationUnit->SyntaxNode; + + auto attr = entryPoint->decl->FindModifier<PatchConstantFuncAttribute>(); + + if (attr) + { + if (attr->args.Count() != 1) + { + sink->diagnose(translationUnitSyntax, Diagnostics::badlyDefinedPatchConstantFunc, entryPoint->name); + return; + } + + Expr* expr = attr->args[0]; + StringLiteralExpr* stringLit = expr->As<StringLiteralExpr>(); + + if (!stringLit) + { + sink->diagnose(translationUnitSyntax, Diagnostics::badlyDefinedPatchConstantFunc, entryPoint->name); + return; + } + + Name* name = entryPoint->compileRequest->getNamePool()->getName(stringLit->value); + FuncDecl* funcDecl = findFunctionDeclByName(entryPoint, name); + if (!funcDecl) + { + sink->diagnose(translationUnitSyntax, Diagnostics::attributeFunctionNotFound, name, "patchconstantfunc"); + return; + } + + attr->patchConstantFuncDecl = funcDecl; + } + } } // Given an `EntryPointRequest` specified via API or command line options, @@ -8445,7 +8605,7 @@ namespace Slang // set to `Batman` to know whether the setting for `B` is valid. In this limit // the constraints can be mutually recursive (so `A : IMentor<B>`). // - // The only way to check things corectly is to validate each conformance under + // The only way to check things correctly is to validate each conformance under // a set of assumptions (substitutions) that includes all the type substitutions, // and possibly also all the other constraints *except* the one to be validated. // |
