Skip to content

Commit 830671c

Browse files
author
Tim Foley
authored
Add surface syntax for "this type" (shader-slang#1236)
Within the context of an aggregate type (or an `extension` of one), the programmer can use `this` to refer to the "current" instance of the surrounding type, but there is no easy way to utter the name of the type itself. This is especially relevant inside of an `interface`, where the type of `this` isn't actually the `interface` type, but rather a placeholder for the as-yet-unknown concrete type that will implement the interface. This change adds a keyword `This` that works similarly to `this`, but names the current *type* instead of the current instance. It can be used to declare things like binary methods or factory functions in an interface: ``` interface IBasicMathType { This absoluteValue(); This sumWith(This left); } T doSomeMath<T:IBasicMathType>(T value) { return value.sumWith(value.absoluteValue()); } ``` The `This` type is consistent with the type named `Self` in Rust and Swift (where Rust/Swift use `self` instead of `this`). Other names could be considered (e.g., `ThisType`) if we find that users don't like the name in this change.
1 parent 433ce86 commit 830671c

9 files changed

+122
-0
lines changed

source/slang/slang-check-expr.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -1677,6 +1677,12 @@ namespace Slang
16771677
expr->type.IsLeftValue = true;
16781678
}
16791679
}
1680+
else if( auto typeOrExtensionDecl = as<AggTypeDeclBase>(containerDecl) )
1681+
{
1682+
expr->type.type = calcThisType(makeDeclRef(typeOrExtensionDecl));
1683+
return expr;
1684+
}
1685+
#if 0
16801686
else if (auto aggTypeDecl = as<AggTypeDecl>(containerDecl))
16811687
{
16821688
ensureDecl(aggTypeDecl, DeclCheckState::CanUseAsType);
@@ -1706,11 +1712,35 @@ namespace Slang
17061712
expr->type.type = extensionDecl->targetType.type;
17071713
return expr;
17081714
}
1715+
#endif
17091716

17101717
scope = scope->parent;
17111718
}
17121719

17131720
getSink()->diagnose(expr, Diagnostics::thisExpressionOutsideOfTypeDecl);
17141721
return CreateErrorExpr(expr);
17151722
}
1723+
1724+
RefPtr<Expr> SemanticsExprVisitor::visitThisTypeExpr(ThisTypeExpr* expr)
1725+
{
1726+
auto scope = expr->scope;
1727+
while (scope)
1728+
{
1729+
auto containerDecl = scope->containerDecl;
1730+
if( auto typeOrExtensionDecl = as<AggTypeDeclBase>(containerDecl) )
1731+
{
1732+
auto thisType = calcThisType(makeDeclRef(typeOrExtensionDecl));
1733+
auto thisTypeType = getTypeType(thisType);
1734+
1735+
expr->type.type = thisTypeType;
1736+
return expr;
1737+
}
1738+
1739+
scope = scope->parent;
1740+
}
1741+
1742+
getSink()->diagnose(expr, Diagnostics::thisTypeOutsideOfTypeDecl);
1743+
return CreateErrorExpr(expr);
1744+
}
1745+
17161746
}

source/slang/slang-check-impl.h

+1
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,7 @@ namespace Slang
13631363
RefPtr<Expr> visitInitializerListExpr(InitializerListExpr* expr);
13641364

13651365
RefPtr<Expr> visitThisExpr(ThisExpr* expr);
1366+
RefPtr<Expr> visitThisTypeExpr(ThisTypeExpr* expr);
13661367
};
13671368

13681369
struct SemanticsStmtVisitor

source/slang/slang-diagnostic-defs.h

+1
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ DIAGNOSTIC(38100, Error, typeDoesntImplementInterfaceRequirement, "type '$0' doe
384384
DIAGNOSTIC(38101, Error, thisExpressionOutsideOfTypeDecl, "'this' expression can only be used in members of an aggregate type")
385385
DIAGNOSTIC(38102, Error, initializerNotInsideType, "an 'init' declaration is only allowed inside a type or 'extension' declaration")
386386
DIAGNOSTIC(38102, Error, accessorMustBeInsideSubscriptOrProperty, "an accessor declaration is only allowed inside a subscript or property declaration")
387+
DIAGNOSTIC(38103, Error, thisTypeOutsideOfTypeDecl, "'This' type can only be used inside of an aggregate type")
387388

388389
DIAGNOSTIC(38020, Error, mismatchEntryPointTypeArgument, "expecting $0 entry-point type arguments, provided $1.")
389390
DIAGNOSTIC(38021, Error, typeArgumentForGenericParameterDoesNotConformToInterface, "type argument `$0` for generic parameter `$1` does not conform to interface `$2`.")

source/slang/slang-expr-defs.h

+8
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,12 @@ SYNTAX_CLASS(TaggedUnionTypeExpr, Expr)
203203
RAW(
204204
List<TypeExp> caseTypes;
205205
)
206+
END_SYNTAX_CLASS()
207+
208+
/// A type expression of the form `This`
209+
///
210+
/// Refers to the type of `this` in the current context.
211+
///
212+
SYNTAX_CLASS(ThisTypeExpr, Expr)
213+
FIELD(RefPtr<Scope>, scope);
206214
END_SYNTAX_CLASS()

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

+6
Original file line numberDiff line numberDiff line change
@@ -3004,6 +3004,12 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
30043004
UNREACHABLE_RETURN(LoweredValInfo());
30053005
}
30063006

