Skip to content

Commit 6e591ad

Browse files
csyongheTim Foley
authored and
Tim Foley
committedNov 8, 2017
Support generic interface methods (shader-slang#251)
* improve diagnostic messages and prevent fatal errors from crashing the compiler. * fix top level exception catching. * spelling fix * change wording of invalidSwizzleExpr diagnostic * add speculative GenericsApp expr parsing * add new test case of cascading generics call. * Fixing bugs in compiling cascaded generic function calls. Add implementation of DeclaredSubTypeWitness::SubstituteImpl() This is not needed by the type checker, but needed by IR specialization. When input source contains cascading generic function call, the arguments to `specialize` instruction is currently represented as a substitution. The arg values of this subsittution can be a `DeclaredSubTypeWitness` when a generic function uses one of its generic parameter to specialize another generic function. When the top level generics function is being specialized, this substitution argument, which is a `DeclaredSubTypeWitness`, needs to be substituted with the witness that used to specialize the top level function in the specialized specialize instruction as well. * add a test case for cascading generic function call. * parser bug fix * fixes shader-slang#255 * add test case for issue shader-slang#255 * Generate missing `specialize` instruction when calling a generic method from an interface constraint. When calling a generic method via an interface, we should be generating the following ir: ... f = lookup_interface_method(...) f_s = specailize(f, declRef) ... This commit fixes this `emitFuncRef` function to emit the needed `specialize` instruction. * fixes shader-slang#260 This fix follows the second apporach in the disucssion. It generated mangled name for specialized functions by appending new substitution type names to the original mangled name. * Disabling removing and re-inserting specailized functions in getSpecalizeFunc() I am not sure why it is needed, it seems HLSL and GLSL backends are generating forward declarations anyways, so the order of functions in IRModule shouldn't matter. * cleanup and complete test cases. * fix warnings
1 parent 939688e commit 6e591ad

24 files changed

+524
-65
lines changed
 

‎source/core/exception.h

+11
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,17 @@ namespace Slang
121121
{
122122
}
123123
};
124+
125+
class AbortCompilationException : public Exception
126+
{
127+
public:
128+
AbortCompilationException()
129+
{}
130+
AbortCompilationException(const String & message)
131+
: Exception(message)
132+
{
133+
}
134+
};
124135
}
125136

126137
#endif

‎source/slang/check.cpp

+54-6
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,44 @@ namespace Slang
15881588
return true;
15891589
}
15901590

1591+
bool doesGenericSignatureMatchRequirement(
1592+
GenericDecl * genDecl,
1593+
DeclRef<GenericDecl> requirementGenDecl)
1594+
{
1595+
// TODO: genDecl should be a DeclRef to capture the environment generic variables needed to get
1596+
// a concrete type for a generic constraint super type (e.g. when this member belongs to a generic type)
1597+
1598+
if (genDecl->Members.Count() != requirementGenDecl.getDecl()->Members.Count())
1599+
return false;
1600+
for (UInt i = 0; i < genDecl->Members.Count(); i++)
1601+
{
1602+
auto genMbr = genDecl->Members[i];
1603+
auto requiredGenMbr = genDecl->Members[i];
1604+
if (auto genTypeMbr = genMbr.As<GenericTypeParamDecl>())
1605+
{
1606+
if (auto requiredGenTypeMbr = requiredGenMbr.As<GenericTypeParamDecl>())
1607+
{
1608+
}
1609+
else
1610+
return false;
1611+
}
1612+
else if (auto genTypeConstraintMbr = genMbr.As<GenericTypeConstraintDecl>())
1613+
{
1614+
if (auto requiredTypeConstraintMbr = requiredGenMbr.As<GenericTypeConstraintDecl>())
1615+
{
1616+
if (!genTypeConstraintMbr->sup->Equals(requiredTypeConstraintMbr->sup))
1617+
{
1618+
return false;
1619+
}
1620+
}
1621+
else
1622+
return false;
1623+
}
1624+
}
1625+
return doesMemberSatisfyRequirement(genDecl->inner.Ptr(),
1626+
DeclRef<Decl>(requirementGenDecl.getDecl()->inner.Ptr(), requirementGenDecl.substitutions));
1627+
}
1628+
15911629
// Does the given `memberDecl` work as an implementation
15921630
// to satisfy the requirement `requiredMemberDeclRef`
15931631
// from an interface?
@@ -1634,6 +1672,13 @@ namespace Slang
16341672
requiredInitDecl);
16351673
}
16361674
}
1675+
else if (auto genDecl = dynamic_cast<GenericDecl*>(memberDecl))
1676+
{
1677+
if (auto requiredGenDeclRef = requiredMemberDeclRef.As<GenericDecl>())
1678+
{
1679+
return doesGenericSignatureMatchRequirement(genDecl, requiredGenDeclRef);
1680+
}
1681+
}
16371682
else if (auto subStructTypeDecl = dynamic_cast<AggTypeDecl*>(memberDecl))
16381683
{
16391684
// this is a sub type (e.g. nested struct declaration) in an aggregate type
@@ -4462,9 +4507,13 @@ namespace Slang
44624507

44634508
DeclRef<Decl> innerDeclRef(GetInner(baseGenericRef), subst);
44644509

4510+
RefPtr<Expr> base;
4511+
if (auto mbrExpr = baseExpr.As<MemberExpr>())
4512+
base = mbrExpr->BaseExpression;
4513+
44654514
return ConstructDeclRefExpr(
44664515
innerDeclRef,
4467-
nullptr,
4516+
base,
44684517
originalExpr->loc);
44694518
}
44704519

