diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-ast-iterator.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 160 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 70 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-check-modifier.cpp | 22 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-language-server-ast-lookup.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 11 | ||||
| -rw-r--r-- | source/slang/slang-mangle.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 407 | ||||
| -rw-r--r-- | source/slang/slang-syntax.cpp | 71 |
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) |
