summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2023-12-11 16:13:32 -0800
committerGitHub <noreply@github.com>2023-12-11 16:13:32 -0800
commitec0224edc3a867bbf059e790ad7b2a1a881a0705 (patch)
tree9c56c3fd2dd11f79f597c152326d555d81414fc3 /source
parent12fcffaaaf2d1ffa2eefa680e2d7c9971e38a5db (diff)
Diagnose for invalid decl nesting + namespace lookup fixes. (#3397)
* Diagnose for invalid decl nesting. * Fix. * Fix. * Fix. * Fix `namespace` lookup and `using` resolution. * fix project files. * revert project files. * Enhance namespace syntax, docs. * Fixes. --------- Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-ast-iterator.h6
-rw-r--r--source/slang/slang-ast-support-types.h5
-rw-r--r--source/slang/slang-check-decl.cpp160
-rw-r--r--source/slang/slang-check-expr.cpp70
-rw-r--r--source/slang/slang-check-impl.h5
-rw-r--r--source/slang/slang-check-modifier.cpp22
-rw-r--r--source/slang/slang-diagnostic-defs.h5
-rw-r--r--source/slang/slang-language-server-ast-lookup.cpp6
-rw-r--r--source/slang/slang-lower-to-ir.cpp11
-rw-r--r--source/slang/slang-mangle.cpp4
-rw-r--r--source/slang/slang-parser.cpp407
-rw-r--r--source/slang/slang-syntax.cpp71
12 files changed, 628 insertions, 144 deletions
diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h
index dbf9bc8fc..7dc358a8e 100644
--- a/source/slang/slang-ast-iterator.h
+++ b/source/slang/slang-ast-iterator.h
@@ -432,7 +432,7 @@ template <typename CallbackFunc>
void ASTIterator<CallbackFunc>::visitDecl(DeclBase* decl)
{
// Don't look at the decl if it is defined in a different file.
- if (!as<ModuleDecl>(decl) && !sourceManager->getHumaneLoc(decl->loc, SourceLocType::Actual)
+ if (!as<NamespaceDeclBase>(decl) && !sourceManager->getHumaneLoc(decl->loc, SourceLocType::Actual)
.pathInfo.foundPath.getUnownedSlice()
.endsWithCaseInsensitive(fileName))
return;
@@ -468,6 +468,10 @@ void ASTIterator<CallbackFunc>::visitDecl(DeclBase* decl)
{
visitExpr(extDecl->targetType.exp);
}
+ else if (auto usingDecl = as<UsingDecl>(decl))
+ {
+ visitExpr(usingDecl->arg);
+ }
if (auto container = as<ContainerDecl>(decl))
{
for (auto member : container->members)
diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h
index f771e24b6..955aff06c 100644
--- a/source/slang/slang-ast-support-types.h
+++ b/source/slang/slang-ast-support-types.h
@@ -65,6 +65,7 @@ namespace Slang
void printDiagnosticArg(StringBuilder& sb, QualType const& type);
void printDiagnosticArg(StringBuilder& sb, Val* val);
void printDiagnosticArg(StringBuilder& sb, DeclRefBase* declRefBase);
+ void printDiagnosticArg(StringBuilder& sb, ASTNodeType nodeType);
struct QualifiedDeclPath
{
@@ -391,6 +392,10 @@ namespace Slang
///
ModifiersChecked,
+ /// Wiring up scopes of namespaces with their siblings defined in different
+ /// files/modules, and other namespaces imported via `using`.
+ ScopesWired,
+
/// The type/signature of the declaration has been checked.
///
/// For a value declaration like a variable or function, this means that
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 396fdd297..75aed010d 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -36,6 +36,23 @@ namespace Slang
}
};
+ struct SemanticsDeclScopeWiringVisitor : public SemanticsDeclVisitorBase, public DeclVisitor<SemanticsDeclScopeWiringVisitor>
+ {
+ SemanticsDeclScopeWiringVisitor(SemanticsContext const& outer)
+ : SemanticsDeclVisitorBase(outer)
+ {}
+
+ void visitDeclGroup(DeclGroup*) {}
+
+ void visitDecl(Decl*) {}
+
+ void visitUsingDecl(UsingDecl* decl);
+
+ void visitImplementingDecl(ImplementingDecl* decl);
+
+ void visitNamespaceDecl(NamespaceDecl* decl);
+ };
+
struct SemanticsDeclAttributesVisitor
: public SemanticsDeclVisitorBase
, public DeclVisitor<SemanticsDeclAttributesVisitor>
@@ -87,10 +104,6 @@ namespace Slang
void visitIncludeDecl(IncludeDecl* decl);
- void visitImplementingDecl(ImplementingDecl* decl);
-
- void visitUsingDecl(UsingDecl* decl);
-
void visitGenericTypeParamDecl(GenericTypeParamDecl* decl);
void visitGenericValueParamDecl(GenericValueParamDecl* decl);
@@ -1864,6 +1877,10 @@ namespace Slang
{
ensureDecl(implementingDecl, DeclCheckState::Checked);
}
+ else if (auto importDecl = as<ImportDecl>(decl))
+ {
+ ensureDecl(importDecl, DeclCheckState::Checked);
+ }
}
};
visitIncludeDecls(moduleDecl);
@@ -1927,7 +1944,7 @@ namespace Slang
//
DeclCheckState states[] =
{
- DeclCheckState::ModifiersChecked,
+ DeclCheckState::ScopesWired,
DeclCheckState::ReadyForReference,
DeclCheckState::ReadyForLookup,
DeclCheckState::ReadyForConformances,
@@ -6831,14 +6848,9 @@ namespace Slang
{
// Create a new sub-scope to wire the module
// into our lookup chain.
- auto subScope = getASTBuilder()->create<Scope>();
- subScope->containerDecl = fileDecl;
-
- subScope->nextSibling = scope->nextSibling;
- scope->nextSibling = subScope;
+ addSiblingScopeForContainerDecl(scope, fileDecl);
}
-
void SemanticsVisitor::importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl)
{
// If we've imported this one already, then
@@ -6859,11 +6871,7 @@ namespace Slang
if (moduleScope->containerDecl != moduleDecl && moduleScope->containerDecl->parentDecl != moduleDecl)
continue;
- auto subScope = getASTBuilder()->create<Scope>();
- subScope->containerDecl = moduleScope->containerDecl;
-
- subScope->nextSibling = scope->nextSibling;
- scope->nextSibling = subScope;
+ addSiblingScopeForContainerDecl(scope, moduleScope->containerDecl);
}
// Also import any modules from nested `import` declarations
@@ -7014,7 +7022,7 @@ namespace Slang
getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::includedFileMissingImplementing, name);
}
- void SemanticsDeclHeaderVisitor::visitImplementingDecl(ImplementingDecl* decl)
+ void SemanticsDeclScopeWiringVisitor::visitImplementingDecl(ImplementingDecl* decl)
{
// Don't need to do anything unless we are in a language server context.
if (!getShared()->isInLanguageServer())
@@ -7044,10 +7052,8 @@ namespace Slang
if (auto moduleDeclaration = as<ModuleDeclarationDecl>(firstMember))
{
// We are trying to implement a file that defines a module, this is expected.
- return;
}
-
- if (auto implementing = as<ImplementingDecl>(firstMember))
+ else if (auto implementing = as<ImplementingDecl>(firstMember))
{
getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::implementingMustReferencePrimaryModuleFile);
return;
@@ -7057,7 +7063,7 @@ namespace Slang
importFileDeclIntoScope(moduleDecl->ownedScope, fileDecl);
}
- void SemanticsDeclHeaderVisitor::visitUsingDecl(UsingDecl* decl)
+ void SemanticsDeclScopeWiringVisitor::visitUsingDecl(UsingDecl* decl)
{
// First, we need to look up whatever the argument of the `using`
// declaration names.
@@ -7067,45 +7073,98 @@ namespace Slang
// Next, we want to ensure that whatever is being named by `decl->arg`
// is a namespace (or a module, since modules are namespace-like).
//
- // TODO: The logic here assumes that we can't have multiple `NamespaceDecl`s
- // with the same name in scope, but that assumption is only valid in the
- // context of a single module (where we deduplicate `namespace`s during
- // parsing). If a user `import`s multiple modules that all have namespaces
+ // If a user `import`s multiple modules that all have namespaces
// of the same name, it would be possible for `decl->arg` to be overloaded.
- // In that case we should really iterate over all the entities that are
+ // To handle that case, we will iterate over all the entities that are
// named and import any that are namespace-like.
//
- NamespaceDeclBase* namespaceDecl = nullptr;
- if( auto declRefExpr = as<DeclRefExpr>(decl->arg) )
+ bool scopesAdded = false;
+ bool hasValidNamespace = false;
+
+ // TODO: consider caching the scope set in NamespaceDecl.
+ HashSet<ContainerDecl*> addedScopes;
+ for (auto s = decl->scope; s; s = s->nextSibling)
+ addedScopes.add(s->containerDecl);
+
+ auto addAllSiblingScopesFromDecl = [&](Scope* scope, ContainerDecl* containerDecl)
+ {
+ for (auto s = containerDecl->ownedScope; s; s = s->nextSibling)
+ {
+ if (addedScopes.add(s->containerDecl))
+ {
+ scopesAdded = true;
+ addSiblingScopeForContainerDecl(scope, s->containerDecl);
+ }
+ }
+ };
+
+ if (auto declRefExpr = as<DeclRefExpr>(decl->arg))
+ {
+ if (auto namespaceDeclRef = declRefExpr->declRef.as<NamespaceDeclBase>())
+ {
+ auto namespaceDecl = namespaceDeclRef.getDecl();
+ addAllSiblingScopesFromDecl(decl->scope, namespaceDecl);
+ hasValidNamespace = true;
+ }
+ }
+ else if (auto overloadedExpr = as<OverloadedExpr>(decl->arg))
{
- if( auto namespaceDeclRef = declRefExpr->declRef.as<NamespaceDeclBase>() )
+ for (auto item : overloadedExpr->lookupResult2)
{
- namespaceDecl = namespaceDeclRef.getDecl();
+ if (auto namespaceDeclRef = item.declRef.as<NamespaceDeclBase>())
+ {
+ addAllSiblingScopesFromDecl(decl->scope, namespaceDeclRef.getDecl());
+ hasValidNamespace = true;
+ }
}
}
- if( !namespaceDecl )
+
+ if (!scopesAdded)
{
- getSink()->diagnose(decl->arg, Diagnostics::expectedANamespace, decl->arg->type);
+ if (!hasValidNamespace)
+ getSink()->diagnose(decl->arg, Diagnostics::expectedANamespace, decl->arg->type);
return;
}
+ }
- // Once we have identified the namespace to bring into scope,
- // we need to create a new sibling sub-scope to add to the
- // lookup scope that was in place when the `using` was parsed.
- //
- // Subsequent lookup in that scope will walk through our new
- // sub-scope and see the namespace.
- //
- // TODO: If we update the `containerDecl` in a scope to allow
- // for a more general `DeclRef`, or even a full `DeclRefExpr`,
- // then it would be possible for `using` to apply to more kinds
- // of entities than just namespaces.
- //
- auto scope = decl->scope;
- auto subScope = getASTBuilder()->create<Scope>();
- subScope->containerDecl = namespaceDecl;
- subScope->nextSibling = scope->nextSibling;
- scope->nextSibling = subScope;
+ void SemanticsDeclScopeWiringVisitor::visitNamespaceDecl(NamespaceDecl* decl)
+ {
+ // We need to wire up the scope of namespaces with other namespace decls of the same name
+ // that is accessible from the current context.
+ auto parent = as<ContainerDecl>(getParentDecl(decl));
+ if (!parent)
+ return;
+ for (auto parentScope = parent->ownedScope; parentScope; parentScope = parentScope->parent)
+ {
+ for (auto scope = parentScope; scope; scope = scope->nextSibling)
+ {
+ auto container = scope->containerDecl;
+ auto nsDeclPtr = container->getMemberDictionary().tryGetValue(decl->getName());
+ if (!nsDeclPtr) continue;
+ auto nsDecl = *nsDeclPtr;
+ for (auto ns = nsDecl; ns; ns = ns->nextInContainerWithSameName)
+ {
+ if (ns == decl)
+ continue;
+ auto otherNamespace = as<NamespaceDeclBase>(ns);
+ if (!otherNamespace)
+ continue;
+
+ if (!ns->checkState.isBeingChecked())
+ {
+ ensureDecl(ns, DeclCheckState::ScopesWired);
+ }
+ addSiblingScopeForContainerDecl(decl, otherNamespace);
+ }
+ }
+ // For file decls, we need to continue searching up in the parent module scope.
+ if (!as<FileDecl>(parentScope->containerDecl))
+ break;
+ }
+ for (auto usingDecl : decl->getMembersOfType<UsingDecl>())
+ {
+ ensureDecl(usingDecl, DeclCheckState::ScopesWired);
+ }
}
/// Get a reference to the candidate extension list for `typeDecl` in the given dictionary
@@ -7470,6 +7529,9 @@ namespace Slang
case DeclCheckState::ModifiersChecked:
SemanticsDeclModifiersVisitor(shared).dispatch(decl);
break;
+ case DeclCheckState::ScopesWired:
+ SemanticsDeclScopeWiringVisitor(shared).dispatch(decl);
+ break;
case DeclCheckState::SignatureChecked:
SemanticsDeclHeaderVisitor(shared).dispatch(decl);
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 9d1898cf5..753f14768 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -248,6 +248,20 @@ namespace Slang
return SourceLoc();
}
+ void SemanticsVisitor::addSiblingScopeForContainerDecl(ContainerDecl* dest, ContainerDecl* source)
+ {
+ addSiblingScopeForContainerDecl(dest->ownedScope, source);
+ }
+
+ void SemanticsVisitor::addSiblingScopeForContainerDecl(Scope* destScope, ContainerDecl* source)
+ {
+ auto subScope = getASTBuilder()->create<Scope>();
+ subScope->containerDecl = source;
+
+ subScope->nextSibling = destScope->nextSibling;
+ destScope->nextSibling = subScope;
+ }
+
void SemanticsVisitor::diagnoseDeprecatedDeclRefUsage(
DeclRef<Decl> declRef,
SourceLoc loc,
@@ -847,7 +861,10 @@ namespace Slang
{
return getDeclVisibility(interfaceDecl);
}
-
+ else if (as<NamespaceDecl>(decl))
+ {
+ return DeclVisibility::Public;
+ }
if (auto parentModule = getModuleDecl(decl))
return parentModule->isInLegacyLanguage ? DeclVisibility::Public : DeclVisibility::Internal;
@@ -3541,6 +3558,11 @@ namespace Slang
LookupResult globalLookupResult;
bool hasErrors = false;
Expr* base = nullptr;
+
+ // Keep track of namespace scopes we've already looked up in to avoid producing
+ // duplicates.
+ HashSet<ContainerDecl*> processedNamespaceScopes;
+
auto handleLeafCase = [&](DeclRef<Decl> baseDeclRef, Type* type)
{
auto aggTypeDeclRef = as<AggTypeDeclBase>(baseDeclRef);
@@ -3549,18 +3571,44 @@ namespace Slang
{
// We are looking up a namespace member.
//
- // This ought to be the easy case, because
- // there are no restrictions on whether
- // we can reference the declaration here.
+ // We should lookup in all sibling scopes of the namespace.
+ // Another detail here is that we need to skip scopes that are transitively imported.
+ // For example, given:
+ // ```
+ // module a;
+ // namespace ns { int f_a(); }
+ //
+ // module b;
+ // namespace ns { int f_b(); } // will have a sibling scope that refers to a::ns.
+ //
+ // module c;
+ // import b;
+ // void test() {ns.f_a(); // should not be valid, because c does not import a. }
+ // ```
+ // Note that this logic doesn't work nicely with __exported import, but we should consider
+ // deprecate this feature anyway.
//
- LookupResult nsLookupResult = lookUpDirectAndTransparentMembers(
- m_astBuilder,
- this,
- expr->name,
- namespaceDeclRef.getDecl(),
- namespaceDeclRef);
- AddToLookupResult(globalLookupResult, nsLookupResult);
+ auto namespaceModule = getModuleDecl(namespaceDeclRef.getDecl());
+ auto thisModule = m_outerScope ? getModuleDecl(m_outerScope->containerDecl) : namespaceModule;
+ for (auto scope = namespaceDeclRef.getDecl()->ownedScope; scope; scope = scope->nextSibling)
+ {
+ auto namespaceDecl = as<NamespaceDeclBase>(scope->containerDecl);
+ if (!namespaceDecl)
+ continue;
+ if (thisModule != namespaceModule && namespaceModule != getModuleDecl(namespaceDecl))
+ continue;
+ if (processedNamespaceScopes.add(scope->containerDecl))
+ {
+ LookupResult nsLookupResult = lookUpDirectAndTransparentMembers(
+ m_astBuilder,
+ this,
+ expr->name,
+ namespaceDecl,
+ DeclRef(namespaceDecl));
+ AddToLookupResult(globalLookupResult, nsLookupResult);
+ }
+ }
}
else if (aggTypeDeclRef || type)
{
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 43844cf70..4102b3eba 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -1048,6 +1048,11 @@ namespace Slang
Scope* getScope(SyntaxNode* node);
+ // Add a sibling lookup scope for `dest` to refer to `source`.
+ void addSiblingScopeForContainerDecl(ContainerDecl* dest, ContainerDecl* source);
+ void addSiblingScopeForContainerDecl(Scope* destScope, ContainerDecl* source);
+
+
void diagnoseDeprecatedDeclRefUsage(DeclRef<Decl> declRef, SourceLoc loc, Expr* originalExpr);
DeclRef<Decl> getDefaultDeclRef(Decl* decl)
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp
index 339ecba4c..20a393ff5 100644
--- a/source/slang/slang-check-modifier.cpp
+++ b/source/slang/slang-check-modifier.cpp
@@ -1044,9 +1044,18 @@ namespace Slang
if (as<PrivateModifier>(m))
{
- if (as<AggTypeDeclBase>(syntaxNode) || as<NamespaceDeclBase>(syntaxNode))
+ if (auto decl = as<Decl>(syntaxNode))
{
- getSink()->diagnose(m, Diagnostics::invalidUseOfPrivateVisibility, as<Decl>(syntaxNode));
+ if (isGlobalDecl(decl))
+ {
+ getSink()->diagnose(m, Diagnostics::invalidUseOfPrivateVisibility, as<Decl>(syntaxNode));
+ return m;
+ }
+ }
+ if (as<NamespaceDeclBase>(syntaxNode))
+ {
+ getSink()->diagnose(m, Diagnostics::invalidVisibilityModifierOnTypeOfDecl, syntaxNode->astNodeType);
+ return m;
}
else if (auto decl = as<Decl>(syntaxNode))
{
@@ -1057,7 +1066,14 @@ namespace Slang
}
}
}
-
+ else if (as<InternalModifier>(m))
+ {
+ if (as<NamespaceDeclBase>(syntaxNode))
+ {
+ getSink()->diagnose(m, Diagnostics::invalidVisibilityModifierOnTypeOfDecl, syntaxNode->astNodeType);
+ return m;
+ }
+ }
// Default behavior is to leave things as they are,
// and assume that modifiers are mostly already checked.
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index ac3220f4b..5f28e2af2 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -240,6 +240,8 @@ DIAGNOSTIC(20015, Error, unknownSPIRVCapability, "unknown SPIR-V capability '$0'
DIAGNOSTIC(20101, Warning, unintendedEmptyStatement, "potentially unintended empty statement at this location; use {} instead.")
+DIAGNOSTIC(30102, Error, declNotAllowed, "$0 is not allowed here.")
+
// 29xxx - Snippet parsing and inline asm
DIAGNOSTIC(29000, Error, snippetParsingFailed, "unable to parse target intrinsic snippet: $0")
@@ -361,8 +363,9 @@ DIAGNOSTIC(30505, Error, implementingMustReferencePrimaryModuleFile, "the source
DIAGNOSTIC(30600, Error, declIsNotVisible, "'$0' is not accessible from the current context.")
DIAGNOSTIC(30601, Error, declCannotHaveHigherVisibility, "'$0' cannot have a higher visibility than '$1'.")
DIAGNOSTIC(30602, Error, satisfyingDeclCannotHaveLowerVisibility, "'$0' is less visible than the interface requirement it satisfies.")
-DIAGNOSTIC(30603, Error, invalidUseOfPrivateVisibility, "'$0' cannot have private visibility.")
+DIAGNOSTIC(30603, Error, invalidUseOfPrivateVisibility, "'$0' cannot have private visibility because it is not a member of a type.")
DIAGNOSTIC(30604, Error, useOfLessVisibleType, "'$0' references less visible type '$1'.")
+DIAGNOSTIC(36005, Error, invalidVisibilityModifierOnTypeOfDecl, "visibility modifier is not allowed on '$0'.")
// Attributes
DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'")
diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp
index 3aa66ee02..0e8520f3a 100644
--- a/source/slang/slang-language-server-ast-lookup.cpp
+++ b/source/slang/slang-language-server-ast-lookup.cpp
@@ -669,6 +669,12 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node)
if (visitor.dispatchIfNotNull(extDecl->targetType.exp))
return true;
}
+ else if (auto usingDecl = as<UsingDecl>(node))
+ {
+ ASTLookupExprVisitor visitor(&context);
+ if (visitor.dispatchIfNotNull(usingDecl->arg))
+ return true;
+ }
else if (auto importDecl = as<FileReferenceDeclBase>(node))
{
if (_isLocInRange(&context, importDecl->startLoc, importDecl->endLoc))
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index f0fd33e13..146f54932 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -2267,6 +2267,10 @@ static String getNameForNameHint(
if(auto genericParentDecl = as<GenericDecl>(parentDecl))
parentDecl = genericParentDecl->parentDecl;
+ // Skip past a FileDecl parent.
+ if (auto fileParentDecl = as<FileDecl>(parentDecl))
+ parentDecl = fileParentDecl->parentDecl;
+
// A `ModuleDecl` can have a name too, but in the common case
// we don't want to generate name hints that include the module
// name, simply because they would lead to every global symbol
@@ -10148,6 +10152,13 @@ static void ensureAllDeclsRec(
ensureAllDeclsRec(context, memberDecl);
}
}
+ else if (auto fileDecl = as<FileDecl>(decl))
+ {
+ for (auto memberDecl : fileDecl->members)
+ {
+ ensureAllDeclsRec(context, memberDecl);
+ }
+ }
else if (auto genericDecl = as<GenericDecl>(decl))
{
ensureAllDeclsRec(context, genericDecl->inner);
diff --git a/source/slang/slang-mangle.cpp b/source/slang/slang-mangle.cpp
index 3111ab132..3f701f385 100644
--- a/source/slang/slang-mangle.cpp
+++ b/source/slang/slang-mangle.cpp
@@ -40,7 +40,6 @@ namespace Slang
void emitNameImpl(ManglingContext* context, UnownedStringSlice str)
{
Index length = str.getLength();
-
// If the name consists of only traditional "identifer characters"
// (`[a-zA-Z_]`), then we want to emit it more or less directly.
//
@@ -360,6 +359,9 @@ namespace Slang
DeclRef<Decl> declRef)
{
auto parentDeclRef = declRef.getParent();
+ if (as<FileDecl>(parentDeclRef))
+ parentDeclRef = parentDeclRef.getParent();
+
auto parentGenericDeclRef = parentDeclRef.as<GenericDecl>();
if( parentDeclRef )
{
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 7367007e6..9914612ac 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -83,6 +83,7 @@ namespace Slang
{
bool enableEffectAnnotations = false;
bool allowGLSLInput = false;
+ bool isInLanguageServer = false;
};
// TODO: implement two pass parsing for file reference and struct type recognition
@@ -3294,8 +3295,7 @@ namespace Slang
{
// We start by parsing the name of the namespace that is being opened.
//
- // TODO: We should eventually support a qualified name for
- // a namespace declaration:
+ // We support a qualified name for a namespace declaration:
//
// namespace A.B { ... }
//
@@ -3304,86 +3304,94 @@ namespace Slang
//
// namespace A { namespace B { ... } }
//
- // TODO: Support we also support the degenerate case of
- // a namesapce without a name? Should that be treated as
- // an anonymous (and implicitly imported) namespace, or
- // something else?
- //
- // TODO: Should we support a shorthand syntax for putting
- // the rest of the current scope/file into a namespace:
- //
- // namespace A.B;
- //
- // ...
- //
- NameLoc nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));
-
- // Once we have the name for the namespace, we face a challenge:
- // either the namespace hasn't been seen before (in which case
- // we need to create it and start filling it in), or we've seen
- // the same namespace before inside the same module, such that
- // we should be adding the declarations we parse to the existing
- // declarations (so that they share a common scope/parent).
- //
- // In each case we will find a namespace that we want to fill in,
- // but depending on the case we may or may not want to return
- // a declaration to the caller (since they will try to add
- // any non-null pointer we return to the AST).
- //
+ auto parentDecl = parser->currentScope->containerDecl;
+ SLANG_ASSERT(parentDecl);
+ NamespaceDecl* result = nullptr;
NamespaceDecl* namespaceDecl = nullptr;
-
- // In order to find out what case we are in, we start by looking
- // for a namespace declaration of the same name in the parent
- // declaration.
- //
- {
- auto parentDecl = parser->currentScope->containerDecl;
- SLANG_ASSERT(parentDecl);
-
- // We meed to make sure that the member dictionary of
- // the parent declaration has been built/rebuilt so that
- // lookup by name will work.
+ List<NamespaceDecl*> nestedNamespaceDecls;
+ do
+ {
+ namespaceDecl = nullptr;
+ NameLoc nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));
+ // Once we have the name for the namespace, we face a challenge:
+ // either the namespace hasn't been seen before (in which case
+ // we need to create it and start filling it in), or we've seen
+ // the same namespace before inside the same module, such that
+ // we should be adding the declarations we parse to the existing
+ // declarations (so that they share a common scope/parent).
//
- // TODO: The current way we rebuild the member dictionary
- // would make for O(N^2) parsing time in a file that
- // consisted of N back-to-back `namespace`s, since each
- // would trigger a rebuild of the member dictionary that
- // would take O(N) time.
+ // In each case we will find a namespace that we want to fill in,
+ // but depending on the case we may or may not want to return
+ // a declaration to the caller (since they will try to add
+ // any non-null pointer we return to the AST).
//
- // There might be multiple members of the same name
- // (if we define a namespace `foo` after an overloaded
- // function `foo` has been defined), and direct member
- // lookup will only give us the first.
- //
- Decl* firstDecl = nullptr;
- parentDecl->getMemberDictionary().tryGetValue(nameAndLoc.name, firstDecl);
- //
- // We will search through the declarations of the name
- // and find the first that is a namespace (if any).
+ // In order to find out what case we are in, we start by looking
+ // for a namespace declaration of the same name in the parent
+ // declaration.
//
- // Note: we do not issue diagnostics here based on
- // the potential conflicts between these declarations,
- // because we want to do as little semantic analysis
- // as possible in the parser, and we'd rather be
- // as permissive as possible right now.
- //
- for(Decl* d = firstDecl; d; d = d->nextInContainerWithSameName)
{
- namespaceDecl = as<NamespaceDecl>(d);
- if(namespaceDecl)
- break;
- }
- // If we didn't find a pre-existing namespace, then
- // we will go ahead and create one now.
- //
- if( !namespaceDecl )
+ // We meed to make sure that the member dictionary of
+ // the parent declaration has been built/rebuilt so that
+ // lookup by name will work.
+ //
+ // TODO: The current way we rebuild the member dictionary
+ // would make for O(N^2) parsing time in a file that
+ // consisted of N back-to-back `namespace`s, since each
+ // would trigger a rebuild of the member dictionary that
+ // would take O(N) time.
+ //
+
+ // There might be multiple members of the same name
+ // (if we define a namespace `foo` after an overloaded
+ // function `foo` has been defined), and direct member
+ // lookup will only give us the first.
+ //
+ Decl* firstDecl = nullptr;
+ parentDecl->getMemberDictionary().tryGetValue(nameAndLoc.name, firstDecl);
+ //
+ // We will search through the declarations of the name
+ // and find the first that is a namespace (if any).
+ //
+ // Note: we do not issue diagnostics here based on
+ // the potential conflicts between these declarations,
+ // because we want to do as little semantic analysis
+ // as possible in the parser, and we'd rather be
+ // as permissive as possible right now.
+ //
+ for (Decl* d = firstDecl; d; d = d->nextInContainerWithSameName)
+ {
+ namespaceDecl = as<NamespaceDecl>(d);
+ if (namespaceDecl)
+ break;
+ }
+
+ // If we didn't find a pre-existing namespace, then
+ // we will go ahead and create one now.
+ //
+ if (!namespaceDecl)
+ {
+ namespaceDecl = parser->astBuilder->create<NamespaceDecl>();
+ namespaceDecl->nameAndLoc = nameAndLoc;
+ }
+ }
+ if (!result)
{
- namespaceDecl = parser->astBuilder->create<NamespaceDecl>();
- namespaceDecl->nameAndLoc = nameAndLoc;
+ result = namespaceDecl;
}
- }
+ else if (parentDecl)
+ {
+ if (auto parentNamespace = as<NamespaceDecl>(parentDecl))
+ {
+ parser->PushScope(parentDecl);
+ nestedNamespaceDecls.add(parentNamespace);
+ }
+ AddMember(parentDecl, namespaceDecl);
+ }
+
+ parentDecl = namespaceDecl;
+ } while (AdvanceIf(parser, TokenType::Dot) || AdvanceIf(parser, TokenType::Scope));
// Now that we have a namespace declaration to fill in
// (whether a new or existing one), we can parse the
@@ -3392,7 +3400,13 @@ namespace Slang
//
parseDeclBody(parser, namespaceDecl);
- return namespaceDecl;
+ for (auto ns : nestedNamespaceDecls)
+ {
+ ns->loc = ns->nameAndLoc.loc;
+ ns->closingSourceLoc = namespaceDecl->closingSourceLoc;
+ parser->PopScope();
+ }
+ return result;
}
static NodeBase* parseUsingDecl(Parser* parser, void* /*userData*/)
@@ -3929,6 +3943,221 @@ namespace Slang
return paramDecl;
}
+ static bool shouldDeclBeCheckedForNestingValidity(ASTNodeType declType)
+ {
+ switch (declType)
+ {
+ case ASTNodeType::ExtensionDecl:
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::GLSLInterfaceBlockDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::InterfaceDecl:
+ case ASTNodeType::ConstructorDecl:
+ case ASTNodeType::AccessorDecl:
+ case ASTNodeType::GetterDecl:
+ case ASTNodeType::SetterDecl:
+ case ASTNodeType::RefAccessorDecl:
+ case ASTNodeType::FuncDecl:
+ case ASTNodeType::SubscriptDecl:
+ case ASTNodeType::PropertyDecl:
+ case ASTNodeType::NamespaceDecl:
+ case ASTNodeType::ModuleDecl:
+ case ASTNodeType::FileDecl:
+ case ASTNodeType::GenericDecl:
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::UsingDecl:
+ case ASTNodeType::ImportDecl:
+ case ASTNodeType::IncludeDeclBase:
+ case ASTNodeType::IncludeDecl:
+ case ASTNodeType::ImplementingDecl:
+ case ASTNodeType::ModuleDeclarationDecl:
+ case ASTNodeType::AssocTypeDecl:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Can a decl of `declType` be allowed as a children of `parentType`?
+ static bool isDeclAllowed(bool languageServer, ASTNodeType parentType, ASTNodeType declType)
+ {
+ // If decl is not known as a decl that can be written by the user (e.g. a synthesized decl type),
+ // then we just allow it.
+ if (!shouldDeclBeCheckedForNestingValidity(declType))
+ return true;
+
+ switch (parentType)
+ {
+ case ASTNodeType::ExtensionDecl:
+ switch (declType)
+ {
+ case ASTNodeType::FuncDecl:
+ case ASTNodeType::SubscriptDecl:
+ case ASTNodeType::PropertyDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::GenericDecl:
+ case ASTNodeType::ConstructorDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ switch (declType)
+ {
+ case ASTNodeType::FuncDecl:
+ case ASTNodeType::SubscriptDecl:
+ case ASTNodeType::PropertyDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::EnumCaseDecl:
+ case ASTNodeType::GenericDecl:
+ case ASTNodeType::ConstructorDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::InterfaceDecl:
+ switch (declType)
+ {
+ case ASTNodeType::FuncDecl:
+ case ASTNodeType::SubscriptDecl:
+ case ASTNodeType::PropertyDecl:
+ case ASTNodeType::AssocTypeDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::GenericDecl:
+ case ASTNodeType::ConstructorDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::GLSLInterfaceBlockDecl:
+ switch (declType)
+ {
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::ConstructorDecl:
+ case ASTNodeType::AccessorDecl:
+ case ASTNodeType::GetterDecl:
+ case ASTNodeType::SetterDecl:
+ case ASTNodeType::RefAccessorDecl:
+ case ASTNodeType::FuncDecl:
+ switch (declType)
+ {
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::GenericDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::SubscriptDecl:
+ case ASTNodeType::PropertyDecl:
+ switch (declType)
+ {
+ case ASTNodeType::AccessorDecl:
+ case ASTNodeType::GetterDecl:
+ case ASTNodeType::SetterDecl:
+ case ASTNodeType::RefAccessorDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::ModuleDecl:
+ case ASTNodeType::FileDecl:
+ case ASTNodeType::NamespaceDecl:
+ switch (declType)
+ {
+ case ASTNodeType::ImplementingDecl:
+ return parentType == ASTNodeType::FileDecl || languageServer && parentType == ASTNodeType::ModuleDecl;
+ case ASTNodeType::ModuleDeclarationDecl:
+ return parentType == ASTNodeType::ModuleDecl || languageServer && parentType == ASTNodeType::FileDecl;
+ case ASTNodeType::NamespaceDecl:
+ case ASTNodeType::FileDecl:
+ case ASTNodeType::UsingDecl:
+ case ASTNodeType::ImportDecl:
+ case ASTNodeType::IncludeDecl:
+ case ASTNodeType::GenericDecl:
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::FuncDecl:
+ case ASTNodeType::SubscriptDecl:
+ case ASTNodeType::PropertyDecl:
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::InterfaceDecl:
+ case ASTNodeType::GLSLInterfaceBlockDecl:
+ case ASTNodeType::ExtensionDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::GenericDecl:
+ switch (declType)
+ {
+ case ASTNodeType::StructDecl:
+ case ASTNodeType::ClassDecl:
+ case ASTNodeType::EnumDecl:
+ case ASTNodeType::InterfaceDecl:
+ case ASTNodeType::FuncDecl:
+ case ASTNodeType::ConstructorDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::ExtensionDecl:
+ return true;
+ default:
+ return false;
+ }
+ case ASTNodeType::VarDecl:
+ case ASTNodeType::LetDecl:
+ case ASTNodeType::TypeDefDecl:
+ case ASTNodeType::TypeAliasDecl:
+ case ASTNodeType::UsingDecl:
+ case ASTNodeType::ImportDecl:
+ case ASTNodeType::IncludeDecl:
+ case ASTNodeType::ImplementingDecl:
+ case ASTNodeType::ModuleDeclarationDecl:
+ case ASTNodeType::AssocTypeDecl:
+ return true;
+ default:
+ return true;
+ }
+ }
+
// Parse declaration of a name to be used for resolving `[attribute(...)]` style modifiers.
//
// These are distinct from `syntax` declarations, because their names don't get added
@@ -4008,7 +4237,7 @@ namespace Slang
// Finish up work on a declaration that was parsed
static void CompleteDecl(
- Parser* /*parser*/,
+ Parser* parser,
Decl* decl,
ContainerDecl* containerDecl,
Modifiers modifiers)
@@ -4034,10 +4263,31 @@ namespace Slang
declToModify = genericDecl->inner;
_addModifiers(declToModify, modifiers);
- if (containerDecl && !as<GenericDecl>(containerDecl))
+ if (containerDecl)
{
- // Make sure the decl is properly nested inside its lexical parent
- AddMember(containerDecl, decl);
+ // Check that the declaration is actually allowed to be nested inside container.
+ if (!isDeclAllowed(parser->options.isInLanguageServer, containerDecl->astNodeType, decl->astNodeType))
+ {
+ parser->sink->diagnose(decl->loc, Diagnostics::declNotAllowed, decl->astNodeType);
+ }
+ else
+ {
+ // For generic decls, we also need to check if the inner decl type is allowed to be
+ // nested here.
+ if (declToModify && declToModify != decl)
+ {
+ if (!isDeclAllowed(parser->options.isInLanguageServer, containerDecl->astNodeType, declToModify->astNodeType))
+ {
+ parser->sink->diagnose(decl->loc, Diagnostics::declNotAllowed, declToModify->astNodeType);
+ }
+ }
+ }
+
+ if (!as<GenericDecl>(containerDecl))
+ {
+ // Make sure the decl is properly nested inside its lexical parent
+ AddMember(containerDecl, decl);
+ }
}
}
@@ -6982,6 +7232,7 @@ namespace Slang
ParserOptions options = {};
options.enableEffectAnnotations = translationUnit->compileRequest->getLinkage()->getEnableEffectAnnotations();
options.allowGLSLInput = translationUnit->compileRequest->getLinkage()->getAllowGLSLInput();
+ options.isInLanguageServer = translationUnit->compileRequest->getLinkage()->isInLanguageServer();
Parser parser(astBuilder, tokens, sink, outerScope, options);
parser.namePool = translationUnit->getNamePool();
diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp
index 6350a7e4d..ab0dfcd74 100644
--- a/source/slang/slang-syntax.cpp
+++ b/source/slang/slang-syntax.cpp
@@ -25,6 +25,77 @@ void printDiagnosticArg(StringBuilder& sb, DeclRefBase* declRefBase)
printDiagnosticArg(sb, declRefBase->getDecl());
}
+void printDiagnosticArg(StringBuilder& sb, ASTNodeType nodeType)
+{
+ // TODO: this can be generated.
+
+ switch (nodeType)
+ {
+ case ASTNodeType::Decl: sb << "Decl"; break;
+ case ASTNodeType::UnresolvedDecl: sb << "UnresolvedDecl"; break;
+ case ASTNodeType::ContainerDecl: sb << "ContainerDecl"; break;
+ case ASTNodeType::AggTypeDeclBase: sb << "AggTypeDeclBase"; break;
+ case ASTNodeType::ExtensionDecl: sb << "extension"; break;
+ case ASTNodeType::AggTypeDecl: sb << "AggTypeDecl"; break;
+ case ASTNodeType::StructDecl: sb << "struct"; break;
+ case ASTNodeType::ClassDecl: sb << "class"; break;
+ case ASTNodeType::GLSLInterfaceBlockDecl: sb << "GLSL interface block"; break;
+ case ASTNodeType::EnumDecl: sb << "enum"; break;
+ case ASTNodeType::ThisTypeDecl: sb << "This"; break;
+ case ASTNodeType::InterfaceDecl: sb << "interface"; break;
+ case ASTNodeType::AssocTypeDecl: sb << "associatedtype"; break;
+ case ASTNodeType::GlobalGenericParamDecl: sb << "global generic param"; break;
+ case ASTNodeType::ScopeDecl: sb << "scope"; break;
+ case ASTNodeType::CallableDecl: sb << "CallableDecl"; break;
+ case ASTNodeType::FunctionDeclBase: sb << "FunctionDeclBase"; break;
+ case ASTNodeType::ConstructorDecl: sb << "__init"; break;
+ case ASTNodeType::AccessorDecl: sb << "accessor"; break;
+ case ASTNodeType::GetterDecl: sb << "getter"; break;
+ case ASTNodeType::SetterDecl: sb << "setter"; break;
+ case ASTNodeType::RefAccessorDecl: sb << "ref accessor"; break;
+ case ASTNodeType::FuncDecl: sb << "function"; break;
+ case ASTNodeType::DerivativeRequirementDecl: sb << "DerivativeRequirementDecl"; break;
+ case ASTNodeType::ForwardDerivativeRequirementDecl: sb << "ForwardDerivativeRequirementDecl"; break;
+ case ASTNodeType::BackwardDerivativeRequirementDecl: sb << "BackwardDerivativeRequirementDecl"; break;
+ case ASTNodeType::DerivativeRequirementReferenceDecl: sb << "DerivativeRequirementReferenceDecl"; break;
+ case ASTNodeType::SubscriptDecl: sb << "__subscript"; break;
+ case ASTNodeType::PropertyDecl: sb << "property"; break;
+ case ASTNodeType::NamespaceDeclBase: sb << "NamespaceDeclBase"; break;
+ case ASTNodeType::NamespaceDecl: sb << "namespace"; break;
+ case ASTNodeType::ModuleDecl: sb << "module"; break;
+ case ASTNodeType::FileDecl: sb << "included file"; break;
+ case ASTNodeType::GenericDecl: sb << "generic"; break;
+ case ASTNodeType::AttributeDecl: sb << "attribute"; break;
+ case ASTNodeType::VarDeclBase: sb << "variable definition"; break;
+ case ASTNodeType::VarDecl: sb << "variable definition"; break;
+ case ASTNodeType::LetDecl: sb << "immutable value definition"; break;
+ case ASTNodeType::GlobalGenericValueParamDecl: sb << "GlobalGenericValueParamDecl"; break;
+ case ASTNodeType::ParamDecl: sb << "parameter"; break;
+ case ASTNodeType::ModernParamDecl: sb << "parameter"; break;
+ case ASTNodeType::GenericValueParamDecl: sb << "GenericValueParamDecl"; break;
+ case ASTNodeType::EnumCaseDecl: sb << "enum case"; break;
+ case ASTNodeType::TypeConstraintDecl: sb << "TypeConstraintDecl"; break;
+ case ASTNodeType::ThisTypeConstraintDecl: sb << "ThisTypeConstraintDecl"; break;
+ case ASTNodeType::InheritanceDecl: sb << "InheritanceDecl"; break;
+ case ASTNodeType::GenericTypeConstraintDecl: sb << "GenericTypeConstraintDecl"; break;
+ case ASTNodeType::SimpleTypeDecl: sb << "SimpleTypeDecl"; break;
+ case ASTNodeType::TypeDefDecl: sb << "typedef"; break;
+ case ASTNodeType::TypeAliasDecl: sb << "typealias"; break;
+ case ASTNodeType::GenericTypeParamDecl: sb << "GenericTypeParamDecl"; break;
+ case ASTNodeType::UsingDecl: sb << "using"; break;
+ case ASTNodeType::FileReferenceDeclBase: sb << "FileReferenceDeclBase"; break;
+ case ASTNodeType::ImportDecl: sb << "import"; break;
+ case ASTNodeType::IncludeDeclBase: sb << "IncludeDeclBase"; break;
+ case ASTNodeType::IncludeDecl: sb << "__include"; break;
+ case ASTNodeType::ImplementingDecl: sb << "implementing"; break;
+ case ASTNodeType::ModuleDeclarationDecl: sb << "module"; break;
+ case ASTNodeType::EmptyDecl: sb << "empty"; break;
+ case ASTNodeType::SyntaxDecl: sb << "syntax"; break;
+ case ASTNodeType::DeclGroup: sb << "decl-group"; break;
+ default: sb << "decl"; break;
+ }
+}
+
void printDiagnosticArg(StringBuilder& sb, Type* type)
{
if (!type)