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.cpp434
1 files changed, 290 insertions, 144 deletions
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<Expr> visitGenericAppExpr(GenericAppExpr * genericAppExpr)
+ RefPtr<Expr> 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<Expr> 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<PatchConstantFuncAttribute>();
+ auto attr = entryPoint->getFuncDecl()->FindModifier<PatchConstantFuncAttribute>();
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<int>` 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<GenericDecl>(decl))
+ decl = genericDecl->inner;
+
// Is this declaration a function?
- if (auto funcDecl = as<FuncDecl>(ee))
+ if (auto funcDecl = as<FuncDecl>(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<RefPtr<Scope>> scopesToTry;
scopesToTry.Add(entryPoint->getTranslationUnit()->SyntaxNode->scope);
for (auto & module : entryPoint->compileRequest->loadedModulesList)
scopesToTry.Add(module->moduleDecl->scope);
- List<RefPtr<Type>> 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<RefPtr<Expr>> genericArgs;
for (auto name : entryPoint->genericArgStrings)
{
- // parse type name
- RefPtr<Type> type;
+ RefPtr<Expr> argExpr;
for (auto & s : scopesToTry)
{
- RefPtr<Expr> 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<TaggedUnionType>(type) )
+ if( auto typeType = as<TypeType>(argExpr->type) )
{
- entryPoint->taggedUnionTypes.Add(taggedUnionType);
+ auto type = typeType->type;
+ if( auto taggedUnionType = as<TaggedUnionType>(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<GenericDecl>(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<RefPtr<GlobalGenericParamDecl>> globalGenericParams;
- // add current translation unit first
- {
- auto globalGenParams = translationUnit->SyntaxNode->getMembersOfType<GlobalGenericParamDecl>();
- for (auto p : globalGenParams)
- globalGenericParams.Add(p);
- }
- // add imported modules
- for (auto loadedModule : entryPoint->compileRequest->loadedModulesList)
- {
- auto moduleDecl = loadedModule->moduleDecl;
- auto globalGenParams = moduleDecl->getMembersOfType<GlobalGenericParamDecl>();
- 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<A,B,C>` 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<Substitutions> 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<VarExpr> 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<A>;
+ RefPtr<GenericAppExpr> 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<Batman>`, 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<B>`).
+ 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<DeclRefExpr>() )
+ {
+ // 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<FuncDecl>();
+ }
+ 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<DeclRefType>(globalGenericArg) )
+ // collect global generic parameters from all imported modules
+ List<RefPtr<GlobalGenericParamDecl>> globalGenericParams;
+ // add current translation unit first
+ {
+ auto globalGenParams = translationUnit->SyntaxNode->getMembersOfType<GlobalGenericParamDecl>();
+ for (auto p : globalGenParams)
+ globalGenericParams.Add(p);
+ }
+ // add imported modules
+ for (auto loadedModule : entryPoint->compileRequest->loadedModulesList)
+ {
+ auto moduleDecl = loadedModule->moduleDecl;
+ auto globalGenParams = moduleDecl->getMembersOfType<GlobalGenericParamDecl>();
+ for (auto p : globalGenParams)
+ globalGenericParams.Add(p);
+ }
+
+ if (globalGenericParams.Count() != genericArgs.Count())
{
- auto argDeclRef = argDeclRefType->declRef;
- if(auto argGenericParamDeclRef = argDeclRef.as<GlobalGenericParamDecl>())
+ 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<Substitutions> 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<A>;
+ //
+ // In that case, if a user tries to set `B` to `Robin` and `Robin` conforms to
+ // `ISidekick<Batman>`, 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<B>`).
+ //
+ // 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<DeclRefType>() )
+ {
+ auto argDeclRef = argDeclRefType->declRef;
+ if(auto argGenericParamDeclRef = argDeclRef.as<GlobalGenericParamDecl>())
{
- // 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<GlobalGenericParamSubstitution> subst = new GlobalGenericParamSubstitution();
+ subst->paramDecl = globalGenericParam;
+ subst->actualType = globalGenericArg;
- // Create a substitution for this parameter/argument.
- RefPtr<GlobalGenericParamSubstitution> 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<GenericTypeConstraintDecl>())
+ {
+ // Get the type that the constraint is enforcing conformance to
+ auto interfaceType = GetSup(DeclRef<GenericTypeConstraintDecl>(constraint, nullptr));
- // Walk through the declared constraints for the parameter,
- // and check that the argument actually satisfies them.
- for(auto constraint : globalGenericParam->getMembersOfType<GenericTypeConstraintDecl>())
- {
- // Get the type that the constraint is enforcing conformance to
- auto interfaceType = GetSup(DeclRef<GenericTypeConstraintDecl>(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<EntryPointRequest> entryPointReq = new EntryPointRequest();
entryPointReq->compileRequest = compileRequest;
entryPointReq->translationUnitIndex = int(tt);
- entryPointReq->decl = funcDecl;
+ entryPointReq->funcDeclRef = makeDeclRef(funcDecl);
entryPointReq->name = funcDecl->getName();
entryPointReq->profile = profile;