summaryrefslogtreecommitdiff
path: root/source/slang/slang-lower-to-ir.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2021-03-10 15:18:06 -0800
committerGitHub <noreply@github.com>2021-03-10 15:18:06 -0800
commit6cbd9d68a03f0a22305d4e224a3da7633b23de38 (patch)
treede436717081a9b2b7ddd3644f2e7ada130951141 /source/slang/slang-lower-to-ir.cpp
parent6ef4054f8a8aea4ec61481057fa7e16aaecde6d7 (diff)
A bunch of overlapping semantic-checking fixes (#1743)
This change originally started with the simple goal of allowing generic functions with default argument values on their parameters to work: ``` void someFunction<T>(T value, int optional = 0); ``` The core problem there was that the compiler code was (correctly) anticipate the case where the default argument value for a parameter depends on a generic parameter, such as: ``` interface IDefaultable { static This getDefault(); } void anotherFunction<T : IDefaultable>(T first, T second = T.getDefault()); ``` Supporting this latter case requires some kind of ability to apply subsitutions to an `Expr`, but our compiler logic simply errored out in that case. The first major fix that went into this change was to add a new `SubstExpr<T>` type that behaves a lot like `DeclRef<T>` in that it stores a `T*` plus a set of substititions that need to be applied to it. In addition, it was found that even if `anotherFunction<ConcreteType>(...)` might work, when generic argument inference was used for just `anotherFunction(...)` would fail because it includes a strict match on the number of arguments/parameters in the call expression. The next problem that arose was that the test I'd created used an interace with an `__init` requirement, and it appeared that our code generation didn't work for that case: ``` interface IStuff { __init(int val); } void f<T : IStuff>(T x = T(0)); ``` In this case, the `T(0)` initialization would get compiled to `(ConcreteType) 0` in the output rather than calling the function generated for the `__init` inside `ConcreteType`. The basic problem there was a bit of crufty old logic we have in place to work around the large number of `__init` declarations in the stdlib that don't have proper `__intrinsic_op` modifiers on them. We really need to fix the underlying problem there, but I worked around it by having the IR lowering pass only do its workaround magic on stdlib declarations. The next problem down this line was that my test had two different `__init` declarations in the concrete type and the logic for checking interface conformance was picking the wrong one to satisfying an interface requirement despite it being obviously wrong (not even the right number of parameter). This last problem led me down the rabbit-hole of trying to actually get our semantic checking for interface requirements right. There were a few pieces to that work: * Actually checking that the parameter and result types for two callables match is the simple part. If that was all that would be required we would have implement this logic a long time ago. * Next we have to deal with functions that make use of the `This` type, associated types, etc. We have to know that when the interface uses `This`, we want to treat that as equivalent to `ConcreteType`, and similarly for associated types. Getting that working is mostly a matter of setting up a this-type subsitution for the interface member being checked. * Finally, when comparing generic declarations like `IBase::doThing<T>` and `Derived::doThing<U>` we need to deal with the way that `T` and `U` represent the "same" logical type parameter, but are distinct `Decl`s. This is handled by specializing the base declaration to the parameters of the derived one (e.g., forming `IBase::doThing<U>` using the `U` from `Derived::doThing`). The result seems to be passing our tests, but there are still a few gotchas lurking, I'm sure.
Diffstat (limited to 'source/slang/slang-lower-to-ir.cpp')
-rw-r--r--source/slang/slang-lower-to-ir.cpp105
1 files changed, 67 insertions, 38 deletions
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index fb9fc70fd..3c9256178 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -633,7 +633,7 @@ LoweredValInfo emitCallToDeclRef(
if( auto ctorDeclRef = funcDeclRef.as<ConstructorDecl>() )
{
- if(!ctorDeclRef.getDecl()->body)
+ if(!ctorDeclRef.getDecl()->body && isFromStdLib(ctorDeclRef.decl))
{
// HACK: For legacy reasons, all of the built-in initializers
// in the standard library are declared without proper
@@ -1114,7 +1114,6 @@ void getGenericTypeConformances(IRGenContext* context, ShortList<IRType*>& supTy
}
}
-SubstitutionSet lowerSubstitutions(IRGenContext* context, SubstitutionSet subst);
//
struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, LoweredValInfo>
@@ -3141,6 +3140,45 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
UNREACHABLE_RETURN(LoweredValInfo());
}
+ void _lowerSubstitutionArg(IRGenContext* subContext, GenericSubstitution* subst, Decl* paramDecl, Index argIndex)
+ {
+ SLANG_ASSERT(argIndex < subst->args.getCount());
+ auto argVal = lowerVal(subContext, subst->args[argIndex]);
+ setValue(subContext, paramDecl, argVal);
+ }
+
+ void _lowerSubstitutionEnv(IRGenContext* subContext, Substitutions* subst)
+ {
+ if(!subst) return;
+ _lowerSubstitutionEnv(subContext, subst->outer);
+
+ if (auto genSubst = as<GenericSubstitution>(subst))
+ {
+ auto genDecl = genSubst->genericDecl;
+
+ Index argCounter = 0;
+ for( auto memberDecl: genDecl->members )
+ {
+ if(auto typeParamDecl = as<GenericTypeParamDecl>(memberDecl) )
+ {
+ _lowerSubstitutionArg(subContext, genSubst, typeParamDecl, argCounter++);
+ }
+ else if( auto valParamDecl = as<GenericValueParamDecl>(memberDecl) )
+ {
+ _lowerSubstitutionArg(subContext, genSubst, valParamDecl, argCounter++);
+ }
+ }
+ for( auto memberDecl: genDecl->members )
+ {
+ if(auto constraintDecl = as<GenericTypeConstraintDecl>(memberDecl) )
+ {
+ _lowerSubstitutionArg(subContext, genSubst, constraintDecl, argCounter++);
+ }
+ }
+ }
+ // TODO: also need to handle this-type substitution here?
+ }
+
void addDirectCallArgs(
InvokeExpr* expr,
DeclRef<CallableDecl> funcDeclRef,
@@ -3156,10 +3194,10 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
auto paramDirection = getParameterDirection(paramDecl);
UInt argIndex = argCounter++;
- Expr* argExpr = nullptr;
if(argIndex < argCount)
{
- argExpr = expr->arguments[argIndex];
+ auto argExpr = expr->arguments[argIndex];
+ addCallArgsForParam(context, paramType, paramDirection, argExpr, ioArgs, ioFixups);
}
else
{
@@ -3167,11 +3205,31 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
// but there are still parameters remaining. This must mean
// that these parameters have default argument expressions
// associated with them.
- argExpr = getInitExpr(getASTBuilder(), paramDeclRef);
-
- // Assert that such an expression must have been present.
+ //
+ // Currently we simply extract the initial-value expression
+ // from the parameter declaration and then lower it in
+ // the context of the caller.
+ //
+ // Note that the expression could involve subsitutions because
+ // in the general case it could depend on the generic parameters
+ // used the specialize the callee. For now we do not handle that
+ // case, and simply ignore generic arguments.
+ //
+ SubstExpr<Expr> argExpr = getInitExpr(getASTBuilder(), paramDeclRef);
SLANG_ASSERT(argExpr);
+ IRGenEnv subEnvStorage;
+ IRGenEnv* subEnv = &subEnvStorage;
+ subEnv->outer = context->env;
+
+ IRGenContext subContextStorage = *context;
+ IRGenContext* subContext = &subContextStorage;
+ subContext->env = subEnv;
+
+ _lowerSubstitutionEnv(subContext, argExpr.getSubsts());
+
+ addCallArgsForParam(subContext, paramType, paramDirection, argExpr.getExpr(), ioArgs, ioFixups);
+
// TODO: The approach we are taking here to default arguments
// is simplistic, and has consequences for the front-end as
// well as binary serialization of modules.
@@ -3186,9 +3244,9 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
//
// Each of these options involves trade-offs, and we need to
// make a conscious decision at some point.
- }
- addCallArgsForParam(context, paramType, paramDirection, argExpr, ioArgs, ioFixups);
+ // Assert that such an expression must have been present.
+ }
}
}
@@ -7347,35 +7405,6 @@ LoweredValInfo ensureDecl(
return result;
}
-IRInst* lowerSubstitutionArg(
- IRGenContext* context,
- Val* val)
-{
- if (auto type = dynamicCast<Type>(val))
- {
- return lowerType(context, type);
- }
- else if (auto declaredSubtypeWitness = as<DeclaredSubtypeWitness>(val))
- {
- // We need to look up the IR-level representation of the witness (which will be a witness table).
- auto supType = lowerType(
- context,
- DeclRefType::create(context->astBuilder, declaredSubtypeWitness->declRef));
- auto irWitnessTable = getSimpleVal(
- context,
- emitDeclRef(
- context,
- declaredSubtypeWitness->declRef,
- context->irBuilder->getWitnessTableType(supType)));
- return irWitnessTable;
- }
- else
- {
- SLANG_UNIMPLEMENTED_X("value cases");
- UNREACHABLE_RETURN(nullptr);
- }
-}
-
// Can the IR lowered version of this declaration ever be an `IRGeneric`?
bool canDeclLowerToAGeneric(Decl* decl)
{