Skip to content

Commit dd9d24d

Browse files
authored
Allow partial specialization of existential arguments. (#6487)
* Allow partial specialization of existential arguments. * Fix. * Add test case for improved diagnostics. * Fix compile error. * Fix tests. * Fix. * Fix test. * Fix compile issue. * Fix typo. * Address comment.
1 parent 700c38a commit dd9d24d

14 files changed

+270
-70
lines changed

source/slang/slang-diagnostic-defs.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -2203,12 +2203,12 @@ DIAGNOSTIC(
22032203
Error,
22042204
typeDoesNotFitAnyValueSize,
22052205
"type '$0' does not fit in the size required by its conforming interface.")
2206-
DIAGNOSTIC(41012, Note, typeAndLimit, "sizeof($0) is $1, limit is $2")
2206+
DIAGNOSTIC(-1, Note, typeAndLimit, "sizeof($0) is $1, limit is $2")
22072207
DIAGNOSTIC(
2208-
41012,
2208+
41014,
22092209
Error,
22102210
typeCannotBePackedIntoAnyValue,
2211-
"type '$0' contains fields that cannot be packed into an AnyValue.")
2211+
"type '$0' contains fields that cannot be packed into ordinary bytes for dynamic dispatch.")
22122212
DIAGNOSTIC(
22132213
41020,
22142214
Error,

source/slang/slang-ir-generics-lowering-context.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -405,12 +405,23 @@ bool SharedGenericsLoweringContext::doesTypeFitInAnyValue(
405405
IRType* concreteType,
406406
IRInterfaceType* interfaceType,
407407
IRIntegerValue* outTypeSize,
408-
IRIntegerValue* outLimit)
408+
IRIntegerValue* outLimit,
409+
bool* outIsTypeOpaque)
409410
{
410411
auto anyValueSize = getInterfaceAnyValueSize(interfaceType, interfaceType->sourceLoc);
411412
if (outLimit)
412413
*outLimit = anyValueSize;
413414

415+
if (!areResourceTypesBindlessOnTarget(targetProgram->getTargetReq()))
416+
{
417+
IRType* opaqueType = nullptr;
418+
if (isOpaqueType(concreteType, &opaqueType))
419+
{
420+
if (outIsTypeOpaque)
421+
*outIsTypeOpaque = true;
422+
return false;
423+
}
424+
}
414425
IRSizeAndAlignment sizeAndAlignment;
415426
Result result =
416427
getNaturalSizeAndAlignment(targetProgram->getOptionSet(), concreteType, &sizeAndAlignment);

source/slang/slang-ir-generics-lowering-context.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ struct SharedGenericsLoweringContext
9898
IRType* concreteType,
9999
IRInterfaceType* interfaceType,
100100
IRIntegerValue* outTypeSize = nullptr,
101-
IRIntegerValue* outLimit = nullptr);
101+
IRIntegerValue* outLimit = nullptr,
102+
bool* outIsTypeOpaque = nullptr);
102103
};
103104

104105
List<IRWitnessTable*> getWitnessTablesFromInterfaceType(IRModule* module, IRInst* interfaceType);