@@ -5738,7 +5787,6 @@ namespace Slang
57385787
// declarations with the same name, so this becomes a specialized case of
57395788
// overload resolution.
57405789

5741-
57425790
// Start by checking the base expression and arguments.
57435791
auto& baseExpr = genericAppExpr->FunctionExpr;
57445792
baseExpr = CheckTerm(baseExpr);
@@ -6095,9 +6143,9 @@ namespace Slang
60956143
case 'w': case 'a': elementIndex = 3; break;
60966144
default:
60976145
// An invalid character in the swizzle is an error
6098-
if (!isRewriteMode())
6146+
if (!isRewriteMode() && !anyError)
60996147
{
6100-
getSink()->diagnose(swizExpr, Diagnostics::unimplemented, "invalid component name for swizzle");
6148+
getSink()->diagnose(swizExpr, Diagnostics::invalidSwizzleExpr, swizzleText, baseElementType->ToString());
61016149
}
61026150
anyError = true;
61036151
continue;
@@ -6109,9 +6157,9 @@ namespace Slang
61096157
// Make sure the index is in range for the source type
61106158
if (elementIndex >= limitElement)
61116159
{
6112-
if (!isRewriteMode())
6160+
if (!isRewriteMode() && !anyError)
61136161
{
6114-
getSink()->diagnose(swizExpr, Diagnostics::unimplemented, "swizzle component out of range for type");
6162+
getSink()->diagnose(swizExpr, Diagnostics::invalidSwizzleExpr, swizzleText, baseElementType->ToString());
61156163
}
61166164
anyError = true;
61176165
continue;

‎source/slang/diagnostic-defs.h

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ DIAGNOSTIC(30035, Error, componentOverloadTypeMismatch, "'$0': type of overloade
191191
DIAGNOSTIC(30041, Error, bitOperationNonIntegral, "bit operation: operand must be integral type.")
192192
DIAGNOSTIC(30047, Error, argumentExpectedLValue, "argument passed to parameter '$0' must be l-value.")
193193
DIAGNOSTIC(30051, Error, invalidValueForArgument, "invalid value for argument '$0'")
194+
DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on type '$1'")
194195
DIAGNOSTIC(33070, Error, expectedFunction, "expression preceding parenthesis of apparent call must have function type.")
195196

196197
// 303xx: interfaces and associated types

‎source/slang/diagnostics.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ void DiagnosticSink::diagnoseImpl(SourceLoc const& pos, DiagnosticInfo const& in
209209
if (diagnostic.severity >= Severity::Fatal)
210210
{
211211
// TODO: figure out a better policy for aborting compilation
212-
throw InvalidOperationException();
212+
throw AbortCompilationException();
213213
}
214214
}
215215

‎source/slang/emit.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -4754,7 +4754,7 @@ emitDeclImpl(decl, nullptr);
47544754
void readSimpleIntVal()
47554755
{
47564756
int c = peek();
4757-
if(isDigit(c))
4757+
if(isDigit((char)c))
47584758
{
47594759
get();
47604760
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -469,12 +469,12 @@ static LegalVal legalizeFieldAddress(
469469
}
470470
}
471471
SLANG_UNEXPECTED("didn't find tuple element");
472-
return LegalVal();
472+
UNREACHABLE_RETURN(LegalVal());
473473
}
474474

475475
default:
476476
SLANG_UNEXPECTED("unhandled");
477-
return LegalVal();
477+
UNREACHABLE_RETURN(LegalVal());
478478
}
479479
}
480480

