summaryrefslogtreecommitdiff
path: root/source/slang/slang-check.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-check.cpp')
-rw-r--r--source/slang/slang-check.cpp1312
1 files changed, 692 insertions, 620 deletions
diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp
index edf41fff0..b7c1ccdc2 100644
--- a/source/slang/slang-check.cpp
+++ b/source/slang/slang-check.cpp
@@ -7107,8 +7107,7 @@ namespace Slang
{
if(context.mode != OverloadResolveContext::Mode::JustTrying)
{
- // TODO: diagnose a problem here
- getSink()->diagnose(context.loc, Diagnostics::unimplemented, "generic constraint not satisfied");
+ getSink()->diagnose(context.loc, Diagnostics::typeArgumentDoesNotConformToInterface, sub, sup);
}
return false;
}
@@ -9663,15 +9662,15 @@ namespace Slang
}
}
- /// Recursively walk `paramDeclRef` and add any required existential slots to `ioSlots`.
- static void _collectExistentialTypeParamsRec(
- ExistentialTypeSlots& ioSlots,
+ /// Recursively walk `paramDeclRef` and add any existential/interface specialization parameters to `ioSpecializationParams`.
+ static void _collectExistentialSpecializationParamsRec(
+ SpecializationParams& ioSpecializationParams,
DeclRef<VarDeclBase> paramDeclRef);
- /// Recursively walk `type` and discover any required existential type parameters.
- static void _collectExistentialTypeParamsRec(
- ExistentialTypeSlots& ioSlots,
- Type* type)
+ /// Recursively walk `type` and add any existential/interface specialization parameters to `ioSpecializationParams`.
+ static void _collectExistentialSpecializationParamsRec(
+ SpecializationParams& ioSpecializationParams,
+ Type* type)
{
// Whether or not something is an array does not affect
// the number of existential slots it introduces.
@@ -9683,7 +9682,9 @@ namespace Slang
if( auto parameterGroupType = as<ParameterGroupType>(type) )
{
- _collectExistentialTypeParamsRec(ioSlots, parameterGroupType->getElementType());
+ _collectExistentialSpecializationParamsRec(
+ ioSpecializationParams,
+ parameterGroupType->getElementType());
return;
}
@@ -9692,9 +9693,14 @@ namespace Slang
auto typeDeclRef = declRefType->declRef;
if( auto interfaceDeclRef = typeDeclRef.as<InterfaceDecl>() )
{
- // Each leaf parameter of interface type adds one slot.
+ // Each leaf parameter of interface type adds a specialization
+ // parameter, which determines the concrete type(s) that may
+ // be provided as arguments for that parameter.
//
- ioSlots.paramTypes.add(type);
+ SpecializationParam specializationParam;
+ specializationParam.flavor = SpecializationParam::Flavor::ExistentialType;
+ specializationParam.object = type;
+ ioSpecializationParams.add(specializationParam);
}
else if( auto structDeclRef = typeDeclRef.as<StructDecl>() )
{
@@ -9706,7 +9712,9 @@ namespace Slang
if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
continue;
- _collectExistentialTypeParamsRec(ioSlots, fieldDeclRef);
+ _collectExistentialSpecializationParamsRec(
+ ioSpecializationParams,
+ fieldDeclRef);
}
}
}
@@ -9716,26 +9724,58 @@ namespace Slang
// element types.
}
- static void _collectExistentialTypeParamsRec(
- ExistentialTypeSlots& ioSlots,
+ static void _collectExistentialSpecializationParamsRec(
+ SpecializationParams& ioSpecializationParams,
DeclRef<VarDeclBase> paramDeclRef)
{
- _collectExistentialTypeParamsRec(ioSlots, GetType(paramDeclRef));
+ _collectExistentialSpecializationParamsRec(
+ ioSpecializationParams,
+ GetType(paramDeclRef));
}
- /// Add information about a shader parameter to `ioParams` and `ioSlots`
- static void _collectExistentialSlotsForShaderParam(
+ /// Collect any interface/existential specialization parameters for `paramDeclRef` into `ioParamInfo` and `ioSpecializationParams`
+ static void _collectExistentialSpecializationParamsForShaderParam(
ShaderParamInfo& ioParamInfo,
- ExistentialTypeSlots& ioSlots,
+ SpecializationParams& ioSpecializationParams,
DeclRef<VarDeclBase> paramDeclRef)
{
- Index startSlot = ioSlots.paramTypes.getCount();
- _collectExistentialTypeParamsRec(ioSlots, paramDeclRef);
- Index endSlot = ioSlots.paramTypes.getCount();
+ Index beginParamIndex = ioSpecializationParams.getCount();
+ _collectExistentialSpecializationParamsRec(ioSpecializationParams, paramDeclRef);
+ Index endParamIndex = ioSpecializationParams.getCount();
- ioParamInfo.firstExistentialTypeSlot = UInt(startSlot);
- ioParamInfo.existentialTypeSlotCount = UInt(endSlot - startSlot);;
+ ioParamInfo.firstSpecializationParamIndex = beginParamIndex;
+ ioParamInfo.specializationParamCount = endParamIndex - beginParamIndex;
+ }
+
+ void EntryPoint::_collectGenericSpecializationParamsRec(Decl* decl)
+ {
+ if(!decl)
+ return;
+
+ _collectGenericSpecializationParamsRec(decl->ParentDecl);
+
+ auto genericDecl = as<GenericDecl>(decl);
+ if(!genericDecl)
+ return;
+
+ for(auto m : genericDecl->Members)
+ {
+ if(auto genericTypeParam = as<GenericTypeParamDecl>(m))
+ {
+ SpecializationParam param;
+ param.flavor = SpecializationParam::Flavor::GenericType;
+ param.object = genericTypeParam;
+ m_genericSpecializationParams.add(param);
+ }
+ else if(auto genericValParam = as<GenericValueParamDecl>(m))
+ {
+ SpecializationParam param;
+ param.flavor = SpecializationParam::Flavor::GenericValue;
+ param.object = genericValParam;
+ m_genericSpecializationParams.add(param);
+ }
+ }
}
/// Enumerate the existential-type parameters of an `EntryPoint`.
@@ -9744,6 +9784,24 @@ namespace Slang
///
void EntryPoint::_collectShaderParams()
{
+ // We don't currently treat an entry point as having any
+ // *global* shader parameters.
+ //
+ // TODO: We could probably clean up the code a bit by treating
+ // an entry point as introducing a global shader parameter
+ // that is based on the implicit "parameters struct" type
+ // of the entry point itself.
+
+ // We collect the generic parameters of the entry point,
+ // along with those of any outer generics first.
+ //
+ _collectGenericSpecializationParamsRec(getFuncDecl());
+
+ // After geneic specialization parameters have been collected,
+ // we look through the value parameters of the entry point
+ // function and see if any of them introduce existential/interface
+ // specialization parameters.
+ //
// 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.
@@ -9755,9 +9813,9 @@ namespace Slang
ShaderParamInfo shaderParamInfo;
shaderParamInfo.paramDeclRef = paramDeclRef;
- _collectExistentialSlotsForShaderParam(
+ _collectExistentialSpecializationParamsForShaderParam(
shaderParamInfo,
- m_existentialSlots,
+ m_existentialSpecializationParams,
paramDeclRef);
m_shaderParams.add(shaderParamInfo);
@@ -9765,111 +9823,6 @@ namespace Slang
}
}
-static bool shouldUseFalcorCustomSharedKeywordSemantics(
- EntryPointGroup* entryPointGroup)
-{
- if( !entryPointGroup->getLinkageImpl()->m_useFalcorCustomSharedKeywordSemantics )
- return false;
-
- // As a sanity check, if we are being asked to lay out an
- // empty entry-point group, then don't apply the convention.
- //
- if(entryPointGroup->getEntryPointCount() == 0)
- return false;
-
- // Otherwise we will look at the first entry point in the group,
- // and use that to determine whether it looks like we are compiling
- // ray-tracing shaders or not.
- //
- switch( entryPointGroup->getEntryPoint(0)->getStage() )
- {
- case Stage::AnyHit:
- case Stage::Callable:
- case Stage::ClosestHit:
- case Stage::Intersection:
- case Stage::Miss:
- case Stage::RayGeneration:
- return true;
-
- default:
- return false;
- }
-}
-
-
-static bool shouldUseFalcorCustomSharedKeywordSemantics(
- Program* program)
-{
- if( !program->getLinkageImpl()->m_useFalcorCustomSharedKeywordSemantics )
- return false;
-
- // As a sanity check, if we are being asked to lay out a program
- // with *no* entry points, then we don't apply the convention.
- //
- if(program->getEntryPointGroupCount() == 0)
- return false;
-
- // Otherwise we let the first entry-point group determine if we should
- // apply the policy for the entire program.
- //
- // Note: this could lead to confusing results if a `Program` mixes
- // entry point groups for RT and non-RT pipelines, but that isn't
- // a scenario we expect to come up and this whole routine is handling
- // "do what I mean" semantics for legacy behavior.
- //
- return shouldUseFalcorCustomSharedKeywordSemantics(program->getEntryPointGroup(0));
-}
-
-
-void EntryPointGroup::_collectShaderParams(DiagnosticSink* sink)
-{
- // If and only if we are in the special mode for Falcor support
- // where non-`shared` global shader parameters are actually
- // supposed to go into the "local root signature," then we
- // will consider such parameters as if they were entry-point
- // parameters, attached to the group.
- //
- if( shouldUseFalcorCustomSharedKeywordSemantics(this) )
- {
- for( auto module : getModuleDependencies() )
- {
- auto moduleDecl = module->getModuleDecl();
- for( auto globalVar : moduleDecl->getMembersOfType<VarDecl>() )
- {
- // Don't consider globals that aren't shader parameters.
- //
- if(!isGlobalShaderParameter(globalVar))
- continue;
-
- // Don't consider global shader paramters that were marked
- // `shared`, since that is how global-root-signature parameters
- // are being specified.
- //
- if( globalVar->HasModifier<HLSLEffectSharedModifier>() )
- continue;
-
- auto paramDeclRef = makeDeclRef(globalVar.Ptr());
-
- ShaderParamInfo shaderParamInfo;
- shaderParamInfo.paramDeclRef = paramDeclRef;
-
- ExistentialTypeSlots slots;
- _collectExistentialSlotsForShaderParam(
- shaderParamInfo,
- slots,
- paramDeclRef);
-
- if( slots.paramTypes.getCount() != 0 )
- {
- sink->diagnose(globalVar, Diagnostics::typeParametersNotAllowedOnEntryPointGlobal, globalVar);
- }
-
- m_shaderParams.add(shaderParamInfo);
- }
- }
- }
-}
-
// Validate that an entry point function conforms to any additional
// constraints based on the stage (and profile?) it specifies.
void validateEntryPoint(
@@ -10015,6 +9968,7 @@ void EntryPointGroup::_collectShaderParams(DiagnosticSink* sink)
//
auto compileRequest = entryPointReq->getCompileRequest();
auto translationUnit = entryPointReq->getTranslationUnit();
+ auto linkage = compileRequest->getLinkage();
auto sink = compileRequest->getSink();
auto translationUnitSyntax = translationUnit->getModuleDecl();
@@ -10133,8 +10087,8 @@ void EntryPointGroup::_collectShaderParams(DiagnosticSink* sink)
// a more uniform representation in the AST?
}
-
RefPtr<EntryPoint> entryPoint = EntryPoint::create(
+ linkage,
makeDeclRef(entryPointFuncDecl),
entryPointProfile);
@@ -10548,11 +10502,97 @@ static bool doesParameterMatch(
return true;
}
- /// Enumerate the existential-type parameters of a `Program`.
- ///
- /// Any parameters found will be added to the list of existential slots on `this`.
- ///
- void Program::_collectShaderParams(DiagnosticSink* sink)
+ void Module::_collectShaderParams()
+ {
+ auto moduleDecl = m_moduleDecl;
+
+ // We are going to walk the global declarations in the body of the
+ // module, and use those to build up our lists of:
+ //
+ // * Global shader parameters
+ // * Specialization parameters (both generic and interface/existential)
+ // * Requirements (`import`ed modules)
+ //
+ // For requirements, we want to be careful to only
+ // add each required module once (in case the same
+ // module got `import`ed multiple times), so we
+ // will keep a set of the modules we've already
+ // seen and processed.
+ //
+ HashSet<Module*> requiredModuleSet;
+
+ for( auto globalDecl : moduleDecl->Members )
+ {
+ if(auto globalVar = globalDecl.as<VarDecl>())
+ {
+ // We do not want to consider global variable declarations
+ // that don't represents shader parameters. This includes
+ // things like `static` globals and `groupshared` variables.
+ //
+ if(!isGlobalShaderParameter(globalVar))
+ continue;
+
+ // At this point we know we have a global shader parameter.
+
+ GlobalShaderParamInfo shaderParamInfo;
+ shaderParamInfo.paramDeclRef = makeDeclRef(globalVar.Ptr());
+
+ // We need to consider what specialization parameters
+ // are introduced by this shader parameter. This step
+ // fills in fields on `shaderParamInfo` so that we
+ // can assocaite specialization arguments supplied later
+ // with the correct parameter.
+ //
+ _collectExistentialSpecializationParamsForShaderParam(
+ shaderParamInfo,
+ m_specializationParams,
+ makeDeclRef(globalVar.Ptr()));
+
+ m_shaderParams.add(shaderParamInfo);
+ }
+ else if( auto globalGenericParam = as<GlobalGenericParamDecl>(globalDecl) )
+ {
+ // A global generic type parameter declaration introduces
+ // a suitable specialization parameter.
+ //
+ SpecializationParam specializationParam;
+ specializationParam.flavor = SpecializationParam::Flavor::GenericType;
+ specializationParam.object = globalGenericParam;
+ m_specializationParams.add(specializationParam);
+ }
+ else if( auto importDecl = as<ImportDecl>(globalDecl) )
+ {
+ // An `import` declaration creates a requirement dependency
+ // from this module to another module.
+ //
+ auto importedModule = getModule(importDecl->importedModuleDecl);
+ if(!requiredModuleSet.Contains(importedModule))
+ {
+ requiredModuleSet.Add(importedModule);
+ m_requirements.add(importedModule);
+ }
+ }
+ }
+ }
+
+ Index Module::getRequirementCount()
+ {
+ return m_requirements.getCount();
+ }
+
+ RefPtr<ComponentType> Module::getRequirement(Index index)
+ {
+ return m_requirements[index];
+ }
+
+ void Module::acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ {
+ visitor->visitModule(this, as<ModuleSpecializationInfo>(specializationInfo));
+ }
+
+
+ /// Enumerate the parameters of a `LegacyProgram`.
+ void LegacyProgram::_collectShaderParams(DiagnosticSink* sink)
{
// We need to collect all of the global shader parameters
// referenced by the compile request, and for each we
@@ -10568,10 +10608,22 @@ static bool doesParameterMatch(
// To deal with the first issue, we will maintain a map from a parameter
// name to the index of an existing parameter with that name.
//
+ // TODO: Eventually we should deprecate support for the
+ // deduplication feature of `LegaqcyProgram`, at which point
+ // this entire type and all its complications can be eliminated
+ // from the code (that includes a lot of support in the "parameter
+ // binding" step for shader parameters with multiple declarations).
+ // Until that point this type will have a fair amount of duplication
+ // with stuff in `Module` and `CompositeComponentType`.
+
+ // We use a dictionary to keep track of any shader parameter
+ // we've alrady collected with a given name.
+ //
Dictionary<Name*, Int> mapNameToParamIndex;
- for( auto module : getModuleDependencies() )
+ for( auto translationUnit : m_translationUnits )
{
+ auto module = translationUnit->getModule();
auto moduleDecl = module->getModuleDecl();
for( auto globalVar : moduleDecl->getMembersOfType<VarDecl>() )
{
@@ -10582,27 +10634,6 @@ static bool doesParameterMatch(
if(!isGlobalShaderParameter(globalVar))
continue;
- // HACK: In order to support existing policy in the Falcor
- // application, we support a custom mode where only
- // global variables marked as `shared` should be considered
- // as global shader parameters (that go in the "global
- // root signature" for DXR).
- //
- // TODO: Eliminate this special case once all of the client
- // application code has been ported to use more general-purpose
- // Slang mechanisms (e.g., entry-point `uniform` parameters).
- //
- if( shouldUseFalcorCustomSharedKeywordSemantics(this) )
- {
- if( !globalVar->HasModifier<HLSLEffectSharedModifier>() )
- {
- // Skip a non-`shared` global for purposes of enumerating
- // shader parameters.
- //
- continue;
- }
- }
-
// This declaration may represent the same logical parameter
// as a declaration that came from a different translation unit.
// If that is the case, we want to re-use the same `ShaderParamInfo`
@@ -10646,9 +10677,9 @@ static bool doesParameterMatch(
GlobalShaderParamInfo shaderParamInfo;
shaderParamInfo.paramDeclRef = makeDeclRef(globalVar.Ptr());
- _collectExistentialSlotsForShaderParam(
+ _collectExistentialSpecializationParamsForShaderParam(
shaderParamInfo,
- m_globalExistentialSlots,
+ m_specializationParams,
makeDeclRef(globalVar.Ptr()));
m_shaderParams.add(shaderParamInfo);
@@ -10656,13 +10687,59 @@ static bool doesParameterMatch(
}
}
- /// Create a `Program` to represent the compiled code.
+ /// Create a new component type based on `inComponentType`, but with all its requiremetns filled.
+ RefPtr<ComponentType> fillRequirements(
+ ComponentType* inComponentType)
+ {
+ auto linkage = inComponentType->getLinkage();
+
+ // We are going to simplify things by solving the problem iteratively.
+ // If the current `componentType` has requirements for `A`, `B`, ... etc.
+ // then we will create a composite of `componentType`, `A`, `B`, ...
+ // and then see if the resulting composite has any requirements.
+ //
+ // This avoids the problem of trying to compute teh transitive closure
+ // of the requirements relationship (while dealing with deduplication,
+ // etc.)
+
+ RefPtr<ComponentType> componentType = inComponentType;
+ for(;;)
+ {
+ auto requirementCount = componentType->getRequirementCount();
+ if(requirementCount == 0)
+ break;
+
+ List<RefPtr<ComponentType>> allComponents;
+ allComponents.add(componentType);
+
+ for(Index rr = 0; rr < requirementCount; ++rr)
+ {
+ auto requirement = componentType->getRequirement(rr);
+ allComponents.add(requirement);
+ }
+
+ componentType = CompositeComponentType::create(
+ linkage,
+ allComponents);
+ }
+ return componentType;
+ }
+
+ /// Create a component type to represent the "global scope" of a compile request.
///
- /// The created program will comprise all of the translation
- /// units that were compiled as part of the request, as
- /// well as any entry points in those translation units.
+ /// This component type will include all the modules and their global
+ /// parameters from the compile request, but not anything specific
+ /// to any entry point functions.
///
- RefPtr<Program> createUnspecializedProgram(
+ /// The layout for this component type will thus represent the things that
+ /// a user is likely to want to have stay the same across all compiled
+ /// entry points.
+ ///
+ /// The component type that this function creates is unspecialized, in
+ /// that it doesn't take into account any specialization arguments
+ /// that might have been supplied as part of the compile request.
+ ///
+ RefPtr<ComponentType> createUnspecializedGlobalComponentType(
FrontEndCompileRequest* compileRequest)
{
// We want our resulting program to depend on
@@ -10677,16 +10754,51 @@ static bool doesParameterMatch(
//
auto linkage = compileRequest->getLinkage();
auto sink = compileRequest->getSink();
- auto program = new Program(linkage);
- for(auto translationUnit : compileRequest->translationUnits )
+
+ RefPtr<ComponentType> globalComponentType;
+ if(compileRequest->translationUnits.getCount() == 1)
{
- program->addReferencedLeafModule(translationUnit->getModule());
+ // The common case is that a compilation only uses
+ // a single translation unit, and thus results in
+ // a single `Module`. We can then use that module
+ // as the component type that represents the global scope.
+ //
+ globalComponentType = compileRequest->translationUnits[0]->getModule();
}
- for(auto translationUnit : compileRequest->translationUnits )
+ else
{
- program->addReferencedModule(translationUnit->getModule());
+ globalComponentType = new LegacyProgram(
+ linkage,
+ compileRequest->translationUnits,
+ sink);
}
+ return fillRequirements(globalComponentType);
+ }
+
+ /// Create a component type that represents the global scope for a compile request,
+ /// along with any entry point functions.
+ ///
+ /// The resulting component type will include the global-scope information
+ /// first, so its layout will be compatible with the result of
+ /// `createUnspecializedGlobalComponentType`.
+ ///
+ /// The new component type will also add on any entry-point functions
+ /// that were requested and will thus include space for their `uniform` parameters.
+ /// If multiple entry points were requested then they will be given non-overlapping
+ /// parameter bindings, consistent with them being used together in
+ /// a single pipeline state, hit group, etc.
+ ///
+ /// The result of this function is unspecialized and doesn't take into
+ /// account any specialization arguments the user might have supplied.
+ ///
+ RefPtr<ComponentType> createUnspecializedGlobalAndEntryPointsComponentType(
+ FrontEndCompileRequest* compileRequest)
+ {
+ auto linkage = compileRequest->getLinkage();
+ auto sink = compileRequest->getSink();
+
+ auto globalComponentType = compileRequest->getGlobalComponentType();
// The validation of entry points here will be modal, and controlled
// by whether the user specified any entry points directly via
@@ -10699,6 +10811,9 @@ static bool doesParameterMatch(
//
bool anyExplicitEntryPoints = compileRequest->getEntryPointReqCount() != 0;
+ List<RefPtr<ComponentType>> allComponentTypes;
+ allComponentTypes.add(globalComponentType);
+
if( anyExplicitEntryPoints )
{
// If there were any explicit requests for entry points to be
@@ -10716,8 +10831,9 @@ static bool doesParameterMatch(
// but didn't specify any groups (since the current
// compilation API doesn't allow for grouping).
//
- program->addEntryPoint(entryPoint, sink);
entryPointReq->getTranslationUnit()->entryPoints.add(entryPoint);
+
+ allComponentTypes.add(entryPoint);
}
}
@@ -10777,6 +10893,7 @@ static bool doesParameterMatch(
profile.setStage(entryPointAttr->stage);
RefPtr<EntryPoint> entryPoint = EntryPoint::create(
+ linkage,
makeDeclRef(funcDecl),
profile);
@@ -10792,179 +10909,340 @@ static bool doesParameterMatch(
// group, so that its entry-point parameters lay out
// independent of the others.
//
- program->addEntryPoint(entryPoint, sink);
translationUnit->entryPoints.add(entryPoint);
+
+ allComponentTypes.add(entryPoint);
}
}
}
- program->_collectShaderParams(sink);
-
- return program;
+ if(allComponentTypes.getCount() > 1)
+ {
+ auto composite = CompositeComponentType::create(
+ linkage,
+ allComponentTypes);
+ return composite;
+ }
+ else
+ {
+ return globalComponentType;
+ }
}
- static void _specializeExistentialTypeParams(
- Linkage* linkage,
- ExistentialTypeSlots& ioSlots,
- List<RefPtr<Expr>> const& args,
+ RefPtr<ComponentType::SpecializationInfo> Module::_validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
DiagnosticSink* sink)
{
- Index slotCount = ioSlots.paramTypes.getCount();
- Index argCount = args.getCount();
+ SLANG_ASSERT(argCount == getSpecializationParamCount());
- if( slotCount != argCount )
- {
- sink->diagnose(SourceLoc(), Diagnostics::mismatchExistentialSlotArgCount, slotCount, argCount);
- return;
- }
+ SemanticsVisitor visitor(getLinkage(), sink);
- SemanticsVisitor visitor(linkage, sink);
+ RefPtr<Module::ModuleSpecializationInfo> specializationInfo = new Module::ModuleSpecializationInfo();
- for( Index ii = 0; ii < slotCount; ++ii )
+ for( Index ii = 0; ii < argCount; ++ii )
{
- auto slotType = ioSlots.paramTypes[ii];
- auto argExpr = args[ii];
+ auto& arg = args[ii];
+ auto& param = m_specializationParams[ii];
- auto argType = checkProperType(linkage, TypeExp(argExpr), sink);
- if(!argType)
+ auto argType = arg.val.as<Type>();
+ SLANG_ASSERT(argType);
+
+ switch( param.flavor )
{
- // TODO: Each slot should track a source location and/or a `VarDeclBase`
- // that names the parameter that the slot corresponds to.
+ case SpecializationParam::Flavor::GenericType:
+ {
+ auto genericTypeParamDecl = param.object.as<GlobalGenericParamDecl>();
+ SLANG_ASSERT(genericTypeParamDecl);
- sink->diagnose(SourceLoc(), Diagnostics::existentialSlotArgNotAType, ii);
- return;
+ // 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.
+
+ // As a quick sanity check, see if the argument that is being supplied for a
+ // global generic type parameter is a reference to *another* global generic
+ // type parameter, since that should always be an error.
+ //
+ if( auto argDeclRefType = argType.as<DeclRefType>() )
+ {
+ auto argDeclRef = argDeclRefType->declRef;
+ if(auto argGenericParamDeclRef = argDeclRef.as<GlobalGenericParamDecl>())
+ {
+ if(argGenericParamDeclRef.getDecl() == genericTypeParamDecl)
+ {
+ // We are trying to specialize a generic parameter using itself.
+ sink->diagnose(genericTypeParamDecl,
+ Diagnostics::cannotSpecializeGlobalGenericToItself,
+ genericTypeParamDecl->getName());
+ continue;
+ }
+ else
+ {
+ // We are trying to specialize a generic parameter using a *different*
+ // global generic type parameter.
+ sink->diagnose(genericTypeParamDecl,
+ Diagnostics::cannotSpecializeGlobalGenericToAnotherGenericParam,
+ genericTypeParamDecl->getName(),
+ argGenericParamDeclRef.GetName());
+ continue;
+ }
+ }
+ }
+
+ ModuleSpecializationInfo::GenericArgInfo genericArgInfo;
+ genericArgInfo.paramDecl = genericTypeParamDecl;
+ genericArgInfo.argVal = argType;
+ specializationInfo->genericArgs.add(genericArgInfo);
+
+ // Walk through the declared constraints for the parameter,
+ // and check that the argument actually satisfies them.
+ for(auto constraintDecl : genericTypeParamDecl->getMembersOfType<GenericTypeConstraintDecl>())
+ {
+ // Get the type that the constraint is enforcing conformance to
+ auto interfaceType = GetSup(DeclRef<GenericTypeConstraintDecl>(constraintDecl, nullptr));
+
+ // Use our semantic-checking logic to search for a witness to the required conformance
+ auto witness = visitor.tryGetSubtypeWitness(argType, interfaceType);
+ if (!witness)
+ {
+ // If no witness was found, then we will be unable to satisfy
+ // the conformances required.
+ sink->diagnose(genericTypeParamDecl,
+ Diagnostics::typeArgumentForGenericParameterDoesNotConformToInterface,
+ argType,
+ genericTypeParamDecl->nameAndLoc.name,
+ interfaceType);
+ }
+
+ ModuleSpecializationInfo::GenericArgInfo constraintArgInfo;
+ constraintArgInfo.paramDecl = constraintDecl;
+ constraintArgInfo.argVal = witness;
+ specializationInfo->genericArgs.add(constraintArgInfo);
+ }
+ }
+ break;
+
+ case SpecializationParam::Flavor::ExistentialType:
+ {
+ auto interfaceType = param.object.as<Type>();
+ SLANG_ASSERT(interfaceType);
+
+ auto witness = visitor.tryGetSubtypeWitness(argType, interfaceType);
+ if (!witness)
+ {
+ // If no witness was found, then we will be unable to satisfy
+ // the conformances required.
+ sink->diagnose(SourceLoc(),
+ Diagnostics::typeArgumentDoesNotConformToInterface,
+ argType,
+ interfaceType);
+ }
+
+ ExpandedSpecializationArg expandedArg;
+ expandedArg.val = argType;
+ expandedArg.witness = witness;
+
+ specializationInfo->existentialArgs.add(expandedArg);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unhandled specialization parameter flavor");
}
+ }
+ return specializationInfo;
+ }
- auto witness = visitor.tryGetSubtypeWitness(argType, slotType);
- if (!witness)
+
+ static void _extractSpecializationArgs(
+ ComponentType* componentType,
+ List<RefPtr<Expr>> const& argExprs,
+ List<SpecializationArg>& outArgs,
+ DiagnosticSink* sink)
+ {
+ auto linkage = componentType->getLinkage();
+
+ auto argCount = argExprs.getCount();
+ for(Index ii = 0; ii < argCount; ++ii )
+ {
+ auto argExpr = argExprs[ii];
+ auto paramInfo = componentType->getSpecializationParam(ii);
+
+ // TODO: We should support non-type arguments here
+
+ auto argType = checkProperType(linkage, TypeExp(argExpr), sink);
+ if( !argType )
{
// If no witness was found, then we will be unable to satisfy
// the conformances required.
- sink->diagnose(SourceLoc(), Diagnostics::existentialSlotArgDoesNotConform, ii, slotType);
- return;
+ sink->diagnose(argExpr,
+ Diagnostics::expectedAType,
+ argExpr->type);
+ continue;
}
- ExistentialTypeSlots::Arg arg;
- arg.type = argType;
- arg.witness = witness;
- ioSlots.args.add(arg);
+ SpecializationArg arg;
+ arg.val = argType;
+ outArgs.add(arg);
}
}
- void EntryPoint::_specializeExistentialTypeParams(
- List<RefPtr<Expr>> const& args,
+ RefPtr<ComponentType::SpecializationInfo> EntryPoint::_validateSpecializationArgsImpl(
+ SpecializationArg const* inArgs,
+ Index inArgCount,
DiagnosticSink* sink)
{
- Slang::_specializeExistentialTypeParams(getLinkage(), m_existentialSlots, args, sink);
- }
+ auto args = inArgs;
+ auto argCount = inArgCount;
- /// 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();
+ SemanticsVisitor visitor(getLinkage(), sink);
- // 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.
+ // The first N arguments will be for the explicit generic parameters
+ // of the entry point (if it has any).
//
- auto entryPointFuncDecl = unspecializedEntryPoint->getFuncDecl();
+ auto genericSpecializationParamCount = getGenericSpecializationParamCount();
+ SLANG_ASSERT(argCount >= genericSpecializationParamCount);
- SemanticsVisitor semantics(
- linkage,
- sink);
+ Result result = SLANG_OK;
- DeclRef<FuncDecl> entryPointFuncDeclRef = makeDeclRef(entryPointFuncDecl.Ptr());
- if( auto genericDecl = as<GenericDecl>(entryPointFuncDecl->ParentDecl) )
+ RefPtr<EntryPointSpecializationInfo> info = new EntryPointSpecializationInfo();
+
+ DeclRef<FuncDecl> specializedFuncDeclRef = m_funcDeclRef;
+ if(genericSpecializationParamCount)
{
- // 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.
+ // We need to construct a generic application and use
+ // the semantic checking machinery to expand out
+ // the rest of the arguments via inference...
- auto session = linkage->getSessionImpl();
- auto genericDeclRef = makeDeclRef(genericDecl);
+ auto genericDeclRef = m_funcDeclRef.GetParent().as<GenericDecl>();
+ SLANG_ASSERT(genericDeclRef); // otherwise we wouldn't have generic parameters
- // The first pieces is a `VarExpr` that refers to `genericDecl`.
- //
- // TODO: This would not be needed if we instead parsed
- // the supplied entry-point name into an expression
- // earlier in this function.
- //
- RefPtr<VarExpr> genericExpr = new VarExpr();
- genericExpr->declRef = genericDeclRef;
- genericExpr->type.type = getTypeForDeclRef(session, genericDeclRef);
-
- // Next we construct the actual `GenericAppExpr`
- //
- RefPtr<GenericAppExpr> genericAppExpr = new GenericAppExpr();
- genericAppExpr->FunctionExpr = genericExpr;
- genericAppExpr->Arguments = genericArgs;
+ RefPtr<GenericSubstitution> genericSubst = new GenericSubstitution();
+ genericSubst->outer = genericDeclRef.substitutions.substitutions;
+ genericSubst->genericDecl = genericDeclRef.getDecl();
- // We use the semantics visitor to perform the
- // actual checking logic (this might report
- // errors)
- //
- auto checkedExpr = semantics.checkGenericAppWithCheckedArgs(genericAppExpr);
-
- // Now we need to extract an appropriate decl-ref for the entry
- // point from the `checkedExpr`.
- //
- if( auto declRefExpr = checkedExpr.as<DeclRefExpr>() )
+ for(Index ii = 0; ii < genericSpecializationParamCount; ++ii)
{
- // 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.
-
- entryPointFuncDeclRef = declRefExpr->declRef.as<FuncDecl>();
+ auto specializationArg = args[ii];
+ genericSubst->args.add(specializationArg.val);
}
- else if( semantics.IsErrorExpr(checkedExpr) )
+
+ for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
{
- // Any semantic error that occured should have been
- // reported already.
- return nullptr;
+ auto constraintSubst = genericDeclRef.substitutions;
+ constraintSubst.substitutions = genericSubst;
+
+ DeclRef<GenericTypeConstraintDecl> constraintDeclRef(
+ constraintDecl, constraintSubst);
+
+ auto sub = GetSub(constraintDeclRef);
+ auto sup = GetSup(constraintDeclRef);
+
+ auto subTypeWitness = visitor.tryGetSubtypeWitness(sub, sup);
+ if(subTypeWitness)
+ {
+ genericSubst->args.add(subTypeWitness);
+ }
+ else
+ {
+ // TODO: diagnose a problem here
+ sink->diagnose(constraintDecl, Diagnostics::typeArgumentDoesNotConformToInterface, sub, sup);
+ result = SLANG_FAIL;
+ continue;
+ }
}
- else
+
+ specializedFuncDeclRef.substitutions.substitutions = genericSubst;
+ }
+
+ info->specializedFuncDeclRef = specializedFuncDeclRef;
+
+ // Once the generic parameters (if any) have been dealt with,
+ // any remaining specialization arguments are for existential/interface
+ // specialization parameters, attached to the value parameters
+ // of the entry point.
+ //
+ args += genericSpecializationParamCount;
+ argCount -= genericSpecializationParamCount;
+
+ auto existentialSpecializationParamCount = getExistentialSpecializationParamCount();
+ SLANG_ASSERT(argCount == existentialSpecializationParamCount);
+
+ for( Index ii = 0; ii < existentialSpecializationParamCount; ++ii )
+ {
+ auto& param = m_existentialSpecializationParams[ii];
+ auto& specializationArg = args[ii];
+
+ // TODO: We need to handle all the cases of "flavor" for the `param`s (not just types)
+
+ auto paramType = param.object.as<Type>();
+ auto argType = specializationArg.val.as<Type>();
+
+ auto witness = visitor.tryGetSubtypeWitness(argType, paramType);
+ if (!witness)
{
- // 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`");
- UNREACHABLE_RETURN(nullptr);
+ // If no witness was found, then we will be unable to satisfy
+ // the conformances required.
+ sink->diagnose(SourceLoc(), Diagnostics::typeArgumentDoesNotConformToInterface, argType, paramType);
+ result = SLANG_FAIL;
+ continue;
}
+
+ ExpandedSpecializationArg expandedArg;
+ expandedArg.val = specializationArg.val;
+ expandedArg.witness = witness;
+ info->existentialSpecializationArgs.add(expandedArg);
}
- RefPtr<EntryPoint> specializedEntryPoint = EntryPoint::create(
- entryPointFuncDeclRef,
- unspecializedEntryPoint->getProfile());
+ return info;
+ }
- // Next we need to validate the existential arguments.
- specializedEntryPoint->_specializeExistentialTypeParams(existentialArgs, sink);
+ /// Create a specialization an existing entry point based on specialization argument expressions.
+ RefPtr<ComponentType> createSpecializedEntryPoint(
+ EntryPoint* unspecializedEntryPoint,
+ List<RefPtr<Expr>> const& argExprs,
+ DiagnosticSink* sink)
+ {
+ // We need to convert all of the `Expr` arguments
+ // into `SpecializationArg`s, so that we can bottleneck
+ // through the shared logic.
+ //
+ List<SpecializationArg> args;
+ _extractSpecializationArgs(unspecializedEntryPoint, argExprs, args, sink);
+ if(sink->GetErrorCount())
+ return nullptr;
- return specializedEntryPoint;
+ return unspecializedEntryPoint->specialize(
+ args.getBuffer(),
+ args.getCount(),
+ sink);
}
- /// Parse an array of strings as generic arguments.
+ /// Parse an array of strings as specialization arguments.
///
/// Names in the strings will be parsed in the context of
/// the code loaded into the given compile request.
///
- void parseGenericArgStrings(
+ void parseSpecializationArgStrings(
EndToEndCompileRequest* endToEndReq,
List<String> const& genericArgStrings,
List<RefPtr<Expr>>& outGenericArgs)
{
- auto unspecialiedProgram = endToEndReq->getUnspecializedProgram();
+ auto unspecialiedProgram = endToEndReq->getUnspecializedGlobalComponentType();
// TODO: Building a list of `scopesToTry` here shouldn't
// be required, since the `Scope` type itself has the ability
@@ -11004,351 +11282,109 @@ static bool doesParameterMatch(
}
}
+ if(!argExpr)
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::internalCompilerError, "couldn't parse specialization argument");
+ return;
+ }
+
outGenericArgs.add(argExpr);
}
}
- void Program::_specializeExistentialTypeParams(
- List<RefPtr<Expr>> const& args,
- DiagnosticSink* sink)
- {
- Slang::_specializeExistentialTypeParams(getLinkageImpl(), m_globalExistentialSlots, args, sink);
- }
-
Type* Linkage::specializeType(
Type* unspecializedType,
Int argCount,
Type* const* args,
DiagnosticSink* sink)
{
+ SLANG_ASSERT(unspecializedType);
+
// TODO: We should cache and re-use specialized types
// when the exact same arguments are provided again later.
SemanticsVisitor visitor(this, sink);
+ SpecializationParams specializationParams;
+ _collectExistentialSpecializationParamsRec(specializationParams, unspecializedType);
- ExistentialTypeSlots slots;
- _collectExistentialTypeParamsRec(slots, unspecializedType);
-
- assert(slots.paramTypes.getCount() == argCount);
+ assert(specializationParams.getCount() == argCount);
+ ExpandedSpecializationArgs specializationArgs;
for( Int aa = 0; aa < argCount; ++aa )
{
+ auto paramType = specializationParams[aa].object.as<Type>();
auto argType = args[aa];
- ExistentialTypeSlots::Arg arg;
- arg.type = argType;
- arg.witness = visitor.tryGetSubtypeWitness(argType, slots.paramTypes[aa]);
- slots.args.add(arg);
+ ExpandedSpecializationArg arg;
+ arg.val = argType;
+ arg.witness = visitor.tryGetSubtypeWitness(argType, paramType);
+ specializationArgs.add(arg);
}
RefPtr<ExistentialSpecializedType> specializedType = new ExistentialSpecializedType();
specializedType->baseType = unspecializedType;
- specializedType->slots = slots;
+ specializedType->args = specializationArgs;
m_specializedTypes.add(specializedType);
return specializedType;
}
- // Shared implementation logic for the `_createSpecializedProgram*` entry points.
- static RefPtr<Program> _createSpecializedProgramImpl(
+ /// Shared implementation logic for the `_createSpecializedProgram*` entry points.
+ static RefPtr<ComponentType> _createSpecializedProgramImpl(
Linkage* linkage,
- Program* unspecializedProgram,
- List<RefPtr<Expr>> const& globalGenericArgs,
- List<RefPtr<Expr>> const& globalExistentialArgs,
+ ComponentType* unspecializedProgram,
+ List<RefPtr<Expr>> const& specializationArgExprs,
DiagnosticSink* sink)
{
- // TODO: If there are no specialization arguments,
+ // If there are no specialization arguments,
// then the the result of specialization should
- // be the same as the input... *but* we are promising
- // to return a program without any entry points,
- // and that screws things up here.
- //
- // For now we just carefully avod this early-exit
- // if we have any entry points.
+ // be the same as the input.
//
- // Eventually we should try to revise the model so
- // that specialization of a program that includes
- // entry points can make some kind of sense.
- //
- if( globalGenericArgs.getCount() == 0
- && globalExistentialArgs.getCount() == 0
- && unspecializedProgram->getEntryPointCount() == 0)
+ auto specializationArgCount = specializationArgExprs.getCount();
+ if( specializationArgCount == 0 )
{
return unspecializedProgram;
}
- // The given `unspecializedProgram` should be one that
- // was checked through the front-end, so that now we
- // only need to check if the given arguments can satisfy
- // the requirements of the global generic parameters.
- //
- // The new program needs to start off with the same
- // module dependency list as the original.
- //
- RefPtr<Program> specializedProgram = new Program(linkage);
- for(auto module : unspecializedProgram->getModuleDependencies())
- {
- specializedProgram->addReferencedLeafModule(module);
- }
-
-
- // We will collect all the global generic parameters
- // defined in the modules being referenced, to find
- // the global generic parameter signature of the
- // program.
- //
- // TODO: Note that this doesn't handle the case where one
- // or more of the type *arguments* that we are specifying
- // ends up requiring additional modules to be referenced,
- // which might in turn introduce new global generic parameters.
- //
- List<RefPtr<GlobalGenericParamDecl>> globalGenericParams;
- for(auto module : unspecializedProgram->getModuleDependencies())
+ auto specializationParamCount = unspecializedProgram->getSpecializationParamCount();
+ if(specializationArgCount != specializationParamCount )
{
- for(auto param : module->getModuleDecl()->getMembersOfType<GlobalGenericParamDecl>())
- globalGenericParams.add(param);
- }
-
- // Next, we will check whether the supplied arguments can
- // satisfy those parameters.
- //
- // An easy early-out case will be if the number of
- // arguments isn't correct.
- //
- if (globalGenericParams.getCount() != globalGenericArgs.getCount())
- {
- sink->diagnose(SourceLoc(), Diagnostics::mismatchGlobalGenericArguments,
- globalGenericParams.getCount(),
- globalGenericArgs.getCount());
+ sink->diagnose(SourceLoc(), Diagnostics::mismatchSpecializationArguments,
+ specializationParamCount,
+ specializationArgCount);
return nullptr;
}
- // We have an appropriate number of arguments for the global generic parameters,
+ // We have an appropriate number of arguments for the global specialization 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.
- //
- 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.
- //
- Index argCounter = 0;
- for(auto& globalGenericParam : globalGenericParams)
- {
- // Get the argument that matches this parameter.
- Index argIndex = argCounter++;
- SLANG_ASSERT(argIndex < globalGenericArgs.getCount());
- auto globalGenericArg = checkProperType(linkage, TypeExp(globalGenericArgs[argIndex]), sink);
- if (!globalGenericArg)
- {
- sink->diagnose(globalGenericParam, Diagnostics::globalGenericArgumentNotAType, globalGenericParam->getName());
- return nullptr;
- }
-
- // 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>())
- {
- if(argGenericParamDeclRef.getDecl() == globalGenericParam)
- {
- // We are trying to specialize a generic parameter using itself.
- sink->diagnose(globalGenericParam,
- Diagnostics::cannotSpecializeGlobalGenericToItself,
- globalGenericParam->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());
- continue;
- }
- }
- }
-
- // 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));
-
- // Use our semantic-checking logic to search for a witness to the required conformance
- 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);
- }
-
- // Add the substitution for this parameter to the global substitution
- // set that we are building.
-
- *globalGenericSubstLink = subst;
- globalGenericSubstLink = &subst->outer;
- }
+ List<SpecializationArg> specializationArgs;
+ _extractSpecializationArgs(unspecializedProgram, specializationArgExprs, specializationArgs, sink);
if(sink->GetErrorCount())
return nullptr;
- specializedProgram->setGlobalGenericSubsitution(globalGenericSubsts);
-
- return specializedProgram;
- }
-
- /// Create a specialized copy of `unspecializedProgram`.
- ///
- /// The specialized program will include the entry points
- /// from the original program (whether thsoe entry points
- /// are specialized or not).
- ///
- static RefPtr<Program> _createSpecializedProgram(
- Linkage* linkage,
- Program* unspecializedProgram,
- List<RefPtr<Expr>> const& globalGenericArgs,
- List<RefPtr<Expr>> const& globalExistentialArgs,
- DiagnosticSink* sink)
- {
- auto specializedProgram = _createSpecializedProgramImpl(
- linkage,
- unspecializedProgram,
- globalGenericArgs,
- globalExistentialArgs,
+ auto specializedProgram = unspecializedProgram->specialize(
+ specializationArgs.getBuffer(),
+ specializationArgs.getCount(),
sink);
- // We need to ensure that the specialized program has whatever
- // entry points (and groups) the unspecialized one had.
- //
- for( auto entryPointGroup : unspecializedProgram->getEntryPointGroups() )
- {
- specializedProgram->addEntryPointGroup(entryPointGroup);
- }
-
- // Now deal with the shader parameters and existential arguments
- //
- // Note: We should in theory be able to just copy over the shader
- // parameters and existential slot information from the unspecialized
- // program. This could save some time, but it would also mean that
- // the only way to create a specialized program is by creating an
- // unspecialized on first, which is maybe not always desirable.
- //
- specializedProgram->_collectShaderParams(sink);
- specializedProgram->_specializeExistentialTypeParams(globalExistentialArgs, sink);
-
return specializedProgram;
}
- SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::specializeProgram(
- slang::IProgram* inUnspecializedProgram,
- SlangInt specializationArgCount,
- slang::SpecializationArg const* specializationArgs,
- slang::IProgram** outSpecializedProgram,
- ISlangBlob** outDiagnostics)
- {
- auto unspecializedProgram = asInternal(inUnspecializedProgram);
-
- if( specializationArgCount == 0 )
- {
- *outSpecializedProgram = ComPtr<slang::IProgram>(asExternal(unspecializedProgram)).detach();
- return SLANG_OK;
- }
-
- List<RefPtr<Expr>> globalGenericArgs;
-
- List<RefPtr<Expr>> globalExistentialArgs;
- for( Int ii = 0; ii < specializationArgCount; ++ii )
- {
- auto& specializationArg = specializationArgs[ii];
- switch( specializationArg.kind )
- {
- case slang::SpecializationArg::Kind::Type:
- {
- auto typeArg = asInternal(specializationArg.type);
- RefPtr<SharedTypeExpr> argExpr = new SharedTypeExpr();
- argExpr->base = TypeExp(typeArg);
- argExpr->type = QualType(getTypeType(typeArg));
-
- globalExistentialArgs.add(argExpr);
- }
- break;
-
- default:
- return SLANG_E_INVALID_ARG;
- }
- }
-
- DiagnosticSink sink(getSourceManager());
- auto specializedProgram = _createSpecializedProgram(
- this,
- unspecializedProgram,
- globalGenericArgs,
- globalExistentialArgs,
- &sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- if(!specializedProgram)
- return SLANG_FAIL;
-
- *outSpecializedProgram = ComPtr<slang::IProgram>(asExternal(specializedProgram)).detach();
- return SLANG_OK;
- }
-
- /// Specialize an entry point that was checked by the front-end, based on generic arguments.
+ /// Specialize an entry point that was checked by the front-end, based on specialization arguments.
///
- /// If the end-to-end compile request included generic argument strings
+ /// If the end-to-end compile request included specialization argument strings
/// for this entry point, then they will be parsed, checked, and used
/// as arguments to the generic entry point.
///
/// Returns a specialized entry point if everything worked as expected.
/// Returns null and diagnoses errors if anything goes wrong.
///
- RefPtr<EntryPoint> createSpecializedEntryPoint(
+ RefPtr<ComponentType> createSpecializedEntryPoint(
EndToEndCompileRequest* endToEndReq,
EntryPoint* unspecializedEntryPoint,
EndToEndCompileRequest::EntryPointInfo const& entryPointInfo)
@@ -11359,33 +11395,35 @@ static bool doesParameterMatch(
// If the user specified generic arguments for the entry point,
// then we will need to parse the arguments first.
//
- List<RefPtr<Expr>> genericArgs;
- parseGenericArgStrings(
- endToEndReq,
- entryPointInfo.genericArgStrings,
- genericArgs);
-
- List<RefPtr<Expr>> existentialArgs;
- parseGenericArgStrings(
+ List<RefPtr<Expr>> specializationArgExprs;
+ parseSpecializationArgStrings(
endToEndReq,
- entryPointInfo.existentialArgStrings,
- existentialArgs);
+ entryPointInfo.specializationArgStrings,
+ specializationArgExprs);
// Next we specialize the entry point function given the parsed
// generic argument expressions.
//
auto entryPoint = createSpecializedEntryPoint(
unspecializedEntryPoint,
- genericArgs,
- existentialArgs,
+ specializationArgExprs,
sink);
return entryPoint;
}
- /// Create a specialized program based on the given compile request.
+ /// Create a specialized component type for the global scope of the given compile request.
+ ///
+ /// The specialized program will be consistent with that created by
+ /// `createUnspecializedGlobalComponentType`, and will simply fill in
+ /// its specialization parameters with the arguments (if any) supllied
+ /// as part fo the end-to-end compile request.
+ ///
+ /// The layout of the new component type will be consistent with that
+ /// of the original *if* there are no global generic type parameters
+ /// (only interface/existential parameters).
///
- RefPtr<Program> createSpecializedProgram(
+ RefPtr<ComponentType> createSpecializedGlobalComponentType(
EndToEndCompileRequest* endToEndReq)
{
// The compile request must have already completed front-end processing,
@@ -11393,36 +11431,32 @@ static bool doesParameterMatch(
// to parse and check any generic arguments that are being supplied for
// global or entry-point generic parameters.
//
- auto unspecializedProgram = endToEndReq->getUnspecializedProgram();
+ auto unspecializedProgram = endToEndReq->getUnspecializedGlobalComponentType();
auto linkage = endToEndReq->getLinkage();
+ auto sink = endToEndReq->getSink();
- // First, let's parse the generic argument strings that were
- // provided via the API, so taht we can match them
+ // First, let's parse the specialization argument strings that were
+ // provided via the API, so that we can match them
// against what was declared in the program.
//
- List<RefPtr<Expr>> globalGenericArgs;
- parseGenericArgStrings(
+ List<RefPtr<Expr>> globalSpecializationArgs;
+ parseSpecializationArgStrings(
endToEndReq,
- endToEndReq->globalGenericArgStrings,
- globalGenericArgs);
+ endToEndReq->globalSpecializationArgStrings,
+ globalSpecializationArgs);
- // Also handle global existential type arguments.
- List<RefPtr<Expr>> globalExistentialArgs;
- parseGenericArgStrings(
- endToEndReq,
- endToEndReq->globalExistentialSlotArgStrings,
- globalExistentialArgs);
+ // Don't proceed further if anything failed to parse.
+ if(sink->GetErrorCount())
+ return nullptr;
// Now we create the initial specialized program by
// applying the global generic arguments (if any) to the
// unspecialized program.
//
- auto sink = endToEndReq->getSink();
auto specializedProgram = _createSpecializedProgramImpl(
- endToEndReq->getLinkage(),
+ linkage,
unspecializedProgram,
- globalGenericArgs,
- globalExistentialArgs,
+ globalSpecializationArgs,
sink);
// If anything went wrong with the global generic
@@ -11450,33 +11484,69 @@ static bool doesParameterMatch(
endToEndReq->entryPoints.setCount(entryPointCount);
}
- Index entryPointCounter = 0;
+ return specializedProgram;
+ }
- for( auto unspecializedEntryPointGroup : unspecializedProgram->getEntryPointGroups() )
- {
- List<RefPtr<EntryPoint>> specializedEntryPoints;
- for( auto unspecializedEntryPoint : unspecializedEntryPointGroup->getEntryPoints() )
- {
- Index entryPointIndex = entryPointCounter++;
- auto& entryPointInfo = endToEndReq->entryPoints[entryPointIndex];
+ /// Create a specialized program based on the given compile request.
+ ///
+ /// The specialized program created here includes both the global
+ /// scope for all the translation units involved and all the entry
+ /// points, and it also includes any specialization arguments
+ /// that were supplied.
+ ///
+ /// It is important to note that this function specializes
+ /// the global scope and the entry points in isolation and then
+ /// composes them, and that this can lead to different layout
+ /// from the result of `createUnspecializedGlobalAndEntryPointsComponentType`.
+ ///
+ /// If we have a module `M` with entry point `E`, and each has one
+ /// specialization parameter, then `createUnspecialized...` will yield:
+ ///
+ /// compose(M,E)
+ ///
+ /// That composed type will have two specialization parameters (the one
+ /// from `M` plus the one from `E`) and so we might specialize it to get:
+ ///
+ /// specialize(compose(M,E), X, Y)
+ ///
+ /// while if we use `createSpecialized...` we will get:
+ ///
+ /// compose(specialize(M,X), specialize(E,Y))
+ ///
+ /// While these options are semantically equivalent, they would not lay
+ /// out the same way in memory.
+ ///
+ /// There are many reasons why an application might prefer one over the
+ /// other, and an application that cares should use the more explicit
+ /// APIs to construct what they want. The behavior of this function
+ /// is just to provide a reasonable default for use by end-to-end
+ /// compilation (e.g., from the command line).
+ ///
+ RefPtr<ComponentType> createSpecializedGlobalAndEntryPointsComponentType(
+ EndToEndCompileRequest* endToEndReq)
+ {
+ auto specializedGlobalComponentType = endToEndReq->getSpecializedGlobalComponentType();
- auto specializedEntryPoint = createSpecializedEntryPoint(endToEndReq, unspecializedEntryPoint, entryPointInfo);
- specializedEntryPoints.add(specializedEntryPoint);
- }
+ List<RefPtr<ComponentType>> allComponentTypes;
+ allComponentTypes.add(specializedGlobalComponentType);
- RefPtr<EntryPointGroup> specializedEntryPointGroup = EntryPointGroup::create(linkage, specializedEntryPoints, endToEndReq->getSink());
- specializedProgram->addEntryPointGroup(specializedEntryPointGroup);
- }
+ auto unspecializedGlobalAndEntryPointsComponentType = endToEndReq->getUnspecializedGlobalAndEntryPointsComponentType();
+ auto entryPointCount = unspecializedGlobalAndEntryPointsComponentType->getEntryPointCount();
- // Finalize the information for the specialized program,
- // now that we have computed its entry point list, etc.
- //
- specializedProgram->_collectShaderParams(sink);
- specializedProgram->_specializeExistentialTypeParams(globalExistentialArgs, sink);
+ for(Index ii = 0; ii < entryPointCount; ++ii)
+ {
+ auto& entryPointInfo = endToEndReq->entryPoints[ii];
+ auto unspecializedEntryPoint = unspecializedGlobalAndEntryPointsComponentType->getEntryPoint(ii);
- return specializedProgram;
+ auto specializedEntryPoint = createSpecializedEntryPoint(endToEndReq, unspecializedEntryPoint, entryPointInfo);
+ allComponentTypes.add(specializedEntryPoint);
+ }
+
+ RefPtr<ComponentType> composed = CompositeComponentType::create(endToEndReq->getLinkage(), allComponentTypes);
+ return composed;
}
+
void checkTranslationUnit(
TranslationUnitRequest* translationUnit)
{
@@ -11488,6 +11558,8 @@ static bool doesParameterMatch(
// checking that is required on all declarations
// in the translation unit.
visitor.checkDecl(translationUnit->getModuleDecl());
+
+ translationUnit->getModule()->_collectShaderParams();
}