From 60cc9f24c4bec54561bea873ee943aa3d0973dc2 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Tue, 5 Feb 2019 16:47:25 -0800 Subject: Allow entry points to have explicit generic parameters (#826) * Allow entry points to have explicit generic parameters Prior to this change, the Slang implementation required users to use global `type_param` declarations in order to specialize a full shader. For example: ```hlsl type_param L : ILight; ParameterBlock gLight; [shader("fragment")] float4 fs(...) { ... gLight.doSomething() ... } ``` With this change we can rewrite code like the above using explicit generics, plus the ability to have `uniform` entry-point parameters: ```hlsl [shader("fragment")] float4 fs( uniform ParameterBlock light, ...) { ... light.doSomething() ... } ``` Having this support in place should make it possible for us to eliminate global generic type parameters and the complications they cause (both at a conceptual and implementation level). The most central and visible piece of the change is that `EntryPointRequest` now holds a `DeclRef` instead of just ` RefPtr`, which allows it to refer to a specialization of a generic function. Various places in the code that refer to the `EntryPointRequest::decl` member now use a `getFuncDecl()` or `getFuncDeclRef()` method as appropriate (see `compiler.h`). In order to fill in the new data, the `findAndValidateEntryPoint` function has been greaterly overhauled. The changes to its operation include: * The by-name lookup step for the entry point function has been adapted to accept either a function or a generic function. * The generic argument strings provided by API or command line are no longer parsed all the way to `Type`s, but instead just to `Expr`s in the first pass. * There are now two cases for checking the global generic arguments against their matching parameters. The first case is the new one, where we plug the generic argument `Expr`s into the explicit generic parameters of an entry point (that case re-uses existing semantic checking logic). The second case is the pre-existing code for dealing with global generic type arguments. The `lower-to-ir.cpp` logic for hadling entry points then had to be extended. Making it deal with a full `DeclRef` instead of just a `Decl` was the easy part (just call `emitDeclRef` instead of `ensureDecl`). The more interesting bits were: * We need to carefully add the `IREntryPointDecoration` to the nested function and not the generic in the case where we have a generic entry point. There is a handy `getResolvedInstForDecorations` that can extract the return value for an IR generic so that we can decorate the right hting. * We need to make sure that in the case where we emit a `specialize` instruction (which normally wouldn't get a linkage decoration), we attach an `[export(...)]` decoration to it with the mangled name of the decl-ref, so that it can be found during the linking step. The IR linking step is then slightly more complicated because the mangled entry point name could either refer directly to an `IRFunc` or to a `specialize` instruction for a generic entry point. The logic was refactored to first clone the entry point symbol without concern for which case it is (the old code was specific to functions), and then *if* the result is a `specialize` instruction, we attempt to run generic specialization on-demand. That on-demand specialization is a bit of a kludge, but it deals with the fact that all the downstream passing only expect to see an `IRFunc`. A future cleanup might try to split out that specialization step into its own pass, which ends up being a limited form of the specialization pass. Since I was already having to touch a lot of the code around IR linking, I went ahead and refactored the signature of the operations. I eliminated the need for the caller to create, pass in, and then destroy an `IRSpecializationState` (really an IR *linking* state), and replaced it with a structure local to the pass (that data structure was a remnant of an older approach in the compiler), and then also renamed the main operation to `linkIR` to reflect what it is doing in our conceptual flow. Smaller changes made along the way include: * Refactored `visitGenericAppExpr` to create a subroutine `checkGenericAppWithCheckedArgs` so that it can be used by the entry-point validation logic described above). * Refactored the declarations around the IR passes in `emitEntryPoint()` (`emit.cpp`), to show that things are more self-contained than they used to be (e.g., that the `TypeLegalizationContext` is now only needed by one pass). * Refactored the generic specialization code so that there is a stand-along free function that can perform specialization on a `specialize` instruction without all the other context being required. This is only to support the limited specialization that needs to be done as part of linking. * Updated the `global-type-param.slang` test to actually test entry-point generic parameters. In a later pass we can/should rework all the tests/examples for global type parameters over to use explicit entry-point generic parameters (at which point we should rename the tests as well). For now I am leaving thigns with just one test case, with the expectation that bugs will be found and ironed out as we expand to more tests. * fixup * Fixup: don't leave entry-point decorations on stuff we don't want to keep The IR `[entryPoint]` decoration is effectively a "keep this alive" decoration, which means that attaching it to something we don't intend to keep around can lead to Bad Things. The approach to generic entry points was attaching `[entryPoint]` to the underlying `IRFunc` because that seemed to make sense, but that meant that the `specialize` instruction at global scope scould instantiate that generic and then keep it alive, even if the resulting function wouldn't be valid according to the language rules. As a quick fix, I'm attaching `[entryPoint]` to the `specialize` instruction instead in such cases, and then re-attaching it to the result of explicit specialization during linking. * Port most of remaining test and rename global type parameters This change ports as many as possible of the existing tests for global type parameters over to use entry-point generic parameters instead. For the most part this is a mechanical change. A few test cases remain using global generic parameters, as does the `model-viewer` example application. The reason for this is that the shaders have either or both the following features: * A vertex and fragment shader that can/shold agree on their parameters * A type declaration (e.g., a `struct`) that is dependent on one of the generic type parameters In these cases, it would really only make sense to switch to explicit parameters once we support shader entry points nested inside of a `struct` type, so that we can use an outer generic `struct` as a mechanism to scope the entry points and other type-dependent declrations. Since global-scope type parameters need to persist for at least a bit longer, I went ahead and renamed all the use sites over to use `type_param` for consistency. --- source/slang/check.cpp | 434 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 290 insertions(+), 144 deletions(-) (limited to 'source/slang/check.cpp') diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 921ef61e6..c3f10fb4c 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -8326,12 +8326,8 @@ namespace Slang } } - RefPtr visitGenericAppExpr(GenericAppExpr * genericAppExpr) + RefPtr visitGenericAppExpr(GenericAppExpr* genericAppExpr) { - // We are applying a generic to arguments, but there might be multiple generic - // declarations with the same name, so this becomes a specialized case of - // overload resolution. - // Start by checking the base expression and arguments. auto& baseExpr = genericAppExpr->FunctionExpr; baseExpr = CheckTerm(baseExpr); @@ -8341,6 +8337,19 @@ namespace Slang arg = CheckTerm(arg); } + return checkGenericAppWithCheckedArgs(genericAppExpr); + } + + /// Check a generic application where the operands have already been checked. + RefPtr checkGenericAppWithCheckedArgs(GenericAppExpr* genericAppExpr) + { + // We are applying a generic to arguments, but there might be multiple generic + // declarations with the same name, so this becomes a specialized case of + // overload resolution. + + auto& baseExpr = genericAppExpr->FunctionExpr; + auto& args = genericAppExpr->Arguments; + // If there was an error in the base expression, or in any of // the arguments, then just bail. if (IsErrorExpr(baseExpr)) @@ -9228,7 +9237,7 @@ namespace Slang // if( entryPoint->getStage() == Stage::Unknown ) { - sink->diagnose(entryPoint->decl, Diagnostics::entryPointHasNoStage, entryPoint->name); + sink->diagnose(entryPoint->getFuncDecl(), Diagnostics::entryPointHasNoStage, entryPoint->name); } if (entryPoint->getStage() == Stage::Hull) @@ -9236,7 +9245,7 @@ namespace Slang auto translationUnit = entryPoint->getTranslationUnit(); auto translationUnitSyntax = translationUnit->SyntaxNode; - auto attr = entryPoint->decl->FindModifier(); + auto attr = entryPoint->getFuncDecl()->FindModifier(); if (attr) { @@ -9278,6 +9287,11 @@ namespace Slang { // The first step in validating the entry point is to find // the (unique) function declaration that matches its name. + // + // TODO: We will eventually need to update this logic + // to work by parsing the provided `entryPoint->name` string + // as an expression, so that we can handle more complex + // names like `foo` or `SomeType.vs`. auto translationUnit = entryPoint->getTranslationUnit(); auto sink = &entryPoint->compileRequest->mSink; @@ -9305,11 +9319,20 @@ namespace Slang // 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) { + // We want to support the case where the declaration is + // a generic function, so we will automatically + // unwrap any outer `GenericDecl` we find here. + // + auto decl = ee; + if(auto genericDecl = as(decl)) + decl = genericDecl->inner; + // Is this declaration a function? - if (auto funcDecl = as(ee)) + if (auto funcDecl = as(decl)) { // Skip non-primary declarations, so that // we don't give an error when an entry @@ -9381,34 +9404,65 @@ namespace Slang // Phew, we have at least found a suitable decl. // Let's record that in the entry-point request so // that we don't have to re-do this effort again later. - entryPoint->decl = entryPointFuncDecl; + // + // Note: we may replace the decl-ref we store at this point + // later in this function, when we (potentially) specialize + // a generic entry point to generic arguments provided + // via the API. + // + entryPoint->funcDeclRef = makeDeclRef(entryPointFuncDecl); - // Lookup generic parameter types in global scope + // If the user specified generic arguments for the entry point, + // then we will want to parse those arguments as expressions + // in a scope that includes the tanslation unit that holds + // the entry point, as well as any other modules that got + // transitively loaded via `import`. + // + // TODO: This would be better handled by giving the user + // more explicit ways to parse/build types at the API level, + // rather than keeping things string-based this far along. + // + // TODO: Building a list of `scopesToTry` here shouldn't + // be required, since the `Scope` type itself has the ability + // for form chains for lookup purposes (e.g., the way that + // `import` is handled by modifying a scope). + // List> scopesToTry; scopesToTry.Add(entryPoint->getTranslationUnit()->SyntaxNode->scope); for (auto & module : entryPoint->compileRequest->loadedModulesList) scopesToTry.Add(module->moduleDecl->scope); - List> globalGenericArgs; + // We are going to do some semantic checking, so we need to + // set up a `SemanticsVistitor` that we can use. + // + SemanticsVisitor semantics( + &entryPoint->compileRequest->mSink, + entryPoint->compileRequest, + entryPoint->getTranslationUnit()); + + // We will be looping over the generic argument strings + // that the user provided via the API (or command line), + // and parsing+checking each into an `Expr`. + // + // This loop will *not* handle coercing the arguments + // to be types. + // + List> genericArgs; for (auto name : entryPoint->genericArgStrings) { - // parse type name - RefPtr type; + RefPtr argExpr; for (auto & s : scopesToTry) { - RefPtr typeExpr = entryPoint->compileRequest->parseTypeString(entryPoint->getTranslationUnit(), - name, s); - type = checkProperType(translationUnit, TypeExp(typeExpr)); - if (type) + argExpr = entryPoint->compileRequest->parseTypeString( + entryPoint->getTranslationUnit(), + name, + s); + argExpr = semantics.CheckTerm(argExpr); + if( argExpr ) { break; } } - if (!type) - { - sink->diagnose(firstDeclWithName, Diagnostics::entryPointTypeSymbolNotAType, name); - return; - } // The following is a bit of a hack. // @@ -9424,165 +9478,257 @@ namespace Slang // as a (top-level) argument for a generic type parameter, so that we // can check for them here and cache them on the entry point request. // - if( auto taggedUnionType = as(type) ) + if( auto typeType = as(argExpr->type) ) { - entryPoint->taggedUnionTypes.Add(taggedUnionType); + auto type = typeType->type; + if( auto taggedUnionType = as(type) ) + { + entryPoint->taggedUnionTypes.Add(taggedUnionType); + } } - globalGenericArgs.Add(type); + genericArgs.Add(argExpr); } - // validate global type arguments only when we are generating code - if ((entryPoint->compileRequest->compileFlags & SLANG_COMPILE_FLAG_NO_CODEGEN) == 0) + // There are two cases we care about here, and we are going to treat them + // as mutually exclusive for simplicity. + // + // The first case is when the entry point function is itself generic, + // in which case we will assume that `genericArgs` lines up one-to-one + // with the explicit generic parameters of the entry point. + // + if( auto genericDecl = as(entryPointFuncDecl->ParentDecl) ) { - // check that user-provided type arguments conforms to the generic type - // parameter declaration of this translation unit - - // collect global generic parameters from all imported modules - List> globalGenericParams; - // add current translation unit first - { - auto globalGenParams = translationUnit->SyntaxNode->getMembersOfType(); - for (auto p : globalGenParams) - globalGenericParams.Add(p); - } - // add imported modules - for (auto loadedModule : entryPoint->compileRequest->loadedModulesList) - { - auto moduleDecl = loadedModule->moduleDecl; - auto globalGenParams = moduleDecl->getMembersOfType(); - for (auto p : globalGenParams) - globalGenericParams.Add(p); - } + // We will construct a suitable `GenericAppExpr` to represent + // the user-specified `genericDecl` being applied to the + // supplied `genericArgs`, and then use the existing + // semantic checking logic that would apply to an explicit + // generic application like `F` if it were + // encountered in the source code. - if (globalGenericParams.Count() != globalGenericArgs.Count()) - { - sink->diagnose(entryPoint->decl, Diagnostics::mismatchEntryPointTypeArgument, - globalGenericParams.Count(), - globalGenericArgs.Count()); - return; - } + auto session = entryPoint->compileRequest->mSession; + auto genericDeclRef = makeDeclRef(genericDecl); - // We have an appropriate number of arguments for the global generic parameters, - // and now we need to check that the arguments conform to the declared constraints. + // The first pieces is a `VarExpr` that refers to `genericDecl`. // - // Along the way, we will build up an appropriate set of substitutions to represent - // the generic arguments and their conformances. + // TODO: This would not be needed if we instead parsed + // the supplied entry-point name into an expression + // earlier in this function. // - RefPtr globalGenericSubsts; - auto globalGenericSubstLink = &globalGenericSubsts; - // - // TODO: There is a serious flaw to this checking logic if we ever have cases where - // the constraints on one `type_param` can depend on another `type_param`, e.g.: + RefPtr genericExpr = new VarExpr(); + genericExpr->declRef = genericDeclRef; + genericExpr->type.type = getTypeForDeclRef(session, genericDeclRef); + + // Next we construct the actual `GenericAppExpr` // - // type_param A; - // type_param B : ISidekick; + RefPtr genericAppExpr = new GenericAppExpr(); + genericAppExpr->FunctionExpr = genericExpr; + genericAppExpr->Arguments = genericArgs; + + // We use the semantics visitor to perform the + // actual checking logic (this might report + // errors) // - // In that case, if a user tries to set `B` to `Robin` and `Robin` conforms to - // `ISidekick`, then the compiler needs to know whether `A` is being - // set to `Batman` to know whether the setting for `B` is valid. In this limit - // the constraints can be mutually recursive (so `A : IMentor`). + auto checkedExpr = semantics.checkGenericAppWithCheckedArgs(genericAppExpr); + + // Now we need to extract an appropriate decl-ref for the entry + // point from the `checkedExpr`. // - // 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. + if( auto declRefExpr = checkedExpr.as() ) + { + // TODO: We should eventually check for the case + // where we have a `MemberExpr` or another case of + // `DeclRefExpr` that cannot be summarized as just + // its decl-ref. + // + // The basic `VarExpr` and `StaticMemberExpr` cases + // should be allow-able. + + entryPoint->funcDeclRef = declRefExpr->declRef.as(); + } + else if( semantics.IsErrorExpr(checkedExpr) ) + { + // Any semantic error that occured should have been + // reported already. + } + else + { + // The result of specializing a reference to a generic + // function should always be a `DeclRefExpr` + // + SLANG_UNEXPECTED("reference to generic decl wasn't a `DeclRefExpr`"); + } + } + else + { + // The other case is when the entry point function is *not* itself + // generic, so we assume that any generic arguments must have been intended + // to match up with global generic parameters instead. // - // We will punt on this for now, and just check each constraint in isolation. + // We will only validate global generic type arguments when we are going + // to generate code, since in a no-codegen pass we will typically *not* + // have arguments to associate with the parameters. // - UInt argCounter = 0; - for(auto& globalGenericParam : globalGenericParams) + if ((entryPoint->compileRequest->compileFlags & SLANG_COMPILE_FLAG_NO_CODEGEN) == 0) { - // Get the argument that matches this parameter. - UInt argIndex = argCounter++; - SLANG_ASSERT(argIndex < globalGenericArgs.Count()); - auto globalGenericArg = globalGenericArgs[argIndex]; + // check that user-provioded type arguments conforms to the generic type + // parameter declaration of this translation unit - // As a quick sanity check, see if the argument that is being supplied for a parameter - // is just the parameter itself, because this should always be an error: - // - if( auto argDeclRefType = as(globalGenericArg) ) + // collect global generic parameters from all imported modules + List> globalGenericParams; + // add current translation unit first + { + auto globalGenParams = translationUnit->SyntaxNode->getMembersOfType(); + for (auto p : globalGenParams) + globalGenericParams.Add(p); + } + // add imported modules + for (auto loadedModule : entryPoint->compileRequest->loadedModulesList) + { + auto moduleDecl = loadedModule->moduleDecl; + auto globalGenParams = moduleDecl->getMembersOfType(); + for (auto p : globalGenParams) + globalGenericParams.Add(p); + } + + if (globalGenericParams.Count() != genericArgs.Count()) { - auto argDeclRef = argDeclRefType->declRef; - if(auto argGenericParamDeclRef = argDeclRef.as()) + sink->diagnose(entryPoint->getFuncDecl(), Diagnostics::mismatchEntryPointTypeArgument, + globalGenericParams.Count(), + genericArgs.Count()); + return; + } + + // We have an appropriate number of arguments for the global generic parameters, + // and now we need to check that the arguments conform to the declared constraints. + // + // Along the way, we will build up an appropriate set of substitutions to represent + // the generic arguments and their conformances. + // + RefPtr globalGenericSubsts; + auto globalGenericSubstLink = &globalGenericSubsts; + // + // TODO: There is a serious flaw to this checking logic if we ever have cases where + // the constraints on one `type_param` can depend on another `type_param`, e.g.: + // + // type_param A; + // type_param B : ISidekick; + // + // In that case, if a user tries to set `B` to `Robin` and `Robin` conforms to + // `ISidekick`, then the compiler needs to know whether `A` is being + // set to `Batman` to know whether the setting for `B` is valid. In this limit + // the constraints can be mutually recursive (so `A : IMentor`). + // + // 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. + // + // We will punt on this for now, and just check each constraint in isolation. + // + UInt argCounter = 0; + for(auto& globalGenericParam : globalGenericParams) + { + // Get the argument that matches this parameter. + UInt argIndex = argCounter++; + SLANG_ASSERT(argIndex < genericArgs.Count()); + auto globalGenericArg = checkProperType(translationUnit, TypeExp(genericArgs[argIndex])); + if (!globalGenericArg) { - if(argGenericParamDeclRef.getDecl() == globalGenericParam) - { - // We are trying to specialize a generic parameter using itself. - sink->diagnose(globalGenericParam, - Diagnostics::cannotSpecializeGlobalGenericToItself, - globalGenericParam->getName()); - sink->diagnose(entryPointFuncDecl, - Diagnostics::noteWhenCompilingEntryPoint, - entryPointFuncDecl->getName()); - continue; - } - else + sink->diagnose(firstDeclWithName, Diagnostics::entryPointTypeSymbolNotAType, entryPoint->genericArgStrings[argIndex]); + return; + } + + // As a quick sanity check, see if the argument that is being supplied for a parameter + // is just the parameter itself, because this should always be an error: + // + if( auto argDeclRefType = globalGenericArg.as() ) + { + auto argDeclRef = argDeclRefType->declRef; + if(auto argGenericParamDeclRef = argDeclRef.as()) { - // We are trying to specialize a generic parameter using a *different* - // global generic type parameter. - sink->diagnose(globalGenericParam, - Diagnostics::cannotSpecializeGlobalGenericToAnotherGenericParam, - globalGenericParam->getName(), - argGenericParamDeclRef.GetName()); - sink->diagnose(entryPointFuncDecl, - Diagnostics::noteWhenCompilingEntryPoint, - entryPointFuncDecl->getName()); - continue; + if(argGenericParamDeclRef.getDecl() == globalGenericParam) + { + // We are trying to specialize a generic parameter using itself. + sink->diagnose(globalGenericParam, + Diagnostics::cannotSpecializeGlobalGenericToItself, + globalGenericParam->getName()); + sink->diagnose(entryPointFuncDecl, + Diagnostics::noteWhenCompilingEntryPoint, + entryPointFuncDecl->getName()); + continue; + } + else + { + // We are trying to specialize a generic parameter using a *different* + // global generic type parameter. + sink->diagnose(globalGenericParam, + Diagnostics::cannotSpecializeGlobalGenericToAnotherGenericParam, + globalGenericParam->getName(), + argGenericParamDeclRef.GetName()); + sink->diagnose(entryPointFuncDecl, + Diagnostics::noteWhenCompilingEntryPoint, + entryPointFuncDecl->getName()); + continue; + } } } - } + // Create a substitution for this parameter/argument. + RefPtr subst = new GlobalGenericParamSubstitution(); + subst->paramDecl = globalGenericParam; + subst->actualType = globalGenericArg; - // Create a substitution for this parameter/argument. - RefPtr subst = new GlobalGenericParamSubstitution(); - subst->paramDecl = globalGenericParam; - subst->actualType = globalGenericArg; + // Walk through the declared constraints for the parameter, + // and check that the argument actually satisfies them. + for(auto constraint : globalGenericParam->getMembersOfType()) + { + // Get the type that the constraint is enforcing conformance to + auto interfaceType = GetSup(DeclRef(constraint, nullptr)); - // Walk through the declared constraints for the parameter, - // and check that the argument actually satisfies them. - for(auto constraint : globalGenericParam->getMembersOfType()) - { - // Get the type that the constraint is enforcing conformance to - auto interfaceType = GetSup(DeclRef(constraint, nullptr)); + // Use our semantic-checking logic to search for a witness to the required conformance + SemanticsVisitor visitor(sink, entryPoint->compileRequest, translationUnit); + auto witness = visitor.tryGetSubtypeWitness(globalGenericArg, interfaceType); + if (!witness) + { + // If no witness was found, then we will be unable to satisfy + // the conformances required. + sink->diagnose(globalGenericParam, + Diagnostics::typeArgumentDoesNotConformToInterface, + globalGenericParam->nameAndLoc.name, + globalGenericArg, + interfaceType); + } - // Use our semantic-checking logic to search for a witness to the required conformance - SemanticsVisitor visitor(sink, entryPoint->compileRequest, translationUnit); - auto witness = visitor.tryGetSubtypeWitness(globalGenericArg, interfaceType); - if (!witness) - { - // If no witness was found, then we will be unable to satisfy - // the conformances required. - sink->diagnose(globalGenericParam, - Diagnostics::typeArgumentDoesNotConformToInterface, - globalGenericParam->nameAndLoc.name, - globalGenericArg, - interfaceType); + // Attach the concrete witness for this conformance to the + // substutiton + GlobalGenericParamSubstitution::ConstraintArg constraintArg; + constraintArg.decl = constraint; + constraintArg.val = witness; + subst->constraintArgs.Add(constraintArg); } - // Attach the concrete witness for this conformance to the - // substutiton - GlobalGenericParamSubstitution::ConstraintArg constraintArg; - constraintArg.decl = constraint; - constraintArg.val = witness; - subst->constraintArgs.Add(constraintArg); - } + // Add the substitution for this parameter to the global substitution + // set that we are building. - // Add the substitution for this parameter to the global substitution - // set that we are building. + *globalGenericSubstLink = subst; + globalGenericSubstLink = &subst->outer; + } - *globalGenericSubstLink = subst; - globalGenericSubstLink = &subst->outer; + entryPoint->globalGenericSubst = globalGenericSubsts; } - - entryPoint->globalGenericSubst = globalGenericSubsts; } + + // If any errors occured while we were checking the generic arguments + // of the entry point, then we should bail out rather than try to + // perform the next step of validation. + // if (sink->errorCount != 0) return; // Now that we've *found* the entry point, it is time to validate // that it actually meets the constraints for the chosen stage/profile. // - // TODO: This validation should be performed "under" any global generic + // TODO: This validation should (probably?) be performed "under" any global generic // parameter substitution we might have created, so that we can validate // based on knowledge of actual types. // @@ -9679,7 +9825,7 @@ namespace Slang RefPtr entryPointReq = new EntryPointRequest(); entryPointReq->compileRequest = compileRequest; entryPointReq->translationUnitIndex = int(tt); - entryPointReq->decl = funcDecl; + entryPointReq->funcDeclRef = makeDeclRef(funcDecl); entryPointReq->name = funcDecl->getName(); entryPointReq->profile = profile; -- cgit v1.2.3