Skip to content

Commit 11748a7

Browse files
author
Tim Foley
authored
Initial support for a using construct (shader-slang#1506)
The basic idea is that if you have a namespace: namespace MyCoolNamespace { void f() { ... } ... } then you can bring the declarations from that namespace into scope with: using MyCoolNamespace; f(); The `using` construct is allowed in any scope where declarations are allowed. As an additional feature, the construct allows and then ignores the keyword `namespace` if it occurs right after `using`: using namespace MyCoolNamespace; Note that unlike in C++, `using` a namespace inside another namespace doesn't implicitly make the symbols available to clients of that namespace: namespace hidden { void secret() {...} ... } namespace api { using hidden; ... } api.secret(); // ERROR: `secret()` isn't a member of `api` The implementation of this feature was relatively simple, although it does leave out more advanced features that might be desirable in the future: * No support for `using MCN = MyCoolNamespace` sorts of tricks to define a short name * No support for `using` anything that isn't a namespace (e.g., to make the members of a type available without qualification) * No support for cases where multiple visible modules have a namespace of the same name (or dealing with overloaded namespaces in general)
1 parent b5a4161 commit 11748a7

7 files changed

+163
-6
lines changed

source/slang/slang-ast-decl.h

+12
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,18 @@ class ModuleDecl : public NamespaceDeclBase
373373
Dictionary<AggTypeDecl*, RefPtr<CandidateExtensionList>> mapTypeToCandidateExtensions;
374374
};
375375

376+
/// A declaration that brings members of another declaration or namespace into scope
377+
class UsingDecl : public Decl
378+
{
379+
SLANG_CLASS(UsingDecl)
380+
381+
/// An expression that identifies the entity (e.g., a namespace) to be brought into `scope`
382+
Expr* arg;
383+
384+
/// The scope that the entity named by `arg` will be brought into
385+
RefPtr<Scope> scope;
386+
};
387+
376388
class ImportDecl : public Decl
377389
{
378390
SLANG_CLASS(ImportDecl)

source/slang/slang-check-decl.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ namespace Slang
5656

5757
void visitImportDecl(ImportDecl* decl);
5858

59+
void visitUsingDecl(UsingDecl* decl);
60+
5961
void visitGenericTypeParamDecl(GenericTypeParamDecl* decl);
6062

6163
void visitGenericValueParamDecl(GenericValueParamDecl* decl);
@@ -4615,6 +4617,58 @@ namespace Slang
46154617
}
46164618
}
46174619

4620+
void SemanticsDeclHeaderVisitor::visitUsingDecl(UsingDecl* decl)
4621+
{
4622+
// First, we need to look up whatever the argument of the `using`
4623+
// declaration names.
4624+
//
4625+
decl->arg = CheckTerm(decl->arg);
4626+
4627+
// Next, we want to ensure that whatever is being named by `decl->arg`
4628+
// is a namespace (or a module, since modules are namespace-like).
4629+
//
4630+
// TODO: The logic here assumes that we can't have multiple `NamespaceDecl`s
4631+
// with the same name in scope, but that assumption is only valid in the
4632+
// context of a single module (where we deduplicate `namespace`s during
4633+
// parsing). If a user `import`s multiple modules that all have namespaces
4634+
// of the same name, it would be possible for `decl->arg` to be overloaded.
4635+
// In that case we should really iterate over all the entities that are
4636+
// named and import any that are namespace-like.
4637+
//
4638+
NamespaceDeclBase* namespaceDecl = nullptr;
4639+
if( auto declRefExpr = as<DeclRefExpr>(decl->arg) )
4640+
{
4641+
if( auto namespaceDeclRef = declRefExpr->declRef.as<NamespaceDeclBase>() )
4642+
{
4643+
SLANG_ASSERT(!namespaceDeclRef.substitutions.substitutions);
4644+
namespaceDecl = namespaceDeclRef.getDecl();
4645+
}
4646+
}
4647+
if( !namespaceDecl )
4648+
{
4649+
getSink()->diagnose(decl->arg, Diagnostics::expectedANamespace, decl->arg->type);
4650+
return;
4651+
}
4652+
4653+
// Once we have identified the namespace to bring into scope,
4654+
// we need to create a new sibling sub-scope to add to the
4655+
// lookup scope that was in place when the `using` was parsed.
4656+
//
4657+
// Subsequent lookup in that scope will walk through our new
4658+
// sub-scope and see the namespace.
4659+
//
4660+
// TODO: If we update the `containerDecl` in a scope to allow
4661+
// for a more general `DeclRef`, or even a full `DeclRefExpr`,
4662+
// then it would be possible for `using` to apply to more kinds
4663+
// of entities than just namespaces.
4664+
//
4665+
auto scope = decl->scope;
4666+
auto subScope = new Scope();
4667+
subScope->containerDecl = namespaceDecl;
4668+
subScope->nextSibling = scope->nextSibling;
4669+
scope->nextSibling = subScope;
4670+
}
4671+
46184672
/// Get a reference to the candidate extension list for `typeDecl` in the given dictionary
46194673
///
46204674
/// Note: this function creates an empty list of candidates for the given type if

source/slang/slang-diagnostic-defs.h

+1
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on ty
255255
DIAGNOSTIC(30043, Error, getStringHashRequiresStringLiteral, "getStringHash parameter can only accept a string literal")
256256

