diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-03-06 20:16:20 -0500 |
|---|---|---|
| committer | Tim Foley <tfoleyNV@users.noreply.github.com> | 2019-03-06 17:16:20 -0800 |
| commit | c850ba44164ac2bee895137abdd184beb4123090 (patch) | |
| tree | f6e42ce5998038a1162e95c2a879bb7b06d0573f | |
| parent | 87610f6def3e3dceac0082c1b60abbe2aee09014 (diff) | |
* Add support for enum and type lookup via :: (scope operator) (#882)
* Added test for scope operator
| -rw-r--r-- | source/slang/check.cpp | 162 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 29 | ||||
| -rw-r--r-- | tests/compute/scope-operator.slang | 77 | ||||
| -rw-r--r-- | tests/compute/scope-operator.slang.expected.txt | 4 |
4 files changed, 207 insertions, 65 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 37c0d5baf..91bbd694a 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -8843,63 +8843,14 @@ namespace Slang } } - RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* /*expr*/) + // Look up a static member + // @param expr Can be StaticMemberExpr or MemberExpr + // @param baseExpression Is the underlying type expression determined from resolving expr + RefPtr<Expr> _lookupStaticMember(RefPtr<DeclRefExpr> expr, RefPtr<Expr> baseExpression) { - // StaticMemberExpr means it is already checked - SLANG_UNEXPECTED("should not occur in unchecked AST"); - UNREACHABLE_RETURN(nullptr); - } - - RefPtr<Expr> lookupResultFailure( - MemberExpr* expr, - QualType const& baseType) - { - getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->name, baseType); - expr->type = QualType(getSession()->getErrorType()); - return expr; - - } - - RefPtr<Expr> visitMemberExpr(MemberExpr * expr) - { - expr->BaseExpression = CheckExpr(expr->BaseExpression); - - expr->BaseExpression = MaybeDereference(expr->BaseExpression); + auto& baseType = baseExpression->type; - // If the base of the member lookup has an interface type - // *without* a suitable this-type substitution, then we are - // trying to perform lookup on a value of existential type, - // and we should "open" the existential here so that we - // can expose its structure. - // - expr->BaseExpression = maybeOpenExistential(expr->BaseExpression); - - auto & baseType = expr->BaseExpression->type; - - // Note: Checking for vector types before declaration-reference types, - // because vectors are also declaration reference types... - // - // Also note: the way this is done right now means that the ability - // to swizzle vectors interferes with any chance of looking up - // members via extension, for vector or scalar types. - // - // TODO: Matrix swizzles probably need to be handled at some point. - if (auto baseVecType = as<VectorExpressionType>(baseType)) - { - return CheckSwizzleExpr( - expr, - baseVecType->elementType, - baseVecType->elementCount); - } - else if(auto baseScalarType = as<BasicExpressionType>(baseType)) - { - // Treat scalar like a 1-element vector when swizzling - return CheckSwizzleExpr( - expr, - baseScalarType, - 1); - } - else if(auto typeType = as<TypeType>(baseType)) + if (auto typeType = as<TypeType>(baseType)) { // We are looking up a member inside a type. // We want to be careful here because we should only find members @@ -8921,7 +8872,7 @@ namespace Slang type); if (!lookupResult.isValid()) { - return lookupResultFailure(expr, baseType); + return lookupMemberResultFailure(expr, baseType); } // We need to confirm that whatever member we @@ -8947,13 +8898,13 @@ namespace Slang // For now let's just be expedient and disallow all of that, because // we can always add it back in later. - if(!lookupResult.isOverloaded()) + if (!lookupResult.isOverloaded()) { // The non-overloaded case is relatively easy. We just want // to look at the member being referenced, and check if // it is allowed in a `static` context: // - if(!isUsableAsStaticMember(lookupResult.item)) + if (!isUsableAsStaticMember(lookupResult.item)) { getSink()->diagnose( expr->loc, @@ -8972,10 +8923,10 @@ namespace Slang // are non-static. bool anyNonStatic = false; List<LookupResultItem> staticItems; - for(auto item : lookupResult.items) + for (auto item : lookupResult.items) { // Is this item usable as a static member? - if(isUsableAsStaticMember(item)) + if (isUsableAsStaticMember(item)) { // If yes, then it will be part of the output. staticItems.Add(item); @@ -8988,11 +8939,11 @@ namespace Slang } // Was there anything non-static in the list? - if(anyNonStatic) + if (anyNonStatic) { // If we had some static items, then that's okay, // we just want to use our newly-filtered list. - if(staticItems.Count()) + if (staticItems.Count()) { lookupResult.items = staticItems; } @@ -9012,13 +8963,96 @@ namespace Slang return createLookupResultExpr( lookupResult, - expr->BaseExpression, + baseExpression, expr->loc); } else if (as<ErrorType>(baseType)) { return CreateErrorExpr(expr); } + + // Failure + return lookupMemberResultFailure(expr, baseType); + } + + RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* expr) + { + expr->BaseExpression = CheckExpr(expr->BaseExpression); + + // Not sure this is needed -> but guess someone could do + expr->BaseExpression = MaybeDereference(expr->BaseExpression); + + // If the base of the member lookup has an interface type + // *without* a suitable this-type substitution, then we are + // trying to perform lookup on a value of existential type, + // and we should "open" the existential here so that we + // can expose its structure. + // + + expr->BaseExpression = maybeOpenExistential(expr->BaseExpression); + // Do a static lookup + return _lookupStaticMember(expr, expr->BaseExpression); + } + + RefPtr<Expr> lookupMemberResultFailure( + DeclRefExpr* expr, + QualType const& baseType) + { + // Check it's a member expression + SLANG_ASSERT(as<StaticMemberExpr>(expr) || as<MemberExpr>(expr)); + + getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->name, baseType); + expr->type = QualType(getSession()->getErrorType()); + return expr; + } + + RefPtr<Expr> visitMemberExpr(MemberExpr * expr) + { + expr->BaseExpression = CheckExpr(expr->BaseExpression); + + expr->BaseExpression = MaybeDereference(expr->BaseExpression); + + // If the base of the member lookup has an interface type + // *without* a suitable this-type substitution, then we are + // trying to perform lookup on a value of existential type, + // and we should "open" the existential here so that we + // can expose its structure. + // + expr->BaseExpression = maybeOpenExistential(expr->BaseExpression); + + auto & baseType = expr->BaseExpression->type; + + // Note: Checking for vector types before declaration-reference types, + // because vectors are also declaration reference types... + // + // Also note: the way this is done right now means that the ability + // to swizzle vectors interferes with any chance of looking up + // members via extension, for vector or scalar types. + // + // TODO: Matrix swizzles probably need to be handled at some point. + if (auto baseVecType = as<VectorExpressionType>(baseType)) + { + return CheckSwizzleExpr( + expr, + baseVecType->elementType, + baseVecType->elementCount); + } + else if(auto baseScalarType = as<BasicExpressionType>(baseType)) + { + // Treat scalar like a 1-element vector when swizzling + return CheckSwizzleExpr( + expr, + baseScalarType, + 1); + } + else if(auto typeType = as<TypeType>(baseType)) + { + return _lookupStaticMember(expr, expr->BaseExpression); + } + else if (as<ErrorType>(baseType)) + { + return CreateErrorExpr(expr); + } else { LookupResult lookupResult = lookUpMember( @@ -9028,7 +9062,7 @@ namespace Slang baseType.Ptr()); if (!lookupResult.isValid()) { - return lookupResultFailure(expr, baseType); + return lookupMemberResultFailure(expr, baseType); } // TODO: need to filter for declarations that are valid to refer diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index a7ccb7b8a..5cb712489 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -1738,8 +1738,10 @@ namespace Slang } static RefPtr<Expr> parseMemberType(Parser * parser, RefPtr<Expr> base) { + // When called the :: or . have been consumed, so don't need to consume here. + RefPtr<MemberExpr> memberExpr = new MemberExpr(); - parser->ReadToken(TokenType::Dot); + parser->FillPosition(memberExpr.Ptr()); memberExpr->BaseExpression = base; memberExpr->name = expectIdentifier(parser).name; @@ -1853,7 +1855,12 @@ namespace Slang case TokenType::OpLess: typeExpr = parseGenericApp(parser, typeExpr); break; + case TokenType::Scope: + parser->ReadToken(TokenType::Scope); + typeExpr = parseMemberType(parser, typeExpr); + break; case TokenType::Dot: + parser->ReadToken(TokenType::Dot); typeExpr = parseMemberType(parser, typeExpr); break; default: @@ -4192,6 +4199,26 @@ namespace Slang } break; + // Scope access `x::m` + case TokenType::Scope: + { + RefPtr<StaticMemberExpr> staticMemberExpr = new StaticMemberExpr(); + + // TODO(tfoley): why would a member expression need this? + staticMemberExpr->scope = parser->currentScope.Ptr(); + + parser->FillPosition(staticMemberExpr.Ptr()); + staticMemberExpr->BaseExpression = expr; + parser->ReadToken(TokenType::Scope); + staticMemberExpr->name = expectIdentifier(parser).name; + + if (peekTokenType(parser) == TokenType::OpLess) + expr = maybeParseGenericApp(parser, staticMemberExpr); + else + expr = staticMemberExpr; + + break; + } // Member access `x.m` case TokenType::Dot: { diff --git a/tests/compute/scope-operator.slang b/tests/compute/scope-operator.slang new file mode 100644 index 000000000..c50d95965 --- /dev/null +++ b/tests/compute/scope-operator.slang @@ -0,0 +1,77 @@ +// scope.slang +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute + +// Confirm that scoping on enums and types works + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out +RWStructuredBuffer<int> outputBuffer; + +enum Color +{ + Red, + Green = 2, + Blue, +} + +struct Thing +{ + int a; + struct Another + { + int b; + } +}; + +int test(int val) +{ + Color c = Color.Red; + + Thing::Another another; + another.b = 20; + + if(val > 1) + { + c = Color.Green; + } + + if(c == Color::Red) + { + if(val & 1) + { + c = Color::Blue; + } + } + + switch(c) + { + case Color::Red: + val = 1; + break; + + case Color::Green: + val = 2; + break; + + case Color::Blue: + val = 3; + break; + + default: + val = -1; + break; + } + + return (val << 4) + int(c) + another.b - 20; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + + int val = int(tid); + val = test(val); + + outputBuffer[tid] = val; +}
\ No newline at end of file diff --git a/tests/compute/scope-operator.slang.expected.txt b/tests/compute/scope-operator.slang.expected.txt new file mode 100644 index 000000000..946476280 --- /dev/null +++ b/tests/compute/scope-operator.slang.expected.txt @@ -0,0 +1,4 @@ +10 +33 +22 +22 |
