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.cpp395
1 files changed, 326 insertions, 69 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 483db60bb..a9f84c5c3 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -557,6 +557,117 @@ namespace Slang
return isDeclUsableAsStaticMember(decl);
}
+ /// Move `expr` into a temporary variable and execute `func` on that variable.
+ ///
+ /// Returns an expression that wraps both the creation and initialization of
+ /// the temporary, and the computation created by `func`.
+ ///
+ template<typename F>
+ RefPtr<Expr> moveTemp(RefPtr<Expr> const& expr, F const& func)
+ {
+ RefPtr<VarDecl> varDecl = new VarDecl();
+ varDecl->ParentDecl = nullptr; // TODO: need to fill this in somehow!
+ varDecl->checkState = DeclCheckState::Checked;
+ varDecl->nameAndLoc.loc = expr->loc;
+ varDecl->initExpr = expr;
+ varDecl->type.type = expr->type.type;
+
+ auto varDeclRef = makeDeclRef(varDecl.Ptr());
+
+ RefPtr<LetExpr> letExpr = new LetExpr();
+ letExpr->decl = varDecl;
+
+ auto body = func(varDeclRef);
+
+ letExpr->body = body;
+ letExpr->type = body->type;
+
+ return letExpr;
+ }
+
+ /// Execute `func` on a variable with the value of `expr`.
+ ///
+ /// If `expr` is just a reference to an immutable (e.g., `let`) variable
+ /// then this might use the existing variable. Otherwise it will create
+ /// a new variable to hold `expr`, using `moveTemp()`.
+ ///
+ template<typename F>
+ RefPtr<Expr> maybeMoveTemp(RefPtr<Expr> const& expr, F const& func)
+ {
+ if(auto varExpr = as<VarExpr>(expr))
+ {
+ auto declRef = varExpr->declRef;
+ if(auto varDeclRef = declRef.as<LetDecl>())
+ return func(varDeclRef);
+ }
+
+ return moveTemp(expr, func);
+ }
+
+ /// Return an expression that represents "opening" the existential `expr`.
+ ///
+ /// The type of `expr` must be an interface type, matching `interfaceDeclRef`.
+ ///
+ /// If we scope down the PL theory to just the case that Slang cares about,
+ /// a value of an existential type like `IMover` is a tuple of:
+ ///
+ /// * a concrete type `X`
+ /// * a witness `w` of the fact that `X` implements `IMover`
+ /// * a value `v` of type `X`
+ ///
+ /// "Opening" an existential value is the process of decomposing a single
+ /// value `e : IMover` into the pieces `X`, `w`, and `v`.
+ ///
+ /// Rather than return all those pieces individually, this operation
+ /// returns an expression that logically corresponds to `v`: an expression
+ /// of type `X`, where the type carries the knowledge that `X` implements `IMover`.
+ ///
+ RefPtr<Expr> openExistential(
+ RefPtr<Expr> expr,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ // If `expr` refers to an immutable binding,
+ // then we can use it directly. If it refers
+ // to an arbitrary expression or a mutable
+ // binding, we will move its value into an
+ // immutable temporary so that we can use
+ // it directly.
+ //
+ auto interfaceDecl = interfaceDeclRef.getDecl();
+ return maybeMoveTemp(expr, [&](DeclRef<VarDeclBase> varDeclRef)
+ {
+ RefPtr<ExtractExistentialType> openedType = new ExtractExistentialType();
+ openedType->declRef = varDeclRef;
+
+ RefPtr<ExtractExistentialSubtypeWitness> openedWitness = new ExtractExistentialSubtypeWitness();
+ openedWitness->sub = openedType;
+ openedWitness->sup = expr->type.type;
+ openedWitness->declRef = varDeclRef;
+
+ RefPtr<ThisTypeSubstitution> openedThisType = new ThisTypeSubstitution();
+ openedThisType->outer = interfaceDeclRef.substitutions.substitutions;
+ openedThisType->interfaceDecl = interfaceDecl;
+ openedThisType->witness = openedWitness;
+
+ DeclRef<InterfaceDecl> substDeclRef = DeclRef<InterfaceDecl>(interfaceDecl, openedThisType);
+ auto substDeclRefType = DeclRefType::Create(getSession(), substDeclRef);
+
+ RefPtr<ExtractExistentialValueExpr> openedValue = new ExtractExistentialValueExpr();
+ openedValue->declRef = varDeclRef;
+ openedValue->type = QualType(substDeclRefType);
+
+ return openedValue;
+ });
+ }
+
+ /// If `expr` has existential type, then open it.
+ ///
+ /// Returns an expression that opens `expr` if it had existential type.
+ /// Otherwise returns `expr` itself.
+ ///
+ /// See `openExistential` for a discussion of what "opening" an
+ /// existential-type value means.
+ ///
RefPtr<Expr> maybeOpenExistential(RefPtr<Expr> expr)
{
auto exprType = expr->type.type;
@@ -584,45 +695,7 @@ namespace Slang
{
// Okay, here is the case that matters.
//
-
- auto interfaceDecl = interfaceDeclRef.getDecl();
-
- RefPtr<VarDecl> varDecl = new VarDecl();
- varDecl->ParentDecl = nullptr; // TODO: need to fill this in somehow!
- varDecl->checkState = DeclCheckState::Checked;
- varDecl->nameAndLoc.loc = expr->loc;
- varDecl->initExpr = expr;
- varDecl->type.type = expr->type.type;
-
- auto varDeclRef = makeDeclRef(varDecl.Ptr());
-
- RefPtr<LetExpr> letExpr = new LetExpr();
- letExpr->decl = varDecl;
-
- RefPtr<ExtractExistentialType> openedType = new ExtractExistentialType();
- openedType->declRef = varDeclRef;
-
- RefPtr<ExtractExistentialSubtypeWitness> openedWitness = new ExtractExistentialSubtypeWitness();
- openedWitness->sub = openedType;
- openedWitness->sup = expr->type.type;
- openedWitness->declRef = varDeclRef;
-
- RefPtr<ThisTypeSubstitution> openedThisType = new ThisTypeSubstitution();
- openedThisType->outer = interfaceDeclRef.substitutions.substitutions;
- openedThisType->interfaceDecl = interfaceDecl;
- openedThisType->witness = openedWitness;
-
- DeclRef<InterfaceDecl> substDeclRef = DeclRef<InterfaceDecl>(interfaceDecl, openedThisType);
- auto substDeclRefType = DeclRefType::Create(getSession(), substDeclRef);
-
- RefPtr<ExtractExistentialValueExpr> openedValue = new ExtractExistentialValueExpr();
- openedValue->declRef = varDeclRef;
- openedValue->type = QualType(substDeclRefType);
-
- letExpr->body = openedValue;
- letExpr->type = openedValue->type;
-
- return letExpr;
+ return openExistential(expr, interfaceDeclRef);
}
}
}
@@ -9232,13 +9305,78 @@ namespace Slang
}
}
+ /// Recursively walk `paramDeclRef` and add any required existential slots to `ioSlots`.
+ static void _collectExistentialParamsRec(
+ ExistentialSlots& ioSlots,
+ DeclRef<VarDeclBase> paramDeclRef)
+ {
+ auto type = GetType(paramDeclRef);
+
+ // Whether or not something is an array does not affect
+ // the number of existential slots it introduces.
+ //
+ while( auto arrayType = as<ArrayExpressionType>(type) )
+ {
+ type = arrayType->baseType;
+ }
+
+ if( auto declRefType = as<DeclRefType>(type) )
+ {
+ auto typeDeclRef = declRefType->declRef;
+ if( auto interfaceDeclRef = typeDeclRef.as<InterfaceDecl>() )
+ {
+ // Each leaf parameter of interface type adds one slot.
+ //
+ ioSlots.types.Add(type);
+ }
+ else if( auto structDeclRef = typeDeclRef.as<StructDecl>() )
+ {
+ // A structure type should recursively introduce
+ // existential slots for its fields.
+ //
+ for( auto fieldDeclRef : GetFields(structDeclRef) )
+ {
+ if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
+ continue;
+
+ _collectExistentialParamsRec(ioSlots, fieldDeclRef);
+ }
+ }
+ }
+
+ // TODO: We eventually need to handle cases like constant
+ // buffers and parameter blocks that may have existential
+ // element types.
+ }
+
+ /// Enumerate the existential-type parameters of an `EntryPoint`.
+ ///
+ /// Any parameters found will be added to the list of existential slots on `this`.
+ ///
+ void EntryPoint::_collectExistentialParams()
+ {
+ // Note: we defensively test whether there is a function decl-ref
+ // because this routine gets called from the constructor, and
+ // a "dummy" entry point will have a null pointer for the function.
+ //
+ if( auto funcDeclRef = getFuncDeclRef() )
+ {
+ for( auto paramDeclRef : GetParameters(funcDeclRef) )
+ {
+ _collectExistentialParamsRec(m_existentialSlots, paramDeclRef);
+ }
+ }
+ }
+
// Validate that an entry point function conforms to any additional
// constraints based on the stage (and profile?) it specifies.
void validateEntryPoint(
- FuncDecl* entryPointFuncDecl,
- Stage stage,
+ EntryPoint* entryPoint,
DiagnosticSink* sink)
{
+ auto entryPointFuncDecl = entryPoint->getFuncDecl();
+ auto stage = entryPoint->getStage();
+
// TODO: We currently do minimal checking here, but this is the
// right place to perform the following validation checks:
//
@@ -9494,21 +9632,43 @@ namespace Slang
}
- // Now that we've *found* the entry point, it is time to validate
- // that it actually meets the constraints for the chosen stage/profile.
- //
- validateEntryPoint(
- entryPointFuncDecl,
- entryPointProfile.GetStage(),
- sink);
-
RefPtr<EntryPoint> entryPoint = EntryPoint::create(
makeDeclRef(entryPointFuncDecl),
entryPointProfile);
+ // Now that we've *found* the entry point, it is time to validate
+ // that it actually meets the constraints for the chosen stage/profile.
+ //
+ validateEntryPoint(entryPoint, sink);
+
return entryPoint;
}
+ /// Enumerate the existential-type parameters of a `Program`.
+ ///
+ /// Any parameters found will be added to the list of existential slots on `this`.
+ ///
+ void Program::_collectExistentialParams()
+ {
+ // We need to inspect all of the global shader parameters
+ // referenced by the compile request, and for each we
+ // need to determine what existential types parameters it implies.
+ //
+ for( auto module : getModuleDependencies() )
+ {
+ auto moduleDecl = module->getModuleDecl();
+ for( auto globalVar : moduleDecl->getMembersOfType<VarDecl>() )
+ {
+ if(!isGlobalShaderParameter(globalVar))
+ continue;
+
+ _collectExistentialParamsRec(
+ m_globalExistentialSlots,
+ makeDeclRef(globalVar.Ptr()));
+ }
+ }
+ }
+
/// Create a `Program` to represent the compiled code.
///
/// The created program will comprise all of the translation
@@ -9623,32 +9783,99 @@ namespace Slang
Profile profile;
profile.setStage(entryPointAttr->stage);
- validateEntryPoint(funcDecl, entryPointAttr->stage, sink);
-
RefPtr<EntryPoint> entryPoint = EntryPoint::create(
makeDeclRef(funcDecl),
profile);
+
+ validateEntryPoint(entryPoint, sink);
+
program->addEntryPoint(entryPoint);
translationUnit->entryPoints.Add(entryPoint);
}
}
}
+ program->_collectExistentialParams();
+
return program;
}
- /// Create a specialization an existing entry point based on generic arguments.
- DeclRef<FuncDecl> specializeEntryPoint(
+ static void _specializeExistentialSlots(
Linkage* linkage,
- FuncDecl* entryPointFuncDecl,
+ ExistentialSlots& ioSlots,
+ List<RefPtr<Expr>> const& args,
+ DiagnosticSink* sink)
+ {
+ UInt slotCount = ioSlots.types.Count();
+ UInt argCount = args.Count();
+
+ if( slotCount != argCount )
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::mismatchExistentialSlotArgCount, slotCount, argCount);
+ return;
+ }
+
+ SemanticsVisitor visitor(linkage, sink);
+
+ for( UInt ii = 0; ii < slotCount; ++ii )
+ {
+ auto slotType = ioSlots.types[ii];
+ auto argExpr = args[ii];
+
+ auto argType = checkProperType(linkage, TypeExp(argExpr), sink);
+ if(!argType)
+ {
+ // TODO: Each slot should track a source location and/or a `VarDeclBase`
+ // that names the parameter that the slot corresponds to.
+
+ sink->diagnose(SourceLoc(), Diagnostics::existentialSlotArgNotAType, ii);
+ return;
+ }
+
+
+ auto witness = visitor.tryGetSubtypeWitness(argType, slotType);
+ if (!witness)
+ {
+ // If no witness was found, then we will be unable to satisfy
+ // the conformances required.
+ sink->diagnose(SourceLoc(), Diagnostics::existentialSlotArgDoesNotConform, ii, slotType);
+ return;
+ }
+
+ ExistentialSlots::Arg arg;
+ arg.type = argType;
+ arg.witness = witness;
+ ioSlots.args.Add(arg);
+ }
+ }
+
+ void EntryPoint::_specializeExistentialSlots(
+ List<RefPtr<Expr>> const& args,
+ DiagnosticSink* sink)
+ {
+ Slang::_specializeExistentialSlots(getLinkage(), m_existentialSlots, args, sink);
+ }
+
+ /// Create a specialization an existing entry point based on generic arguments.
+ RefPtr<EntryPoint> createSpecializedEntryPoint(
+ EntryPoint* unspecializedEntryPoint,
List<RefPtr<Expr>> const& genericArgs,
+ List<RefPtr<Expr>> const& existentialArgs,
DiagnosticSink* sink)
{
+ auto linkage = unspecializedEntryPoint->getLinkage();
+
+ // TODO: Need to be careful in case entry point already has a decl-ref,
+ // pertaining to outer specializations (e.g., when entry point was
+ // nested in a generic type.
+ //
+ auto entryPointFuncDecl = unspecializedEntryPoint->getFuncDecl();
+
SemanticsVisitor semantics(
linkage,
sink);
- DeclRef<FuncDecl> entryPointFuncDeclRef = makeDeclRef(entryPointFuncDecl);
+ DeclRef<FuncDecl> entryPointFuncDeclRef = makeDeclRef(entryPointFuncDecl.Ptr());
if( auto genericDecl = as<GenericDecl>(entryPointFuncDecl->ParentDecl) )
{
// We will construct a suitable `GenericAppExpr` to represent
@@ -9702,7 +9929,7 @@ namespace Slang
{
// Any semantic error that occured should have been
// reported already.
- return DeclRef<FuncDecl>();
+ return nullptr;
}
else
{
@@ -9710,11 +9937,18 @@ namespace Slang
// function should always be a `DeclRefExpr`
//
SLANG_UNEXPECTED("reference to generic decl wasn't a `DeclRefExpr`");
- UNREACHABLE_RETURN(DeclRef<FuncDecl>());
+ UNREACHABLE_RETURN(nullptr);
}
}
- return entryPointFuncDeclRef;
+ RefPtr<EntryPoint> specializedEntryPoint = EntryPoint::create(
+ entryPointFuncDeclRef,
+ unspecializedEntryPoint->getProfile());
+
+ // Next we need to validate the existential arguments.
+ specializedEntryPoint->_specializeExistentialSlots(existentialArgs, sink);
+
+ return specializedEntryPoint;
}
/// Parse an array of strings as generic arguments.
@@ -9771,11 +10005,20 @@ namespace Slang
}
}
+ void Program::_specializeExistentialSlots(
+ List<RefPtr<Expr>> const& args,
+ DiagnosticSink* sink)
+ {
+ Slang::_specializeExistentialSlots(getLinkage(), m_globalExistentialSlots, args, sink);
+ }
+
+
/// Specialize a program to global generic arguments
RefPtr<Program> createSpecializedProgram(
Linkage* linkage,
Program* unspecializedProgram,
List<RefPtr<Expr>> const& globalGenericArgs,
+ List<RefPtr<Expr>> const& globalExistentialArgs,
DiagnosticSink* sink)
{
// The given `unspecializedProgram` should be one that
@@ -9827,6 +10070,8 @@ namespace Slang
// 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.
//
+ SemanticsVisitor visitor(linkage, sink);
+
// Along the way, we will build up an appropriate set of substitutions to represent
// the generic arguments and their conformances.
//
@@ -9905,7 +10150,6 @@ namespace Slang
auto interfaceType = GetSup(DeclRef<GenericTypeConstraintDecl>(constraint, nullptr));
// Use our semantic-checking logic to search for a witness to the required conformance
- SemanticsVisitor visitor(linkage, sink);
auto witness = visitor.tryGetSubtypeWitness(globalGenericArg, interfaceType);
if (!witness)
{
@@ -9937,6 +10181,10 @@ namespace Slang
specializedProgram->setGlobalGenericSubsitution(globalGenericSubsts);
+ // Now deal with the existential arguments
+ specializedProgram->_collectExistentialParams();
+ specializedProgram->_specializeExistentialSlots(globalExistentialArgs, sink);
+
return specializedProgram;
}
@@ -9949,12 +10197,11 @@ namespace Slang
/// Returns a specialized entry point if everything worked as expected.
/// Returns null and diagnoses errors if anything goes wrong.
///
- RefPtr<EntryPoint> specializeEntryPoint(
+ RefPtr<EntryPoint> createSpecializedEntryPoint(
EndToEndCompileRequest* endToEndReq,
EntryPoint* unspecializedEntryPoint,
EndToEndCompileRequest::EntryPointInfo const& entryPointInfo)
{
- auto linkage = endToEndReq->getLinkage();
auto sink = endToEndReq->getSink();
auto entryPointFuncDecl = unspecializedEntryPoint->getFuncDecl();
@@ -9967,19 +10214,21 @@ namespace Slang
entryPointInfo.genericArgStrings,
genericArgs);
+ List<RefPtr<Expr>> existentialArgs;
+ parseGenericArgStrings(
+ endToEndReq,
+ entryPointInfo.existentialArgStrings,
+ existentialArgs);
+
// Next we specialize the entry point function given the parsed
// generic argument expressions.
//
- auto entryPointFuncDeclRef = specializeEntryPoint(
- linkage,
- entryPointFuncDecl,
+ auto entryPoint = createSpecializedEntryPoint(
+ unspecializedEntryPoint,
genericArgs,
+ existentialArgs,
sink);
- RefPtr<EntryPoint> entryPoint = EntryPoint::create(
- entryPointFuncDeclRef,
- unspecializedEntryPoint->getProfile());
-
return entryPoint;
}
@@ -10005,6 +10254,13 @@ namespace Slang
endToEndReq->globalGenericArgStrings,
globalGenericArgs);
+ // Also handle global existential type arguments.
+ List<RefPtr<Expr>> globalExistentialArgs;
+ parseGenericArgStrings(
+ endToEndReq,
+ endToEndReq->globalExistentialSlotArgStrings,
+ globalExistentialArgs);
+
// Now we create the initial specialized program by
// applying the global generic arguments (if any) to the
// unspecialized program.
@@ -10013,6 +10269,7 @@ namespace Slang
endToEndReq->getLinkage(),
unspecializedProgram,
globalGenericArgs,
+ globalExistentialArgs,
endToEndReq->getSink());
// If anything went wrong with the global generic
@@ -10045,7 +10302,7 @@ namespace Slang
auto unspecializedEntryPoint = unspecializedProgram->getEntryPoint(ii);
auto& entryPointInfo = endToEndReq->entryPoints[ii];
- auto specializedEntryPoint = specializeEntryPoint(endToEndReq, unspecializedEntryPoint, entryPointInfo);
+ auto specializedEntryPoint = createSpecializedEntryPoint(endToEndReq, unspecializedEntryPoint, entryPointInfo);
specializedProgram->addEntryPoint(specializedEntryPoint);
}