‎source/slang/ir.cpp

+24-12
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,15 @@ namespace Slang
5555
}
5656
}
5757

58-
void IRUse::set(IRValue* usedValue)
58+
void IRUse::set(IRValue* usedVal)
5959
{
6060
// clear out the old value
61-
if (usedValue)
61+
if (usedVal)
6262
{
6363
*prevLink = nextUse;
6464
}
6565

66-
init(user, usedValue);
66+
init(user, usedVal);
6767
}
6868

6969
void IRUse::clear()
@@ -3750,8 +3750,6 @@ namespace Slang
37503750
{
37513751
if( auto subtypeWitness = dynamic_cast<SubtypeWitness*>(val) )
37523752
{
3753-
// We need to look up the IR value that represents the
3754-
// given subtype witness.
37553753
String mangledName = getMangledNameForConformanceWitness(
37563754
subtypeWitness->sub,
37573755
subtypeWitness->sup);
@@ -3948,8 +3946,11 @@ namespace Slang
39483946
// has already been made. To do that we will need to
39493947
// compute the mangled name of the specialized function,
39503948
// so that we can look for existing declarations.
3951-
3952-
String specMangledName = getMangledName(specDeclRef);
3949+
String specMangledName;
3950+
if (genericFunc->genericDecl == specDeclRef.decl)
3951+
specMangledName = getMangledName(specDeclRef);
3952+
else
3953+
specMangledName = mangleSpecializedFuncName(genericFunc->mangledName, specDeclRef.substitutions);
39533954

39543955
// TODO: This is a terrible linear search, and we should
39553956
// avoid it by building a dictionary ahead of time,
@@ -3992,8 +3993,8 @@ namespace Slang
39923993
//
39933994
// TODO: This shouldn't be needed, if we introduce a sorting
39943995
// step before we emit code.
3995-
specFunc->removeFromParent();
3996-
specFunc->insertAfter(genericFunc);
3996+
//specFunc->removeFromParent();
3997+
//specFunc->insertAfter(genericFunc);
39973998

39983999
// At this point we've created a new non-generic function,
39994000
// which means we should add it to our work list for
@@ -4026,8 +4027,20 @@ namespace Slang
40264027
auto keyDeclRef = ((IRDeclRef*) requirementKey)->declRef;
40274028

40284029
// If the keys don't match, continue with the next entry.
4029-
if(!keyDeclRef.Equals(requirementDeclRef))
4030-
continue;
4030+
if (!keyDeclRef.Equals(requirementDeclRef))
4031+
{
4032+
// requirementDeclRef may be pointing to the inner decl of a generic decl
4033+
// in this case we compare keyDeclRef against the parent decl of requiredDeclRef
4034+
if (auto genRequiredDeclRef = requirementDeclRef.GetParent().As<GenericDecl>())
4035+
{
4036+
if (!keyDeclRef.Equals(genRequiredDeclRef))
4037+
{
4038+
continue;
4039+
}
4040+
}
4041+
else
4042+
continue;
4043+
}
40314044

40324045
// If the keys matched, then we use the value from
40334046
// this entry.
@@ -4178,7 +4191,6 @@ namespace Slang
41784191
// Use the witness table to look up the value that
41794192
// satisfies the requirement.
41804193
auto satisfyingVal = findWitnessVal(witnessTable, requirementDeclRef);
4181-
41824194
// We expect to always find something, but lets just
41834195
// be careful here.
41844196
if(!satisfyingVal)

‎source/slang/lower-to-ir.cpp

+31-3
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,13 @@ LoweredValInfo emitDeclRef(
314314
IRGenContext* context,
315315
DeclRef<Decl> declRef);
316316

317+
// Emit necessary `specialize` instruction needed by a declRef.
318+
// This is currently used by emitDeclRef() and emitFuncRef()
319+
LoweredValInfo maybeEmitSpecializeInst(IRGenContext* context,
320+
LoweredValInfo loweredDecl, // the lowered value of the inner decl
321+
DeclRef<Decl> declRef // the full decl ref containing substitutions
322+
);
323+
317324

318325
IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered);
319326

@@ -488,10 +495,11 @@ LoweredValInfo emitFuncRef(
488495

489496
RefPtr<Type> type = funcExpr->type;
490497

491-
return LoweredValInfo::simple(context->irBuilder->emitLookupInterfaceMethodInst(
498+
auto loweredVal = LoweredValInfo::simple(context->irBuilder->emitLookupInterfaceMethodInst(
492499
type,
493500
baseMemberDeclRef,
494501
funcDeclRef));
502+
return maybeEmitSpecializeInst(context, loweredVal, funcDeclRef);
495503
}
496504
}
497505
}
@@ -2893,6 +2901,13 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
28932901
return LoweredValInfo::simple(irFunc);
28942902
}
28952903