source/slang/slang-ir-layout.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ static Result _calcSizeAndAlignment(
341341
case kIROp_ComPtrType:
342342
case kIROp_NativeStringType:
343343
case kIROp_HLSLConstBufferPointerType:
344+
case kIROp_RaytracingAccelerationStructureType:
344345
{
345346
*outSizeAndAlignment = IRSizeAndAlignment(kPointerSize, kPointerSize);
346347
return SLANG_OK;

source/slang/slang-ir-legalize-types.cpp

+3-5
Original file line numberDiff line numberDiff line change
@@ -2053,15 +2053,13 @@ static LegalVal coerceToLegalType(IRTypeLegalizationContext* context, LegalType
20532053

20542054
static LegalVal legalizeUndefined(IRTypeLegalizationContext* context, IRInst* inst)
20552055
{
2056-
List<IRType*> opaqueTypes;
2057-
if (isOpaqueType(inst->getFullType(), opaqueTypes))
2056+
IRType* opaqueType = nullptr;
2057+
if (isOpaqueType(inst->getFullType(), &opaqueType))
20582058
{
2059-
auto opaqueType = opaqueTypes[0];
2060-
auto containerType = opaqueTypes.getCount() > 1 ? opaqueTypes[1] : opaqueType;
20612059
SourceLoc loc = findBestSourceLocFromUses(inst);
20622060

20632061
if (!loc.isValid())
2064-
loc = getDiagnosticPos(containerType);
2062+
loc = getDiagnosticPos(opaqueType);
20652063

20662064
context->m_sink->diagnose(loc, Diagnostics::useOfUninitializedOpaqueHandle, opaqueType);
20672065
}

source/slang/slang-ir-lower-reinterpret.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ struct ReinterpretLoweringContext
7979
Slang::Diagnostics::typeCannotBePackedIntoAnyValue,
8080
toType);
8181
}
82-
if (fromTypeSize != toTypeSize && cantPack == false)
82+
if (fromTypeSize != toTypeSize && !cantPack && !as<IRExtractExistentialType>(fromType))
8383
{
8484
sink->diagnose(
8585
inst->sourceLoc,

source/slang/slang-ir-specialize.cpp

+75-36
Original file line numberDiff line numberDiff line change
@@ -1373,11 +1373,16 @@ struct SpecializationContext
13731373
if (!isExistentialType(param->getDataType()))
13741374
continue;
13751375

1376+
// Is arg in the most simplified form for specialization? If not we are
1377+
// not ready to consider specialization yet.
1378+
if (!isSimplifiedExistentialArg(arg))
1379+
return false;
1380+
13761381
// We *cannot* specialize unless the argument value corresponding
13771382
// to such a parameter is one we can specialize.
13781383
//
13791384
if (!canSpecializeExistentialArg(arg))
1380-
return false;
1385+
continue;
13811386

13821387
argumentNeedSpecialization = true;
13831388
}
@@ -1416,7 +1421,6 @@ struct SpecializationContext
14161421
auto arg = inst->getArg(argCounter++);
14171422
if (!isExistentialType(param->getDataType()))
14181423
continue;
1419-
14201424
if (auto makeExistential = as<IRMakeExistential>(arg))
14211425
{
14221426
// Note that we use the *type* stored in the
@@ -1426,25 +1430,32 @@ struct SpecializationContext
14261430
// call sites that pass in the exact same argument).
14271431
//
14281432
auto val = makeExistential->getWrappedValue();
1429-
auto valType = val->getFullType();
1430-
key.vals.add(valType);
1431-
1432-
// We are also including the witness table in the key.
1433-
// This isn't required with our current language model,
1434-
// since a given type can only conform to a given interface
1435-
// in one way (so there can be only one witness table).
1436-
// That means that the `valType` and the existential
1437-
// type of `param` above should uniquely determine
1438-
// the witness table we see.
1439-
//
1440-
// There are forward-looking cases where supporting
1441-
// "overlapping conformances" could be required, and
1442-
// there is low incremental cost to future-proofing
1443-
// this code, so we go ahead and add the witness
1444-
// table even if it is redundant.
1445-
//
1446-
auto witnessTable = makeExistential->getWitnessTable();
1447-
key.vals.add(witnessTable);
1433+
auto valType = val->getDataType();
1434+
if (isCompileTimeConstantType(valType))
1435+
{
1436+
key.vals.add(valType);
1437+
1438+
// We are also including the witness table in the key.
1439+
// This isn't required with our current language model,
1440+
// since a given type can only conform to a given interface
1441+
// in one way (so there can be only one witness table).
1442+
// That means that the `valType` and the existential
1443+
// type of `param` above should uniquely determine
1444+
// the witness table we see.
1445+
//
1446+
// There are forward-looking cases where supporting
1447+
// "overlapping conformances" could be required, and
1448+
// there is low incremental cost to future-proofing
1449+
// this code, so we go ahead and add the witness
1450+
// table even if it is redundant.
1451+
//
1452+
auto witnessTable = makeExistential->getWitnessTable();
1453+
key.vals.add(witnessTable);
1454+
}
1455+
else
1456+
{
1457+
key.vals.add(param->getDataType());
1458+
}
14481459
}
14491460
else if (auto wrapExistential = as<IRWrapExistential>(arg))
14501461
{
@@ -1508,7 +1519,11 @@ struct SpecializationContext
15081519
if (auto makeExistential = as<IRMakeExistential>(arg))
15091520
{
15101521
auto val = makeExistential->getWrappedValue();
1511-
newArgs.add(val);
1522+
auto valType = val->getDataType();
1523+
if (isCompileTimeConstantType(valType))
1524+
newArgs.add(val);
1525+
else
1526+
newArgs.add(arg);
15121527
}
15131528
else if (auto wrapExistential = as<IRWrapExistential>(arg))
15141529
{
@@ -1634,6 +1649,18 @@ struct SpecializationContext
16341649
return true;
16351650
}
16361651

1652+
1653+
// Returns true if `inst` is a simplified existential argument ready for specialization.
1654+
bool isSimplifiedExistentialArg(IRInst* inst)
1655+
{
1656+
if (as<IRMakeExistential>(inst))
1657+
return true;
1658+
if (as<IRWrapExistential>(inst))
1659+
return true;
1660+
return false;
1661+
}
1662+
1663+
16371664
// Similarly, we want to be able to test whether an instruction
16381665
// used as an argument for an existential-type parameter is
16391666
// suitable for use in specialization.
@@ -1760,21 +1787,33 @@ struct SpecializationContext
17601787
// created.
17611788
//
17621789
auto valType = val->getFullType();
1763-
auto newParam = builder->createParam(valType);
1764-
newParams.add(newParam);
1790+
if (auto extractExistentialType = as<IRExtractExistentialType>(valType))
1791+
{
1792+
valType = extractExistentialType->getOperand(0)->getDataType();
1793+
auto newParam = builder->createParam(valType);
1794+
newParams.add(newParam);
1795+
replacementVal = newParam;
1796+
}
1797+
else
1798+
{
1799+
auto newParam = builder->createParam(valType);
1800+
newParams.add(newParam);
17651801

1766-
// Within the body of the function we cannot just use `val`
1767-
// directly, because the existing code expects an existential
1768-
// value, including its witness table.
1769-
//
1770-
// Therefore we will create a `makeExistential(newParam, witnessTable)`
1771-
// in the body of the new function and use *that* as the replacement
1772-
// value for the original parameter (since it will have the
1773-
// correct existential type, and stores the right witness table).
1774-
//
1775-
auto newMakeExistential =
1776-
builder->emitMakeExistential(oldParam->getFullType(), newParam, witnessTable);
1777-
replacementVal = newMakeExistential;
1802+
// Within the body of the function we cannot just use `val`
1803+
// directly, because the existing code expects an existential
1804+
// value, including its witness table.
1805+
//
1806+
// Therefore we will create a `makeExistential(newParam, witnessTable)`
1807+
// in the body of the new function and use *that* as the replacement
1808+
// value for the original parameter (since it will have the
1809+
// correct existential type, and stores the right witness table).
1810+
//
1811+
auto newMakeExistential = builder->emitMakeExistential(
1812+
oldParam->getFullType(),
1813+
newParam,
1814+
witnessTable);
1815+
replacementVal = newMakeExistential;
1816+
}
17781817
}
17791818
else if (auto oldWrapExistential = as<IRWrapExistential>(arg))
17801819
{

source/slang/slang-ir-util.h

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ IRType* getMatrixElementType(IRType* type);
8383

8484
// True if type is a resource backing memory
8585
bool isResourceType(IRType* type);
86+
bool isOpaqueType(IRType* type, IRType** outLeafOpaqueHandleType);
8687

8788
// True if type is a pointer to a resource
8889
bool isPointerToResourceType(IRType* type);

source/slang/slang-ir-witness-table-wrapper.cpp

+28-12
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,35 @@ struct GenerateWitnessTableWrapperContext
190190
//
191191
auto concreteType = witnessTable->getConcreteType();
192192
IRIntegerValue typeSize, sizeLimit;
193-
if (!sharedContext
194-
->doesTypeFitInAnyValue(concreteType, interfaceType, &typeSize, &sizeLimit))
195-
{
196-
sharedContext->sink->diagnose(
197-
concreteType,
198-
Diagnostics::typeDoesNotFitAnyValueSize,
199-
concreteType);
200-
sharedContext->sink->diagnoseWithoutSourceView(
201-
concreteType,
202-
Diagnostics::typeAndLimit,
193+
bool isTypeOpaque = false;
194+
if (!sharedContext->doesTypeFitInAnyValue(
203195
concreteType,
204-
typeSize,
205-
sizeLimit);
196+
interfaceType,
197+
&typeSize,
198+
&sizeLimit,
199+
&isTypeOpaque))
200+
{
201+
HashSet<IRType*> visited;
202+
if (isTypeOpaque)
203+
{
204+
sharedContext->sink->diagnose(
205+
concreteType,
206+
Diagnostics::typeCannotBePackedIntoAnyValue,
207+
concreteType);
208+
}
209+
else
210+
{
211+
sharedContext->sink->diagnose(
212+
concreteType,
213+
Diagnostics::typeDoesNotFitAnyValueSize,
214+
concreteType);
215+
sharedContext->sink->diagnoseWithoutSourceView(
216+
concreteType,
217+
Diagnostics::typeAndLimit,
218+
concreteType,
219+
typeSize,
220+
sizeLimit);
221+
}
206222
return;
207223
}
208224

source/slang/slang-legalize-types.cpp

+22-8
Original file line numberDiff line numberDiff line change
@@ -198,31 +198,40 @@ bool isResourceType(IRType* type)
198198
return false;
199199
}
200200

201-
bool isOpaqueType(IRType* type, List<IRType*>& opaqueTypes)
201+
202+
bool isOpaqueTypeImpl(IRType* type, HashSet<IRType*>& visited, IRType** outLeafOpaqueHandleType)
202203
{
204+
if (visited.contains(type))
205+
{
206+
if (outLeafOpaqueHandleType)
207+
*outLeafOpaqueHandleType = type;
208+
return true;
209+
}
210+
203211
if (isResourceType(type))
204212
{
205-
opaqueTypes.add(type);
213+
if (outLeafOpaqueHandleType)
214+
*outLeafOpaqueHandleType = type;
206215
return true;
207216
}
208217

209218
if (auto structType = as<IRStructType>(type))
210219
{
220+
visited.add(type);
211221
for (auto field : structType->getFields())
212222
{
213-
if (isOpaqueType(field->getFieldType(), opaqueTypes))
223+
if (isOpaqueTypeImpl(field->getFieldType(), visited, outLeafOpaqueHandleType))
214224
{
215-
opaqueTypes.add(type);
216225
return true;
217226
}
218227
}
228+
visited.remove(type);
219229
}
220230

221231
if (auto arrayType = as<IRArrayTypeBase>(type))
222232
{
223-
if (isOpaqueType(arrayType->getElementType(), opaqueTypes))
233+
if (isOpaqueTypeImpl(arrayType->getElementType(), visited, outLeafOpaqueHandleType))
224234
{
225-
opaqueTypes.add(type);
226235
return true;
227236
}
228237
}
@@ -233,9 +242,8 @@ bool isOpaqueType(IRType* type, List<IRType*>& opaqueTypes)
233242
{
234243
if (auto elementType = as<IRType>(tupleType->getOperand(i)))
235244
{
236-
if (isOpaqueType(elementType, opaqueTypes))
245+
if (isOpaqueTypeImpl(elementType, visited, outLeafOpaqueHandleType))
237246
{
238-
opaqueTypes.add(type);
239247
return true;
240248
}
241249
}
@@ -245,6 +253,12 @@ bool isOpaqueType(IRType* type, List<IRType*>& opaqueTypes)
245253
return false;
246254
}
247255

256+
bool isOpaqueType(IRType* type, IRType** outLeafOpaqueHandleType)
257+
{
258+
HashSet<IRType*> visited;
259+
return isOpaqueTypeImpl(type, visited, outLeafOpaqueHandleType);
260+
}
261+
248262
SourceLoc findBestSourceLocFromUses(IRInst* inst)
249263
{
250264
for (auto use = inst->firstUse; use; use = use->nextUse)

source/slang/slang-legalize-types.h

-2
Original file line numberDiff line numberDiff line change
@@ -703,8 +703,6 @@ void legalizeEmptyTypes(TargetProgram* target, IRModule* module, DiagnosticSink*
703703

704704
bool isResourceType(IRType* type);
705705

706-
bool isOpaqueType(IRType* type, List<IRType*>& opaqueTypes);
707-
708706
SourceLoc findBestSourceLocFromUses(IRInst* inst);
709707
} // namespace Slang
710708

0 commit comments

Comments
 (0)