Skip to content

Commit 42f1cff

Browse files
Support explicit this expressions
This is the first step towards supporting traditional object-oriented method definitions; the second step will be to allow `this` expressions to be implicit. - Add a test case using explicit `this`, and expected output - Update parsing logic for expressions so that it handled identifiers similarly to the declaration and statement logic: first try to parse using a syntax declaration looked up in the curent scope, and otherwise fall back to the ordinary `VarExpr` case. * As long as I'm making that change: switch `true` and `false` to be parsed via the callback mechanism rather than be special-cased. * This change will also help out if we ever wanted to add `super`/`base` expressions, `new`, `sizeof`/`alignof` or any other expression keywords. - Add a `ThisExpr` node and register a parser callback for it. - Add semantic checks for `ThisExpr`: basically just look upwards through scopes until we find either an aggregate type declaration or an `extension` declaration, and then use that as the type of the expression. - TODO: eventually we need to guard against a `this` expression inside of a `static` member. - The IR generation logic already handled creation of `this` parameters in function signatures; the missing piece was to register the appropriate parameter in the context, so that we can use it as the lowering of a `this` expression.
1 parent 4ab545b commit 42f1cff

8 files changed

+196
-17
lines changed

source/slang/check.cpp

+57
Original file line numberDiff line numberDiff line change
@@ -5970,6 +5970,11 @@ namespace Slang
59705970
// We need to fix that.
59715971
auto type = typeType->type;
59725972

5973+
if (type->As<ErrorType>())
5974+
{
5975+
return CreateErrorExpr(expr);
5976+
}
5977+
59735978
LookupResult lookupResult = lookUpMember(
59745979
getSession(),
59755980
this,
@@ -5988,6 +5993,10 @@ namespace Slang
59885993
expr->BaseExpression,
59895994
expr->loc);
59905995
}
5996+
else if (baseType->As<ErrorType>())
5997+
{
5998+
return CreateErrorExpr(expr);
5999+
}
59916000
else
59926001
{
59936002
LookupResult lookupResult = lookUpMember(
@@ -6093,6 +6102,54 @@ namespace Slang
60936102

60946103
decl->SetCheckState(DeclCheckState::Checked);
60956104
}
6105+
6106+
// Perform semantic checking of an object-oriented `this`
6107+
// expression.
6108+
RefPtr<Expr> visitThisExpr(ThisExpr* expr)
6109+
{
6110+
// We will do an upwards search starting in the current
6111+
// scope, looking for a surrounding type (or `extension`)
6112+
// declaration that could be the referrant of the expression.
6113+
auto scope = expr->scope;
6114+
while (scope)
6115+
{
6116+
auto containerDecl = scope->containerDecl;
6117+
if (auto aggTypeDecl = containerDecl->As<AggTypeDecl>())
6118+
{
6119+
EnsureDecl(aggTypeDecl);
6120+
6121+
// Okay, we are using `this` in the context of an
6122+
// aggregate type, so the expression should be
6123+
// of the corresponding type.
6124+
expr->type = DeclRefType::Create(
6125+
getSession(),
6126+
makeDeclRef(aggTypeDecl));
6127+
return expr;
6128+
}
6129+
else if (auto extensionDecl = containerDecl->As<ExtensionDecl>())
6130+
{
6131+
EnsureDecl(extensionDecl);
6132+
6133+
// When `this` is used in the context of an `extension`
6134+
// declaration, then it should refer to an instance of
6135+
// the type being extended.
6136+
//
6137+
// TODO: There is potentially a small gotcha here that
6138+
// lookup through such a `this` expression should probably
6139+
// prioritize members declared in the current extension
6140+
// if there are multiple extensions in scope that add
6141+
// members with the same name...
6142+
//
6143+
expr->type = QualType(extensionDecl->targetType.type);
6144+
return expr;
6145+
}
6146+
6147+
scope = scope->parent;
6148+
}
6149+
6150+
getSink()->diagnose(expr, Diagnostics::thisExpressionOutsideOfTypeDecl);
6151+
return CreateErrorExpr(expr);
6152+
}
60966153
};
60976154

