Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions source/slang/slang-ast-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,11 @@ TypeCoercionWitness* ASTBuilder::getTypeCoercionWitness(
return getOrCreate<TypeCoercionWitness>(subType, superType, declRef.declRefBase);
}

NoneWitness* ASTBuilder::getNoneWitness(Type* subType, Type* superType)
{
return getOrCreate<NoneWitness>(subType, superType);
}

DeclRef<Decl> _getMemberDeclRef(ASTBuilder* builder, DeclRef<Decl> parent, Decl* decl)
{
return builder->getMemberDeclRef(parent, decl);
Expand Down
2 changes: 2 additions & 0 deletions source/slang/slang-ast-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,8 @@ class ASTBuilder : public RefObject
Type* toType,
DeclRef<Decl> declRef);

NoneWitness* getNoneWitness(Type* subType, Type* superType);

/// Helpers to get type info from the SharedASTBuilder
SyntaxClass<NodeBase> findSyntaxClass(const UnownedStringSlice& slice)
{
Expand Down
20 changes: 18 additions & 2 deletions source/slang/slang-ast-val.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,12 +901,28 @@ Val* TypeCoercionWitness::_resolveImplOverride()

void NoneWitness::_toTextOverride(StringBuilder& out)
{
out.append("none");
out << "NoneWitness(";
if (getSub())
out << getSub();
out << ",";
if (getSup())
out << getSup();
out << ")";
}

Val* NoneWitness::_resolveImplOverride()
{
return this;
int diff = 0;
auto newSub = as<Type>(getSub()->resolve());
if (newSub != getSub())
diff++;
auto newSup = as<Type>(getSup()->resolve());
if (newSup != getSup())
diff++;

if (!diff)
return this;
return getCurrentASTBuilder()->getNoneWitness(newSub, newSup);
}

// UNormModifierVal
Expand Down
2 changes: 1 addition & 1 deletion source/slang/slang-ast-val.h
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ class ExtractFromConjunctionSubtypeWitness : public SubtypeWitness

/// A witness for the "none" value of optional constraints.
FIDDLE()
class NoneWitness : public Witness
class NoneWitness : public SubtypeWitness
{
FIDDLE(...)

Expand Down
21 changes: 11 additions & 10 deletions source/slang/slang-check-constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ DeclRef<Decl> SemanticsVisitor::trySolveConstraintSystem(
else if (!subTypeWitness && constraintIsOptional)
{
// Optional witness failed to resolve; not an error.
auto noneWitness = m_astBuilder->getOrCreate<NoneWitness>();
auto noneWitness = m_astBuilder->getNoneWitness(sub, sup);
args.add(noneWitness);
outBaseCost += kConversionCost_FailedOptionalConstraint;
}
Expand Down Expand Up @@ -870,20 +870,21 @@ bool SemanticsVisitor::TryUnifyVals(
// Two subtype witnesses can be unified if they exist (non-null) and
// prove that some pair of types are subtypes of types that can be unified.
//
const auto fstSubtypeWitness = as<SubtypeWitness>(fst);
const auto sndSubtypeWitness = as<SubtypeWitness>(snd);
const auto fstNoneWitness = as<NoneWitness>(fst);
const auto sndNoneWitness = as<NoneWitness>(snd);
if (fstSubtypeWitness && sndSubtypeWitness)
auto fstSubtypeWitness = as<SubtypeWitness>(fst);
auto sndSubtypeWitness = as<SubtypeWitness>(snd);
auto fstNoneWitness = as<NoneWitness>(fst);
auto sndNoneWitness = as<NoneWitness>(snd);
if ((fstNoneWitness && !sndNoneWitness) || (!fstNoneWitness && sndNoneWitness))
{
// Don't confuse a NoneWitness with a real SubtypeWitness.
return false;
}
else if (fstSubtypeWitness && sndSubtypeWitness)
return TryUnifyTypes(
constraints,
unifyCtx,
fstSubtypeWitness->getSup(),
sndSubtypeWitness->getSup());
else if (fstNoneWitness && sndNoneWitness)
return true;
else if ((fstNoneWitness && sndSubtypeWitness) || (fstSubtypeWitness && sndNoneWitness))
return false;

SLANG_UNIMPLEMENTED_X("value unification case");

Expand Down
2 changes: 1 addition & 1 deletion source/slang/slang-check-overload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,7 @@ bool SemanticsVisitor::TryCheckOverloadCandidateConstraints(
}
else if (!subTypeWitness && constraintIsOptional)
{
newArgs.add(m_astBuilder->getOrCreate<NoneWitness>());
newArgs.add(m_astBuilder->getNoneWitness(sub, sup));
}
else
{
Expand Down
3 changes: 3 additions & 0 deletions source/slang/slang-ir-insts-stable-names.lua
Original file line number Diff line number Diff line change
Expand Up @@ -685,4 +685,7 @@ return {
["StoreBase.copyLogical"] = 681,
["MakeStorageTypeLoweringConfig"] = 682,
["Decoration.experimentalModule"] = 683,
["Type.WitnessTableTypeBase.witness_table_none_t"] = 684,
["none_witness_table"] = 685,
["CheckOptionalWitness"] = 686
}
21 changes: 21 additions & 0 deletions source/slang/slang-ir-insts.h
Original file line number Diff line number Diff line change
Expand Up @@ -2209,6 +2209,21 @@ struct IRWitnessTable : IRInst
IRType* getConcreteType() { return (IRType*)getOperand(0); }
};

// The NoneWitnessTable is used in place of a witness table when the constraint
// is optional and not being conformed to.
FIDDLE()
struct IRNoneWitnessTable : IRInst
{
FIDDLE(leafInst())

IRInst* getConformanceType()
{
return cast<IRWitnessTableNoneType>(getDataType())->getConformanceType();
}

IRType* getConcreteType() { return (IRType*)getOperand(0); }
};

/// Represents an RTTI object.
/// An IRRTTIObject has 1 operand, specifying the type
/// this RTTI object provides info for.
Expand Down Expand Up @@ -3415,6 +3430,8 @@ struct IRBuilder

IRInst* emitGetSequentialIDInst(IRInst* rttiObj);

IRInst* emitCheckOptionalWitness(IRInst* witness);

IRInst* emitAlloca(IRInst* type, IRInst* rttiObjPtr);

IRInst* emitGlobalValueRef(IRInst* globalInst);
Expand Down Expand Up @@ -3666,6 +3683,10 @@ struct IRBuilder
IRInst* requirementKey,
IRInst* satisfyingVal);

// Create an empty witness table, this is used for optional constraints
// when baseType does not conform to subType.
IRNoneWitnessTable* createNoneWitnessTable(IRType* baseType, IRType* subType);

IRInst* createThisTypeWitness(IRType* interfaceType);

IRInst* getTypeEqualityWitness(IRType* witnessType, IRType* type1, IRType* type2);
Expand Down
11 changes: 11 additions & 0 deletions source/slang/slang-ir-insts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,15 @@ local insts = {
operands = { { "baseType", "IRType" } },
},
},
{
witness_table_none_t = {
-- A type for NoneWitness, which is used to satisfy
-- optional constraints when the base type does not
-- conform.
struct_name = "WitnessTableNoneType",
operands = { { "baseType", "IRType" } },
},
},
{
witness_table_id_t = {
-- An integer type representing a witness table for targets where
Expand Down Expand Up @@ -713,6 +722,7 @@ local insts = {
{ key = { struct_name = "StructKey", global = true } },
{ global_generic_param = { global = true } },
{ witness_table = { hoistable = true } },
{ none_witness_table = { hoistable = true } },
{ indexedFieldKey = { operands = { { "baseType" }, { "index" } }, hoistable = true } },
-- A placeholder witness that ThisType implements the enclosing interface.
-- Used only in interface definitions.
Expand Down Expand Up @@ -852,6 +862,7 @@ local insts = {
operands = { { "param", "IRGlobalGenericParam" }, { "val", "IRInst" } },
},
},
{ CheckOptionalWitness = { operands = { { "witness" } }, hoistable = true } },
{ allocObj = {} },
{ globalValueRef = { operands = { { "value" } } } },
{ makeUInt64 = { operands = { { "low" }, { "high" } } } },
Expand Down
21 changes: 21 additions & 0 deletions source/slang/slang-ir-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,20 @@ IRWitnessTable* cloneWitnessTableWithoutRegistering(
false);
}

IRNoneWitnessTable* cloneNoneWitnessTableImpl(
IRSpecContextBase* context,
IRBuilder* builder,
IRNoneWitnessTable* originalTable,
IROriginalValuesForClone const& originalValues)
{
auto clonedBaseType = cloneType(context, (IRType*)(originalTable->getConformanceType()));
auto clonedSubType = cloneType(context, (IRType*)(originalTable->getConcreteType()));
auto clonedTable = builder->createNoneWitnessTable(clonedBaseType, clonedSubType);
registerClonedValue(context, clonedTable, originalValues);

return clonedTable;
}

IRStructType* cloneStructTypeImpl(
IRSpecContextBase* context,
IRBuilder* builder,
Expand Down Expand Up @@ -1381,6 +1395,13 @@ IRInst* cloneInst(
cast<IRWitnessTable>(originalInst),
originalValues);

case kIROp_NoneWitnessTable:
return cloneNoneWitnessTableImpl(
context,
builder,
cast<IRNoneWitnessTable>(originalInst),
originalValues);

case kIROp_StructType:
return cloneStructTypeImpl(
context,
Expand Down
7 changes: 3 additions & 4 deletions source/slang/slang-ir-lower-generic-call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,10 @@ struct GenericCallLoweringContext
return;
}

auto interfaceType = as<IRInterfaceType>(
cast<IRWitnessTableTypeBase>(lookupInst->getWitnessTable()->getDataType())
->getConformanceType());
auto witnessTableType = cast<IRWitnessTableTypeBase>(lookupInst->getWitnessTable()->getDataType());
auto interfaceType = as<IRInterfaceType>(witnessTableType->getConformanceType());

if (!interfaceType)
if (as<IRWitnessTableNoneType>(witnessTableType))
{
// NoneWitness -> remove call.
callInst->removeAndDeallocate();
Expand Down
20 changes: 4 additions & 16 deletions source/slang/slang-ir-lower-generic-function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,8 @@ struct GenericFunctionLoweringContext
// and emission of wrapper functions.
void lowerWitnessTable(IRWitnessTable* witnessTable)
{
IRInterfaceType* conformanceType = as<IRInterfaceType>(witnessTable->getConformanceType());
if (!conformanceType)
return;

auto interfaceType = maybeLowerInterfaceType(conformanceType);
auto interfaceType =
maybeLowerInterfaceType(cast<IRInterfaceType>(witnessTable->getConformanceType()));
IRBuilder builderStorage(sharedContext->module);
auto builder = &builderStorage;
builder->setInsertBefore(witnessTable);
Expand Down Expand Up @@ -357,17 +354,8 @@ struct GenericFunctionLoweringContext
return;
if (witnessTableType->getConformanceType()->findDecoration<IRComInterfaceDecoration>())
return;

IRInterfaceType* conformanceType =
as<IRInterfaceType>(witnessTableType->getConformanceType());

// NoneWitness generates conformance types which aren't interfaces. In
// that case, the method can just be skipped entirely, since there's no
// real witness for it and it should be in unreachable code at this
// point.
if (!conformanceType)
return;
auto interfaceType = maybeLowerInterfaceType(conformanceType);
auto interfaceType =
maybeLowerInterfaceType(cast<IRInterfaceType>(witnessTableType->getConformanceType()));
interfaceRequirementVal = sharedContext->findInterfaceRequirementVal(
interfaceType,
lookupInst->getRequirementKey());
Expand Down
32 changes: 31 additions & 1 deletion source/slang/slang-ir-lower-generics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,34 @@ void specializeRTTIObjects(SharedGenericsLoweringContext* sharedContext, Diagnos
cleanUpInterfaceTypes(sharedContext);
}

void lowerOptionalWitnesses(SharedGenericsLoweringContext* sharedContext)
{
InstPassBase pass(sharedContext->module);
IRBuilder builder(sharedContext->module);

pass.processInstsOfType<IRCheckOptionalWitness>(
kIROp_CheckOptionalWitness,
[&](IRCheckOptionalWitness* inst)
{
builder.setInsertBefore(inst);
auto checkInst = builder.getBoolValue(inst->getWitness()->getOp() != kIROp_NoneWitnessTable);
inst->replaceUsesWith(checkInst);
inst->removeAndDeallocate();
});

// Remove all NoneWitnessTables, they're no longer referenced.
builder.setInsertInto(sharedContext->module->getModuleInst());

List<IRInst*> noneWitnesses;
for (auto inst : sharedContext->module->getGlobalInsts())
{
if (inst->getOp() == kIROp_NoneWitnessTable)
noneWitnesses.add(inst);
}
for (auto inst : noneWitnesses)
inst->removeAndDeallocate();
}

void checkTypeConformanceExists(SharedGenericsLoweringContext* context)
{
HashSet<IRInst*> implementedInterfaces;
Expand All @@ -177,7 +205,7 @@ void checkTypeConformanceExists(SharedGenericsLoweringContext* context)
if (!witnessTableType)
return;
auto interfaceType =
cast<IRWitnessTableType>(witnessTableType)->getConformanceType();
cast<IRWitnessTableTypeBase>(witnessTableType)->getConformanceType();
if (isComInterfaceType((IRType*)interfaceType))
return;
if (!implementedInterfaces.contains(interfaceType))
Expand Down Expand Up @@ -258,6 +286,8 @@ void lowerGenerics(TargetProgram* targetProgram, IRModule* module, DiagnosticSin
if (sink->getErrorCount() != 0)
return;

lowerOptionalWitnesses(&sharedContext);

// This optional step replaces all uses of witness tables and RTTI objects with
// sequential IDs. Without this step, we will emit code that uses function pointers and
// real RTTI objects and witness tables.
Expand Down
34 changes: 13 additions & 21 deletions source/slang/slang-ir-specialize-dispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,31 +232,23 @@ void ensureWitnessTableSequentialIDs(SharedGenericsLoweringContext* sharedContex
{
auto interfaceType =
cast<IRWitnessTableType>(inst->getDataType())->getConformanceType();
if (as<IRInterfaceType>(interfaceType))
auto interfaceLinkage = interfaceType->findDecoration<IRLinkageDecoration>();
SLANG_ASSERT(
interfaceLinkage && "An interface type does not have a linkage,"
"but a witness table associated with it has one.");
auto interfaceName = interfaceLinkage->getMangledName();
auto idAllocator =
linkage->mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(
interfaceName);
if (!idAllocator)
{
auto interfaceLinkage = interfaceType->findDecoration<IRLinkageDecoration>();
SLANG_ASSERT(
interfaceLinkage && "An interface type does not have a linkage,"
"but a witness table associated with it has one.");
auto interfaceName = interfaceLinkage->getMangledName();
auto idAllocator =
linkage->mapInterfaceMangledNameToSequentialIDCounters[interfaceName] = 0;
idAllocator =
linkage->mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(
interfaceName);
if (!idAllocator)
{
linkage->mapInterfaceMangledNameToSequentialIDCounters[interfaceName] = 0;
idAllocator =
linkage->mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(
interfaceName);
}
seqID = *idAllocator;
++(*idAllocator);
}
else
{
// NoneWitness, has special ID of -1.
seqID = uint32_t(-1);
}
seqID = *idAllocator;
++(*idAllocator);
linkage->mapMangledNameToRTTIObjectIndex[witnessTableMangledName] = seqID;
}

Expand Down
Loading
Loading