diff --git a/source/slang/slang-ast-base.h b/source/slang/slang-ast-base.h index b4b0a345351..5b77f9d53e3 100644 --- a/source/slang/slang-ast-base.h +++ b/source/slang/slang-ast-base.h @@ -759,8 +759,15 @@ class Decl : public DeclBase DeclCheckStateExt checkState = DeclCheckState::Unchecked; - // The next declaration defined in the same container with the same name - Decl* nextInContainerWithSameName = nullptr; + /// The previous declaration defined in the same `ContainerDecl` + /// that has the same name as this declaration. + /// + /// Note: it is not recommended to ever access this member directly; + /// instead, code should use the `ContainerDecl::getPrevDeclWithSameName()` + /// method, which ensures that the `_prevInContainerWithSameName` fields + /// have been properly set for all declarations in that container. + /// + Decl* _prevInContainerWithSameName = nullptr; bool isChecked(DeclCheckState state) const { return checkState >= state; } void setCheckState(DeclCheckState state) diff --git a/source/slang/slang-ast-decl-ref.cpp b/source/slang/slang-ast-decl-ref.cpp index 89fa52b092d..76d0324c6b8 100644 --- a/source/slang/slang-ast-decl-ref.cpp +++ b/source/slang/slang-ast-decl-ref.cpp @@ -255,7 +255,7 @@ void GenericAppDeclRef::_toTextOverride(StringBuilder& out) { auto genericDecl = as(getGenericDeclRef()->getDecl()); Index paramCount = 0; - for (auto member : genericDecl->members) + for (auto member : genericDecl->getDirectMemberDecls()) if (as(member) || as(member)) paramCount++; getGenericDeclRef()->toText(out); @@ -381,7 +381,7 @@ void DeclRefBase::toText(StringBuilder& out) if (auto genericAppDeclRef = substSet.findGenericAppDeclRef(genericDecl)) { Index paramCount = 0; - for (auto member : genericDecl->members) + for (auto member : genericDecl->getDirectMemberDecls()) if (as(member) || as(member)) paramCount++; diff --git a/source/slang/slang-ast-decl.cpp b/source/slang/slang-ast-decl.cpp index 4c5d32f713d..5152f090184 100644 --- a/source/slang/slang-ast-decl.cpp +++ b/source/slang/slang-ast-decl.cpp @@ -49,76 +49,218 @@ bool isInterfaceRequirement(Decl* decl) return false; } -void ContainerDecl::buildMemberDictionary() +// +// ContainerDecl +// + +List const& ContainerDecl::getDirectMemberDecls() +{ + return _directMemberDecls.decls; +} + +Count ContainerDecl::getDirectMemberDeclCount() +{ + return _directMemberDecls.decls.getCount(); +} + +Decl* ContainerDecl::getDirectMemberDecl(Index index) +{ + return _directMemberDecls.decls[index]; +} + +Decl* ContainerDecl::getFirstDirectMemberDecl() +{ + if (getDirectMemberDeclCount() == 0) + return nullptr; + return getDirectMemberDecl(0); +} + +DeclsOfNameList ContainerDecl::getDirectMemberDeclsOfName(Name* name) +{ + return DeclsOfNameList(findLastDirectMemberDeclOfName(name)); +} + +Decl* ContainerDecl::findLastDirectMemberDeclOfName(Name* name) +{ + _ensureLookupAcceleratorsAreValid(); + if (auto found = _directMemberDecls.accelerators.mapNameToLastDeclOfThatName.tryGetValue(name)) + return *found; + return nullptr; +} + +Decl* ContainerDecl::getPrevDirectMemberDeclWithSameName(Decl* decl) +{ + SLANG_ASSERT(decl); + SLANG_ASSERT(decl->parentDecl == this); + + _ensureLookupAcceleratorsAreValid(); + return decl->_prevInContainerWithSameName; +} + +void ContainerDecl::addDirectMemberDecl(Decl* decl) +{ + if (!decl) + return; + + decl->parentDecl = this; + _directMemberDecls.decls.add(decl); +} + +List const& ContainerDecl::getTransparentDirectMemberDecls() +{ + _ensureLookupAcceleratorsAreValid(); + return _directMemberDecls.accelerators.filteredListOfTransparentDecls; +} + +bool ContainerDecl::_areLookupAcceleratorsValid() +{ + return _directMemberDecls.accelerators.declCountWhenLastUpdated == + _directMemberDecls.decls.getCount(); +} + +void ContainerDecl::_invalidateLookupAccelerators() +{ + _directMemberDecls.accelerators.declCountWhenLastUpdated = -1; +} + +void ContainerDecl::_ensureLookupAcceleratorsAreValid() { - // Don't rebuild if already built - if (isMemberDictionaryValid()) + if (_areLookupAcceleratorsValid()) return; - // If it's < 0 it means that the dictionaries are entirely invalid - if (dictionaryLastCount < 0) + // If the `declCountWhenLastUpdated` is less than zero, it means that + // the accelerators are entirely invalidated, and must be rebuilt + // from scratch. + // + if (_directMemberDecls.accelerators.declCountWhenLastUpdated < 0) { - dictionaryLastCount = 0; - memberDictionary.clear(); - transparentMembers.clear(); + _directMemberDecls.accelerators.declCountWhenLastUpdated = 0; + _directMemberDecls.accelerators.mapNameToLastDeclOfThatName.clear(); + _directMemberDecls.accelerators.filteredListOfTransparentDecls.clear(); } // are we a generic? GenericDecl* genericDecl = as(this); - const Index membersCount = members.getCount(); + Count memberCount = _directMemberDecls.decls.getCount(); + Count memberCountWhenLastUpdated = _directMemberDecls.accelerators.declCountWhenLastUpdated; - SLANG_ASSERT(dictionaryLastCount >= 0 && dictionaryLastCount <= membersCount); + SLANG_ASSERT(memberCountWhenLastUpdated >= 0 && memberCountWhenLastUpdated <= memberCount); - for (Index i = dictionaryLastCount; i < membersCount; ++i) + for (Index i = memberCountWhenLastUpdated; i < memberCount; ++i) { - Decl* m = members[i]; - - auto name = m->getName(); - - // Add any transparent members to a separate list for lookup - if (m->hasModifier()) + Decl* memberDecl = _directMemberDecls.decls[i]; + + // Transparent member declarations will go into a separate list, + // so that they can be conveniently queried later for lookup + // operations. + // + // TODO: Rather than track these using a separate table, we + // could design a scheme where transparent members are put into + // the same lookup dictionary as everything else, just under + // a pseudo-name that identifies transparent members. + // + if (memberDecl->hasModifier()) { - TransparentMemberInfo info; - info.decl = m; - transparentMembers.add(info); + _directMemberDecls.accelerators.filteredListOfTransparentDecls.add(memberDecl); } - // Ignore members with no name - if (!name) + // Members that don't have a name don't go into the lookup dictionary. + // + auto memberName = memberDecl->getName(); + if (!memberName) continue; - // Ignore the "inner" member of a generic declaration - if (genericDecl && m == genericDecl->inner) + // As a special case, we ignore the `inner` member of a + // `GenericDecl`, since it will always have the same name + // as the outer generic, and should not be found by lookup. + // + // TODO: We really ought to change up our entire encoding + // of generic declarations in the AST. + // + if (genericDecl && memberDecl == genericDecl->inner) continue; - m->nextInContainerWithSameName = nullptr; + // It is possible that we have encountered previous declarations + // that have the same name as `memberDecl`, and in that + // case we want to wire them up into a singly-linked list. + // + // This list makes it easy for a lookup operation to find, e.g., + // all of the overloaded functions with a given name. + // + Decl* prevMemberWithSameName = nullptr; + _directMemberDecls.accelerators.mapNameToLastDeclOfThatName.tryGetValue( + memberName, + prevMemberWithSameName); + memberDecl->_prevInContainerWithSameName = prevMemberWithSameName; + + // Whether or not there was a previous declaration with this + // name, the current `memberDecl` is the last member declaration + // with that name encountered so far, and it is what we will + // store in the lookup dictionary. + // + _directMemberDecls.accelerators.mapNameToLastDeclOfThatName[memberName] = memberDecl; + } - Decl* next = nullptr; - if (memberDictionary.tryGetValue(name, next)) - m->nextInContainerWithSameName = next; + _directMemberDecls.accelerators.declCountWhenLastUpdated = memberCount; + SLANG_ASSERT(_areLookupAcceleratorsValid()); +} - memberDictionary[name] = m; - } +void ContainerDecl:: + _invalidateLookupAcceleratorsBecauseUnscopedEnumAttributeWillBeTurnedIntoTransparentModifier( + UnscopedEnumAttribute* unscopedEnumAttr, + TransparentModifier* transparentModifier) +{ + SLANG_ASSERT(unscopedEnumAttr); + SLANG_ASSERT(transparentModifier); + + SLANG_UNUSED(unscopedEnumAttr); + SLANG_UNUSED(transparentModifier); - dictionaryLastCount = membersCount; - SLANG_ASSERT(isMemberDictionaryValid()); + _invalidateLookupAccelerators(); } -Index ContainerDecl::getDeclIndex(Decl* decl) +void ContainerDecl:: + _removeDirectMemberConstructorDeclBecauseSynthesizedAnotherDefaultConstructorInstead( + ConstructorDecl* decl) { - if (Index* ptr = mapDeclMemberToIndex.tryGetValue(decl)) - { - return *ptr; - } - Index res = members.findFirstIndex([&](Decl* d) { return d == decl; }); - if (res >= Index(0)) - { - mapDeclMemberToIndex[decl] = res; - } - return res; + SLANG_ASSERT(decl); + + _invalidateLookupAccelerators(); + _directMemberDecls.decls.remove(decl); +} + +void ContainerDecl:: + _replaceDirectMemberBitFieldVariableDeclAtIndexWithPropertyDeclThatWasSynthesizedForIt( + Index index, + VarDecl* oldDecl, + PropertyDecl* newDecl) +{ + SLANG_ASSERT(oldDecl); + SLANG_ASSERT(newDecl); + SLANG_ASSERT(index >= 0 && index < getDirectMemberDeclCount()); + SLANG_ASSERT(getDirectMemberDecl(index) == oldDecl); + + SLANG_UNUSED(oldDecl); + _invalidateLookupAccelerators(); + _directMemberDecls.decls[index] = newDecl; } +void ContainerDecl::_insertDirectMemberDeclAtIndexForBitfieldPropertyBackingMember( + Index index, + VarDecl* backingVarDecl) +{ + SLANG_ASSERT(backingVarDecl); + SLANG_ASSERT(index >= 0 && index <= getDirectMemberDeclCount()); + + _invalidateLookupAccelerators(); + _directMemberDecls.decls.insert(index, backingVarDecl); +} + +// +// +// + bool isLocalVar(const Decl* decl) { const auto varDecl = as(decl); @@ -137,14 +279,12 @@ bool isLocalVar(const Decl* decl) ThisTypeDecl* InterfaceDecl::getThisTypeDecl() { - for (auto member : members) + auto thisTypeDecl = findFirstDirectMemberDeclOfType(); + if (!thisTypeDecl) { - if (auto thisTypeDeclCandidate = as(member)) - { - return thisTypeDeclCandidate; - } + SLANG_UNEXPECTED("InterfaceDecl does not have a ThisType decl."); } - SLANG_UNREACHABLE("InterfaceDecl does not have a ThisType decl."); + return thisTypeDecl; } InterfaceDecl* ThisTypeConstraintDecl::getInterfaceDecl() diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index e281081a7d5..3410806dc8f 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -25,84 +25,280 @@ class UnresolvedDecl : public Decl FIDDLE(...) }; +/// Holds the direct member declarations of a `ContainerDecl`. +/// +/// This type is used to encapsulate the logic the creating +/// and maintaing the acceleration structures used for member +/// lookup. +/// +struct ContainerDeclDirectMemberDecls +{ +public: + List const& getDecls() const { return decls; } + + List& _refDecls() { return decls; } + +private: + friend class ContainerDecl; + friend struct ASTDumpContext; + + List decls; + + struct + { + Count declCountWhenLastUpdated = 0; + + Dictionary mapNameToLastDeclOfThatName; + List filteredListOfTransparentDecls; + } accelerators; +}; + +/// A conceptual list of declarations of the same name, in the same container. +struct DeclsOfNameList +{ +public: + DeclsOfNameList() {} + + explicit DeclsOfNameList(Decl* decl) + : _lastDecl(decl) + { + } + + struct Iterator + { + public: + Iterator() {} + Iterator(Decl* decl) + : _decl(decl) + { + } + + Decl* operator*() const { return _decl; } + + Iterator& operator++() + { + SLANG_ASSERT(_decl); + _decl = _decl->_prevInContainerWithSameName; + return *this; + } + + bool operator!=(const Iterator& other) const { return _decl != other._decl; } + + private: + Decl* _decl = nullptr; + }; + + Iterator begin() const { return _lastDecl; } + Iterator end() const { return nullptr; } + +private: + Decl* _lastDecl = nullptr; +}; + // A "container" decl is a parent to other declarations FIDDLE(abstract) class ContainerDecl : public Decl { FIDDLE(...) - FIDDLE() List members; SourceLoc closingSourceLoc; // The associated scope owned by this decl. Scope* ownedScope = nullptr; - template - FilteredMemberList getMembersOfType() - { - return FilteredMemberList(members); - } - - void buildMemberDictionary(); + /// Get all of the direct member declarations inside this container decl. + /// + List const& getDirectMemberDecls(); - bool isMemberDictionaryValid() const { return dictionaryLastCount == members.getCount(); } + /// Get the number of direct member declarations inside this container decl. + /// + Count getDirectMemberDeclCount(); - void invalidateMemberDictionary() - { - dictionaryLastCount = -1; - mapDeclMemberToIndex.clear(); - } + /// Get the direct member declaration of this container decl at the given `index`. + /// + Decl* getDirectMemberDecl(Index index); - Dictionary& getMemberDictionary() - { - buildMemberDictionary(); - return memberDictionary; - } + /// Get the first direct member declaration inside of this container decl, if any. + /// + /// If the container has no direct member declarations, returns null. + /// + Decl* getFirstDirectMemberDecl(); - List& getTransparentMembers() + /// Get all of the direct member declarations inside of this container decl + /// that are instances of the specified AST node type `T`. + /// + template + FilteredMemberList getDirectMemberDeclsOfType() { - buildMemberDictionary(); - return transparentMembers; + return FilteredMemberList(getDirectMemberDecls()); } - void addMember(Decl* member) + /// Find the first direct member declaration of this container decl that + /// is an instance of the specified AST node type `T`. + /// + /// If there are no direct member declrations of type `T`, then returns null. + /// Otherwise, returns the first matching member, in declaration order. + /// + template + T* findFirstDirectMemberDeclOfType() { - if (member) + auto count = getDirectMemberDeclCount(); + for (Index i = 0; i < count; ++i) { - member->parentDecl = this; - auto index = members.getCount(); - members.add(member); - mapDeclMemberToIndex[member] = index; + auto decl = getDirectMemberDecl(i); + if (auto found = as(decl)) + return found; } + return nullptr; } - static void setParent(ContainerDecl* parent, Decl* child) + /// Find all direct member declarations of this container decl that have the given name. + /// + DeclsOfNameList getDirectMemberDeclsOfName(Name* name); + + /// Find the last direct member declaration of this container decl that has the given name. + /// + Decl* findLastDirectMemberDeclOfName(Name* name); + + /// Get the previous direct member declaration that has the same name as `decl`. + /// + Decl* getPrevDirectMemberDeclWithSameName(Decl* decl); + + /// Append the given `decl` to the direct member declarations of this container decl. + /// + void addDirectMemberDecl(Decl* decl); + + /// Get the subset of direct member declarations that are "transparent." + /// + /// Transparent members will themselves be considered when performing + /// looking on the parent. E.g., if `a` has a transparent member `b`, + /// then a lookup like `a.x` will also consider `a.b.x` as a possible + /// result. + /// + List const& getTransparentDirectMemberDecls(); + + // Note: Just an alias for `getDirectMemberDecls()`, + // but left in place because of just how many call sites were + // already using this name. + // + List const& getMembers() { return getDirectMemberDecls(); } + + // Note: Just an alias for `getDirectMemberDeclsOfType()`, + // but left in place because of just how many call sites were + // already using this name. + // + template + FilteredMemberList getMembersOfType() { - if (child) - child->parentDecl = parent; - if (parent) - parent->addMember(child); + return getDirectMemberDeclsOfType(); } - Index getDeclIndex(Decl* d); + // Note: Just an alias for `addDirectMemberDecl()`, + // but left in place because of just how many call sites were + // already using this name. + // + void addMember(Decl* member) { addDirectMemberDecl(member); } + + // + // NOTE: The operations after this point are *not* considered part of + // the public API of `ContainerDecl`, and new code should not be + // written that uses them. + // + // They are being left in place because the existing code that uses + // them would be difficult or impossible to refactor to use the public + // API, but such parts of the codebase should *not* be considered as + // examples of how to interact with the Slang AST. + // + + /// Invalidate the acceleration structures used for declaration lookup, + /// because the code is about to replace `unscopedEnumAttr` with `transparentModifier` + /// as part of semantic checking of an `EnumDecl` nested under this `ContainerDecl`. + /// + /// Cannot be expressed in terms of the rest of the public API because the + /// existing assumption has been that any needed `TransparentModifier`s would + /// be manifestly obvious just from the syntax being parsed, so that they would + /// already be in place on parsed ASTs. the `UnscopedEnumAttribute` is a `TransparentModifier` + /// in all but name, but the two don't share a common base class such that code + /// could check for them together. + /// + /// TODO: In the long run, the obvious fix is to eliminate `UnscopedEnumAttribute`, + /// becuase it only exists to enable legacy code to be expressed in Slang, rather than + /// representing anything we want/intend to support long-term. + /// + /// TODO: In the even *longer* run, we should eliminate `TransparentModifier` as well, + /// because it only exists to support legacy `cbuffer` declarations and similar syntax, + /// and those should be deprecated over time. + /// + void _invalidateLookupAcceleratorsBecauseUnscopedEnumAttributeWillBeTurnedIntoTransparentModifier( + UnscopedEnumAttribute* unscopedEnumAttr, + TransparentModifier* transparentModifier); + + /// Remove a constructor declaration from the direct member declarations of this container. + /// + /// This operation is seemingly used when a default constructor declaration has been synthesized + /// for a type, but that type already contained a default constructor of its own. + /// + /// TODO: Somebody should investigate why this operation is even needed; it seems like an + /// indication that we are doing something Deeply Wrong in the way that default constructors are + /// being handled and synthesized (on top of the things that we know are Wrong By Design). + /// + void _removeDirectMemberConstructorDeclBecauseSynthesizedAnotherDefaultConstructorInstead( + ConstructorDecl* decl); + + /// Replace the given `oldVarDecl` with the given `newPropertyDecl` in the direct member + /// declarations of this container decl, because the variable declaration had a bit-field + /// specification on it, and the property was synthesized to stand in for that variable + /// by providing a getter/settter pair. + /// + /// This operation cannot be expressed in terms of the rest of the public API, because there + /// is currently no other example of parsing a declaration as one AST node class, and + /// then determining as part of semantic checking that it should *actually* be represented + /// as a different class of declaration entirely. + /// + /// TODO: In the long run we would either eliminate support for C-style bit-field specifications + /// on what would otherwise be an ordinary member variable declaration. Some other syntax, or + /// a type-based solution, should be introduced to server the same use cases, without requiring + /// us to parse something as a variable that is semantically *not* a variable in almost any + /// of the ways that count. + /// + void _replaceDirectMemberBitFieldVariableDeclAtIndexWithPropertyDeclThatWasSynthesizedForIt( + Index index, + VarDecl* oldVarDecl, + PropertyDecl* newPropertyDecl); + + /// Insert `backingVarDecl` into this container declaration at `index`, to handle the case + /// where the backing variable has been synthesized to store the bits of one or more bitfield + /// properties. + /// + /// This operation cannot be expressed in terms of the rest of the public API because the usual + /// assumption is that member declarations may be added to a declaration but that, once added, + /// their indices in the member list are consistent and stable. + /// + /// The reason the code that calls this operation can't just add `backingVarDecl` to the end of + /// the declaration is that there is an underlying assumption made by users that any + /// non-bitfield members before/after a bitfield declaration will have their storage laid out + /// before/after the storage for that bitfield. + /// + /// TODO: A simple cleanup would be to *not* guarantee the order of storage layout for bitfield + /// members relative to other member variables, but the real long-term fix is to have an + /// alternative means for users to define bitfields that does not inherit the C-like syntax, and + /// that can make the storage that is introduced clear at parse time, so that the relevant + /// declaration(s) can already be in the correct order. + /// + void _insertDirectMemberDeclAtIndexForBitfieldPropertyBackingMember( + Index index, + VarDecl* backingVarDecl); + + // TODO: The following should be a private member, but currently + // we have auto-generated code for things like dumping and serialization + // that expect to have access to it. + // + FIDDLE() ContainerDeclDirectMemberDecls _directMemberDecls; private: - // Denotes how much of Members has been placed into the dictionary/transparentMembers. - // If this value equals the Members.getCount(), the dictionary is completely full and valid. - // If it's >= 0, then the Members after dictionaryLastCount are all that need to be added. - // If it < 0 it means that the dictionary/transparentMembers is invalid and needs to be - // recreated. - Index dictionaryLastCount = 0; - - // Dictionary for looking up members by name. - // This is built on demand before performing lookup. - Dictionary memberDictionary; - - Dictionary mapDeclMemberToIndex; - - // A list of transparent members, to be used in lookup - // Note: this is only valid if `memberDictionaryIsValid` is true - List transparentMembers; + bool _areLookupAcceleratorsValid(); + void _invalidateLookupAccelerators(); + void _ensureLookupAcceleratorsAreValid(); }; // Base class for all variable declarations diff --git a/source/slang/slang-ast-dump.cpp b/source/slang/slang-ast-dump.cpp index 7f6f7796c6c..b07714bf92d 100644 --- a/source/slang/slang-ast-dump.cpp +++ b/source/slang/slang-ast-dump.cpp @@ -555,8 +555,6 @@ struct ASTDumpContext } void dump(const ExpandedSpecializationArg& arg) { dump(arg.witness); } - void dump(const TransparentMemberInfo& memInfo) { dump(memInfo.decl); } - void dumpRemaining() { // Have to keep checking count, as dumping objects can add objects @@ -570,6 +568,32 @@ struct ASTDumpContext } } + void dump(ContainerDeclDirectMemberDecls const& decls) + { + m_writer->emit(" { \n"); + m_writer->indent(); + bool first = true; + for (auto decl : decls.getDecls()) + { + if (!first) + { + m_writer->emit(",\n"); + } + first = false; + + if (decl) + { + dump(decl); + } + else + { + m_writer->emit("null"); + } + } + m_writer->dedent(); + m_writer->emit("}"); + } + void dump(ContainerDecl* decl) { if (auto moduleDecl = dynamicCast(decl)) diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h index 18a46b3032e..4c866fc5a05 100644 --- a/source/slang/slang-ast-iterator.h +++ b/source/slang/slang-ast-iterator.h @@ -532,7 +532,7 @@ void ASTIterator::visitDecl(DeclBase* decl) } if (auto container = as(decl)) { - for (auto member : container->members) + for (auto member : container->getDirectMemberDecls()) { visitDecl(member); } diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 19e3f33c808..d05f24e56fe 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1096,12 +1096,6 @@ FIDDLE() namespace Slang MemberFilterStyle m_filterStyle; }; - struct TransparentMemberInfo - { - // The declaration of the transparent member - Decl* decl = nullptr; - }; - template struct FilteredMemberRefList { diff --git a/source/slang/slang-ast-synthesis.h b/source/slang/slang-ast-synthesis.h index bdfd90aeb90..7613a6fae73 100644 --- a/source/slang/slang-ast-synthesis.h +++ b/source/slang/slang-ast-synthesis.h @@ -71,7 +71,10 @@ class ASTSynthesizer ASTEmitScope scope = getCurrentScope(); auto scopeDecl = m_builder->create(); auto newScope = m_builder->create(); - ContainerDecl::setParent(scope.m_parent, scopeDecl); + + if (scope.m_parent) + scope.m_parent->addDirectMemberDecl(scopeDecl); + newScope->parent = scope.m_scope; newScope->containerDecl = scopeDecl; scope.m_scope = newScope; diff --git a/source/slang/slang-ast-type.cpp b/source/slang/slang-ast-type.cpp index 7cfc6a67aac..5056e5407c0 100644 --- a/source/slang/slang-ast-type.cpp +++ b/source/slang/slang-ast-type.cpp @@ -863,13 +863,7 @@ DeclRef ExtractExistentialType::getThisTypeDeclRef() SubtypeWitness* openedWitness = getSubtypeWitness(); - ThisTypeDecl* thisTypeDecl = nullptr; - for (auto member : interfaceDecl->members) - if (as(member)) - { - thisTypeDecl = as(member); - break; - } + ThisTypeDecl* thisTypeDecl = interfaceDecl->getThisTypeDecl(); SLANG_ASSERT(thisTypeDecl); DeclRef specialiedInterfaceDeclRef = diff --git a/source/slang/slang-ast-val.cpp b/source/slang/slang-ast-val.cpp index 92e170515b3..40daae3a600 100644 --- a/source/slang/slang-ast-val.cpp +++ b/source/slang/slang-ast-val.cpp @@ -213,7 +213,7 @@ Val* maybeSubstituteGenericParam(Val* paramVal, Decl* paramDecl, SubstitutionSet Count argCount = args.getCount(); Count argIndex = 0; - for (auto m : outerGeneric->members) + for (auto m : outerGeneric->getDirectMemberDecls()) { // If we have run out of arguments, then we can stop // iterating over the parameters, because `this` @@ -541,17 +541,15 @@ Val* DeclaredSubtypeWitness::_substituteImplOverride( bool found = false; Index index = 0; - for (auto m : genericDecl->members) + for (auto constraintParam : + genericDecl->getDirectMemberDeclsOfType()) { - if (auto constraintParam = as(m)) + if (constraintParam == getDeclRef().getDecl()) { - if (constraintParam == getDeclRef().getDecl()) - { - found = true; - break; - } - index++; + found = true; + break; } + index++; } if (found) { diff --git a/source/slang/slang-check-constraint.cpp b/source/slang/slang-check-constraint.cpp index 6259e2fb887..3020554c8bf 100644 --- a/source/slang/slang-check-constraint.cpp +++ b/source/slang/slang-check-constraint.cpp @@ -541,7 +541,7 @@ DeclRef SemanticsVisitor::trySolveConstraintSystem( // should have been filled with the resolved types and values for the // generic parameters. We can now verify if they are complete and consolidate // them into final argument list. - for (auto member : genericDeclRef.getDecl()->members) + for (auto member : genericDeclRef.getDecl()->getDirectMemberDecls()) { if (auto typeParam = as(member)) { diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp index 41355597ffc..506abc1bea4 100644 --- a/source/slang/slang-check-conversion.cpp +++ b/source/slang/slang-check-conversion.cpp @@ -1886,7 +1886,7 @@ bool SemanticsVisitor::tryCoerceLambdaToFuncType( // If it does, we can't convert it to a function type. auto operatorName = getName("()"); - for (auto member : lambdaStruct.getDecl()->members) + for (auto member : lambdaStruct.getDecl()->getDirectMemberDecls()) { if (auto field = as(member)) { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index aa84a057f94..b56c3cb0790 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -61,7 +61,7 @@ static void validateDynInterfaceUsage( return; // validate members inside `dyn interface` - for (auto m : decl->members) + for (auto m : decl->getDirectMemberDecls()) { if (isAssociatedTypeDecl(m)) { @@ -154,20 +154,16 @@ static void validateDynInterfaceUseWithInheritanceDecl( { // Ensure if we inherit from a `dyn interface` that the parent does not have: opaque // types, unsized types, non-copyable types - for (auto m : aggTypeDeclParent->members) + for (auto varDecl : aggTypeDeclParent->getDirectMemberDeclsOfType()) { - auto varDecl = as(m); - if (!varDecl) - continue; - visitor->ensureDecl(varDecl, DeclCheckState::ReadyForLookup); if (isNonCopyableType(varDecl->getType())) { sink->diagnose( - m, + varDecl, Diagnostics::cannotHaveNonCopyableMemberWhenInheritingDynInterface, - m, + varDecl, interfaceDecl); } @@ -176,15 +172,15 @@ static void validateDynInterfaceUseWithInheritanceDecl( bool isOpaque = varTypeTags & (int)TypeTag::Opaque; if (isUnsized) sink->diagnose( - m, + varDecl, Diagnostics::cannotHaveUnsizedMemberWhenInheritingDynInterface, - m, + varDecl, interfaceDecl); if (isOpaque) sink->diagnose( - m, + varDecl, Diagnostics::cannotHaveOpaqueMemberWhenInheritingDynInterface, - m, + varDecl, interfaceDecl); } } @@ -567,9 +563,9 @@ struct SemanticsDeclBodyVisitor : public SemanticsDeclVisitorBase, bool isMemberInitCtor, Index& paramIndex); - void synthesizeCtorBodyForMember( + void synthesizeCtorBodyForMemberVar( ConstructorDecl* ctor, - Decl* member, + VarDeclBase* member, ThisExpr* thisExpr, Dictionary& cachedDeclToCheckedVar, SeqStmt* seqStmtChild, @@ -927,7 +923,7 @@ struct SemanticsDeclReferenceVisitor : public SemanticsDeclVisitorBase, void visitContainerDecl(ContainerDecl* decl) { - for (auto m : decl->members) + for (auto m : decl->getDirectMemberDecls()) { dispatchIfNotNull(m); } @@ -1545,10 +1541,9 @@ void SemanticsVisitor::ensureAllDeclsRec(Decl* decl, DeclCheckState state) // takes place, invalidating the iterator and likely a crash. // // Accessing the members via index side steps the issue. - const auto& members = containerDecl->members; - for (Index i = 0; i < members.getCount(); ++i) + for (Index i = 0; i < containerDecl->getDirectMemberDeclCount(); ++i) { - Decl* childDecl = members[i]; + Decl* childDecl = containerDecl->getDirectMemberDecl(i); // As an exception, if any of the child is a `ScopeDecl`, // then that indicates that it represents a scope for local @@ -1710,39 +1705,49 @@ void SemanticsDeclModifiersVisitor::visitStructDecl(StructDecl* structDecl) // Replace any bitfield member with a property, do this here before // name lookup to avoid the original var decl being referenced - for (auto& m : structDecl->members) + // + auto directMemberDeclCount = structDecl->getDirectMemberDeclCount(); + for (Index i = 0; i < directMemberDeclCount; ++i) { - const auto bfm = m->findModifier(); - if (!bfm) + auto varDecl = as(structDecl->getDirectMemberDecl(i)); + if (!varDecl) continue; - auto property = m_astBuilder->create(); - property->modifiers = m->modifiers; - property->type = as(m)->type; - property->loc = m->loc; - property->nameAndLoc = m->getNameAndLoc(); - property->parentDecl = structDecl; - property->ownedScope = m_astBuilder->create(); - property->ownedScope->containerDecl = property; - property->ownedScope->parent = getScope(structDecl); - m = property; - - const auto get = m_astBuilder->create(); - get->ownedScope = m_astBuilder->create(); - get->ownedScope->containerDecl = get; - get->ownedScope->parent = getScope(property); - property->addMember(get); - - const auto set = m_astBuilder->create(); - addModifier(set, m_astBuilder->create()); - set->ownedScope = m_astBuilder->create(); - set->ownedScope->containerDecl = set; - set->ownedScope->parent = getScope(property); - property->addMember(set); + const auto bitFieldModifier = varDecl->findModifier(); + if (!bitFieldModifier) + continue; - structDecl->invalidateMemberDictionary(); + auto propertyDecl = m_astBuilder->create(); + propertyDecl->modifiers = varDecl->modifiers; + propertyDecl->type = varDecl->type; + propertyDecl->loc = varDecl->loc; + propertyDecl->nameAndLoc = varDecl->getNameAndLoc(); + propertyDecl->parentDecl = structDecl; + propertyDecl->ownedScope = m_astBuilder->create(); + propertyDecl->ownedScope->containerDecl = propertyDecl; + propertyDecl->ownedScope->parent = getScope(structDecl); + + auto propertyDeclScope = getScope(propertyDecl); + + const auto getAccessorDecl = m_astBuilder->create(); + getAccessorDecl->ownedScope = m_astBuilder->create(); + getAccessorDecl->ownedScope->containerDecl = getAccessorDecl; + getAccessorDecl->ownedScope->parent = propertyDeclScope; + propertyDecl->addMember(getAccessorDecl); + + const auto setAccessorDecl = m_astBuilder->create(); + addModifier(setAccessorDecl, m_astBuilder->create()); + setAccessorDecl->ownedScope = m_astBuilder->create(); + setAccessorDecl->ownedScope->containerDecl = setAccessorDecl; + setAccessorDecl->ownedScope->parent = propertyDeclScope; + propertyDecl->addMember(setAccessorDecl); + + structDecl + ->_replaceDirectMemberBitFieldVariableDeclAtIndexWithPropertyDeclThatWasSynthesizedForIt( + i, + varDecl, + propertyDecl); } - structDecl->buildMemberDictionary(); } void SemanticsDeclHeaderVisitor::checkDerivativeMemberAttributeParent( @@ -2368,7 +2373,7 @@ static inline bool _isDefaultCtor(ConstructorDecl* ctor) // 1. default ctor must have no parameters // 2. default ctor can have parameters, but all parameters have init expr (Because we won't // differentiate this case from 2.) - if (ctor->members.getCount() == 0 || allParamHaveInitExpr(ctor)) + if (ctor->getDirectMemberDeclCount() == 0 || allParamHaveInitExpr(ctor)) { return true; } @@ -2750,21 +2755,48 @@ void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl) bool isUnknownSize = (((int)varTypeTags & unsizedMask) != 0); if (isUnknownSize) { - // Unsized decl must appear as the last member of the struct. - for (auto memberIdx = parentDecl->members.getCount() - 1; memberIdx >= 0; memberIdx--) + // If an unsized variable declaration appears as a member of + // an aggregate type (such as a `struct`), then it must be + // the *last* non-`static` variable declaration in that type. + // + // We validate that here by iterating over the members in + // reverse order, until we either run into the unsized variable + // declaration in question (in which case things are fine), + // or we encounter some *other* non-`static` variable declaration, + // in which case the unsized declaration is invalid. + // + for (auto memberIdx = parentDecl->getDirectMemberDeclCount() - 1; memberIdx >= 0; + memberIdx--) { - if (parentDecl->members[memberIdx] == varDecl) - { - break; - } - if (auto memberVarDecl = as(parentDecl->members[memberIdx])) - { - if (!memberVarDecl->hasModifier()) - { - getSink()->diagnose(varDecl, Diagnostics::unsizedMemberMustAppearLast); - } + auto memberDecl = parentDecl->getDirectMemberDecl(memberIdx); + + // If we run into the unsized variable declaration before + // hitting anything else, the declaration is in a valid + // location. + // + if (memberDecl == varDecl) break; - } + + // If we run into another declaration, but it isn't a + // variable, then it doesn't impact validity. + // + auto memberVarDecl = as(memberDecl); + if (!memberVarDecl) + continue; + + // If we run into another variable declaration, but + // it is a `static` variable, then it doesn't + // impact the validity of the unsized declaration. + // + if (memberVarDecl->hasModifier()) + continue; + + // At this point we've run into a non-`static` variable declaration + // that comes *after* the unsized variable declaration, which + // means the unsized variable declaration is invalid. + // + getSink()->diagnose(varDecl, Diagnostics::unsizedMemberMustAppearLast); + break; } } } @@ -2873,11 +2905,10 @@ bool SemanticsVisitor::trySynthesizeDifferentialAssociatedTypeRequirementWitness RefPtr witnessTable) { ASTSynthesizer synth(m_astBuilder, getNamePool()); - Decl* existingDecl = nullptr; + AggTypeDecl* aggTypeDecl = nullptr; - if (context->parentDecl->getMemberDictionary().tryGetValue( - requirementDeclRef.getName(), - existingDecl)) + if (auto existingDecl = + context->parentDecl->findLastDirectMemberDeclOfName(requirementDeclRef.getName())) { // Remove the `ToBeSynthesizedModifier`. if (as(existingDecl->modifiers.first)) @@ -2942,10 +2973,10 @@ bool SemanticsVisitor::trySynthesizeDifferentialAssociatedTypeRequirementWitness if (!aggTypeDecl) { aggTypeDecl = m_astBuilder->create(); - context->parentDecl->addMember(aggTypeDecl); aggTypeDecl->nameAndLoc.name = requirementDeclRef.getName(); aggTypeDecl->loc = context->parentDecl->nameAndLoc.loc; - context->parentDecl->invalidateMemberDictionary(); + + context->parentDecl->addDirectMemberDecl(aggTypeDecl); synth.pushScopeForContainer(aggTypeDecl); } @@ -3000,12 +3031,11 @@ bool SemanticsVisitor::trySynthesizeDifferentialAssociatedTypeRequirementWitness diffField->nameAndLoc = member->nameAndLoc; diffField->type.type = diffMemberType; diffField->checkState = DeclCheckState::SignatureChecked; - aggTypeDecl->addMember(diffField); auto visibility = getDeclVisibility(member); addVisibilityModifier(diffField, visibility); - aggTypeDecl->invalidateMemberDictionary(); + aggTypeDecl->addDirectMemberDecl(diffField); // Inject a `DerivativeMember` modifier on the differential field to point to itself. { @@ -3059,25 +3089,17 @@ bool SemanticsVisitor::trySynthesizeDifferentialAssociatedTypeRequirementWitness } // The `Differential` type of a `Differential` type is always itself. - bool hasDifferentialTypeDef = false; - for (auto member : aggTypeDecl->members) - { - if (auto name = member->getName()) - { - if (name->text == "Differential") - { - hasDifferentialTypeDef = true; - break; - } - } - } + auto differentialName = getName("Differential"); + bool hasDifferentialTypeDef = + aggTypeDecl->findLastDirectMemberDeclOfName(differentialName) != nullptr; if (!hasDifferentialTypeDef) { auto assocTypeDef = m_astBuilder->create(); - assocTypeDef->nameAndLoc.name = getName("Differential"); + assocTypeDef->nameAndLoc.name = differentialName; assocTypeDef->type.type = satisfyingType; - aggTypeDecl->addMember(assocTypeDef); assocTypeDef->setCheckState(DeclCheckState::DefinitionChecked); + + aggTypeDecl->addDirectMemberDecl(assocTypeDef); } // Go through all members and collect their differential types. @@ -3261,10 +3283,9 @@ void SemanticsDeclHeaderVisitor::visitGenericDecl(GenericDecl* genericDecl) // Accessing the members via index side steps the issue. int parameterIndex = 0; - const auto& members = genericDecl->members; - for (Index i = 0; i < members.getCount(); ++i) + for (Index i = 0; i < genericDecl->getDirectMemberDeclCount(); ++i) { - Decl* m = members[i]; + Decl* m = genericDecl->getDirectMemberDecl(i); if (auto typeParam = as(m)) { @@ -3492,7 +3513,7 @@ static void _registerBuiltinDeclsRec(Session* session, Decl* decl) } if (auto containerDecl = as(decl)) { - for (auto childDecl : containerDecl->members) + for (auto childDecl : containerDecl->getDirectMemberDecls()) { if (as(childDecl)) continue; @@ -3539,7 +3560,7 @@ void discoverExtensionDecls(List& decls, Decl* parent) decls.add(extDecl); if (auto containerDecl = as(parent)) { - for (auto child : containerDecl->members) + for (auto child : containerDecl->getDirectMemberDecls()) { discoverExtensionDecls(decls, child); } @@ -3570,20 +3591,19 @@ void SemanticsDeclVisitorBase::checkModule(ModuleDecl* moduleDecl) _registerBuiltinDeclsRec(getSession(), moduleDecl); } - if (moduleDecl->members.getCount() > 0) + if (auto firstDeclInModule = moduleDecl->getFirstDirectMemberDecl()) { - auto firstMember = moduleDecl->members[0]; - if (as(firstMember)) + if (as(firstDeclInModule)) { if (!getShared()->isInLanguageServer()) { // A primary module file can't start with an "implementing" declaration. getSink()->diagnose( - firstMember, + firstDeclInModule, Diagnostics::primaryModuleFileCannotStartWithImplementingDecl); } } - else if (!as(firstMember)) + else if (!as(firstDeclInModule)) { // A primary module file must start with a `module` declaration. // TODO: this warning is disabled for now to free users from massive change for now. @@ -3613,9 +3633,9 @@ void SemanticsDeclVisitorBase::checkModule(ModuleDecl* moduleDecl) // files are parsed. auto visitIncludeDecls = [&](ContainerDecl* fileDecl) { - for (Index i = 0; i < fileDecl->members.getCount(); i++) + for (Index i = 0; i < fileDecl->getDirectMemberDeclCount(); i++) { - auto decl = fileDecl->members[i]; + auto decl = fileDecl->getDirectMemberDecl(i); if (auto includeDecl = as(decl)) { ensureDecl(includeDecl, DeclCheckState::DefinitionChecked); @@ -3631,9 +3651,9 @@ void SemanticsDeclVisitorBase::checkModule(ModuleDecl* moduleDecl) } }; visitIncludeDecls(moduleDecl); - for (Index i = 0; i < moduleDecl->members.getCount(); i++) + for (Index i = 0; i < moduleDecl->getDirectMemberDeclCount(); i++) { - if (auto fileDecl = as(moduleDecl->members[i])) + if (auto fileDecl = as(moduleDecl->getDirectMemberDecl(i))) visitIncludeDecls(fileDecl); } @@ -4101,8 +4121,8 @@ bool SemanticsVisitor::doesGenericSignatureMatchRequirement( // satisfying value to have the same number of members for it to be an // exact match. // - auto memberCount = requiredGenericDeclRef.getDecl()->members.getCount(); - if (satisfyingGenericDeclRef.getDecl()->members.getCount() != memberCount) + auto memberCount = requiredGenericDeclRef.getDecl()->getDirectMemberDeclCount(); + if (satisfyingGenericDeclRef.getDecl()->getDirectMemberDeclCount() != memberCount) return false; // We then want to check that pairwise members match, in order. @@ -4595,7 +4615,7 @@ GenericDecl* SemanticsVisitor::synthesizeGenericSignatureForRequirementWitness( // that reference those parametesr as arguments for the call expresison // that makes up the body. // - for (auto member : requiredMemberDeclRef.getDecl()->members) + for (auto member : requiredMemberDeclRef.getDecl()->getDirectMemberDecls()) { if (auto typeParamDeclBase = as(member)) { @@ -4670,25 +4690,24 @@ GenericDecl* SemanticsVisitor::synthesizeGenericSignatureForRequirementWitness( // from the original requirement decl. For example, we can simply apply declref substituion on // the original type constraint `U:IDerived` to get `UImpl : IDerived`. // - for (auto member : requiredMemberDeclRef.getDecl()->members) + for (auto constraintDecl : + requiredMemberDeclRef.getDecl()->getDirectMemberDeclsOfType()) { - if (auto constraintDecl = as(member)) - { - auto synConstraintDecl = m_astBuilder->create(); - synConstraintDecl->nameAndLoc = constraintDecl->getNameAndLoc(); - synConstraintDecl->parentDecl = synGenericDecl; + auto synConstraintDecl = m_astBuilder->create(); + synConstraintDecl->nameAndLoc = constraintDecl->getNameAndLoc(); + synConstraintDecl->parentDecl = synGenericDecl; - // For generic constraint Sub : Sup, we need to substitute them with - // synthesized generic parameters. - // - synConstraintDecl->sub = TypeExp((Type*)constraintDecl->sub.type->substitute( - m_astBuilder, - SubstitutionSet(partiallySpecializedRequiredGenericDeclRef))); - synConstraintDecl->sup = TypeExp((Type*)constraintDecl->sup.type->substitute( - m_astBuilder, - SubstitutionSet(partiallySpecializedRequiredGenericDeclRef))); - synGenericDecl->members.add(synConstraintDecl); - } + // For generic constraint Sub : Sup, we need to substitute them with + // synthesized generic parameters. + // + synConstraintDecl->sub = TypeExp((Type*)constraintDecl->sub.type->substitute( + m_astBuilder, + SubstitutionSet(partiallySpecializedRequiredGenericDeclRef))); + synConstraintDecl->sup = TypeExp((Type*)constraintDecl->sup.type->substitute( + m_astBuilder, + SubstitutionSet(partiallySpecializedRequiredGenericDeclRef))); + + synGenericDecl->addDirectMemberDecl(synConstraintDecl); } // Override generic pointer to point to the original generic container. @@ -5182,7 +5201,7 @@ bool SemanticsVisitor::trySynthesizeMethodRequirementWitness( { auto genericAppExpr = m_astBuilder->create(); genericAppExpr->functionExpr = synBase; - for (auto member : genericDeclRef->members) + for (auto member : genericDeclRef->getDirectMemberDecls()) { if (auto typeParamDecl = as(member)) { @@ -5421,48 +5440,44 @@ bool SemanticsVisitor::trySynthesizeConstructorRequirementWitness( SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); - for (auto member : context->parentDecl->members) + if (auto varDecl = context->parentDecl->findFirstDirectMemberDeclOfType()) { - if (auto varDecl = as(member)) - { - auto varExpr = m_astBuilder->create(); - varExpr->scope = ctorDecl->ownedScope; - varExpr->name = varDecl->getName(); - auto checkedVarExpr = CheckTerm(varExpr); - if (!checkedVarExpr) - return false; - if (as(checkedVarExpr->type.type)) - return false; - auto assign = m_astBuilder->create(); - assign->left = checkedVarExpr; - auto temp = m_astBuilder->create(); - auto lookupResult = lookUpMember( - m_astBuilder, - this, - ctorName, - varDecl->type.type, - ctorDecl->ownedScope, - LookupMask::Function, - LookupOptions::IgnoreBaseInterfaces); - temp->functionExpr = createLookupResultExpr( - ctorName, - lookupResult, - nullptr, - context->parentDecl->loc, - nullptr); - temp->arguments.addRange(synArgs); - auto resolvedVar = ResolveInvoke(temp); - if (!resolvedVar) - return false; - assign->right = resolvedVar; - assign->type = m_astBuilder->getVoidType(); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); + auto varExpr = m_astBuilder->create(); + varExpr->scope = ctorDecl->ownedScope; + varExpr->name = varDecl->getName(); + auto checkedVarExpr = CheckTerm(varExpr); + if (!checkedVarExpr) + return false; + if (as(checkedVarExpr->type.type)) + return false; + auto assign = m_astBuilder->create(); + assign->left = checkedVarExpr; + auto temp = m_astBuilder->create(); + auto lookupResult = lookUpMember( + m_astBuilder, + this, + ctorName, + varDecl->type.type, + ctorDecl->ownedScope, + LookupMask::Function, + LookupOptions::IgnoreBaseInterfaces); + temp->functionExpr = createLookupResultExpr( + ctorName, + lookupResult, + nullptr, + context->parentDecl->loc, + nullptr); + temp->arguments.addRange(synArgs); + auto resolvedVar = ResolveInvoke(temp); + if (!resolvedVar) + return false; + assign->right = resolvedVar; + assign->type = m_astBuilder->getVoidType(); + bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); - auto stmt = m_astBuilder->create(); - stmt->expression = assign; - seqStmt->stmts.add(stmt); - break; - } + auto stmt = m_astBuilder->create(); + stmt->expression = assign; + seqStmt->stmts.add(stmt); } } else if (synArgs.getCount()) @@ -6656,8 +6671,9 @@ bool SemanticsVisitor::trySynthesizeEnumTypeMethodRequirementWitness( synFunc->nameAndLoc.loc = synFunc->loc; // synFunc already has its parent set SLANG_ASSERT(context->parentDecl == synFunc->parentDecl); - context->parentDecl->addMember(synFunc); - context->parentDecl->invalidateMemberDictionary(); + + context->parentDecl->addDirectMemberDecl(synFunc); + addModifier(synFunc, intrinsicOpModifier); witnessTable->add( funcDeclRef.getDecl(), @@ -6751,15 +6767,12 @@ bool SemanticsVisitor::trySynthesizeDifferentialMethodRequirementWitness( auto varStmt = synth.emitVarDeclStmt(synFunc->returnType.type, getName("result")); auto resultVarExpr = synth.emitVarExpr(varStmt, synFunc->returnType.type); - for (auto member : context->parentDecl->members) + for (auto varMember : context->parentDecl->getDirectMemberDeclsOfType()) { - auto derivativeAttr = member->findModifier(); + auto derivativeAttr = varMember->findModifier(); if (!derivativeAttr) continue; - auto varMember = as(member); - if (!varMember) - continue; ensureDecl(varMember, DeclCheckState::ReadyForReference); auto memberType = varMember->getType(); auto diffMemberType = tryGetDifferentialType(m_astBuilder, memberType); @@ -6841,8 +6854,9 @@ bool SemanticsVisitor::trySynthesizeDifferentialMethodRequirementWitness( Decl* witnessDecl = synGeneric ? (Decl*)synGeneric : synFunc; SLANG_ASSERT(context->parentDecl == witnessDecl->parentDecl); - context->parentDecl->addMember(witnessDecl); - context->parentDecl->invalidateMemberDictionary(); + + context->parentDecl->addDirectMemberDecl(witnessDecl); + addModifier(synFunc, m_astBuilder->create()); // If `This` is nested inside a generic, we need to form a complete declref type to the @@ -8230,13 +8244,11 @@ void SemanticsDeclBasesVisitor::visitEnumDecl(EnumDecl* decl) if (auto enumTypeTypeInterfaceDecl = as(enumTypeTypeDeclRefType->getDeclRef().getDecl())) { - for (auto memberDecl : enumTypeTypeInterfaceDecl->members) + if (auto foundMemberDecl = + enumTypeTypeInterfaceDecl->findLastDirectMemberDeclOfName( + tagAssociatedTypeName)) { - if (memberDecl->getName() == tagAssociatedTypeName) - { - tagAssociatedTypeDecl = memberDecl; - break; - } + tagAssociatedTypeDecl = foundMemberDecl; } } } @@ -8469,7 +8481,7 @@ void SemanticsVisitor::getGenericParams( List& outParams, List& outConstraints) { - for (auto dd : decl->members) + for (auto dd : decl->getDirectMemberDecls()) { if (dd == decl->inner) continue; @@ -8772,7 +8784,7 @@ List getDefaultSubstitutionArgs( if (astBuilder->m_cachedGenericDefaultArgs.tryGetValue(genericDecl, args)) return args; - for (auto mm : genericDecl->members) + for (auto mm : genericDecl->getDirectMemberDecls()) { if (auto genericTypeParamDecl = as(mm)) { @@ -8801,35 +8813,33 @@ List getDefaultSubstitutionArgs( bool shouldCache = true; // create default substitution arguments for constraints - for (auto mm : genericDecl->members) + for (auto genericTypeConstraintDecl : + genericDecl->getDirectMemberDeclsOfType()) { - if (auto genericTypeConstraintDecl = as(mm)) + if (semantics) + semantics->ensureDecl(genericTypeConstraintDecl, DeclCheckState::ReadyForReference); + auto constraintDeclRef = + astBuilder->getDirectDeclRef(genericTypeConstraintDecl); + auto supType = getSup(astBuilder, constraintDeclRef); + if (!supType) { - if (semantics) - semantics->ensureDecl(genericTypeConstraintDecl, DeclCheckState::ReadyForReference); - auto constraintDeclRef = - astBuilder->getDirectDeclRef(genericTypeConstraintDecl); - auto supType = getSup(astBuilder, constraintDeclRef); - if (!supType) - { - args.add(astBuilder->getErrorType()); - shouldCache = false; - continue; - } - auto witness = astBuilder->getDeclaredSubtypeWitness( - getSub(astBuilder, constraintDeclRef), - getSup(astBuilder, constraintDeclRef), - constraintDeclRef); - // TODO: this is an ugly hack to prevent crashing. - // In early stages of compilation witness->sub and witness->sup may not be checked yet. - // When semanticVisitor is present we have used that to ensure the type is checked. - // However due to how the code is written we cannot guarantee semanticVisitor is always - // available here, and if we can't get the checked sup/sub type this subst is incomplete - // and should not be cached. - if (!witness->getSub()) - shouldCache = false; - args.add(witness); + args.add(astBuilder->getErrorType()); + shouldCache = false; + continue; } + auto witness = astBuilder->getDeclaredSubtypeWitness( + getSub(astBuilder, constraintDeclRef), + getSup(astBuilder, constraintDeclRef), + constraintDeclRef); + // TODO: this is an ugly hack to prevent crashing. + // In early stages of compilation witness->sub and witness->sup may not be checked yet. + // When semanticVisitor is present we have used that to ensure the type is checked. + // However due to how the code is written we cannot guarantee semanticVisitor is always + // available here, and if we can't get the checked sup/sub type this subst is incomplete + // and should not be cached. + if (!witness->getSub()) + shouldCache = false; + args.add(witness); } if (shouldCache) @@ -9219,9 +9229,9 @@ void SemanticsVisitor::checkForRedeclaration(Decl* decl) // We will now look for other declarations with // the same name in the same parent/container. // - parentDecl->buildMemberDictionary(); - for (auto oldDecl = newDecl->nextInContainerWithSameName; oldDecl; - oldDecl = oldDecl->nextInContainerWithSameName) + + for (auto oldDecl = parentDecl->getPrevDirectMemberDeclWithSameName(newDecl); oldDecl; + oldDecl = parentDecl->getPrevDirectMemberDeclWithSameName(oldDecl)) { // For each matching declaration, we will check // whether the redeclaration should be allowed, @@ -9497,20 +9507,20 @@ AssignExpr* SemanticsDeclBodyVisitor::createMemberAssignmentExpr( Expr* SemanticsDeclBodyVisitor::createCtorParamExpr(ConstructorDecl* ctor, Index paramIndex) { - if (paramIndex < ctor->members.getCount()) - { - if (auto param = as(ctor->members[paramIndex])) - { - auto paramType = param->getType(); - auto paramExpr = m_astBuilder->create(); - paramExpr->scope = ctor->ownedScope; - paramExpr->declRef = param; - paramExpr->type = paramType; - paramExpr->loc = param->loc; - return paramExpr; - } - } - return nullptr; + if (paramIndex >= ctor->getDirectMemberDeclCount()) + return nullptr; + + auto param = as(ctor->getDirectMemberDecl(paramIndex)); + if (!param) + return nullptr; + + auto paramType = param->getType(); + auto paramExpr = m_astBuilder->create(); + paramExpr->scope = ctor->ownedScope; + paramExpr->declRef = param; + paramExpr->type = paramType; + paramExpr->loc = param->loc; + return paramExpr; } void SemanticsDeclBodyVisitor::synthesizeCtorBodyForBases( @@ -9581,27 +9591,24 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBodyForBases( } } -void SemanticsDeclBodyVisitor::synthesizeCtorBodyForMember( +void SemanticsDeclBodyVisitor::synthesizeCtorBodyForMemberVar( ConstructorDecl* ctor, - Decl* member, + VarDeclBase* varDeclBase, ThisExpr* thisExpr, Dictionary& cachedDeclToCheckedVar, SeqStmt* seqStmtChild, bool isMemberInitCtor, Index& paramIndex) { - auto varDeclBase = as(member); - // Static variables are initialized at start of runtime, not inside a constructor // Once thing to notice is that if a member variable doesn't have name, it must be synthesized // instead of defined by user, we should not put it into the constructor because it's not a real // member. - if (!varDeclBase || varDeclBase->hasModifier() || - varDeclBase->getName() == nullptr) + if (varDeclBase->hasModifier() || varDeclBase->getName() == nullptr) return; Expr* initExpr = nullptr; - auto structDecl = as(member->parentDecl); + auto structDecl = as(varDeclBase->parentDecl); bool useParamList = isMemberInitCtor; useParamList = isMemberInitCtor && structDecl->m_membersVisibleInCtor.contains(varDeclBase); @@ -9630,21 +9637,21 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBodyForMember( } } - auto assign = createMemberAssignmentExpr(thisExpr, ctor->ownedScope, member, initExpr); + auto assign = createMemberAssignmentExpr(thisExpr, ctor->ownedScope, varDeclBase, initExpr); if (!assign) return; auto stmt = m_astBuilder->create(); stmt->expression = assign; - stmt->loc = member->loc; + stmt->loc = varDeclBase->loc; Expr* checkedMemberVarExpr; - if (cachedDeclToCheckedVar.containsKey(member)) - checkedMemberVarExpr = cachedDeclToCheckedVar[member]; + if (cachedDeclToCheckedVar.containsKey(varDeclBase)) + checkedMemberVarExpr = cachedDeclToCheckedVar[varDeclBase]; else { checkedMemberVarExpr = CheckTerm(assign->left); - cachedDeclToCheckedVar.add({member, checkedMemberVarExpr}); + cachedDeclToCheckedVar.add({varDeclBase, checkedMemberVarExpr}); } seqStmtChild->stmts.add(stmt); @@ -9667,29 +9674,25 @@ void SemanticsDeclBodyVisitor::maybeInsertDefaultInitExpr(StructDecl* structDecl thisExpr->scope = ctor->ownedScope; thisExpr->type = ctor->returnType.type; auto seqStmtChild = m_astBuilder->create(); - seqStmtChild->stmts.reserve(structDecl->members.getCount()); - for (auto& member : structDecl->members) + for (auto varDeclBase : structDecl->getDirectMemberDeclsOfType()) { - if (auto varDeclBase = as(member)) - { - if (varDeclBase->hasModifier() || - varDeclBase->getName() == nullptr || varDeclBase->initExpr == nullptr) - continue; + if (varDeclBase->hasModifier() || + varDeclBase->getName() == nullptr || varDeclBase->initExpr == nullptr) + continue; - auto assign = createMemberAssignmentExpr( - thisExpr, - ctor->ownedScope, - member, - varDeclBase->initExpr); - if (!assign) - continue; + auto assign = createMemberAssignmentExpr( + thisExpr, + ctor->ownedScope, + varDeclBase, + varDeclBase->initExpr); + if (!assign) + continue; - auto stmt = m_astBuilder->create(); - stmt->expression = assign; - stmt->loc = member->loc; - seqStmtChild->stmts.add(stmt); - } + auto stmt = m_astBuilder->create(); + stmt->expression = assign; + stmt->loc = varDeclBase->loc; + seqStmtChild->stmts.add(stmt); } if (seqStmtChild->stmts.getCount() != 0) { @@ -9709,8 +9712,6 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBody( { auto seqStmt = _ensureCtorBodyIsSeqStmt(m_astBuilder, ctor); auto seqStmtChild = m_astBuilder->create(); - seqStmtChild->stmts.reserve( - inheritanceDefaultCtorList.getCount() + structDecl->members.getCount()); ThisExpr* thisExpr = m_astBuilder->create(); thisExpr->scope = ctor->ownedScope; @@ -9737,11 +9738,11 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBody( ioParamIndex); // Then synthesize the initialization of the other members. - for (auto& m : structDecl->members) + for (auto directMemberVarDecl : structDecl->getDirectMemberDeclsOfType()) { - synthesizeCtorBodyForMember( + synthesizeCtorBodyForMemberVar( ctor, - m, + directMemberVarDecl, thisExpr, cachedDeclToCheckedVar, seqStmtChild, @@ -9792,12 +9793,9 @@ void SemanticsDeclBodyVisitor::visitAggTypeDecl(AggTypeDecl* aggTypeDecl) DeclRefType::create(m_astBuilder, structDecl), m_astBuilder->getDefaultInitializableType(), IsSubTypeOptions::None); - for (auto m : structDecl->members) + for (auto varDeclBase : structDecl->getDirectMemberDeclsOfType()) { - auto varDeclBase = as(m); - if (!varDeclBase) - continue; - ensureDecl(m->getDefaultDeclRef(), DeclCheckState::DefaultConstructorReadyForUse); + ensureDecl(varDeclBase->getDefaultDeclRef(), DeclCheckState::DefaultConstructorReadyForUse); if (!isDefaultInitializableType || varDeclBase->initExpr) continue; varDeclBase->initExpr = constructDefaultInitExprForType(this, varDeclBase); @@ -9811,9 +9809,9 @@ void SemanticsDeclBodyVisitor::visitAggTypeDecl(AggTypeDecl* aggTypeDecl) auto seqStmt = as(as(structDeclInfo.defaultCtor->body)->body); if (seqStmt && seqStmt->stmts.getCount() == 0) { - structDecl->members.remove(structDeclInfo.defaultCtor); - structDecl->invalidateMemberDictionary(); - structDecl->buildMemberDictionary(); + structDecl + ->_removeDirectMemberConstructorDeclBecauseSynthesizedAnotherDefaultConstructorInstead( + structDeclInfo.defaultCtor); } } } @@ -10225,12 +10223,22 @@ error:; void SemanticsDeclBasesVisitor::_validateExtensionDeclMembers(ExtensionDecl* decl) { - for (auto m : decl->members) + for (auto ctor : decl->getDirectMemberDeclsOfType()) { - auto ctor = as(m); - if (!ctor || !ctor->body || ctor->members.getCount() != 0) + // Note(tfoley): AFAICT, the logic here is enforcing that an + // `extension` declaration cannot introduce a default constructor, + // unless it introduces that default constructor without a body. + // + // If the constructor declaration is without a body, we allow it + // here, and if it has a non-zero number of direct member declarations + // (which this code is assuming will be equivalent to just the + // parameter declarations), we also allow it. + // + // The underlying rationale + + if (!ctor->body || ctor->getDirectMemberDeclCount() != 0) continue; - getSink()->diagnose(m->loc, Diagnostics::invalidMemberTypeInExtension, m->astNodeType); + getSink()->diagnose(ctor, Diagnostics::invalidMemberTypeInExtension, ctor->astNodeType); } } @@ -10861,18 +10869,22 @@ String getSimpleModuleName(Name* name) return String(slice.head(dotPos)); } -ModuleDeclarationDecl* findExistingModuleDeclarationDecl(ModuleDecl* decl) +ModuleDeclarationDecl* findExistingModuleDeclarationDecl(ModuleDecl* moduleDecl) { - if (decl->members.getCount() == 0) + auto firstMemberOfModule = moduleDecl->getFirstDirectMemberDecl(); + if (!firstMemberOfModule) return nullptr; - if (auto rs = as(decl->members[0])) - return rs; - for (auto fileDecl : decl->getMembersOfType()) + + if (auto found = as(firstMemberOfModule)) + return found; + + for (auto fileDecl : moduleDecl->getMembersOfType()) { - if (fileDecl->members.getCount() == 0) + auto firstMemberOfFile = fileDecl->getFirstDirectMemberDecl(); + if (!firstMemberOfFile) continue; - if (auto rs = as(fileDecl->members[0])) - return rs; + if (auto found = as(firstMemberOfFile)) + return found; } return nullptr; } @@ -10902,11 +10914,11 @@ void SemanticsDeclHeaderVisitor::visitIncludeDecl(IncludeDecl* decl) if (!isNew) return; - if (fileDecl->members.getCount() == 0) + auto firstDeclInFile = fileDecl->getFirstDirectMemberDecl(); + if (!firstDeclInFile) return; - auto firstMember = fileDecl->members[0]; - if (auto moduleDeclaration = as(firstMember)) + if (auto moduleDeclaration = as(firstDeclInFile)) { // We are trying to include a file that defines a module, the user could mean "import" // instead. @@ -10920,25 +10932,25 @@ void SemanticsDeclHeaderVisitor::visitIncludeDecl(IncludeDecl* decl) importFileDeclIntoScope(moduleDecl->ownedScope, fileDecl); - if (auto implementing = as(firstMember)) + if (auto implementing = as(firstDeclInFile)) { // The file we are including must be implementing the current module. auto moduleName = getSimpleModuleName(implementing->moduleNameAndLoc.name); auto expectedModuleName = moduleDecl->getName(); bool shouldSkipDiagnostic = false; - if (moduleDecl->members.getCount()) + if (auto firstDeclInModule = moduleDecl->getFirstDirectMemberDecl()) { - if (auto moduleDeclarationDecl = as(moduleDecl->members[0])) + if (auto moduleDeclarationDecl = as(firstDeclInModule)) { expectedModuleName = moduleDeclarationDecl->getName(); } else if (getShared()->isInLanguageServer()) { - auto moduleDeclarationDecls = findExistingModuleDeclarationDecl(moduleDecl); - if (moduleDeclarationDecls) + auto existingModuleDeclarationDecl = findExistingModuleDeclarationDecl(moduleDecl); + if (existingModuleDeclarationDecl) { - expectedModuleName = moduleDeclarationDecls->getName(); + expectedModuleName = existingModuleDeclarationDecl->getName(); } else { @@ -11020,17 +11032,18 @@ void SemanticsDeclScopeWiringVisitor::visitImplementingDecl(ImplementingDecl* de if (!isNew) return; - if (!fileDecl || fileDecl->members.getCount() == 0) - { + if (!fileDecl) + return; + + auto firstDeclInFile = fileDecl->getFirstDirectMemberDecl(); + if (!firstDeclInFile) return; - } - auto firstMember = fileDecl->members[0]; - if (as(firstMember)) + if (as(firstDeclInFile)) { // We are trying to implement a file that defines a module, this is expected. } - else if (as(firstMember)) + else if (as(firstDeclInFile)) { getSink()->diagnose( decl->moduleNameAndLoc.loc, @@ -11118,11 +11131,10 @@ void SemanticsDeclScopeWiringVisitor::visitNamespaceDecl(NamespaceDecl* decl) for (auto scope = parentScope; scope; scope = scope->nextSibling) { auto container = scope->containerDecl; - auto nsDeclPtr = container->getMemberDictionary().tryGetValue(decl->getName()); - if (!nsDeclPtr) + auto nsDecl = container->findLastDirectMemberDeclOfName(decl->getName()); + if (!nsDecl) continue; - auto nsDecl = *nsDeclPtr; - for (auto ns = nsDecl; ns; ns = ns->nextInContainerWithSameName) + for (auto ns = nsDecl; ns; ns = container->getPrevDirectMemberDeclWithSameName(ns)) { if (ns == decl) continue; @@ -11281,7 +11293,7 @@ void SharedSemanticsContext::registerCandidateExtension( } bool hasInheritanceMember = false; bool hasImplicitCastMember = false; - for (auto member : extDecl->members) + for (auto member : extDecl->getDirectMemberDecls()) { if (as(member)) { @@ -11829,6 +11841,95 @@ ContainerDecl* findDeclsLowestCommonAncestor(Decl*& a, Decl*& b) return a->parentDecl; } +static int _compareDeclsInCommonParentByOrderOfDeclaration( + ContainerDecl* commonParent, + Decl* lhs, + Decl* rhs) +{ + if (lhs == rhs) + return 0; + + SLANG_ASSERT(commonParent); + SLANG_ASSERT(lhs); + SLANG_ASSERT(rhs); + SLANG_ASSERT(lhs->parentDecl == commonParent); + SLANG_ASSERT(rhs->parentDecl == commonParent); + + // Without doing some kind of caching on `commonParent` (which + // would bloat the storage of every `ContainerDecl` just to + // support this one use case), the worst-cast complexity of + // this operation is always going to be linear in the number + // of declarations in `commonParent`. + // + // In an attempt to optimize for the common case, we will assume + // that one or the other of the declarations is near the beginning + // or end of the sequence of children. + // + Count childCount = commonParent->getDirectMemberDeclCount(); + Index loIndex = 0; + Index hiIndex = childCount - 1; + for (;;) + { + SLANG_ASSERT(loIndex < hiIndex); + + auto loDecl = commonParent->getDirectMemberDecl(loIndex); + auto hiDecl = commonParent->getDirectMemberDecl(hiIndex); + + // TODO(tfoley): There is a frustratingly subtle issue lurking + // in the Slang codebase, where operations (notably including + // the reflection API's `getTypeParameterConstraintType()`) + // assume that the a generic with N type parameters will have + // N constraints, with one constraint per type parameter, + // in a matching order. + // + // This assumption is fundamentally *not* guaranteed by the + // Slang compiler, but it has worked often enough in practice + // that code has ended up depending on this behavior. + // + // The long/short is that the ordering of the comparisons here + // (which cases return `-1` vs. `1`) is important for not + // breaking at least one Slang test, and may also have consequences + // for other code. + // + // We need to audit all of the code that is looking up constraints + // on generic declarations and trying to associate constraints + // with specific parameters and... well, in the ideal case *stop* + // them from doing so, because it isn't always a reasonable way + // to do things (e.g., there are constraints that would be missed + // if code only pays attention to constraints that have one of + // the generic parameters on their left-hand-side). Given that + // we probably can't fully deprecate things like the reflection + // API operations that rely on this assumption, we should instead + // author an as-correct-as-possible version of things that, given + // the index of a generic (type) parameter, collects all of the + // (conformance) constraints with that parameter on the left-hand-side + // and forms a conjunction of the interfaces on the right-hand-side. + // + // (Alternatively we could separate the storage of the original + // parameters/constraints as they were declared, vs. the canonicalized + // constraints, and only have the reflection API access the former) + + if (loDecl == lhs) + return 1; + if (loDecl == rhs) + return -1; + + if (hiDecl == lhs) + return -1; + if (hiDecl == rhs) + return 1; + + loIndex++; + hiIndex--; + + if (loIndex >= hiIndex) + { + SLANG_UNEXPECTED("failed to find lhs or rhs in common parent"); + UNREACHABLE_RETURN(0); + } + } +} + int compareDecls(Decl& lhs, Decl& rhs) { int res = compareThreeWays(lhs.astNodeType, rhs.astNodeType); @@ -11836,9 +11937,36 @@ int compareDecls(Decl& lhs, Decl& rhs) return res; Decl* lLCAChild = &lhs; Decl* rLCAChild = &rhs; + + // TODO(tfoley): This logic implements rules that provide very + // little stability of generated mangled names, in cases where + // declarations might ever get reordered. + // + // A more flexible solution would favor comparing declarations + // by name whenever possible, along the lines of: + // + // * For each declaration identify it's deepest ancestor that + // has a meaningful stable name (anything like a module, + // namespace, type, function, etc. and not anything like + // a parameter, local declaration, etc.) + // + // * Compare those deepest ancestors by name lexicographically, + // from the root down. If the module names differ, that's + // enough to go by. If they are in the same moduel but in + // different types, then use that. If one path is a prefix + // of the other, then that establishes an ordering. + // + // * Only if the above wasn't enough to disambiguate things would + // we fall back to anthing based on order of declaration, and + // even *then* we would probably want to have some safety rails + // in place to identify what kinds of declarations are forcing + // us down that path, and ensuring that they are kinds of + // declarations we explicitly need/want to support here (e.g., + // generic parameters). + // if (ContainerDecl* lca = findDeclsLowestCommonAncestor(lLCAChild, rLCAChild)) { - res = compareThreeWays(lca->getDeclIndex(lLCAChild), lca->getDeclIndex(rLCAChild)); + res = _compareDeclsInCommonParentByOrderOfDeclaration(lca, lLCAChild, rLCAChild); } else { @@ -12078,7 +12206,7 @@ void checkDerivativeAttributeImpl( auto appExpr = ctx.getASTBuilder()->create(); Index count = 0; - for (auto member : genericDecl->members) + for (auto member : genericDecl->getDirectMemberDecls()) { if (as(member) || as(member) || as(member)) @@ -13131,7 +13259,8 @@ bool SemanticsDeclAttributesVisitor::_synthesizeCtorSignature(StructDecl* struct ConstructorDecl* ctor = createCtor(structDecl, ctorVisibility); ctor->addFlavor(ConstructorDecl::ConstructorFlavor::SynthesizedMemberInit); - ctor->members.reserve(resultMembers.getCount()); + List ctorParamsBuiltInReverseOrder; + ctorParamsBuiltInReverseOrder.reserve(resultMembers.getCount()); // 2. Add the parameter list bool stopProcessingDefaultValues = false; @@ -13163,7 +13292,7 @@ bool SemanticsDeclAttributesVisitor::_synthesizeCtorSignature(StructDecl* struct ctorParam->nameAndLoc = NameLoc(paramName, ctor->loc); ctorParam->loc = ctor->loc; - ctor->members.add(ctorParam); + ctorParamsBuiltInReverseOrder.add(ctorParam); // We need to ensure member is `no_diff` if it cannot be differentiated, `ctor` // modifiers do not matter in this case since member-wise ctor is always differentiable @@ -13175,7 +13304,11 @@ bool SemanticsDeclAttributesVisitor::_synthesizeCtorSignature(StructDecl* struct addModifier(ctorParam, noDiffMod); } } - ctor->members.reverse(); + ctorParamsBuiltInReverseOrder.reverse(); + for (auto ctorParam : ctorParamsBuiltInReverseOrder) + { + ctor->addDirectMemberDecl(ctorParam); + } return true; } @@ -13247,20 +13380,22 @@ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl) } const auto backingMemberIndex = groupInfo[0].memberIndex; - structDecl->members.insert(backingMemberIndex, backingMember); - structDecl->invalidateMemberDictionary(); + + structDecl->_insertDirectMemberDeclAtIndexForBitfieldPropertyBackingMember( + backingMemberIndex, + backingMember); + ++memberIndex; } - structDecl->buildMemberDictionary(); // Reset everything backingWidth = 0; totalWidth = 0; groupInfo.clear(); }; - for (; memberIndex < structDecl->members.getCount(); ++memberIndex) + for (; memberIndex < structDecl->getDirectMemberDeclCount(); ++memberIndex) { - const auto& m = structDecl->members[memberIndex]; + const auto& m = structDecl->getDirectMemberDecl(memberIndex); // We can trivially skip any non-property decls const auto v = as(m); @@ -13737,7 +13872,7 @@ static inline void _dispatchCapabilitiesVisitorOfFunctionDecl( { visitor->setParentFuncOfVisitor(funcDecl); - for (auto member : funcDecl->members) + for (auto member : funcDecl->getDirectMemberDecls()) { visitor->ensureDecl(member, DeclCheckState::CapabilityChecked); _propagateRequirement( diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index f4c8cd84792..a530eb8f09f 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -455,21 +455,24 @@ DeclRefExpr* SemanticsVisitor::ConstructDeclRefExpr( // Another exception is if we are accessing a property // that provides a [nonmutating] setter. - if (!expr->type.isLeftValue && as(declRef.getDecl())) + if (!expr->type.isLeftValue) { - bool isLValue = false; - for (auto member : as(declRef.getDecl())->members) + if (auto propertyDecl = as(declRef.getDecl())) { - if (as(member) || as(member)) + bool isLValue = false; + for (auto member : propertyDecl->getDirectMemberDeclsOfType()) { - if (member->findModifier()) + if (as(member) || as(member)) { - isLValue = true; + if (member->findModifier()) + { + isLValue = true; + } + break; } - break; } + expr->type.isLeftValue = isLValue; } - expr->type.isLeftValue = isLValue; } } else @@ -479,7 +482,7 @@ DeclRefExpr* SemanticsVisitor::ConstructDeclRefExpr( if (auto propertyDecl = as(declRef.getDecl())) { bool isLValue = false; - for (auto member : propertyDecl->members) + for (auto member : propertyDecl->getDirectMemberDeclsOfType()) { if (as(member) || as(member)) { @@ -681,12 +684,11 @@ Expr* SemanticsVisitor::maybeUseSynthesizedDeclForLookupResult( createDefaultSubstitutionsIfNeeded(m_astBuilder, this, makeDeclRef(structDecl)); typeDef->type.type = DeclRefType::create(m_astBuilder, synthDeclRef); - structDecl->members.add(typeDef); + structDecl->addDirectMemberDecl(typeDef); synthesizedDecl->nameAndLoc.name = item.declRef.getName(); synthesizedDecl->loc = parent->loc; - parent->addMember(synthesizedDecl); - parent->invalidateMemberDictionary(); + parent->addDirectMemberDecl(synthesizedDecl); // Mark the newly synthesized decl as `ToBeSynthesized` so future checking can // differentiate it from user-provided definitions, and proceed to fill in its @@ -711,8 +713,7 @@ Expr* SemanticsVisitor::maybeUseSynthesizedDeclForLookupResult( synthesizedDecl = parent; - parent->addMember(typeDef); - parent->invalidateMemberDictionary(); + parent->addDirectMemberDecl(typeDef); markSelfDifferentialMembersOfType(parent, subType); } @@ -1293,17 +1294,14 @@ bool SemanticsVisitor::canStructBeUsedAsSelfDifferentialType(AggTypeDecl* aggTyp // and their differential types are the same as the original types. // bool canBeUsed = true; - for (auto member : aggTypeDecl->members) + for (auto varDecl : aggTypeDecl->getDirectMemberDeclsOfType()) { - if (auto varDecl = as(member)) + // Try to get the differential type of the member. + Type* diffType = tryGetDifferentialType(getASTBuilder(), varDecl->getType()); + if (!diffType || !diffType->equals(varDecl->getType())) { - // Try to get the differential type of the member. - Type* diffType = tryGetDifferentialType(getASTBuilder(), varDecl->getType()); - if (!diffType || !diffType->equals(varDecl->getType())) - { - canBeUsed = false; - break; - } + canBeUsed = false; + break; } } return canBeUsed; @@ -4290,7 +4288,7 @@ Expr* SemanticsExprVisitor::visitLambdaExpr(LambdaExpr* lambdaExpr) { nameBuilder << getText(m_parentFunc->getName()); nameBuilder << "_"; - nameBuilder << m_parentFunc->members.getCount(); + nameBuilder << m_parentFunc->getDirectMemberDeclCount(); } auto name = getName(nameBuilder.getBuffer()); lambdaStructDecl->nameAndLoc.name = name; @@ -4315,7 +4313,7 @@ Expr* SemanticsExprVisitor::visitLambdaExpr(LambdaExpr* lambdaExpr) synthesizer.popScope(); funcDecl->body = lambdaExpr->bodyStmt; - for (auto param : lambdaExpr->paramScopeDecl->members) + for (auto param : lambdaExpr->paramScopeDecl->getDirectMemberDecls()) { funcDecl->addMember(param); } diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp index e5249ac88cc..1d38fa802c8 100644 --- a/source/slang/slang-check-modifier.cpp +++ b/source/slang/slang-check-modifier.cpp @@ -273,20 +273,17 @@ AttributeDecl* SemanticsVisitor::lookUpAttributeDecl(Name* attributeName, Scope* // // TODO: This step should skip `static` fields. // - for (auto member : structDecl->members) + for (auto varMember : structDecl->getDirectMemberDeclsOfType()) { - if (auto varMember = as(member)) - { - ensureDecl(varMember, DeclCheckState::CanUseTypeOfValueDecl); + ensureDecl(varMember, DeclCheckState::CanUseTypeOfValueDecl); - ParamDecl* paramDecl = m_astBuilder->create(); - paramDecl->nameAndLoc = member->nameAndLoc; - paramDecl->type = varMember->type; - paramDecl->loc = member->loc; - paramDecl->setCheckState(DeclCheckState::DefinitionChecked); + ParamDecl* paramDecl = m_astBuilder->create(); + paramDecl->nameAndLoc = varMember->nameAndLoc; + paramDecl->type = varMember->type; + paramDecl->loc = varMember->loc; + paramDecl->setCheckState(DeclCheckState::DefinitionChecked); - attrDecl->addMember(paramDecl); - } + attrDecl->addMember(paramDecl); } // We need to end by putting the new attribute declaration @@ -298,8 +295,6 @@ AttributeDecl* SemanticsVisitor::lookUpAttributeDecl(Name* attributeName, Scope* // parentDecl->addMember(attrDecl); - SLANG_ASSERT(!parentDecl->isMemberDictionaryValid()); - // Finally, we perform any required semantic checks on // the newly constructed attribute decl. // @@ -1660,11 +1655,17 @@ Modifier* SemanticsVisitor::checkModifier( auto checkedAttr = checkAttribute(hlslUncheckedAttribute, syntaxNode); - if (as(checkedAttr)) + if (auto unscopedEnumAttr = as(checkedAttr)) { - if (auto parentDecl = as(getParentDecl(as(syntaxNode)))) - parentDecl->invalidateMemberDictionary(); - return getASTBuilder()->create(); + auto transparentModifier = getASTBuilder()->create(); + if (auto parentDecl = getParentDecl(as(syntaxNode))) + { + parentDecl + ->_invalidateLookupAcceleratorsBecauseUnscopedEnumAttributeWillBeTurnedIntoTransparentModifier( + unscopedEnumAttr, + transparentModifier); + } + return transparentModifier; } return checkedAttr; } @@ -1940,7 +1941,7 @@ Modifier* SemanticsVisitor::checkModifier( // specialization constant with this ID. Int specConstId = cintVal->getValue(); - for (auto member : decl->parentDecl->members) + for (auto member : decl->parentDecl->getDirectMemberDecls()) { auto constantId = member->findModifier(); if (constantId) @@ -2215,14 +2216,8 @@ void SemanticsVisitor::checkRayPayloadStructFields(StructDecl* structDecl) const HashSet validStages("anyhit", "closesthit", "miss", "caller"); // Check each field in the struct - for (auto member : structDecl->members) + for (auto fieldVarDecl : structDecl->getDirectMemberDeclsOfType()) { - auto fieldVarDecl = as(member); - if (!fieldVarDecl) - { - continue; - } - auto readModifier = fieldVarDecl->findModifier(); auto writeModifier = fieldVarDecl->findModifier(); diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index f13f9a99e5d..8fbdceabb6d 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -71,7 +71,7 @@ SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters( SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(DeclRef genericRef) { ParamCounts counts = {0, 0}; - for (auto m : genericRef.getDecl()->members) + for (auto m : genericRef.getDecl()->getDirectMemberDecls()) { if (auto typeParam = as(m)) { @@ -1169,9 +1169,9 @@ Expr* SemanticsVisitor::CompleteOverloadCandidate( if (auto subscriptDeclRef = candidate.item.declRef.as()) { const auto& decl = subscriptDeclRef.getDecl(); - for (auto member : decl->members) + for (auto accessorDecl : decl->getDirectMemberDeclsOfType()) { - if (as(member) || as(member)) + if (as(accessorDecl) || as(accessorDecl)) { // If the subscript decl has a setter, // then the call is an l-value if base is l-value. @@ -1186,7 +1186,7 @@ Expr* SemanticsVisitor::CompleteOverloadCandidate( // Otherwise, if the accessor is [nonmutating], we can // also consider the result of the subscript call as l-value // regardless of the base. - if (member->findModifier()) + if (accessorDecl->findModifier()) { callExpr->type.isLeftValue = true; break; diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index a57e01a88e1..e6744071b4e 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -156,7 +156,7 @@ void EntryPoint::_collectGenericSpecializationParamsRec(Decl* decl) if (!genericDecl) return; - for (auto m : genericDecl->members) + for (auto m : genericDecl->getDirectMemberDecls()) { if (auto genericTypeParam = as(m)) { @@ -774,7 +774,7 @@ void Module::_collectShaderParams() for (Index i = 0; i < workList.getCount(); i++) { auto moduleDecl = workList[i]; - for (auto globalDecl : moduleDecl->members) + for (auto globalDecl : moduleDecl->getDirectMemberDecls()) { if (auto globalVar = as(globalDecl)) { diff --git a/source/slang/slang-check-stmt.cpp b/source/slang/slang-check-stmt.cpp index 9d1899462da..faf25b716c4 100644 --- a/source/slang/slang-check-stmt.cpp +++ b/source/slang/slang-check-stmt.cpp @@ -76,10 +76,9 @@ void SemanticsStmtVisitor::visitBlockStmt(BlockStmt* stmt) // Make sure to fully check all nested agg type decls first. if (stmt->scopeDecl) { - for (auto decl : stmt->scopeDecl->members) + for (auto aggDecl : stmt->scopeDecl->getDirectMemberDeclsOfType()) { - if (as(decl)) - ensureAllDeclsRec(decl, DeclCheckState::DefinitionChecked); + ensureAllDeclsRec(aggDecl, DeclCheckState::DefinitionChecked); } // Consider this code: diff --git a/source/slang/slang-check-type.cpp b/source/slang/slang-check-type.cpp index b5f5240a5e4..bdf9c829aad 100644 --- a/source/slang/slang-check-type.cpp +++ b/source/slang/slang-check-type.cpp @@ -294,7 +294,7 @@ bool SemanticsVisitor::CoerceToProperTypeImpl( ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric); List args; List witnessArgs; - for (Decl* member : genericDeclRef.getDecl()->members) + for (Decl* member : genericDeclRef.getDecl()->getDirectMemberDecls()) { if (auto typeParam = as(member)) { @@ -333,43 +333,36 @@ bool SemanticsVisitor::CoerceToProperTypeImpl( } } - for (Decl* member : genericDeclRef.getDecl()->members) + for (auto constraintParam : + genericDeclRef.getDecl()->getDirectMemberDeclsOfType()) { - if (auto constraintParam = as(member)) + auto genericParam = as(constraintParam->sub.type)->getDeclRef(); + if (!genericParam) + return false; + auto genericTypeParamDecl = as(genericParam.getDecl()); + if (!genericTypeParamDecl) { - auto genericParam = as(constraintParam->sub.type)->getDeclRef(); - if (!genericParam) - return false; - auto genericTypeParamDecl = as(genericParam.getDecl()); - if (!genericTypeParamDecl) - { - diagSink->diagnose(typeExp.exp, Diagnostics::genericTypeNeedsArgs, typeExp); - return false; - } - auto defaultType = CheckProperType(genericTypeParamDecl->initType); - if (!defaultType) - { - diagSink->diagnose(typeExp.exp, Diagnostics::genericTypeNeedsArgs, typeExp); - return false; - } - auto witness = - tryGetSubtypeWitness(defaultType, CheckProperType(constraintParam->sup)); - if (!witness) - { - // diagnose - getSink()->diagnose( - genericTypeParamDecl->initType.exp, - Diagnostics::typeArgumentDoesNotConformToInterface, - defaultType, - constraintParam->sup); - return false; - } - witnessArgs.add(witness); + diagSink->diagnose(typeExp.exp, Diagnostics::genericTypeNeedsArgs, typeExp); + return false; + } + auto defaultType = CheckProperType(genericTypeParamDecl->initType); + if (!defaultType) + { + diagSink->diagnose(typeExp.exp, Diagnostics::genericTypeNeedsArgs, typeExp); + return false; } - else + auto witness = tryGetSubtypeWitness(defaultType, CheckProperType(constraintParam->sup)); + if (!witness) { - // ignore non-parameter members + // diagnose + getSink()->diagnose( + genericTypeParamDecl->initType.exp, + Diagnostics::typeArgumentDoesNotConformToInterface, + defaultType, + constraintParam->sup); + return false; } + witnessArgs.add(witness); } // Combine args and witnessArgs args.addRange(witnessArgs); diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 4e114eb78c0..8cb50c1e99c 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -2942,7 +2942,7 @@ void Module::_discoverEntryPointsImpl( DiagnosticSink* sink, const List>& targets) { - for (auto globalDecl : containerDecl->members) + for (auto globalDecl : containerDecl->getDirectMemberDecls()) { auto maybeFuncDecl = globalDecl; if (auto genericDecl = as(maybeFuncDecl)) diff --git a/source/slang/slang-doc-ast.cpp b/source/slang/slang-doc-ast.cpp index 7e83d5d592d..e6259167be4 100644 --- a/source/slang/slang-doc-ast.cpp +++ b/source/slang/slang-doc-ast.cpp @@ -76,7 +76,7 @@ static void _addDeclRec(Decl* decl, List& outDecls) { // Add the container - which could be a class, struct, enum, namespace, extension, generic // etc. Now add what the container contains - for (Decl* childDecl : containerDecl->members) + for (Decl* childDecl : containerDecl->getDirectMemberDecls()) { _addDeclRec(childDecl, outDecls); } @@ -85,7 +85,7 @@ static void _addDeclRec(Decl* decl, List& outDecls) /* static */ void ASTMarkupUtil::findDecls(ModuleDecl* moduleDecl, List& outDecls) { - for (Decl* decl : moduleDecl->members) + for (Decl* decl : moduleDecl->getDirectMemberDecls()) { _addDeclRec(decl, outDecls); } diff --git a/source/slang/slang-doc-markdown-writer.cpp b/source/slang/slang-doc-markdown-writer.cpp index f9d615ddb97..2a9a833616e 100644 --- a/source/slang/slang-doc-markdown-writer.cpp +++ b/source/slang/slang-doc-markdown-writer.cpp @@ -17,12 +17,9 @@ namespace Slang template static void _getDecls(ContainerDecl* containerDecl, List& out) { - for (Decl* decl : containerDecl->members) + for (auto decl : containerDecl->getDirectMemberDeclsOfType()) { - if (T* declAsType = as(decl)) - { - out.add(declAsType); - } + out.add(decl); } } @@ -32,7 +29,7 @@ static void _getDeclsOfType( ContainerDecl* containerDecl, List& out) { - for (Decl* decl : containerDecl->members) + for (Decl* decl : containerDecl->getDirectMemberDecls()) { if (as(decl)) { @@ -385,24 +382,21 @@ DocMarkdownWriter::NameAndText DocMarkdownWriter::_getNameAndText( else if (auto typeParam = as(decl)) { bool isFirst = true; - for (auto member : decl->parentDecl->members) + for (auto constraint : decl->parentDecl->getDirectMemberDeclsOfType()) { - if (auto constraint = as(member)) + if (isDeclRefTypeOf(getSub(m_astBuilder, constraint)).getDecl() == typeParam) { - if (isDeclRefTypeOf(getSub(m_astBuilder, constraint)).getDecl() == typeParam) + if (isFirst) { - if (isFirst) - { - sb << ": "; - isFirst = false; - } - else - { - sb << ", "; - } - sb << constraint->getSup().type->toString(); - break; + sb << ": "; + isFirst = false; + } + else + { + sb << ", "; } + sb << constraint->getSup().type->toString(); + break; } } if (auto genericTypeParam = as(decl)) @@ -634,20 +628,24 @@ void DocMarkdownWriter::writeProperty(const ASTMarkup::Entry& entry, PropertyDec propertyDecl->type->toText(typeSB); out << translateToHTMLWithLinks(propertyDecl, typeSB.produceString()); out << "\n{\n"; - for (auto member : propertyDecl->members) + for (auto accessor : propertyDecl->getDirectMemberDeclsOfType()) { - if (as(member)) + if (as(accessor)) { out << " get;\n"; } - else if (as(member)) + else if (as(accessor)) { out << " set;\n"; } - else if (as(member)) + else if (as(accessor)) { out << " ref;\n"; } + else + { + SLANG_UNEXPECTED("unhandled accessor type"); + } } out << "}\n\n\n"; @@ -792,7 +790,7 @@ void DocMarkdownWriter::writeExtensionConditions( if (auto targetTypeParentGenericDecl = as(targetTypeDeclRef.getDecl()->parentDecl)) { - for (auto member : targetTypeParentGenericDecl->members) + for (auto member : targetTypeParentGenericDecl->getDirectMemberDecls()) { if (auto typeParamDecl = as(member)) { @@ -827,20 +825,19 @@ void DocMarkdownWriter::writeExtensionConditions( // `T`. // Find constraints on the originalParamDecl. - for (auto member : genericParamDecl->parentDecl->members) + for (auto typeConstraint : + genericParamDecl->parentDecl + ->getDirectMemberDeclsOfType()) { - if (auto typeConstraint = as(member)) + if (isDeclRefTypeOf(typeConstraint->sub.type).getDecl() == + genericParamDecl) { - if (isDeclRefTypeOf(typeConstraint->sub.type).getDecl() == - genericParamDecl) + if (typeConstraint->isEqualityConstraint) { - if (typeConstraint->isEqualityConstraint) - { - isEqualityConstraint = true; - } - constraintVal = typeConstraint->getSup().type; - break; + isEqualityConstraint = true; } + constraintVal = typeConstraint->getSup().type; + break; } } } @@ -982,22 +979,20 @@ void DocMarkdownWriter::writeSignature(CallableDecl* callableDecl) if (auto genericParent = as(parentDecl->parentDecl)) { - for (auto member : genericParent->members) + for (auto typeConstraint : + genericParent->getDirectMemberDeclsOfType()) { - if (auto typeConstraint = as(member)) - { - out << toSlice("\n where "); - out << translateToHTMLWithLinks( - parentDecl, - getSub(m_astBuilder, typeConstraint)->toString()); - if (typeConstraint->isEqualityConstraint) - out << " == "; - else - out << toSlice(" : "); - out << translateToHTMLWithLinks( - parentDecl, - getSup(m_astBuilder, typeConstraint)->toString()); - } + out << toSlice("\n where "); + out << translateToHTMLWithLinks( + parentDecl, + getSub(m_astBuilder, typeConstraint)->toString()); + if (typeConstraint->isEqualityConstraint) + out << " == "; + else + out << toSlice(" : "); + out << translateToHTMLWithLinks( + parentDecl, + getSup(m_astBuilder, typeConstraint)->toString()); } } parentDecl = getParentDecl(parentDecl); @@ -1238,29 +1233,6 @@ void DocMarkdownWriter::_maybeAppendRequirements( out << toSlice("\n"); } -static Decl* _getSameNameDecl(ContainerDecl* parentDecl, Decl* decl) -{ - Decl* result = nullptr; - parentDecl->getMemberDictionary().tryGetValue(decl->getName(), result); - return result; -} - -static bool _isFirstOverridden(Decl* decl) -{ - decl = _getSameNameDecl(as(getParentDecl(decl)), decl); - - ContainerDecl* parentDecl = decl->parentDecl; - - Name* declName = decl->getName(); - if (declName) - { - Decl** firstDeclPtr = parentDecl->getMemberDictionary().tryGetValue(declName); - return (firstDeclPtr && *firstDeclPtr == decl) || (firstDeclPtr == nullptr); - } - - return false; -} - void ParsedDescription::write(DocMarkdownWriter* writer, Decl* decl, StringBuilder& out) { for (auto span : spans) @@ -1550,12 +1522,10 @@ void DocMarkdownWriter::writeCallableOverridable( { for (auto& entry : page->entries) { - Decl* sameNameDecl = _getSameNameDecl( - as(getParentDecl((Decl*)entry->m_node)), - callableDecl); + auto entryDecl = (Decl*)entry->m_node; + auto parentDecl = getParentDecl(entryDecl); - for (Decl* curDecl = sameNameDecl; curDecl; - curDecl = curDecl->nextInContainerWithSameName) + for (auto curDecl : parentDecl->getDirectMemberDeclsOfName(entryDecl->getName())) { CallableDecl* sig = nullptr; if (GenericDecl* genericDecl = as(curDecl)) @@ -1643,7 +1613,7 @@ void DocMarkdownWriter::writeCallableOverridable( // associated with this callable. if (genericDecl) { - for (Decl* decl : genericDecl->members) + for (Decl* decl : genericDecl->getDirectMemberDecls()) { if (as(decl) || as(decl)) { @@ -1893,15 +1863,13 @@ void DocMarkdownWriter::writeAggType( baseTypes = _getAsStringList(inheritanceDecls); for (auto entry : page->entries) { - for (auto member : as(entry->m_node)->members) + for (auto inheritanceDecl : + as(entry->m_node)->getDirectMemberDeclsOfType()) { - if (auto inheritanceDecl = as(member)) + if (auto extDecl = as(entry->m_node)) { - if (auto extDecl = as(entry->m_node)) - { - conditionalConformanceExts.add(extDecl); - conditionalBaseTypes.add(inheritanceDecl->base->toString()); - } + conditionalConformanceExts.add(extDecl); + conditionalBaseTypes.add(inheritanceDecl->base->toString()); } } } @@ -2016,11 +1984,8 @@ void DocMarkdownWriter::writeAggType( out << "## Conditional Conformances\n\n"; for (auto ext : conditionalConformanceExts) { - for (auto member : ext->members) + for (auto inheritanceDecl : ext->getDirectMemberDeclsOfType()) { - auto inheritanceDecl = as(member); - if (!inheritanceDecl) - continue; out << "### Conformance to "; out << escapeMarkdownText(inheritanceDecl->base.type->toString()); out << "\n"; @@ -2362,7 +2327,7 @@ void DeclDocumentation::writeGenericParameters( // The parameters, in order List params; - for (Decl* member : genericDecl->members) + for (Decl* member : genericDecl->getDirectMemberDecls()) { if (as(member) || as(member)) { @@ -2449,12 +2414,9 @@ void DocMarkdownWriter::createPage(ASTMarkup::Entry& entry, Decl* decl) return; } - if (CallableDecl* callableDecl = as(decl)) + if (as(decl)) { - if (_isFirstOverridden(callableDecl)) - { - ensureDeclPageCreated(entry); - } + ensureDeclPageCreated(entry); } else if (as(decl)) { @@ -2538,7 +2500,7 @@ DocumentPage* DocMarkdownWriter::findPageForToken( continue; if (auto genericParent = as(containerDecl->parentDecl)) { - for (auto member : genericParent->members) + for (auto member : genericParent->getDirectMemberDecls()) { if (getText(member->getName()) == token) { @@ -2551,7 +2513,7 @@ DocumentPage* DocMarkdownWriter::findPageForToken( } } } - for (auto member : containerDecl->members) + for (auto member : containerDecl->getDirectMemberDecls()) { if (as(member) || as(member)) { diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp index 53a9f81b97c..6c5935992b1 100644 --- a/source/slang/slang-language-server-ast-lookup.cpp +++ b/source/slang/slang-language-server-ast-lookup.cpp @@ -835,7 +835,7 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node) } if (shouldInspectChildren) { - for (auto member : container->members) + for (auto member : container->getDirectMemberDecls()) { if (_findAstNodeImpl(context, member)) return true; diff --git a/source/slang/slang-language-server-document-symbols.cpp b/source/slang/slang-language-server-document-symbols.cpp index e8a041ca86b..d5335a3efeb 100644 --- a/source/slang/slang-language-server-document-symbols.cpp +++ b/source/slang/slang-language-server-document-symbols.cpp @@ -139,7 +139,7 @@ static void _getDocumentSymbolsImpl( if (!context.processedDecls.add(parent)) return; auto srcManager = context.linkage->getSourceManager(); - for (auto child : containerDecl->members) + for (auto child : containerDecl->getDirectMemberDecls()) { if (auto genericDecl = as(child)) { diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp index 1813fa95322..59a9854f098 100644 --- a/source/slang/slang-language-server.cpp +++ b/source/slang/slang-language-server.cpp @@ -526,21 +526,24 @@ void appendDefinitionLocation(StringBuilder& sb, Workspace* workspace, const Hum HumaneSourceLoc getModuleLoc(SourceManager* manager, ContainerDecl* moduleDecl) { - if (moduleDecl) - { - if (moduleDecl->members.getCount() && moduleDecl->members[0]) - { - auto loc = moduleDecl->members[0]->loc; - if (loc.isValid()) - { - auto location = manager->getHumaneLoc(loc, SourceLocType::Actual); - location.line = 1; - location.column = 1; - return location; - } - } - } - return HumaneSourceLoc(); + if (!moduleDecl) + return HumaneSourceLoc(); + + if (moduleDecl->getDirectMemberDeclCount() == 0) + return HumaneSourceLoc(); + + auto firstDecl = moduleDecl->getDirectMemberDecl(0); + if (!firstDecl) + return HumaneSourceLoc(); + + auto loc = firstDecl->loc; + if (!loc.isValid()) + return HumaneSourceLoc(); + + auto location = manager->getHumaneLoc(loc, SourceLocType::Actual); + location.line = 1; + location.column = 1; + return location; } SlangResult LanguageServer::hover( diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp index a2284eaa842..d03d763b820 100644 --- a/source/slang/slang-lookup.cpp +++ b/source/slang/slang-lookup.cpp @@ -189,7 +189,7 @@ static void _lookUpDirectAndTransparentMembers( { // If we are looking up for completion suggestions, // return all the members that are available. - for (auto member : containerDecl->members) + for (auto member : containerDecl->getDirectMemberDecls()) { if (!request.shouldConsiderAllLocalNames() && _isUncheckedLocalVar(member)) continue; @@ -204,15 +204,13 @@ static void _lookUpDirectAndTransparentMembers( } else { - // Look up the declarations with the chosen name in the container. - Decl* firstDecl = nullptr; - containerDecl->getMemberDictionary().tryGetValue(name, firstDecl); - - // Now iterate over those declarations (if any) and see if - // we find any that meet our filtering criteria. + // Iterate over the declarations with the chosen name in the container + // (if any), and see if we find any that meet our filtering criteria. + // // For example, we might be filtering so that we only consider // type declarations. - for (auto m = firstDecl; m; m = m->nextInContainerWithSameName) + // + for (auto m : containerDecl->getDirectMemberDeclsOfName(name)) { // Skip this declaration if we are checking and this hasn't been // checked yet. Because we traverse block statements in order, if @@ -247,12 +245,12 @@ static void _lookUpDirectAndTransparentMembers( if (((int)request.options & (int)LookupOptions::IgnoreTransparentMembers) != 0) return; - for (auto transparentInfo : containerDecl->getTransparentMembers()) + for (auto transparentMemberDecl : containerDecl->getTransparentDirectMemberDecls()) { // The reference to the transparent member should use the same // path as we used in referring to its parent. DeclRef transparentMemberDeclRef = - astBuilder->getMemberDeclRef(parentDeclRef, transparentInfo.decl); + astBuilder->getMemberDeclRef(parentDeclRef, transparentMemberDecl); if (transparentMemberDeclRef.getDecl() == request.declToExclude) continue; diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index bc00eb531af..5400ab16630 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -3857,7 +3857,7 @@ struct ExprLoweringContext auto genDecl = genSubst->getGenericDecl(); Index argCounter = 0; - for (auto memberDecl : genDecl->members) + for (auto memberDecl : genDecl->getDirectMemberDecls()) { if (auto typeParamDecl = as(memberDecl)) { @@ -3868,12 +3868,10 @@ struct ExprLoweringContext _lowerSubstitutionArg(subContext, genSubst, valParamDecl, argCounter++); } } - for (auto memberDecl : genDecl->members) + for (auto constraintDecl : + genDecl->getDirectMemberDeclsOfType()) { - if (auto constraintDecl = as(memberDecl)) - { - _lowerSubstitutionArg(subContext, genSubst, constraintDecl, argCounter++); - } + _lowerSubstitutionArg(subContext, genSubst, constraintDecl, argCounter++); } } // TODO: also need to handle this-type substitution here? @@ -8115,7 +8113,7 @@ struct DeclLoweringVisitor : DeclVisitor LoweredValInfo visitExtensionDecl(ExtensionDecl* decl) { - for (auto& member : decl->members) + for (auto& member : decl->getDirectMemberDecls()) ensureDecl(context, member); return LoweredValInfo(); } @@ -9210,17 +9208,18 @@ struct DeclLoweringVisitor : DeclVisitor // First, compute the number of requirement entries that will be included in this // interface type. UInt operandCount = 0; - for (auto requirementDecl : decl->members) + for (auto requirementDecl : decl->getDirectMemberDecls()) { if (as(requirementDecl)) requirementDecl = getInner(requirementDecl); if (as(requirementDecl) || as(requirementDecl)) { - for (auto accessorDecl : as(requirementDecl)->members) + for (auto accessorDecl : + as(requirementDecl)->getDirectMemberDeclsOfType()) { - if (as(accessorDecl)) - operandCount++; + SLANG_UNUSED(accessorDecl); + operandCount++; } } if (!shouldDeclBeTreatedAsInterfaceRequirement(requirementDecl)) @@ -9359,7 +9358,7 @@ struct DeclLoweringVisitor : DeclVisitor context->setValue(requirementDeclRef.getDecl(), LoweredValInfo::simple(entry)); } }; - for (auto requirementDecl : decl->members) + for (auto requirementDecl : decl->getDirectMemberDecls()) { auto requirementKey = getInterfaceRequirementKey(requirementDecl); if (!requirementKey) @@ -9373,19 +9372,15 @@ struct DeclLoweringVisitor : DeclVisitor if (as(requirementDecl) || as(requirementDecl)) { - for (auto member : as(requirementDecl)->members) + for (auto accessorDecl : as(requirementDecl) + ->getDirectMemberDeclsOfType()) { - if (auto accessorDecl = as(member)) + auto accessorKey = getInterfaceRequirementKey(accessorDecl); + if (accessorKey) { - auto accessorKey = getInterfaceRequirementKey(accessorDecl); - if (accessorKey) - { - auto accessorDeclRef = createDefaultSpecializedDeclRef( - subContext, - nullptr, - accessorDecl); - addEntry(accessorKey, accessorDeclRef); - } + auto accessorDeclRef = + createDefaultSpecializedDeclRef(subContext, nullptr, accessorDecl); + addEntry(accessorKey, accessorDeclRef); } } } @@ -9914,7 +9909,7 @@ struct DeclLoweringVisitor : DeclVisitor // // First we start with type and value parameters, // in the order they were declared. - for (auto member : genericDecl->members) + for (auto member : genericDecl->getDirectMemberDecls()) { if (auto typeParamDecl = as(member)) { @@ -9937,12 +9932,10 @@ struct DeclLoweringVisitor : DeclVisitor } // Then we emit constraint parameters, again in // declaration order. - for (auto member : genericDecl->members) + for (auto constraintDecl : + genericDecl->getDirectMemberDeclsOfType()) { - if (auto constraintDecl = as(member)) - { - emitGenericConstraintDecl(subContext, constraintDecl); - } + emitGenericConstraintDecl(subContext, constraintDecl); } return irGeneric; @@ -11934,21 +11927,21 @@ static void ensureAllDeclsRec(IRGenContext* context, Decl* decl) // if (auto containerDecl = as(decl)) { - for (auto memberDecl : containerDecl->members) + for (auto memberDecl : containerDecl->getDirectMemberDecls()) { ensureAllDeclsRec(context, memberDecl); } } else if (auto namespaceDecl = as(decl)) { - for (auto memberDecl : namespaceDecl->members) + for (auto memberDecl : namespaceDecl->getDirectMemberDecls()) { ensureAllDeclsRec(context, memberDecl); } } else if (auto fileDecl = as(decl)) { - for (auto memberDecl : fileDecl->members) + for (auto memberDecl : fileDecl->getDirectMemberDecls()) { ensureAllDeclsRec(context, memberDecl); } @@ -12025,7 +12018,7 @@ RefPtr generateIRForTranslationUnit( // // Next, ensure that all other global declarations have // been emitted. - for (auto decl : translationUnit->getModuleDecl()->members) + for (auto decl : translationUnit->getModuleDecl()->getDirectMemberDecls()) { ensureAllDeclsRec(context, decl); } diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 431cf6669dd..49e0704bcb7 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -3785,9 +3785,6 @@ static NodeBase* parseNamespaceDecl(Parser* parser, void* /*userData*/) // 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). // @@ -3797,7 +3794,7 @@ static NodeBase* parseNamespaceDecl(Parser* parser, void* /*userData*/) // as possible in the parser, and we'd rather be // as permissive as possible right now. // - for (Decl* d = firstDecl; d; d = d->nextInContainerWithSameName) + for (auto d : parentDecl->getDirectMemberDeclsOfName(nameAndLoc.name)) { namespaceDecl = as(d); if (namespaceDecl) diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp index 258266da5c0..c7349b5d3fc 100644 --- a/source/slang/slang-reflection-api.cpp +++ b/source/slang/slang-reflection-api.cpp @@ -3600,7 +3600,7 @@ SLANG_API unsigned int spReflectionDecl_getChildrenCount(SlangReflectionDecl* pa Decl* decl = (Decl*)parentDecl; if (as(decl)) { - return (unsigned int)as(decl)->members.getCount(); + return (unsigned int)as(decl)->getDirectMemberDeclCount(); } return 0; @@ -3613,8 +3613,8 @@ SLANG_API SlangReflectionDecl* spReflectionDecl_getChild( Decl* decl = (Decl*)parentDecl; if (auto containerDecl = as(decl)) { - if (containerDecl->members.getCount() > index) - return (SlangReflectionDecl*)containerDecl->members[index]; + if (containerDecl->getDirectMemberDeclCount() > index) + return (SlangReflectionDecl*)containerDecl->getDirectMemberDecl(index); } return nullptr; diff --git a/source/slang/slang-serialize-ast.cpp b/source/slang/slang-serialize-ast.cpp index 9fa33844976..c5e54b8356b 100644 --- a/source/slang/slang-serialize-ast.cpp +++ b/source/slang/slang-serialize-ast.cpp @@ -10,6 +10,7 @@ namespace Slang { + // TODO(tfoley): have the parser export this, or a utility function // for initializing a `SyntaxDecl` in the common case. // @@ -434,6 +435,11 @@ void serialize(ASTSerializer const& serializer, NameLoc& value) serialize(serializer, value.loc); } +void serialize(ASTSerializer const& serializer, ContainerDeclDirectMemberDecls& value) +{ + serialize(serializer, value._refDecls()); +} + #if 0 // FIDDLE TEMPLATE: %for _,T in ipairs(Slang.NodeBase.subclasses) do void _serializeASTNodeContents(ASTSerializer const& serializer, $T* value) @@ -594,7 +600,7 @@ struct ASTDecodingContext : ASTSerializerImpl void _assignGenericParameterIndices(GenericDecl* genericDecl) { int parameterCounter = 0; - for (auto m : genericDecl->members) + for (auto m : genericDecl->getDirectMemberDecls()) { if (auto typeParam = as(m)) { diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index e45311abc75..55ed224cbc2 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -1093,44 +1093,44 @@ ModuleDecl* getModuleDecl(Scope* scope) return nullptr; } -Decl* getParentDecl(Decl* decl) +ContainerDecl* getParentDecl(Decl* decl) { - decl = decl->parentDecl; - while (as(decl)) - decl = decl->parentDecl; - return decl; + auto parentDecl = decl->parentDecl; + while (auto genericDecl = as(parentDecl)) + parentDecl = genericDecl->parentDecl; + return parentDecl; } -Decl* getParentAggTypeDecl(Decl* decl) +AggTypeDecl* getParentAggTypeDecl(Decl* decl) { decl = decl->parentDecl; while (decl) { - if (as(decl)) - return decl; + if (auto found = as(decl)) + return found; decl = decl->parentDecl; } return nullptr; } -Decl* getParentAggTypeDeclBase(Decl* decl) +AggTypeDeclBase* getParentAggTypeDeclBase(Decl* decl) { decl = decl->parentDecl; while (decl) { - if (as(decl)) - return decl; + if (auto found = as(decl)) + return found; decl = decl->parentDecl; } return nullptr; } -Decl* getParentFunc(Decl* decl) +FunctionDeclBase* getParentFunc(Decl* decl) { while (decl) { - if (as(decl)) - return decl; + if (auto found = as(decl)) + return found; decl = decl->parentDecl; } return nullptr; diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h index 97f829b59fd..1b5fc005dfa 100644 --- a/source/slang/slang-syntax.h +++ b/source/slang/slang-syntax.h @@ -61,7 +61,7 @@ inline FilteredMemberRefList getGenericMembers( { return FilteredMemberRefList( astBuilder, - genericInnerDecl.getParent().getDecl()->members, + genericInnerDecl.getParent().getDecl()->getDirectMemberDecls(), genericInnerDecl, filterStyle); } @@ -73,7 +73,7 @@ inline FilteredMemberRefList getMembers( { return FilteredMemberRefList( astBuilder, - declRef.getDecl()->members, + declRef.getDecl()->getDirectMemberDecls(), declRef, filterStyle); } @@ -84,7 +84,22 @@ inline FilteredMemberRefList getMembersOfType( DeclRef declRef, MemberFilterStyle filterStyle = MemberFilterStyle::All) { - return FilteredMemberRefList(astBuilder, declRef.getDecl()->members, declRef, filterStyle); + // TODO: This should in principle be using: + // + // declRef.getDecl()->getDirectMemberDeclsOfType() + // + // instead of: + // + // declRef.getDecl()->getDirectMemberDecls() + // + // and then the `FilteredMemberRefList` would only be responsible for + // filtering, plus associated each `T*` in the list with a `DeclRef`. + // + return FilteredMemberRefList( + astBuilder, + declRef.getDecl()->getDirectMemberDecls(), + declRef, + filterStyle); } void _foreachDirectOrExtensionMemberOfType( @@ -373,10 +388,10 @@ ModuleDecl* getModuleDecl(Scope* scope); Module* getModule(Decl* decl); /// Get the parent decl, skipping any generic decls in between. -Decl* getParentDecl(Decl* decl); -Decl* getParentAggTypeDecl(Decl* decl); -Decl* getParentAggTypeDeclBase(Decl* decl); -Decl* getParentFunc(Decl* decl); +ContainerDecl* getParentDecl(Decl* decl); +AggTypeDecl* getParentAggTypeDecl(Decl* decl); +AggTypeDeclBase* getParentAggTypeDeclBase(Decl* decl); +FunctionDeclBase* getParentFunc(Decl* decl); } // namespace Slang diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index b2171823bcf..61a453f3772 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -5761,7 +5761,7 @@ void TypeLayoutContext::buildExternTypeMap() if (auto scopeDecl = as(decl)) { - for (auto member : scopeDecl->members) + for (auto member : scopeDecl->getDirectMemberDecls()) { go(go, member); } @@ -5771,7 +5771,7 @@ void TypeLayoutContext::buildExternTypeMap() for (const auto& m : linkage->loadedModulesList) { const auto& ast = m->getModuleDecl(); - for (auto member : ast->members) + for (auto member : ast->getDirectMemberDecls()) { processDecl(processDecl, member); } diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index d66e86b2d7c..daebddeae75 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -2994,17 +2994,14 @@ static void collectExportedConstantInContainer( ASTBuilder* builder, ContainerDecl* containerDecl) { - for (auto m : containerDecl->members) + for (auto varMember : containerDecl->getDirectMemberDeclsOfType()) { - auto varMember = as(m); - if (!varMember) - continue; if (!varMember->val) continue; bool isExported = false; bool isConst = false; bool isExtern = false; - for (auto modifier : m->modifiers) + for (auto modifier : varMember->modifiers) { if (as(modifier)) isExported = true; @@ -3018,14 +3015,14 @@ static void collectExportedConstantInContainer( } if (isExported && isConst) { - auto mangledName = getMangledName(builder, m); + auto mangledName = getMangledName(builder, varMember); if (isExtern && dict.containsKey(mangledName)) continue; dict[mangledName] = varMember->val; } } - for (auto member : containerDecl->members) + for (auto member : containerDecl->getDirectMemberDecls()) { if (as(member) || as(member)) { @@ -5262,7 +5259,7 @@ void Module::_processFindDeclsExportSymbolsRec(Decl* decl) // If it's a container process it's children if (auto containerDecl = as(decl)) { - for (auto child : containerDecl->members) + for (auto child : containerDecl->getDirectMemberDecls()) { _processFindDeclsExportSymbolsRec(child); } @@ -6809,12 +6806,9 @@ SlangResult Linkage::loadSerializedModuleContents( module->_discoverEntryPoints(sink, targets); // Hook up fileDecl's scope to module's scope. - for (auto globalDecl : moduleDecl->members) + for (auto fileDecl : moduleDecl->getDirectMemberDeclsOfType()) { - if (auto fileDecl = as(globalDecl)) - { - addSiblingScopeForContainerDecl(m_astBuilder, moduleDecl->ownedScope, fileDecl); - } + addSiblingScopeForContainerDecl(m_astBuilder, moduleDecl->ownedScope, fileDecl); } return SLANG_OK;