Skip to content

Commit 7ebc987

Browse files
Support generic type constraints when implicitly invoking generic
Fixes shader-slang#326 This basically just copy-pastes logic from the explicit case over to the implicit case. After we've solved for the explicit type/value arguments, we loop over the constraints and for each one we try to find a suitable subtype witness to use (after substituting in the arguments solved so far). This change includes a test case for the new functionality.
1 parent fab52a1 commit 7ebc987

File tree

3 files changed

+110
-8
lines changed

3 files changed

+110
-8
lines changed

source/slang/check.cpp

+66-8
Original file line numberDiff line numberDiff line change
@@ -3970,7 +3970,21 @@ namespace Slang
39703970
{
39713971
// For now the "solver" is going to be ridiculously simplistic.
39723972

3973-
// The generic itself will have some constraints, so we need to try and solve those too
3973+
// The generic itself will have some constraints, and for now we add these
3974+
// to the system of constrains we will use for solving for the type variables.
3975+
//
3976+
// TODO: we need to decide whether constraints are used like this to influence
3977+
// how we solve for type/value variables, or whether constraints in the parameter
3978+
// list just work as a validation step *after* we've solved for the types.
3979+
//
3980+
// That is, should we allow `<T : Int>` to be written, and cause us to "infer"
3981+
// that `T` should be the type `Int`? That seems a little silly.
3982+
//
3983+
// Eventually, though, we may want to support type identity constraints, especially
3984+
// on associated types, like `<C where C : IContainer && C.IndexType == Int>`
3985+
// These seem more reasonable to have influence constraint solving, since it could
3986+
// conceivably let us specialize a `X<T> : IContainer` to `X<Int>` if we find
3987+
// that `X<T>.IndexType == T`.
39743988
for( auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef) )
39753989
{
39763990
if(!TryUnifyTypes(*system, GetSub(constraintDeclRef), GetSup(constraintDeclRef)))
@@ -4062,6 +4076,57 @@ namespace Slang
40624076
}
40634077
}
40644078

4079+
// After we've solved for the explicit arguments, we need to
4080+
// make a second pass and consider the implicit arguments,
4081+
// based on what we've already determined to be the values
4082+
// for the explicit arguments.
4083+
4084+
// Before we begin, we are going to go ahead and create the
4085+
// "solved" substitution that we will return if everything works.
4086+
// This is because we are going to use this substitution,
4087+
// partially filled in with the results we know so far,
4088+
// in order to specialize any constraints on the generic.
4089+
//
4090+
// E.g., if the generic parameters were `<T : ISidekick>`, and
4091+
// we've already decided that `T` is `Robin`, then we want to
4092+
// search for a conformance `Robin : ISidekick`, which involved
4093+
// apply the substitutions we already know...
4094+
4095+
RefPtr<GenericSubstitution> solvedSubst = new GenericSubstitution();
4096+
solvedSubst->genericDecl = genericDeclRef.getDecl();
4097+
solvedSubst->outer = genericDeclRef.substitutions;
4098+
solvedSubst->args = args;
4099+
4100+
for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
4101+
{
4102+
DeclRef<GenericTypeConstraintDecl> constraintDeclRef(
4103+
constraintDecl,
4104+
solvedSubst);
4105+
4106+
// Extract the (substituted) sub- and super-type from the constraint.
4107+
auto sub = GetSub(constraintDeclRef);
4108+
auto sup = GetSup(constraintDeclRef);
4109+
4110+
// Search for a witness that shows the constraint is satisfied.
4111+
auto subTypeWitness = tryGetSubtypeWitness(sub, sup);
4112+
if(subTypeWitness)
4113+
{
4114+
// We found a witness, so it will become an (implicit) argument.
4115+
solvedSubst->args.Add(subTypeWitness);
4116+
}
4117+
else
4118+
{
4119+
// No witness was found, so the inference will now fail.
4120+
//
4121+
// TODO: Ideally we should print an error message in
4122+
// this case, to let the user know why things failed.
4123+
return nullptr;
4124+
}
4125+
4126+
// TODO: We may need to mark some constrains in our constraint
4127+
// system as being solved now, as a result of the witness we found.
4128+
}
4129+
40654130
// Make sure we haven't constructed any spurious constraints
40664131
// that we aren't able to satisfy:
40674132
for (auto c : system->constraints)
@@ -4072,13 +4137,6 @@ namespace Slang
40724137
}
40734138
}
40744139

4075-
// Consruct a reference to the extension with our constraint variables
4076-
// as the
4077-
RefPtr<GenericSubstitution> solvedSubst = new GenericSubstitution();
4078-
solvedSubst->genericDecl = genericDeclRef.getDecl();
4079-
solvedSubst->outer = genericDeclRef.substitutions;
4080-
solvedSubst->args = args;
4081-
40824140
return solvedSubst;
40834141
}
40844142

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir
2+
3+
//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
4+
5+
// Testing that we can implicitly specialize a generic
6+
// that has a constrained type parameter.
7+
8+
9+
interface ISimple
10+
{
11+
int getVal();
12+
}
13+
14+
struct Simple : ISimple
15+
{
16+
int val;
17+
18+
int getVal() { return val; }
19+
};
20+
21+
int doIt<T : ISimple>(T obj)
22+
{
23+
return obj.getVal();
24+
}
25+
26+
int test(int val)
27+
{
28+
Simple simple;
29+
simple.val = val;
30+
return doIt(simple);
31+
}
32+
33+
RWStructuredBuffer<int> outputBuffer : register(u0);
34+
[numthreads(4, 1, 1)]
35+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
36+
{
37+
int tid = (int) dispatchThreadID.x;
38+
int outVal = test(tid);
39+
outputBuffer[tid] = outVal;
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0
2+
1
3+
2
4+
3

0 commit comments

Comments
 (0)