Skip to content

Commit c850ba4

Browse files
jsmall-zzzTim Foley
authored and
Tim Foley
committed
* Add support for enum and type lookup via :: (scope operator) (shader-slang#882)
* Added test for scope operator
1 parent 87610f6 commit c850ba4

File tree

4 files changed

+207
-65
lines changed

4 files changed

+207
-65
lines changed

source/slang/check.cpp

+98-64
Original file line numberDiff line numberDiff line change
@@ -8843,63 +8843,14 @@ namespace Slang
88438843
}
88448844
}
88458845

8846-
RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* /*expr*/)
8846+
// Look up a static member
8847+
// @param expr Can be StaticMemberExpr or MemberExpr
8848+
// @param baseExpression Is the underlying type expression determined from resolving expr
8849+
RefPtr<Expr> _lookupStaticMember(RefPtr<DeclRefExpr> expr, RefPtr<Expr> baseExpression)
88478850
{
8848-
// StaticMemberExpr means it is already checked
8849-
SLANG_UNEXPECTED("should not occur in unchecked AST");
8850-
UNREACHABLE_RETURN(nullptr);
8851-
}
8852-
8853-
RefPtr<Expr> lookupResultFailure(
8854-
MemberExpr* expr,
8855-
QualType const& baseType)
8856-
{
8857-
getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->name, baseType);
8858-
expr->type = QualType(getSession()->getErrorType());
8859-
return expr;
8860-
8861-
}
8862-
8863-
RefPtr<Expr> visitMemberExpr(MemberExpr * expr)
8864-
{
8865-
expr->BaseExpression = CheckExpr(expr->BaseExpression);
8866-
8867-
expr->BaseExpression = MaybeDereference(expr->BaseExpression);
8851+
auto& baseType = baseExpression->type;
88688852

8869-
// If the base of the member lookup has an interface type
8870-
// *without* a suitable this-type substitution, then we are
8871-
// trying to perform lookup on a value of existential type,
8872-
// and we should "open" the existential here so that we
8873-
// can expose its structure.
8874-
//
8875-
expr->BaseExpression = maybeOpenExistential(expr->BaseExpression);
8876-
8877-
auto & baseType = expr->BaseExpression->type;
8878-
8879-
// Note: Checking for vector types before declaration-reference types,
8880-
// because vectors are also declaration reference types...
8881-
//
8882-
// Also note: the way this is done right now means that the ability
8883-
// to swizzle vectors interferes with any chance of looking up
8884-
// members via extension, for vector or scalar types.
8885-
//
8886-
// TODO: Matrix swizzles probably need to be handled at some point.
8887-
if (auto baseVecType = as<VectorExpressionType>(baseType))
8888-
{
8889-
return CheckSwizzleExpr(
8890-
expr,
8891-
baseVecType->elementType,
8892-
baseVecType->elementCount);
8893-
}
8894-
else if(auto baseScalarType = as<BasicExpressionType>(baseType))
8895-
{
8896-
// Treat scalar like a 1-element vector when swizzling
8897-
return CheckSwizzleExpr(
8898-
expr,
8899-
baseScalarType,
8900-
1);
8901-
}
8902-
else if(auto typeType = as<TypeType>(baseType))
8853+
if (auto typeType = as<TypeType>(baseType))
89038854
{
89048855
// We are looking up a member inside a type.
89058856
// We want to be careful here because we should only find members
@@ -8921,7 +8872,7 @@ namespace Slang
89218872
type);
89228873
if (!lookupResult.isValid())
89238874
{
8924-
return lookupResultFailure(expr, baseType);
8875+
return lookupMemberResultFailure(expr, baseType);
89258876
}
89268877

89278878
// We need to confirm that whatever member we
@@ -8947,13 +8898,13 @@ namespace Slang
89478898
// For now let's just be expedient and disallow all of that, because
89488899
// we can always add it back in later.
89498900

8950-
if(!lookupResult.isOverloaded())
8901+
if (!lookupResult.isOverloaded())
89518902
{
89528903
// The non-overloaded case is relatively easy. We just want
89538904
// to look at the member being referenced, and check if
89548905
// it is allowed in a `static` context:
89558906
//
8956-
if(!isUsableAsStaticMember(lookupResult.item))
8907+
if (!isUsableAsStaticMember(lookupResult.item))
89578908
{
89588909
getSink()->diagnose(
89598910
expr->loc,
@@ -8972,10 +8923,10 @@ namespace Slang
89728923
// are non-static.
89738924
bool anyNonStatic = false;
89748925
List<LookupResultItem> staticItems;
8975-
for(auto item : lookupResult.items)
8926+
for (auto item : lookupResult.items)
89768927
{
89778928
// Is this item usable as a static member?
8978-
if(isUsableAsStaticMember(item))
8929+
if (isUsableAsStaticMember(item))
89798930
{
89808931
// If yes, then it will be part of the output.
89818932
staticItems.Add(item);
@@ -8988,11 +8939,11 @@ namespace Slang
89888939
}
89898940

89908941
// Was there anything non-static in the list?
8991-
if(anyNonStatic)
8942+
if (anyNonStatic)
89928943
{
89938944
// If we had some static items, then that's okay,
89948945
// we just want to use our newly-filtered list.
8995-
if(staticItems.Count())
8946+
if (staticItems.Count())
89968947
{
89978948
lookupResult.items = staticItems;
89988949
}
@@ -9012,13 +8963,96 @@ namespace Slang
90128963

90138964
return createLookupResultExpr(
90148965
lookupResult,
9015-
expr->BaseExpression,
8966+
baseExpression,
90168967
expr->loc);
90178968
}
90188969
else if (as<ErrorType>(baseType))
90198970
{
90208971
return CreateErrorExpr(expr);
90218972
}
8973+
8974+
// Failure
8975+
return lookupMemberResultFailure(expr, baseType);
8976+
}
8977+
8978+
RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* expr)
8979+
{
8980+
expr->BaseExpression = CheckExpr(expr->BaseExpression);
8981+
8982+
// Not sure this is needed -> but guess someone could do
8983+
expr->BaseExpression = MaybeDereference(expr->BaseExpression);
8984+
8985+
// If the base of the member lookup has an interface type
8986+
// *without* a suitable this-type substitution, then we are
8987+
// trying to perform lookup on a value of existential type,
8988+
// and we should "open" the existential here so that we
8989+
// can expose its structure.
8990+
//
8991+
8992+
expr->BaseExpression = maybeOpenExistential(expr->BaseExpression);
8993+
// Do a static lookup
8994+
return _lookupStaticMember(expr, expr->BaseExpression);
8995+
}
8996+
8997+
RefPtr<Expr> lookupMemberResultFailure(
8998+
DeclRefExpr* expr,
8999+
QualType const& baseType)
9000+
{
9001+
// Check it's a member expression
9002+
SLANG_ASSERT(as<StaticMemberExpr>(expr) || as<MemberExpr>(expr));
9003+
9004+
getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->name, baseType);
9005+
expr->type = QualType(getSession()->getErrorType());
9006+
return expr;
9007+
}
9008+
9009+
RefPtr<Expr> visitMemberExpr(MemberExpr * expr)
9010+
{
9011+
expr->BaseExpression = CheckExpr(expr->BaseExpression);
9012+
9013+
expr->BaseExpression = MaybeDereference(expr->BaseExpression);
9014+
9015+
// If the base of the member lookup has an interface type
9016+
// *without* a suitable this-type substitution, then we are
9017+
// trying to perform lookup on a value of existential type,
9018+
// and we should "open" the existential here so that we
9019+
// can expose its structure.
9020+
//
9021+
expr->BaseExpression = maybeOpenExistential(expr->BaseExpression);
9022+
9023+
auto & baseType = expr->BaseExpression->type;
9024+
9025+
// Note: Checking for vector types before declaration-reference types,
9026+
// because vectors are also declaration reference types...
9027+
//
9028+
// Also note: the way this is done right now means that the ability
9029+
// to swizzle vectors interferes with any chance of looking up
9030+
// members via extension, for vector or scalar types.
9031+
//
9032+
// TODO: Matrix swizzles probably need to be handled at some point.
9033+
if (auto baseVecType = as<VectorExpressionType>(baseType))
9034+
{
9035+
return CheckSwizzleExpr(
9036+
expr,
9037+
baseVecType->elementType,
9038+
baseVecType->elementCount);
9039+
}
9040+
else if(auto baseScalarType = as<BasicExpressionType>(baseType))
9041+
{
9042+
// Treat scalar like a 1-element vector when swizzling
9043+
return CheckSwizzleExpr(
9044+
expr,
9045+
baseScalarType,
9046+
1);
9047+
}
9048+
else if(auto typeType = as<TypeType>(baseType))
9049+
{
9050+
return _lookupStaticMember(expr, expr->BaseExpression);
9051+
}
9052+
else if (as<ErrorType>(baseType))
9053+
{
9054+
return CreateErrorExpr(expr);
9055+
}
90229056
else
90239057
{
90249058
LookupResult lookupResult = lookUpMember(
@@ -9028,7 +9062,7 @@ namespace Slang
90289062
baseType.Ptr());
90299063
if (!lookupResult.isValid())
90309064
{
9031-
return lookupResultFailure(expr, baseType);
9065+
return lookupMemberResultFailure(expr, baseType);
90329066
}
90339067

90349068
// TODO: need to filter for declarations that are valid to refer

source/slang/parser.cpp

+28-1
Original file line numberDiff line numberDiff line change
@@ -1738,8 +1738,10 @@ namespace Slang
17381738
}
17391739
static RefPtr<Expr> parseMemberType(Parser * parser, RefPtr<Expr> base)
17401740
{
1741+
// When called the :: or . have been consumed, so don't need to consume here.
1742+
17411743
RefPtr<MemberExpr> memberExpr = new MemberExpr();
1742-
parser->ReadToken(TokenType::Dot);
1744+
17431745
parser->FillPosition(memberExpr.Ptr());
17441746
memberExpr->BaseExpression = base;
17451747
memberExpr->name = expectIdentifier(parser).name;
@@ -1853,7 +1855,12 @@ namespace Slang
18531855
case TokenType::OpLess:
18541856
typeExpr = parseGenericApp(parser, typeExpr);
18551857
break;
1858+
case TokenType::Scope:
1859+
parser->ReadToken(TokenType::Scope);
1860+
typeExpr = parseMemberType(parser, typeExpr);
1861+
break;
18561862
case TokenType::Dot:
1863+
parser->ReadToken(TokenType::Dot);
18571864
typeExpr = parseMemberType(parser, typeExpr);
18581865
break;
18591866
default:
@@ -4192,6 +4199,26 @@ namespace Slang
41924199
}
41934200
break;
41944201

4202+
// Scope access `x::m`
4203+
case TokenType::Scope:
4204+
{
4205+
RefPtr<StaticMemberExpr> staticMemberExpr = new StaticMemberExpr();
4206+
4207+
// TODO(tfoley): why would a member expression need this?
4208+
staticMemberExpr->scope = parser->currentScope.Ptr();
4209+
4210+
parser->FillPosition(staticMemberExpr.Ptr());
4211+
staticMemberExpr->BaseExpression = expr;
4212+
parser->ReadToken(TokenType::Scope);
4213+
staticMemberExpr->name = expectIdentifier(parser).name;
4214+
4215+
if (peekTokenType(parser) == TokenType::OpLess)
4216+
expr = maybeParseGenericApp(parser, staticMemberExpr);
4217+
else
4218+
expr = staticMemberExpr;
4219+
4220+
break;
4221+
}
41954222
// Member access `x.m`
41964223
case TokenType::Dot:
41974224
{

tests/compute/scope-operator.slang

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// scope.slang
2+
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
3+
//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
4+
5+
// Confirm that scoping on enums and types works
6+
7+
//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
8+
RWStructuredBuffer<int> outputBuffer;
9+
10+
enum Color
11+
{
12+
Red,
13+
Green = 2,
14+
Blue,
15+
}
16+
17+
struct Thing
18+
{
19+
int a;
20+
struct Another
21+
{
22+
int b;
23+
}
24+
};
25+
26+
int test(int val)
27+
{
28+
Color c = Color.Red;
29+
30+
Thing::Another another;
31+
another.b = 20;
32+
33+
if(val > 1)
34+
{
35+
c = Color.Green;
36+
}
37+
38+
if(c == Color::Red)
39+
{
40+
if(val & 1)
41+
{
42+
c = Color::Blue;
43+
}
44+
}
45+
46+
switch(c)
47+
{
48+
case Color::Red:
49+
val = 1;
50+
break;
51+
52+
case Color::Green:
53+
val = 2;
54+
break;
55+
56+
case Color::Blue:
57+
val = 3;
58+
break;
59+
60+
default:
61+
val = -1;
62+
break;
63+
}
64+
65+
return (val << 4) + int(c) + another.b - 20;
66+
}
67+
68+
[numthreads(4, 1, 1)]
69+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
70+
{
71+
uint tid = dispatchThreadID.x;
72+
73+
int val = int(tid);
74+
val = test(val);
75+
76+
outputBuffer[tid] = val;
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
10
2+
33
3+
22
4+
22

0 commit comments

Comments
 (0)