summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-05-20 10:40:38 -0700
committerGitHub <noreply@github.com>2019-05-20 10:40:38 -0700
commit71e35b6822b9e2846e129a888774d45a5e0827da (patch)
treea8ede019d2d7e4630bd7dd435eb658f83baefe5b /source/slang
parent3b9994bbbc5656125b2a51baa89706dd9b8a3e03 (diff)
Changes required for application adoption of interface-type parameters (#963)
* A few changes required for application adoption of interface-type parameters There are a few small changes here that are all related in that they arose from trying to integrate support for specialization via global interface-type shader parameters into a real application. Allow querying the "pending" layout via reflection API ------------------------------------------------------ The naming here isn't ideal, and could probably use a round of "bikeshedding" to arrive at something better, but the basic idea is that when you have a type like: ``` struct MyStuff { int a; IFoo foo; int b; } ``` the fields `a` and `b` get allocated space directly in the "primary" layout for `MyStuff` (at offsets 0 and 4, with `sizeof(MyStuff) == 8`), but the `foo` field can't be allocated space until we know what concrete type will get plugged in there. If we have a concrete type in mind: ``` struct Bar : IFoo { int bar; } ``` then we can know how much space the `foo` field will take up, but we still can't allocate it space directly in `MyStuff`, because we already decided that `sizeof(MyStuff) == 8`. Now imagine we place some `MyStuff` values into constant buffers: ``` cbuffer X { MyStuff x; } cbuffer Y { MyStuff y; float4 z; } ``` In each case we know that we want to place the `MyStuff::foo` field at the end of the containing constant buffer so that it doesn't disrupt the layout of the existing fields. But that means that the offset of `MyStuff::foo` relative to the start of the `MyStuff` isn't fixed, because of unrelated fields like `z` that need to get in between. In our layout code, we handle this by having a notion of a "pending" layout. Once we know how `MyStuff::foo` will be specialized, we can compute both a "primary" and a "pending" layout for `MyStuff`, which basically treats it as if it were two distinct types: ``` struct MyStuff_Primary { int a; int b; } struct MyStuff_Pending { Bar foo; } ``` Layout for an aggregate type like the `X` or `Y` constant buffer then proceeds by computing an aggregate primary layout and an aggregate pending layout, and then finally a constant buffer or parameter block "flushes" all or part of the pending data by appending it to the primary data to get the final layout. What all this means is that a type like `MyStuff` will have two different layouts (a default one for the primary data and a "pending" one for any specialized interface-type fields), and a variable like `Y::y` will also have two variable layouts that specify offsets (one set of offsets for its primary part, and one set of offsets for its pending part). In order to handle interface-type fields with these layout rules, an application needs a way to query the "pending" part of a type or variable layout, which luckily gives it back just another type/variable layout. The API change here is minimal, although actually exploiting the new API correctly in application code could prove challenging. Allow creating of explicitly specialized types ---------------------------------------------- This feature isn't actually implemented all the way through the compiler (I just needed enough to make the API calls go through), but I've added support for specializing a type that has interface-type fields through the reflection API. This maps to an `ExistentialSpecializedType` in the AST, and I'm lowering it to the IR as a `BindExistentialsType`, although that isn't 100% correct for the future. This feature will require a future PR to actually flesh out the implementation work, but I'll wait until that is the sticking point on the application side before I do that. Introduce a tiny `Hasher` abstraction ------------------------------------- While implementing all the boilerplate for a new `Type` subclass (we really need to reduce that work...), I got fed up with how we do hash-code computation and introduced a small utility `Hasher` type that is intended to wrap up the idiom of combining hashes. For now this isn't a major change, but in the future I'd like to expand on the design a bit to clean up some of the warts around how we handle hashing: * The `Hasher` implementation can and should switch from maintaining a single `HashCode` as its state to something that contains a more complete state (larger than the hash code) and just hashes new bytes into that state as it goes. This should make it possible to implement a `Hasher` for more serious hash functions, whether MD5, CityHash, or whatever we decide is good default. * Things that are hashable shouldn't have a `getHashCode()` method, but instead should have something like a `hashInto(Hasher&)` method. This change would have the dual benefits that (1) a composite type can easily hash all the fields that contribute to its identity into the hasher with minimal fuss/boilerplate, and (2) the hashes for composite types will be of higher quality because they can exploit all the bits of the hasher's state to combine the fields, instead of restricting each sub-field to just the bits in a hash code. We should be able to incrementally improve the quality of our design there over future changes, but for now it probably isn't a critical priority. Fixes for legalization of existential types ------------------------------------------- There were some missing cases in the handling of type legalization, such that a global interface-type shader parameter that got specialized to a type that contains *only* resource-type fields would cause a crash in the legalization step. I added a test for this case, and then made `ir-legalize-types.cpp` account for this case (the code to handle it ias a bit of a kludge, and shows that the `declareVars()` routine there is getting to a level of complexity that is worrying. * fixup: review feedback
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/check.cpp35
-rw-r--r--source/slang/compiler.h33
-rw-r--r--source/slang/diagnostics.h2
-rw-r--r--source/slang/ir-legalize-types.cpp54
-rw-r--r--source/slang/lower-to-ir.cpp18
-rw-r--r--source/slang/reflection.cpp44
-rw-r--r--source/slang/slang.cpp19
-rw-r--r--source/slang/syntax.cpp102
-rw-r--r--source/slang/syntax.h26
-rw-r--r--source/slang/type-defs.h13
10 files changed, 306 insertions, 40 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 6a40f436a..d51785112 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -10763,6 +10763,41 @@ static bool doesParameterMatch(
Slang::_specializeExistentialTypeParams(getLinkage(), m_globalExistentialSlots, args, sink);
}
+ Type* Linkage::specializeType(
+ Type* unspecializedType,
+ Int argCount,
+ Type* const* args,
+ DiagnosticSink* sink)
+ {
+ // TODO: We should cache and re-use specialized types
+ // when the exact same arguments are provided again later.
+
+ SemanticsVisitor visitor(this, sink);
+
+
+ ExistentialTypeSlots slots;
+ _collectExistentialTypeParamsRec(slots, unspecializedType);
+
+ assert(slots.paramTypes.getCount() == argCount);
+
+ for( Int aa = 0; aa < argCount; ++aa )
+ {
+ auto argType = args[aa];
+
+ ExistentialTypeSlots::Arg arg;
+ arg.type = argType;
+ arg.witness = visitor.tryGetSubtypeWitness(argType, slots.paramTypes[aa]);
+ slots.args.add(arg);
+ }
+
+ RefPtr<ExistentialSpecializedType> specializedType = new ExistentialSpecializedType();
+ specializedType->baseType = unspecializedType;
+ specializedType->slots = slots;
+
+ m_specializedTypes.add(specializedType);
+
+ return specializedType;
+ }
/// Specialize a program to global generic arguments
RefPtr<Program> createSpecializedProgram(
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index 233fa3d05..9b7e06be0 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -133,31 +133,6 @@ namespace Slang
ComPtr<ISlangBlob> blob;
};
- /// Collects information about existential type parameters and their arguments.
- struct ExistentialTypeSlots
- {
- /// For each type parameter, holds the interface/existential type that constrains it.
- List<RefPtr<Type>> paramTypes;
-
- /// An argument for an existential type parameter.
- ///
- /// Comprises a concrete type and a witness for its conformance to the desired
- /// interface/existential type for the corresponding parameter.
- ///
- struct Arg
- {
- RefPtr<Type> type;
- RefPtr<Val> witness;
- };
-
- /// Any arguments provided for the existential type parameters.
- ///
- /// It is possible for `args` to be empty even if `paramTypes` is non-empty;
- /// that situation represents an unspecialized program or entry point.
- ///
- List<Arg> args;
- };
-
/// Information collected about global or entry-point shader parameters
struct ShaderParamInfo
{
@@ -665,6 +640,12 @@ namespace Slang
RefPtr<Expr> parseTypeString(String typeStr, RefPtr<Scope> scope);
+ Type* specializeType(
+ Type* unspecializedType,
+ Int argCount,
+ Type* const* args,
+ DiagnosticSink* sink);
+
/// Add a mew target amd return its index.
UInt addTarget(
CodeGenTarget target);
@@ -754,6 +735,8 @@ namespace Slang
/// Is the given module in the middle of being imported?
bool isBeingImported(Module* module);
+
+ List<RefPtr<Type>> m_specializedTypes;
};
/// Shared functionality between front- and back-end compile requests.
diff --git a/source/slang/diagnostics.h b/source/slang/diagnostics.h
index 7d76ffa85..8e5ba809b 100644
--- a/source/slang/diagnostics.h
+++ b/source/slang/diagnostics.h
@@ -227,6 +227,8 @@ namespace Slang
/// During propagation of an exception for an internal
/// error, note that this source location was involved
void noteInternalErrorLoc(SourceLoc const& loc);
+
+ SlangResult getBlobIfNeeded(ISlangBlob** outBlob);
};
/// An `ISlangWriter` that writes directly to a diagnostic sink.
diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp
index cfc495070..18039315e 100644
--- a/source/slang/ir-legalize-types.cpp
+++ b/source/slang/ir-legalize-types.cpp
@@ -127,7 +127,8 @@ static LegalVal declareVars(
LegalVarChain const& varChain,
UnownedStringSlice nameHint,
IRInst* leafVar,
- IRGlobalNameInfo* globalNameInfo);
+ IRGlobalNameInfo* globalNameInfo,
+ bool isSpecial);
/// Unwrap a value with flavor `wrappedBuffer`
///
@@ -1266,9 +1267,10 @@ static LegalVal legalizeLocalVar(
IRVar* irLocalVar)
{
// Legalize the type for the variable's value
+ auto originalValueType = irLocalVar->getDataType()->getValueType();
auto legalValueType = legalizeType(
context,
- irLocalVar->getDataType()->getValueType());
+ originalValueType);
auto originalRate = irLocalVar->getRate();
@@ -1311,7 +1313,7 @@ static LegalVal legalizeLocalVar(
UnownedStringSlice nameHint = findNameHint(irLocalVar);
context->builder->setInsertBefore(irLocalVar);
- LegalVal newVal = declareVars(context, kIROp_Var, legalValueType, typeLayout, varChain, nameHint, irLocalVar, nullptr);
+ LegalVal newVal = declareVars(context, kIROp_Var, legalValueType, typeLayout, varChain, nameHint, irLocalVar, nullptr, context->isSpecialType(originalValueType));
// Remove the old local var.
irLocalVar->removeFromParent();
@@ -1345,7 +1347,7 @@ static LegalVal legalizeParam(
UnownedStringSlice nameHint = findNameHint(originalParam);
context->builder->setInsertBefore(originalParam);
- auto newVal = declareVars(context, kIROp_Param, legalParamType, nullptr, LegalVarChain(), nameHint, originalParam, nullptr);
+ auto newVal = declareVars(context, kIROp_Param, legalParamType, nullptr, LegalVarChain(), nameHint, originalParam, nullptr, context->isSpecialType(originalParam->getDataType()));
originalParam->removeFromParent();
context->replacedInstructions.add(originalParam);
@@ -2219,12 +2221,31 @@ static LegalVal declareVars(
IRTypeLegalizationContext* context,
IROp op,
LegalType type,
- TypeLayout* typeLayout,
- LegalVarChain const& varChain,
+ TypeLayout* inTypeLayout,
+ LegalVarChain const& inVarChain,
UnownedStringSlice nameHint,
IRInst* leafVar,
- IRGlobalNameInfo* globalNameInfo)
+ IRGlobalNameInfo* globalNameInfo,
+ bool isSpecial)
{
+ LegalVarChain varChain = inVarChain;
+ TypeLayout* typeLayout = inTypeLayout;
+ if( isSpecial )
+ {
+ if( varChain.pendingChain )
+ {
+ varChain.primaryChain = varChain.pendingChain;
+ varChain.pendingChain = nullptr;
+ }
+ if( typeLayout )
+ {
+ if( auto pendingTypeLayout = typeLayout->pendingDataTypeLayout )
+ {
+ typeLayout = pendingTypeLayout;
+ }
+ }
+ }
+
switch (type.flavor)
{
case LegalType::Flavor::none:
@@ -2247,7 +2268,8 @@ static LegalVal declareVars(
varChain,
nameHint,
leafVar,
- globalNameInfo);
+ globalNameInfo,
+ isSpecial);
return LegalVal::implicitDeref(val);
}
break;
@@ -2255,8 +2277,8 @@ static LegalVal declareVars(
case LegalType::Flavor::pair:
{
auto pairType = type.getPair();
- auto ordinaryVal = declareVars(context, op, pairType->ordinaryType, typeLayout, varChain, nameHint, leafVar, globalNameInfo);
- auto specialVal = declareVars(context, op, pairType->specialType, typeLayout, varChain, nameHint, leafVar, globalNameInfo);
+ auto ordinaryVal = declareVars(context, op, pairType->ordinaryType, typeLayout, varChain, nameHint, leafVar, globalNameInfo, false);
+ auto specialVal = declareVars(context, op, pairType->specialType, typeLayout, varChain, nameHint, leafVar, globalNameInfo, true);
return LegalVal::pair(ordinaryVal, specialVal, pairType->pairInfo);
}
@@ -2305,7 +2327,8 @@ static LegalVal declareVars(
newVarChain,
fieldNameHint,
ee.key,
- globalNameInfo);
+ globalNameInfo,
+ true);
TuplePseudoVal::Element element;
element.key = ee.key;
@@ -2348,9 +2371,10 @@ static LegalVal legalizeGlobalVar(
IRGlobalVar* irGlobalVar)
{
// Legalize the type for the variable's value
+ auto originalValueType = irGlobalVar->getDataType()->getValueType();
auto legalValueType = legalizeType(
context,
- irGlobalVar->getDataType()->getValueType());
+ originalValueType);
switch (legalValueType.flavor)
{
@@ -2373,7 +2397,7 @@ static LegalVal legalizeGlobalVar(
UnownedStringSlice nameHint = findNameHint(irGlobalVar);
context->builder->setInsertBefore(irGlobalVar);
- LegalVal newVal = declareVars(context, kIROp_GlobalVar, legalValueType, nullptr, LegalVarChain(), nameHint, irGlobalVar, &globalNameInfo);
+ LegalVal newVal = declareVars(context, kIROp_GlobalVar, legalValueType, nullptr, LegalVarChain(), nameHint, irGlobalVar, &globalNameInfo, context->isSpecialType(originalValueType));
// Register the new value as the replacement for the old
registerLegalizedValue(context, irGlobalVar, newVal);
@@ -2417,7 +2441,7 @@ static LegalVal legalizeGlobalConstant(
UnownedStringSlice nameHint = findNameHint(irGlobalConstant);
context->builder->setInsertBefore(irGlobalConstant);
- LegalVal newVal = declareVars(context, kIROp_GlobalConstant, legalValueType, nullptr, LegalVarChain(), nameHint, irGlobalConstant, &globalNameInfo);
+ LegalVal newVal = declareVars(context, kIROp_GlobalConstant, legalValueType, nullptr, LegalVarChain(), nameHint, irGlobalConstant, &globalNameInfo, context->isSpecialType(irGlobalConstant->getDataType()));
// Register the new value as the replacement for the old
registerLegalizedValue(context, irGlobalConstant, newVal);
@@ -2466,7 +2490,7 @@ static LegalVal legalizeGlobalParam(
UnownedStringSlice nameHint = findNameHint(irGlobalParam);
context->builder->setInsertBefore(irGlobalParam);
- LegalVal newVal = declareVars(context, kIROp_GlobalParam, legalValueType, typeLayout, varChain, nameHint, irGlobalParam, &globalNameInfo);
+ LegalVal newVal = declareVars(context, kIROp_GlobalParam, legalValueType, typeLayout, varChain, nameHint, irGlobalParam, &globalNameInfo, context->isSpecialType(irGlobalParam->getDataType()));
// Register the new value as the replacement for the old
registerLegalizedValue(context, irGlobalParam, newVal);
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 2e9915669..a7be244c8 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -1608,6 +1608,24 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
return LoweredValInfo::simple(irType);
}
+ LoweredValInfo visitExistentialSpecializedType(ExistentialSpecializedType* type)
+ {
+ auto irBaseType = lowerType(context, type->baseType);
+
+ List<IRInst*> slotArgs;
+ for(auto arg : type->slots.args)
+ {
+ auto irArgType = lowerType(context, arg.type);
+ auto irArgWitness = lowerSimpleVal(context, arg.witness);
+
+ slotArgs.add(irArgType);
+ slotArgs.add(irArgWitness);
+ }
+
+ auto irType = getBuilder()->getBindExistentialsType(irBaseType, slotArgs.getCount(), slotArgs.getBuffer());
+ return LoweredValInfo::simple(irType);
+ }
+
// We do not expect to encounter the following types in ASTs that have
// passed front-end semantic checking.
#define UNEXPECTED_CASE(NAME) IRType* visit##NAME(NAME*) { SLANG_UNEXPECTED(#NAME); UNREACHABLE_RETURN(nullptr); }
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp
index 326a27854..4ac48d2e7 100644
--- a/source/slang/reflection.cpp
+++ b/source/slang/reflection.cpp
@@ -860,6 +860,24 @@ SLANG_API int spReflectionTypeLayout_getGenericParamIndex(SlangReflectionTypeLay
}
}
+SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getPendingDataTypeLayout(SlangReflectionTypeLayout* inTypeLayout)
+{
+ auto typeLayout = convert(inTypeLayout);
+ if(!typeLayout) return nullptr;
+
+ auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout.Ptr();
+ return convert(pendingDataTypeLayout);
+}
+
+SLANG_API SlangReflectionVariableLayout* spReflectionVariableLayout_getPendingDataLayout(SlangReflectionVariableLayout* inVarLayout)
+{
+ auto varLayout = convert(inVarLayout);
+ if(!varLayout) return nullptr;
+
+ auto pendingDataLayout = varLayout->pendingVarLayout.Ptr();
+ return convert(pendingDataLayout);
+}
+
// Variable Reflection
@@ -1381,3 +1399,29 @@ SLANG_API size_t spReflection_getGlobalConstantBufferSize(SlangReflection* inPro
if (!uniform) return 0;
return getReflectionSize(uniform->count);
}
+
+SLANG_API SlangReflectionType* spReflection_specializeType(
+ SlangReflection* inProgramLayout,
+ SlangReflectionType* inType,
+ SlangInt specializationArgCount,
+ SlangReflectionType* const* specializationArgs,
+ ISlangBlob** outDiagnostics)
+{
+ auto programLayout = convert(inProgramLayout);
+ if(!programLayout) return nullptr;
+
+ auto unspecializedType = convert(inType);
+ if(!unspecializedType) return nullptr;
+
+ auto linkage = programLayout->getProgram()->getLinkage();
+
+ DiagnosticSink sink;
+ sink.sourceManager = linkage->getSourceManager();
+
+ auto specializedType = linkage->specializeType(unspecializedType, specializationArgCount, (Type* const*) specializationArgs, &sink);
+
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ return convert(specializedType);
+}
+
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 7556ac9b2..c78a27f54 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -567,6 +567,9 @@ Type* Program::getTypeFromString(String typeStr, DiagnosticSink* sink)
return type;
}
+
+
+
CompileRequestBase::CompileRequestBase(
Linkage* linkage,
DiagnosticSink* sink)
@@ -1444,6 +1447,22 @@ void DiagnosticSink::noteInternalErrorLoc(SourceLoc const& loc)
internalErrorLocsNoted++;
}
+SlangResult DiagnosticSink::getBlobIfNeeded(ISlangBlob** outBlob)
+{
+ // If the client doesn't want an output blob, there is nothing to do.
+ //
+ if(!outBlob) return SLANG_OK;
+
+ // If there were no errors, and there was no diagnostic output, there is nothing to do.
+ if(!GetErrorCount() && !outputBuffer.getLength()) return SLANG_OK;
+
+ Slang::ComPtr<ISlangBlob> blob = Slang::StringUtil::createStringBlob(outputBuffer);
+ *outBlob = blob.detach();
+
+ return SLANG_OK;
+}
+
+
Session* CompileRequestBase::getSession()
{
return getLinkage()->getSession();
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp
index c069c69d7..17c85175d 100644
--- a/source/slang/syntax.cpp
+++ b/source/slang/syntax.cpp
@@ -2757,7 +2757,109 @@ char const* getGLSLNameForImageFormat(ImageFormat format)
}
}
+//
+// ExistentialSpecializedType
+//
+
+String ExistentialSpecializedType::ToString()
+{
+ String result;
+ result.append("__ExistentialSpecializedType(");
+ result.append(baseType->ToString());
+ for( auto arg : slots.args )
+ {
+ result.append(", ");
+ result.append(arg.type->ToString());
+ }
+ result.append(")");
+ return result;
+}
+
+bool ExistentialSpecializedType::EqualsImpl(Type * type)
+{
+ auto other = as<ExistentialSpecializedType>(type);
+ if(!other)
+ return false;
+
+ if(!baseType->Equals(other->baseType))
+ return false;
+ auto argCount = slots.args.getCount();
+ if(argCount != other->slots.args.getCount())
+ return false;
+ for( Index ii = 0; ii < argCount; ++ii )
+ {
+ if(!slots.args[ii].type->Equals(other->slots.args[ii].type))
+ return false;
+
+ if(!slots.args[ii].witness->EqualsVal(other->slots.args[ii].witness))
+ return false;
+ }
+ return true;
+}
+
+int ExistentialSpecializedType::GetHashCode()
+{
+ Hasher hasher;
+ hasher.hashObject(baseType);
+ for(auto arg : slots.args)
+ {
+ hasher.hashObject(arg.type);
+ hasher.hashObject(arg.witness);
+ }
+ return hasher.getResult();
+}
+
+RefPtr<Type> ExistentialSpecializedType::CreateCanonicalType()
+{
+ RefPtr<ExistentialSpecializedType> canType = new ExistentialSpecializedType();
+ canType->setSession(getSession());
+
+ canType->baseType = baseType->GetCanonicalType();
+ for( auto paramType : slots.paramTypes )
+ {
+ canType->slots.paramTypes.add( paramType->GetCanonicalType() );
+ }
+ for( auto arg : slots.args )
+ {
+ ExistentialTypeSlots::Arg canArg;
+ canArg.type = arg.type->GetCanonicalType();
+ canArg.witness = arg.witness;
+ canType->slots.args.add(canArg);
+ }
+ return canType;
+}
+
+RefPtr<Val> ExistentialSpecializedType::SubstituteImpl(SubstitutionSet subst, int* ioDiff)
+{
+ int diff = 0;
+
+ auto substBaseType = baseType->SubstituteImpl(subst, &diff).as<Type>();
+
+ ExistentialTypeSlots substSlots;
+ for( auto paramType : slots.paramTypes )
+ {
+ substSlots.paramTypes.add( paramType->SubstituteImpl(subst, &diff).as<Type>() );
+ }
+ for( auto arg : slots.args )
+ {
+ ExistentialTypeSlots::Arg substArg;
+ substArg.type = arg.type->SubstituteImpl(subst, &diff).as<Type>();
+ substArg.witness = arg.witness->SubstituteImpl(subst, &diff);
+ substSlots.args.add(substArg);
+ }
+
+ if(!diff)
+ return this;
+
+ (*ioDiff)++;
+
+ RefPtr<ExistentialSpecializedType> substType = new ExistentialSpecializedType();
+ substType->setSession(getSession());
+ substType->baseType = substBaseType;
+ substType->slots = substSlots;
+ return substType;
+}
} // namespace Slang
diff --git a/source/slang/syntax.h b/source/slang/syntax.h
index eb7cee40a..aa3944d0a 100644
--- a/source/slang/syntax.h
+++ b/source/slang/syntax.h
@@ -1104,6 +1104,32 @@ namespace Slang
typedef Dictionary<unsigned int, RefPtr<RefObject>> AttributeArgumentValueDict;
+ /// Collects information about existential type parameters and their arguments.
+ struct ExistentialTypeSlots
+ {
+ /// For each type parameter, holds the interface/existential type that constrains it.
+ List<RefPtr<Type>> paramTypes;
+
+ /// An argument for an existential type parameter.
+ ///
+ /// Comprises a concrete type and a witness for its conformance to the desired
+ /// interface/existential type for the corresponding parameter.
+ ///
+ struct Arg
+ {
+ RefPtr<Type> type;
+ RefPtr<Val> witness;
+ };
+
+ /// Any arguments provided for the existential type parameters.
+ ///
+ /// It is possible for `args` to be empty even if `paramTypes` is non-empty;
+ /// that situation represents an unspecialized program or entry point.
+ ///
+ List<Arg> args;
+ };
+
+
// Generate class definition for all syntax classes
#define SYNTAX_FIELD(TYPE, NAME) TYPE NAME;
#define FIELD(TYPE, NAME) TYPE NAME;
diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h
index 2d376d754..d0c00c73a 100644
--- a/source/slang/type-defs.h
+++ b/source/slang/type-defs.h
@@ -475,3 +475,16 @@ RAW(
virtual RefPtr<Val> SubstituteImpl(SubstitutionSet subst, int* ioDiff) override;
)
END_SYNTAX_CLASS()
+
+SYNTAX_CLASS(ExistentialSpecializedType, Type)
+RAW(
+ RefPtr<Type> baseType;
+ ExistentialTypeSlots slots;
+
+ virtual String ToString() override;
+ virtual bool EqualsImpl(Type * type) override;
+ virtual int GetHashCode() override;
+ virtual RefPtr<Type> CreateCanonicalType() override;
+ virtual RefPtr<Val> SubstituteImpl(SubstitutionSet subst, int* ioDiff) override;
+)
+END_SYNTAX_CLASS() \ No newline at end of file