3007+
LoweredValInfo visitThisTypeExpr(ThisTypeExpr* /*expr*/)
3008+
{
3009+
SLANG_UNIMPLEMENTED_X("this-type expression during code generation");
3010+
UNREACHABLE_RETURN(LoweredValInfo());
3011+
}
3012+
30073013
LoweredValInfo visitAssignExpr(AssignExpr* expr)
30083014
{
30093015
// Because our representation of lowered "values"

source/slang/slang-mangle.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ namespace Slang
151151
}
152152
emitRaw(context, "U");
153153
}
154+
else if( auto thisType = dynamicCast<ThisType>(type) )
155+
{
156+
emitRaw(context, "t");
157+
emitQualifiedName(context, thisType->interfaceDeclRef);
158+
}
154159
else
155160
{
156161
SLANG_UNEXPECTED("unimplemented case in mangling");

source/slang/slang-parser.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,19 @@ namespace Slang
17941794
return parseTaggedUnionType(parser);
17951795
}
17961796

1797+
/// Parse a `This` type expression
1798+
static RefPtr<Expr> parseThisTypeExpr(Parser* parser)
1799+
{
1800+
RefPtr<ThisTypeExpr> expr = new ThisTypeExpr();
1801+
expr->scope = parser->currentScope;
1802+
return expr;
1803+
}
1804+
1805+
static RefPtr<RefObject> parseThisTypeExpr(Parser* parser, void* /*userData*/)
1806+
{
1807+
return parseThisTypeExpr(parser);
1808+
}
1809+
17971810
static TypeSpec parseTypeSpec(Parser* parser)
17981811
{
17991812
TypeSpec typeSpec;
@@ -1848,6 +1861,11 @@ namespace Slang
18481861
typeSpec.expr = parseTaggedUnionType(parser);
18491862
return typeSpec;
18501863
}
1864+
else if(AdvanceIf(parser, "This"))
1865+
{
1866+
typeSpec.expr = parseThisTypeExpr(parser);
1867+
return typeSpec;
1868+
}
18511869

18521870
Token typeName = parser->ReadToken(TokenType::Identifier);
18531871

@@ -4995,6 +5013,7 @@ namespace Slang
49955013
addBuiltinSyntax<Expr>(session, scope, #KEYWORD, &CALLBACK)
49965014

49975015
EXPR(this, parseThisExpr);
5016+
EXPR(This, parseThisTypeExpr);
49985017
EXPR(true, parseTrueExpr);
49995018
EXPR(false, parseFalseExpr);
50005019
EXPR(__TaggedUnion, parseTaggedUnionType);

tests/compute/this-type.slang

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// this-type.slang
2+
3+
// Confirm that that `This` type works as expected
4+
5+
//TEST(compute):COMPARE_COMPUTE:
6+
//TEST(compute):COMPARE_COMPUTE:-cpu
7+
8+
interface IFrobable
9+
{
10+
This frob();
11+
int getValue();
12+
}
13+
14+
struct Thing : IFrobable
15+
{
16+
int value;
17+
18+
Thing frob()
19+
{
20+
Thing result = { value * 16 };
21+
return result;
22+
}
23+
24+
int getValue() { return value; }
25+
}
26+
27+
int frobnicate<F:IFrobable>(F f)
28+
{
29+
return f.frob().getValue();
30+
}
31+
32+
int test(int value)
33+
{
34+
Thing t = { value };
35+
return frobnicate(t) + value;
36+
}
37+
38+
//TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer
39+
RWStructuredBuffer<int> outputBuffer : register(u0);
40+
41+
[numthreads(4, 1, 1)]
42+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
43+
{
44+
uint tid = dispatchThreadID.x;
45+
int inVal = outputBuffer[tid];
46+
int outVal = test(inVal);
47+
outputBuffer[tid] = outVal;
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0
2+
11
3+
22
4+
33

0 commit comments

Comments
 (0)