2904+
LoweredValInfo visitGenericDecl(GenericDecl * genDecl)
2905+
{
2906+
if (auto innerFuncDecl = genDecl->inner->As<FuncDecl>())
2907+
return lowerFuncDecl(innerFuncDecl);
2908+
SLANG_RELEASE_ASSERT(false);
2909+
UNREACHABLE_RETURN(LoweredValInfo());
2910+
}
28962911

28972912
LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl)
28982913
{
@@ -2997,6 +3012,11 @@ RefPtr<Val> lowerSubstitutionArg(
29973012
}
29983013
else if (auto declaredSubtypeWitness = dynamic_cast<DeclaredSubtypeWitness*>(val))
29993014
{
3015+
// We do not have a concrete witness table yet for a GenericTypeConstraintDecl witness
3016+
3017+
if (declaredSubtypeWitness->declRef.As<GenericTypeConstraintDecl>())
3018+
return val;
3019+
30003020
// We need to look up the IR-level representation of the witness
30013021
// (which is a witness table).
30023022

@@ -3073,9 +3093,16 @@ LoweredValInfo emitDeclRef(
30733093
// unspecialized declaration.
30743094
LoweredValInfo loweredDecl = ensureDecl(context, declRef.getDecl());
30753095

3096+
return maybeEmitSpecializeInst(context, loweredDecl, declRef);
3097+
}
3098+
3099+
LoweredValInfo maybeEmitSpecializeInst(IRGenContext* context,
3100+
LoweredValInfo loweredDecl,
3101+
DeclRef<Decl> declRef)
3102+
{
30763103
// If this declaration reference doesn't involve any specializations,
30773104
// then we are done at this point.
3078-
if(!hasGenericSubstitutions(declRef.substitutions))
3105+
if (!hasGenericSubstitutions(declRef.substitutions))
30793106
return loweredDecl;
30803107

30813108
auto val = getSimpleVal(context, loweredDecl);
@@ -3089,7 +3116,7 @@ LoweredValInfo emitDeclRef(
30893116

30903117

30913118
RefPtr<Type> type;
3092-
if(auto declType = val->getType())
3119+
if (auto declType = val->getType())
30933120
{
30943121
type = declType->Substitute(declRef.substitutions).As<Type>();
30953122
}
@@ -3102,6 +3129,7 @@ LoweredValInfo emitDeclRef(
31023129
declRef));
31033130
}
31043131

3132+
31053133
static void lowerEntryPointToIR(
31063134
IRGenContext* context,
31073135
EntryPointRequest* entryPointRequest)

‎source/slang/mangle.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,24 @@ namespace Slang
350350
DeclRef<Decl>(declRef.decl, declRef.substitutions));
351351
}
352352

353+
String mangleSpecializedFuncName(String baseName, RefPtr<Substitutions> subst)
354+
{
355+
ManglingContext context;
356+
emitRaw(&context, baseName.Buffer());
357+
emitRaw(&context, "_G");
358+
while (subst)
359+
{
360+
if (auto genSubst = subst.As<GenericSubstitution>())
361+
{
362+
for (auto a : genSubst->args)
363+
emitVal(&context, a);
364+
break;
365+
}
366+
subst = subst->outer;
367+
}
368+
return context.sb.ProduceString();
369+
}
370+
353371
String getMangledName(Decl* decl)
354372
{
355373
return getMangledName(makeDeclRef(decl));

‎source/slang/mangle.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Slang
1111
String getMangledName(Decl* decl);
1212
String getMangledName(DeclRef<Decl> const & declRef);
1313
String getMangledName(DeclRefBase const & declRef);
14-
14+
String mangleSpecializedFuncName(String baseName, RefPtr<Substitutions> subst);
1515
String getMangledNameForConformanceWitness(
1616
Type* sub,
1717
Type* sup);

0 commit comments

Comments
 (0)