60986155
bool isPrimaryDecl(

source/slang/diagnostic-defs.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -308,15 +308,16 @@ DIAGNOSTIC(39999, Error, tooManyArguments, "too many arguments to call (got $0,
308308
DIAGNOSTIC(39999, Error, invalidIntegerLiteralSuffix, "invalid suffix '$0' on integer literal")
309309
DIAGNOSTIC(39999, Error, invalidFloatingPOintLiteralSuffix, "invalid suffix '$0' on floating-point literal")
310310

311-
DIAGNOSTIC(39999, Error, conflictingExplicitBindingsForParameter, "conflicting explicit bindings for parameter '$0'")
312-
DIAGNOSTIC(39999, Warning, parameterBindingsOverlap, "explicit binding for parameter '$0' overlaps with parameter '$1'")
311+
DIAGNOSTIC(39999, Error, conflictingExplicitBindingsForParameter, "conflicting explicit bindings for parameter '$0'")
312+
DIAGNOSTIC(39999, Warning, parameterBindingsOverlap, "explicit binding for parameter '$0' overlaps with parameter '$1'")
313313

314-
DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'")
315-
DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'")
316-
DIAGNOSTIC(38002, Note, entryPointCandidate, "see candidate declaration for entry point '$0'")
317-
DIAGNOSTIC(38003, Error, entryPointSymbolNotAFunction, "entry point '$0' must be declared as a function")
314+
DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'")
315+
DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'")
316+
DIAGNOSTIC(38002, Note, entryPointCandidate, "see candidate declaration for entry point '$0'")
317+
DIAGNOSTIC(38003, Error, entryPointSymbolNotAFunction, "entry point '$0' must be declared as a function")
318318

319319
DIAGNOSTIC(38100, Error, typeDoesntImplementInterfaceRequirement, "type '$0' does not provide required interface member '$1'")
320+
DIAGNOSTIC(38101, Error, thisExpressionOutsideOfTypeDecl, "'this' expression can only be used in members of an aggregate type")
320321

321322
//
322323
// 4xxxx - IL code generation.

source/slang/emit.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -2381,6 +2381,17 @@ struct EmitVisitor
23812381
if(needClose) Emit(")");
23822382
}
23832383

2384+
void visitThisExpr(ThisExpr* expr, ExprEmitArg const& arg)
2385+
{
2386+
auto prec = kEOp_Atomic;
2387+
auto outerPrec = arg.outerPrec;
2388+
bool needClose = MaybeEmitParens(outerPrec, prec);
2389+
2390+
Emit("this");
2391+
2392+
if(needClose) Emit(")");
2393+
}
2394+
23842395
void visitSwizzleExpr(SwizzleExpr* swizExpr, ExprEmitArg const& arg)
23852396
{
23862397
auto prec = kEOp_Postfix;

source/slang/expr-defs.h

+7
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,10 @@ END_SYNTAX_CLASS()
148148
SYNTAX_CLASS(ParenExpr, Expr)
149149
SYNTAX_FIELD(RefPtr<Expr>, base);
150150
END_SYNTAX_CLASS()
151+
152+
// An object-oriented `this` expression, used to
153+
// refer to the current instance of an enclosing type.
154+
SYNTAX_CLASS(ThisExpr, Expr)
155+
FIELD(RefPtr<Scope>, scope);
156+
END_SYNTAX_CLASS()
157+

source/slang/lower-to-ir.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ struct IRGenContext
288288

289289
IRBuilder* irBuilder;
290290

291+
// The value to use for any `this` expressions
292+
// that appear in the current context.
293+
//
294+
// TODO: If we ever allow nesting of (non-static)
295+
// types, then we may need to support references
296+
// to an "outer `this`", and this representation
297+
// might be insufficient.
298+
LoweredValInfo thisVal;
299+
291300
Session* getSession()
292301
{
293302
return shared->compileRequest->mSession;
@@ -1008,6 +1017,11 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
10081017
return subscriptValue(type, baseVal, indexVal);
10091018
}
10101019

1020+
LoweredValInfo visitThisExpr(ThisExpr* expr)
1021+
{
1022+
return context->thisVal;
1023+
}
1024+
10111025
LoweredValInfo visitMemberExpr(MemberExpr* expr)
10121026
{
10131027
auto loweredType = lowerType(context, expr->type);
@@ -2360,9 +2374,18 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
23602374
};
23612375
struct ParameterInfo
23622376
{
2377+
// This AST-level type of the parameter
23632378
Type* type;
2379+
2380+
// The direction (`in` vs `out` vs `in out`)
23642381
ParameterDirection direction;
2382+
2383+
// The variable/parameter declaration for
2384+
// this parameter (if any)
23652385
VarDeclBase* decl;
2386+
2387+
// Is this the representation of a `this` parameter?
2388+
bool isThisParam = false;
23662389
};
23672390
//
23682391
// We need a way to compute the appropriate `ParameterDirection` for a
@@ -2399,6 +2422,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
23992422
info.type = paramDecl->getType();
24002423
info.decl = paramDecl;
24012424
info.direction = getParameterDirection(paramDecl);
2425+
info.isThisParam = false;
24022426
return info;
24032427
}
24042428
//
@@ -2492,6 +2516,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
24922516
info.type = type;
24932517
info.decl = nullptr;
24942518
info.direction = direction;
2519+
info.isThisParam = true;
24952520

24962521
ioParameterLists->params.Add(info);
24972522
}
@@ -2808,6 +2833,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
28082833
DeclRef<VarDeclBase> paramDeclRef = makeDeclRef(paramDecl);
28092834
subContext->shared->declValues.Add(paramDeclRef, paramVal);
28102835
}
2836+
2837+
if (paramInfo.isThisParam)
2838+
{
2839+
subContext->thisVal = paramVal;
2840+
}
28112841
}
28122842