257257
DIAGNOSTIC(30060, Error, expectedAType, "expected a type, got a '$0'")
258+
DIAGNOSTIC(30061, Error, expectedANamespace, "expected a namespace, got a '$0'")
258259

259260
DIAGNOSTIC(30100, Error, staticRefToNonStaticMember, "type '$0' cannot be used to refer to non-static member '$1'")
260261

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

+1
Original file line numberDiff line numberDiff line change
@@ -5032,6 +5032,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
50325032
LoweredValInfo visit##NAME(NAME*) { return LoweredValInfo(); }
50335033

50345034
IGNORED_CASE(ImportDecl)
5035+
IGNORED_CASE(UsingDecl)
50355036
IGNORED_CASE(EmptyDecl)
50365037
IGNORED_CASE(SyntaxDecl)
50375038
IGNORED_CASE(AttributeDecl)

source/slang/slang-parser.cpp

+44-6
Original file line numberDiff line numberDiff line change
@@ -933,12 +933,16 @@ namespace Slang
933933
return parser->getNamePool()->getName(text);
934934
}
935935

936+
static bool expect(Parser* parser, TokenType tokenType)
937+
{
938+
return parser->ReadToken(tokenType).type == tokenType;
939+
}
940+
936941
static NameLoc expectIdentifier(Parser* parser)
937942
{
938943
return NameLoc(parser->ReadToken(TokenType::Identifier));
939944
}
940945

941-
942946
static NodeBase* parseImportDecl(
943947
Parser* parser, void* /*userData*/)
944948
{
@@ -2629,6 +2633,44 @@ namespace Slang
26292633
return result;
26302634
}
26312635

2636+
static NodeBase* parseUsingDecl(Parser* parser, void* /*userData*/)
2637+
{
2638+
UsingDecl* decl = parser->astBuilder->create<UsingDecl>();
2639+
parser->FillPosition(decl);
2640+
2641+
// A `using` declaration will need to know about the current
2642+
// scope at the point where it appears, so that it can know
2643+
// the scope it is attempting to extend.
2644+
//
2645+
decl->scope = parser->currentScope;
2646+
2647+
// TODO: We may eventually want to support declarations
2648+
// of the form `using <id> = <expr>;` which introduce
2649+
// a shorthand alias for a namespace/type/whatever.
2650+
//
2651+
// For now we are just sticking to the most basic form.
2652+
2653+
// As a compatibility feature for programmers used to C++,
2654+
// we allow the `namespace` keyword to come after `using`,
2655+
// where it has no effect.
2656+
//
2657+
if(parser->LookAheadToken("namespace"))
2658+
{
2659+
advanceToken(parser);
2660+
}
2661+
2662+
// The entity that is going to be used is identified
2663+
// using an arbitrary expression (although we expect
2664+
// that valid code will not typically use the full
2665+
// freedom of what the expression grammar supports.
2666+
//
2667+
decl->arg = parser->ParseExpression();
2668+
2669+
expect(parser, TokenType::Semicolon);
2670+
2671+
return decl;
2672+
}
2673+
26322674
static NodeBase* parseConstructorDecl(Parser* parser, void* /*userData*/)
26332675
{
26342676
ConstructorDecl* decl = parser->astBuilder->create<ConstructorDecl>();
@@ -2750,11 +2792,6 @@ namespace Slang
27502792
return decl;
27512793
}
27522794

2753-
static bool expect(Parser* parser, TokenType tokenType)
2754-
{
2755-
return parser->ReadToken(tokenType).type == tokenType;
2756-
}
2757-
27582795
/// Peek in the token stream and return `true` if it looks like a modern-style variable declaration is coming up.
27592796
static bool _peekModernStyleVarDecl(Parser* parser)
27602797
{
@@ -5659,6 +5696,7 @@ namespace Slang
56595696
DECL(typealias, parseTypeAliasDecl);
56605697
DECL(__generic_value_param, parseGlobalGenericValueParamDecl);
56615698
DECL(namespace, parseNamespaceDecl);
5699+
DECL(using, parseUsingDecl);
56625700

56635701
#undef DECL
56645702

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// using-namespace.slang
2+
3+
// Test that `using` can bring declarations from a namespace into scope
4+
5+
//TEST(compute):COMPARE_COMPUTE:
6+
7+
namespace X
8+
{
9+
struct A { int a; }
10+
}
11+
12+
namespace Y
13+
{
14+
// A `using` that brings one namespace into scope of declarations in another
15+
//
16+
using X;
17+
18+
int b(A a) { return a.a; }
19+
}
20+
21+
// A `using` that brings declarations in a namespace into the global scope
22+
//
23+
using Y;
24+
25+
int test(int value)
26+
{
27+
// A `using` that brings declarations into a local scope
28+
// (and also uses the optional `namespace` placeholder keyword)
29+
//
30+
using namespace X;
31+
32+
A a = { value };
33+
return b(a);
34+
}
35+
36+
//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
37+
RWStructuredBuffer<int> outputBuffer;
38+
39+
[numthreads(4, 1, 1)]
40+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
41+
{
42+
uint tid = dispatchThreadID.x;
43+
int inVal = tid;
44+
int outVal = test(inVal);
45+
outputBuffer[tid] = outVal;
46+
}
47+
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)