28132843
lowerStmt(subContext, decl->Body);

source/slang/parser.cpp

+46-11
Original file line numberDiff line numberDiff line change
@@ -3334,6 +3334,32 @@ namespace Slang
33343334

33353335
static RefPtr<Expr> parsePrefixExpr(Parser* parser);
33363336

3337+
// Parse OOP `this` expression syntax
3338+
static RefPtr<RefObject> parseThisExpr(Parser* parser, void* /*userData*/)
3339+
{
3340+
RefPtr<ThisExpr> expr = new ThisExpr();
3341+
expr->scope = parser->currentScope;
3342+
return expr;
3343+
}
3344+
3345+
static RefPtr<Expr> parseBoolLitExpr(Parser* parser, bool value)
3346+
{
3347+
RefPtr<ConstantExpr> constExpr = new ConstantExpr();
3348+
constExpr->ConstType = ConstantExpr::ConstantType::Bool;
3349+
constExpr->integerValue = value ? 1 : 0;
3350+
return constExpr;
3351+
}
3352+
3353+
static RefPtr<RefObject> parseTrueExpr(Parser* parser, void* /*userData*/)
3354+
{
3355+
return parseBoolLitExpr(parser, true);
3356+
}
3357+
3358+
static RefPtr<RefObject> parseFalseExpr(Parser* parser, void* /*userData*/)
3359+
{
3360+
return parseBoolLitExpr(parser, false);
3361+
}
3362+
33373363
static RefPtr<Expr> parseAtomicExpr(Parser* parser)
33383364
{
33393365
switch( peekTokenType(parser) )
@@ -3577,19 +3603,18 @@ namespace Slang
35773603

35783604
case TokenType::Identifier:
35793605
{
3580-
// TODO(tfoley): Need a name-lookup step here to resolve
3581-
// syntactic keywords in expression context.
3606+
// We will perform name lookup here so that we can find syntax
3607+
// keywords registered for use as expressions.
3608+
Token nameToken = peekToken(parser);
35823609

3583-
if (parser->LookAheadToken("true") || parser->LookAheadToken("false"))
3610+
RefPtr<Expr> parsedExpr;
3611+
if (tryParseUsingSyntaxDecl<Expr>(parser, &parsedExpr))
35843612
{
3585-
RefPtr<ConstantExpr> constExpr = new ConstantExpr();
3586-
auto token = parser->tokenReader.AdvanceToken();
3587-
constExpr->token = token;
3588-
parser->FillPosition(constExpr.Ptr());
3589-
constExpr->ConstType = ConstantExpr::ConstantType::Bool;
3590-
constExpr->integerValue = token.Content == "true" ? 1 : 0;
3591-
3592-
return constExpr;
3613+
if (!parsedExpr->loc.isValid())
3614+
{
3615+
parsedExpr->loc = nameToken.loc;
3616+
}
3617+
return parsedExpr;
35933618
}
35943619

35953620
// Default behavior is just to create a name expression
@@ -4090,6 +4115,16 @@ namespace Slang
40904115

40914116
#undef MODIFIER
40924117

4118+
// Add syntax for expression keywords
4119+
#define EXPR(KEYWORD, CALLBACK) \
4120+
addBuiltinSyntax<Expr>(session, scope, #KEYWORD, &CALLBACK)
4121+
4122+
EXPR(this, parseThisExpr);
4123+
EXPR(true, parseTrueExpr);
4124+
EXPR(false, parseFalseExpr);
4125+
4126+
#undef EXPR
4127+
40934128
return moduleDecl;
40944129
}
40954130

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//TEST(smoke,compute):COMPARE_COMPUTE:-xslang -use-ir
2+
//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
3+
4+
// Access fields of a `struct` type from within a "method" by
5+
// using an explicit `this` expression.
6+
7+
struct A
8+
{
9+
float x;
10+
11+
float addWith(float y)
12+
{
13+
return this.x + y;
14+
}
15+
};
16+
17+
RWStructuredBuffer<float> outputBuffer : register(u0);
18+
19+
20+
float test(float inVal)
21+
{
22+
A a;
23+
a.x = inVal;
24+
return a.addWith(inVal*inVal);
25+
}
26+
27+
[numthreads(4, 1, 1)]
28+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
29+
{
30+
uint tid = dispatchThreadID.x;
31+
float inVal = float(tid);
32+
float outVal = test(inVal);
33+
outputBuffer[tid] = outVal;
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0
2+
40000000
3+
40C00000
4+
41400000

0 commit comments

Comments
 (0)