#pragma once #include "../compiler-core/slang-doc-extractor.h" #include "../compiler-core/slang-lexer.h" #include "../compiler-core/slang-name.h" #include "../core/slang-basic.h" #include "../core/slang-semantic-version.h" #include "slang-ast-forward-declarations.h" #include "slang-profile.h" #include "slang-type-system-shared.h" #include "slang.h" #include #include // #include "slang-ast-support-types.h.fiddle" FIDDLE(hidden class RefObject;) FIDDLE() namespace Slang { #define SLANG_AST_NODE_VIRTUAL_CALL(CLASS, METHOD, ARGS) \ return ASTNodeDispatcherMETHOD ARGS)>::dispatch( \ this, \ [&](auto _this) -> decltype(this->METHOD ARGS) \ { return _this->_##METHOD##Override ARGS; }); class Module; class Name; class Session; class SyntaxVisitor; class FuncDecl; class Layout; class Parser; class SyntaxNode; class Decl; struct QualType; class Type; struct TypeExp; class Val; class DeclRefBase; class NodeBase; class LookupDeclRef; class GenericAppDeclRef; struct CapabilitySet; template T* as(NodeBase * node); template const T* as(const NodeBase* node); void printDiagnosticArg(StringBuilder & sb, Decl * decl); void printDiagnosticArg(StringBuilder & sb, Type * type); void printDiagnosticArg(StringBuilder & sb, TypeExp const& type); void printDiagnosticArg(StringBuilder & sb, QualType const& type); void printDiagnosticArg(StringBuilder & sb, Val * val); void printDiagnosticArg(StringBuilder & sb, DeclRefBase * declRefBase); void printDiagnosticArg(StringBuilder & sb, ASTNodeType nodeType); void printDiagnosticArg(StringBuilder & sb, const CapabilitySet& set); void printDiagnosticArg(StringBuilder & sb, List & set); struct QualifiedDeclPath { DeclRefBase* declRef; QualifiedDeclPath() = default; QualifiedDeclPath(DeclRefBase* declRef) : declRef(declRef) { } }; // Prints the fully qualified decl name. void printDiagnosticArg(StringBuilder & sb, QualifiedDeclPath path); class SyntaxNode; SourceLoc getDiagnosticPos(SyntaxNode const* syntax); SourceLoc getDiagnosticPos(TypeExp const& typeExp); SourceLoc getDiagnosticPos(DeclRefBase * declRef); SourceLoc getDiagnosticPos(Decl * decl); typedef NodeBase* (*SyntaxParseCallback)(Parser* parser, void* userData); typedef unsigned int ConversionCost; enum : ConversionCost { // No conversion at all kConversionCost_None = 0, kConversionCost_GenericParamUpcast = 1, kConversionCost_LambdaToFunc = 1, kConversionCost_UnconstraintGenericParam = 20, kConversionCost_SizedArrayToUnsizedArray = 30, // Convert between matrices of different layout kConversionCost_MatrixLayout = 5, // Conversion from a buffer to the type it carries needs to add a minimal // extra cost, just so we can distinguish an overload on `ConstantBuffer` // from one on `Foo` kConversionCost_GetRef = 5, kConversionCost_ImplicitDereference = 10, kConversionCost_InRangeIntLitConversion = 23, kConversionCost_InRangeIntLitSignedToUnsignedConversion = 32, kConversionCost_InRangeIntLitUnsignedToSignedConversion = 81, kConversionCost_MutablePtrToConstPtr = 20, // Conversions based on explicit sub-typing relationships are the cheapest // // TODO(tfoley): We will eventually need a discipline for ranking // when two up-casts are comparable. kConversionCost_CastToInterface = 50, // Conversion that is lossless and keeps the "kind" of the value the same kConversionCost_BoolToInt = 120, // Converting bool to int has lower cost than other integer // types to prevent ambiguity. kConversionCost_RankPromotion = 150, kConversionCost_NoneToOptional = 150, kConversionCost_ValToOptional = 150, kConversionCost_NullPtrToPtr = 150, kConversionCost_PtrToVoidPtr = 150, kConversionCost_FailedOptionalConstraint = 150, // Conversions that are lossless, but change "kind" kConversionCost_UnsignedToSignedPromotion = 200, // Same-size size unsigned->signed conversions are potentially lossy, but they are commonly // allowed silently. kConversionCost_SameSizeUnsignedToSignedConversion = 300, // Conversion from signed->unsigned integer of same or greater size kConversionCost_SignedToUnsignedConversion = 250, // Cost of converting an integer to a floating-point type kConversionCost_IntegerToFloatConversion = 400, // Cost of converting a pointer to bool kConversionCost_PtrToBool = 400, // Cost of converting an integer to int16_t kConversionCost_IntegerTruncate = 450, // Cost of converting an integer to a half type kConversionCost_IntegerToHalfConversion = 500, // Cost of using a concrete argument pack kConversionCost_ParameterPack = 500, // Default case (usable for user-defined conversions) kConversionCost_Default = 500, // Catch-all for conversions that should be discouraged // (i.e., that really shouldn't be made implicitly) // // TODO: make these conversions not be allowed implicitly in "Slang mode" kConversionCost_GeneralConversion = 900, // This is the cost of an explicit conversion, which should // not actually be performed. kConversionCost_Explicit = 90000, // Additional conversion cost to add when promoting from a scalar to // a vector (this will be added to the cost, if any, of converting // the element type of the vector) kConversionCost_OneVectorToScalar = 1, kConversionCost_ScalarToVector = 2, kConversionCost_ScalarToMatrix = 10, kConversionCost_ScalarIntegerToFloatMatrix = kConversionCost_IntegerToFloatConversion + kConversionCost_ScalarToMatrix, // Additional conversion cost to add when promoting from a scalar to // a CoopVector (this will be added to the cost, if any, of converting // the element type of the CoopVector) kConversionCost_ScalarToCoopVector = 1, // Additional cost when casting an LValue. kConversionCost_LValueCast = 800, // The cost of this conversion is defined by the type coercion constraint. kConversionCost_TypeCoercionConstraint = 1000, kConversionCost_TypeCoercionConstraintPlusScalarToVector = kConversionCost_TypeCoercionConstraint + kConversionCost_ScalarToVector, // Conversion is impossible kConversionCost_Impossible = 0xFFFFFFFF, }; typedef unsigned int BuiltinConversionKind; enum : BuiltinConversionKind { kBuiltinConversion_Unknown = 0, kBuiltinConversion_FloatToDouble = 1, }; enum class ImageFormat { #define SLANG_FORMAT(NAME, OTHER) NAME, #include "slang-image-format-defs.h" #undef SLANG_FORMAT }; struct ImageFormatInfo { SlangScalarType scalarType; ///< If image format is not made up of channels of set sizes ///< this will be SLANG_SCALAR_TYPE_NONE uint8_t channelCount; ///< The number of channels uint8_t sizeInBytes; ///< Size in bytes UnownedStringSlice name; ///< The name associated with this type. NOTE! Currently these ///< names *are* the GLSL format names. }; const ImageFormatInfo& getImageFormatInfo(ImageFormat format); bool findImageFormatByName(const UnownedStringSlice& name, ImageFormat* outFormat); bool findVkImageFormatByName(const UnownedStringSlice& name, ImageFormat* outFormat); char const* getGLSLNameForImageFormat(ImageFormat format); /// Enum for known built-in function names to replace string-based comparisons enum class KnownBuiltinDeclName : uint32_t { GeometryStreamAppend, GeometryStreamRestart, GetAttributeAtVertex, DispatchMesh, saturated_cooperation, saturated_cooperation_using, IDifferentiable, IDifferentiablePtr, NullDifferential, OperatorAddressOf, COUNT }; /// Convert string name to KnownBuiltinDeclName enum KnownBuiltinDeclName getKnownBuiltinDeclNameFromString(UnownedStringSlice name); // TODO(tfoley): We should ditch this enumeration // and just use the IR opcodes that represent these // types directly. The one major complication there // is that the order of the enum values currently // matters, since it determines promotion rank. // We either need to keep that restriction, or // look up promotion rank by some other means. // class Decl; class Val; // Helper type for pairing up a name and the location where it appeared FIDDLE() struct NameLoc { FIDDLE(...) FIDDLE() Name* name; FIDDLE() SourceLoc loc; NameLoc() : name(nullptr) { } explicit NameLoc(Name* inName) : name(inName) { } NameLoc(Name* inName, SourceLoc inLoc) : name(inName), loc(inLoc) { } NameLoc(Token const& token) : name(token.getNameOrNull()), loc(token.getLoc()) { } }; struct StringSliceLoc { UnownedStringSlice name; SourceLoc loc; StringSliceLoc() : name(nullptr) { } explicit StringSliceLoc(const UnownedStringSlice& inName) : name(inName) { } StringSliceLoc(const UnownedStringSlice& inName, SourceLoc inLoc) : name(inName), loc(inLoc) { } StringSliceLoc(Token const& token) : loc(token.getLoc()) { Name* tokenName = token.getNameOrNull(); if (tokenName) { name = tokenName->text.getUnownedSlice(); } } }; // Helper class for iterating over a list of heap-allocated modifiers struct ModifierList { struct Iterator { Modifier* current = nullptr; Modifier* operator*() { return current; } void operator++(); bool operator!=(Iterator other) { return current != other.current; }; Iterator() : current(nullptr) { } Iterator(Modifier* modifier) : current(modifier) { } }; ModifierList() : modifiers(nullptr) { } ModifierList(Modifier* modifiers) : modifiers(modifiers) { } Iterator begin() { return Iterator(modifiers); } Iterator end() { return Iterator(nullptr); } Modifier* modifiers = nullptr; }; // Helper class for iterating over heap-allocated modifiers // of a specific type. template struct FilteredModifierList { struct Iterator { Modifier* current = nullptr; T* operator*() { return (T*)current; } void operator++(); bool operator!=(Iterator other) { return current != other.current; }; Iterator() : current(nullptr) { } Iterator(Modifier* modifier) : current(modifier) { } }; FilteredModifierList() : modifiers(nullptr) { } FilteredModifierList(Modifier* modifiers) : modifiers(adjust(modifiers)) { } Iterator begin() { return Iterator(modifiers); } Iterator end() { return Iterator(nullptr); } static Modifier* adjust(Modifier* modifier); Modifier* modifiers = nullptr; }; // A set of modifiers attached to a syntax node struct Modifiers { // The first modifier in the linked list of heap-allocated modifiers Modifier* first = nullptr; template FilteredModifierList getModifiersOfType() { return FilteredModifierList(first); } // Find the first modifier of a given type, or return `nullptr` if none is found. template T* findModifier() { return *getModifiersOfType().begin(); } template bool hasModifier() { return findModifier() != nullptr; } /// True if has no modifiers bool isEmpty() const { return first == nullptr; } FilteredModifierList::Iterator begin() { return FilteredModifierList::Iterator(first); } FilteredModifierList::Iterator end() { return FilteredModifierList::Iterator(nullptr); } }; class NamedExpressionType; class GenericDecl; class ContainerDecl; // Try to extract a simple integer value from an `IntVal`. // This fill assert-fail if the object doesn't represent a literal value. IntegerLiteralValue getIntVal(IntVal * val); /// Represents how much checking has been applied to a declaration. enum class DeclCheckState : uint8_t { /// The declaration has been parsed, but /// is otherwise completely unchecked. /// Unchecked, /// The declaration is parsed and inserted into the initial scope, /// ready for future lookups from within the parser for disambiguation purposes. ReadyForParserLookup, /// Basic checks on the modifiers of the declaration have been applied. /// /// For example, when a declaration has attributes, the transformation /// of an attribute from the parsed-but-unchecked form into a checked /// form (in which it has the appropriate C++ subclass) happens here. /// ModifiersChecked, /// Wiring up scopes of namespaces with their siblings defined in different /// files/modules, and other namespaces imported via `using`. ScopesWired, /// The type/signature of the declaration has been checked. /// /// For a value declaration like a variable or function, this means that /// the type of the declaration can be queried. /// /// For a type declaration like a `struct` or `typedef` this means /// that a `Type` referring to that declaration can be formed. /// SignatureChecked, /// The declaration's basic signature has been checked to the point that /// it is ready to be referenced in other places. /// /// For a function, this means that it has been organized into a /// "redeclration group" if there are multiple functions with the /// same name in a scope. /// ReadyForReference, /// The declaration is ready for lookup operations to be performed. /// /// For type declarations (e.g., aggregate types, generic type parameters) /// this means that any base type or constraint clauses have been /// sufficiently checked so that we can enumerate the inheritance /// hierarchy of the type and discover all its members. /// ReadyForLookup, /// Any conformance declared on the declaration have been validated. /// /// In particular, this step means that a "witness table" has been /// created to show how a type satisfies the requirements of any /// interfaces it conforms to. /// ReadyForConformances, /// Any DeclRefTypes with substitutions have been fully resolved /// to concrete type. E.g. `T.X` with `T=A` should resolve to `A.X`. /// We need a separate pass to resolve these types because `A.X` /// maybe synthesized and made available only after conformance checking. TypesFullyResolved, /// All attributes are fully checked. This is the final step before /// checking the function body. AttributesChecked, /// The body/definition is checked. /// /// This step includes any validation of the declaration that is /// immaterial to clients code using the declaration, but that is /// nonetheless relevant to checking correctness. /// /// The canonical example here is checking the body of functions. /// Client code cannot depend on *how* a function is implemented, /// but we still need to (eventually) check the bodies of all /// functions, so it belongs in the last phase of checking. /// DefinitionChecked, DefaultConstructorReadyForUse = DefinitionChecked, /// The capabilities required by the decl is infered and validated. /// CapabilityChecked, // For convenience at sites that call `ensureDecl()`, we define // some aliases for the above states that are expressed in terms // of what client code needs to be able to do with a declaration. // // These aliases can be changed over time if we decide to add // more phases to semantic checking. CanEnumerateBases = ReadyForLookup, CanUseBaseOfInheritanceDecl = ReadyForLookup, CanUseTypeOfValueDecl = ReadyForReference, CanUseExtensionTargetType = ReadyForLookup, CanUseAsType = ReadyForReference, CanUseFuncSignature = ReadyForReference, CanSpecializeGeneric = ReadyForReference, CanReadInterfaceRequirements = ReadyForLookup, }; /// A `DeclCheckState` plus a bit to track whether a declaration is currently being checked. struct DeclCheckStateExt { typedef uint8_t RawType; DeclCheckStateExt() {} DeclCheckStateExt(DeclCheckState state) : m_raw(uint8_t(state)) { } enum : RawType { /// A flag to indicate that a declaration is being checked. /// /// The value of this flag is chosen so that it can be /// represented in the bits of a `DeclCheckState` without /// colliding with the bits that represent actual states. /// kBeingCheckedBit = 0x80, }; DeclCheckState getState() const { return DeclCheckState(m_raw & ~kBeingCheckedBit); } void setState(DeclCheckState state) { m_raw = (m_raw & kBeingCheckedBit) | RawType(state); } bool isBeingChecked() const { return (m_raw & kBeingCheckedBit) != 0; } void setIsBeingChecked(bool isBeingChecked) { m_raw = (m_raw & ~kBeingCheckedBit) | (isBeingChecked ? kBeingCheckedBit : 0); } bool operator>=(DeclCheckState state) const { return getState() >= state; } RawType getRaw() const { return m_raw; } void setRaw(RawType raw) { m_raw = raw; } // TODO(JS): // Unfortunately for automatic serialization to see this member, it has to be public. // private: RawType m_raw = 0; }; void addModifier(ModifiableSyntaxNode * syntax, Modifier * modifier); void removeModifier(ModifiableSyntaxNode * syntax, Modifier * modifier); FIDDLE() struct QualType { FIDDLE(...) FIDDLE() Type* type = nullptr; FIDDLE() bool isLeftValue = false; FIDDLE() bool hasReadOnlyOnTarget = false; FIDDLE() bool isWriteOnly = false; QualType() = default; QualType(Type* type); QualType(Type* type, bool isLVal) : QualType(type) { isLeftValue = isLVal; } Type* Ptr() { return type; } operator Type*() { return type; } Type* operator->() { return type; } }; class ASTBuilder; struct SyntaxClassBase; typedef SyntaxClassBase ReflectClassInfo; typedef SyntaxClassBase ASTClassInfo; enum class SyntaxClassInfoDebugVisType { Decl, Expr, Modifier, Stmt, Val, Scope, Unknown, }; struct SyntaxClassInfo { public: char const* name; SyntaxClassInfoDebugVisType debugVisType; ASTNodeType firstTag; Count tagCount; void* (*createFunc)(ASTBuilder*); void (*destructFunc)(void*); template static SyntaxClassInfo* get() { return const_cast(&T::kSyntaxClassInfo); } }; // A reference to a class of syntax node, that can be // used to create instances on the fly struct SyntaxClassBase { SyntaxClassBase() {} explicit SyntaxClassBase(ASTNodeType tag); SyntaxClassBase(SyntaxClassInfo const* info) : _info(info) { } ASTNodeType getTag() const { return getInfo()->firstTag; } UnownedTerminatedStringSlice getName() const; void* createInstanceImpl(ASTBuilder* astBuilder) const; void destructInstanceImpl(void* instance) const; bool isSubClassOf(SyntaxClassBase const& super) const; typedef SyntaxClassInfo Info; Info* getInfo() const { return const_cast(_info); } operator Info*() const { return const_cast(_info); } bool operator==(SyntaxClassBase const& other) const { return _info == other._info; } bool operator!=(SyntaxClassBase const& other) const { return _info != other._info; } private: Info const* _info = nullptr; }; template struct SyntaxClass; template SyntaxClass getSyntaxClass(); template struct SyntaxClass : SyntaxClassBase { SyntaxClass() {} template SyntaxClass( SyntaxClass const& other, typename EnableIf::Value, void>::type* = 0) : SyntaxClassBase(other) { } explicit SyntaxClass(SyntaxClassBase const& other) : SyntaxClassBase(other) { } explicit SyntaxClass(ASTNodeType tag) : SyntaxClassBase(tag) { } explicit SyntaxClass(SyntaxClassInfo const* info) : SyntaxClassBase(info) { } T* createInstance(ASTBuilder* astBuilder) const { return (T*)createInstanceImpl(astBuilder); } void destructInstance(T* instance) { destructInstanceImpl(instance); } bool isSubClassOf(SyntaxClassBase const& other) { return SyntaxClassBase::isSubClassOf(other); } template bool isSubClassOf() { return SyntaxClassBase::isSubClassOf(getSyntaxClass()); } }; template SyntaxClass getSyntaxClass() { return SyntaxClass(SyntaxClassInfo::get()); } struct SubstitutionSet { DeclRefBase* declRef = nullptr; // The element index if the substitution is happening inside a pack expansion. // For example, if we are substituting the pattern type of `expand each T`, where // `T` is a type pack, then packExpansionIndex will have a value starting from 0 // to the count of the type pack during expansion of the `expand` type when we // substitute `each T` with the element of `T` at index `packExpansionIndex`. int packExpansionIndex = -1; SubstitutionSet() = default; SubstitutionSet(DeclRefBase* declRefBase) : declRef(declRefBase) { } explicit operator bool() const; template void forEachGenericSubstitution(F func) const; template void forEachSubstitutionArg(F func) const; Type* applyToType(ASTBuilder* astBuilder, Type* type) const; DeclRefBase* applyToDeclRef(ASTBuilder* astBuilder, DeclRefBase* declRef) const; LookupDeclRef* findLookupDeclRef() const; GenericAppDeclRef* findGenericAppDeclRef(GenericDecl* genericDecl) const; GenericAppDeclRef* findGenericAppDeclRef() const; DeclRefBase* getInnerMostNodeWithSubstInfo() const; }; /// An expression together with (optional) substutions to apply to it /// /// Under the hood this is a pair of an `Expr*` and a `SubstitutionSet`. /// Conceptually it represents the result of applying the substitutions, /// recursively, to the given expression. /// /// `SubstExprBase` exists primarily to provide a non-templated base type /// for `SubstExpr`. Code should prefer to use `SubstExpr` instead /// of `SubstExprBase` as often as possible. /// struct SubstExprBase { public: /// Initialize as a null expression SubstExprBase() {} /// Initialize as the given `expr` with no subsitutions applied SubstExprBase(Expr* expr) : m_expr(expr) { } /// Initialize as the given `expr` with the given `substs` applied SubstExprBase(Expr* expr, SubstitutionSet const& substs) : m_expr(expr), m_substs(substs) { } /// Get the underlying expression without any substitutions Expr* getExpr() const { return m_expr; } /// Get the subsitutions being applied, if any SubstitutionSet const& getSubsts() const { return m_substs; } private: Expr* m_expr = nullptr; SubstitutionSet m_substs; typedef void (SubstExprBase::*SafeBool)(); void SafeBoolTrue() {} public: /// Test whether this is a non-null expression operator SafeBool() { return m_expr ? &SubstExprBase::SafeBoolTrue : nullptr; } /// Test whether this is a null expression bool operator!() const { return m_expr == nullptr; } }; /// An expression together with (optional) substutions to apply to it /// /// Under the hood this is a pair of an `T*` (there `T: Expr`) and a `SubstitutionSet`. /// Conceptually it represents the result of applying the substitutions, /// recursively, to the given expression. /// template struct SubstExpr : SubstExprBase { private: typedef SubstExprBase Super; public: /// Initialize as a null expression SubstExpr() {} /// Initialize as the given `expr` with no subsitutions applied SubstExpr(T* expr) : Super(expr) { } /// Initialize as the given `expr` with the given `substs` applied SubstExpr(T* expr, SubstitutionSet const& substs) : Super(expr, substs) { } /// Initialize as a copy of the given `other` expression template SubstExpr( SubstExpr const& other, typename EnableIf::Value, void>::type* = 0) : Super(other.getExpr(), other.getSubsts()) { } /// Get the underlying expression without any substitutions T* getExpr() const { return (T*)Super::getExpr(); } /// Dynamic cast to an expression of type `U` /// /// Returns a null expression if the cast fails, or if this expression was null. template SubstExpr as() { return SubstExpr(Slang::as(getExpr()), getSubsts()); } }; SubstExpr applySubstitutionToExpr(SubstitutionSet substSet, Expr * expr); class ASTBuilder; template struct DeclRef; Module* getModule(Decl * decl); // If this is a declref to an associatedtype with a ThisTypeSubsitution, // try to find the concrete decl that satisfies the associatedtype requirement from the // concrete type supplied by ThisTypeSubstittution. Val* _tryLookupConcreteAssociatedTypeFromThisTypeSubst( ASTBuilder * builder, DeclRef declRef); template struct DeclRef { friend class ASTBuilder; public: typedef T DeclType; DeclRefBase* declRefBase; DeclRef() : declRefBase(nullptr) { } void init(DeclRefBase* base); DeclRef(Decl* decl); DeclRef(DeclRefBase* base) { init(base); } template::Value, void>::type> DeclRef(DeclRef const& other) : declRefBase(other.declRefBase) { } T* getDecl() const; Name* getName() const; SourceLoc getNameLoc() const; SourceLoc getLoc() const; DeclRef getParent() const; HashCode getHashCode() const; Type* substitute(ASTBuilder* astBuilder, Type* type) const; SubstExpr substitute(ASTBuilder* astBuilder, Expr* expr) const; // Apply substitutions to a type or declaration template DeclRef substitute(ASTBuilder* astBuilder, DeclRef declRef) const; // Apply substitutions to this declaration reference DeclRef substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) const; template DeclRef as() const { DeclRef result = DeclRef(declRefBase); return result; } template bool is() const { return Slang::as(static_cast(getDecl())) != nullptr; } operator DeclRefBase*() const { return declRefBase; } operator DeclRef() const { return DeclRef(declRefBase); } template bool equals(DeclRef other) const { return declRefBase == other.declRefBase; } template bool operator==(DeclRef other) const { return equals(other); } template bool operator!=(DeclRef other) const { return !equals(other); } explicit operator bool() const { return declRefBase; } }; template inline DeclRef makeDeclRef(T * decl) { return DeclRef(decl); } SubstExpr substituteExpr(SubstitutionSet const& substs, Expr* expr); DeclRef substituteDeclRef( SubstitutionSet const& substs, ASTBuilder* astBuilder, DeclRef const& declRef); Type* substituteType(SubstitutionSet const& substs, ASTBuilder* astBuilder, Type* type); enum class MemberFilterStyle { All, ///< All members Instance, ///< Only instance members Static, ///< Only static (ie non instance) members }; Decl* const* adjustFilterCursorImpl( const ReflectClassInfo& clsInfo, MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end); Decl* const* getFilterCursorByIndexImpl( const ReflectClassInfo& clsInfo, MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end, Index index); Index getFilterCountImpl( const ReflectClassInfo& clsInfo, MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end); template Decl* const* adjustFilterCursor( MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end) { return adjustFilterCursorImpl(getSyntaxClass(), filterStyle, ptr, end); } /// Finds the element at index. If there is no element at the index (for example has too few /// elements), returns nullptr. template Decl* const* getFilterCursorByIndex( MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end, Index index) { return getFilterCursorByIndexImpl(getSyntaxClass(), filterStyle, ptr, end, index); } template Index getFilterCount(MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end) { return getFilterCountImpl(getSyntaxClass(), filterStyle, ptr, end); } template bool isFilterNonEmpty(MemberFilterStyle filterStyle, Decl* const* ptr, Decl* const* end) { return adjustFilterCursorImpl(getSyntaxClass(), filterStyle, ptr, end) != end; } template struct FilteredMemberList { typedef Decl* Element; FilteredMemberList() : m_begin(nullptr), m_end(nullptr) { } explicit FilteredMemberList( List const& list, MemberFilterStyle filterStyle = MemberFilterStyle::All) : m_begin(adjustFilterCursor(filterStyle, list.begin(), list.end())) , m_end(list.end()) , m_filterStyle(filterStyle) { } struct Iterator { const Element* m_cursor; const Element* m_end; MemberFilterStyle m_filterStyle; bool operator!=(Iterator const& other) const { return m_cursor != other.m_cursor; } void operator++() { m_cursor = adjustFilterCursor(m_filterStyle, m_cursor + 1, m_end); } T* operator*() { return static_cast(*m_cursor); } }; Iterator begin() { Iterator iter = {m_begin, m_end, m_filterStyle}; return iter; } Iterator end() { Iterator iter = {m_end, m_end, m_filterStyle}; return iter; } // TODO(tfoley): It is ugly to have these. // We should probably fix the call sites instead. T* getFirst() { return *begin(); } Index getCount() { return getFilterCount(m_filterStyle, m_begin, m_end); } T* operator[](Index index) const { Decl* const* ptr = getFilterCursorByIndex(m_filterStyle, m_begin, m_end, index); SLANG_ASSERT(ptr); return static_cast(*ptr); } /// Returns true if empty (equivalent to getCount() == 0) bool isEmpty() const { /// Note we don't have to scan, because m_begin has already been adjusted, when the /// FilteredMemberList is constructed return m_begin == m_end; } /// Returns true if non empty (equivalent to getCount() != 0 but faster) bool isNonEmpty() const { return !isEmpty(); } List toList() { List result; for (auto element : (*this)) { result.add(element); } return result; } const Element* m_begin; ///< Is either equal to m_end, or points to first *valid* filtered member const Element* m_end; MemberFilterStyle m_filterStyle; }; template struct FilteredMemberRefList { List const& m_decls; DeclRef m_parent; MemberFilterStyle m_filterStyle; ASTBuilder* m_astBuilder; FilteredMemberRefList( ASTBuilder* astBuilder, List const& decls, DeclRef parent, MemberFilterStyle filterStyle = MemberFilterStyle::All) : m_decls(decls), m_parent(parent), m_filterStyle(filterStyle), m_astBuilder(astBuilder) { } Index getCount() const { return getFilterCount(m_filterStyle, m_decls.begin(), m_decls.end()); } /// True if empty (equivalent to getCount == 0, but faster) bool isEmpty() const { return !isNonEmpty(); } /// True if non empty (equivalent to getCount() != 0 but faster) bool isNonEmpty() const { return isFilterNonEmpty(m_filterStyle, m_decls.begin(), m_decls.end()); } DeclRef getFirstOrNull() { return isEmpty() ? DeclRef() : (*this)[0]; } DeclRef operator[](Index index) const { Decl* const* decl = getFilterCursorByIndex(m_filterStyle, m_decls.begin(), m_decls.end(), index); SLANG_ASSERT(decl); return _getMemberDeclRef(m_astBuilder, m_parent, (T*)*decl).template as(); } List> toArray() const { List> result; for (auto d : *this) result.add(d); return result; } struct Iterator { FilteredMemberRefList const* m_list; Decl* const* m_ptr; Decl* const* m_end; MemberFilterStyle m_filterStyle; Iterator() : m_list(nullptr), m_ptr(nullptr), m_filterStyle(MemberFilterStyle::All) { } Iterator( FilteredMemberRefList const* list, Decl* const* ptr, Decl* const* end, MemberFilterStyle filterStyle) : m_list(list), m_ptr(ptr), m_end(end), m_filterStyle(filterStyle) { } bool operator!=(const Iterator& other) const { return m_ptr != other.m_ptr; } void operator++() { m_ptr = adjustFilterCursor(m_filterStyle, m_ptr + 1, m_end); } DeclRef operator*() { return _getMemberDeclRef(m_list->m_astBuilder, m_list->m_parent, (T*)*m_ptr) .template as(); } }; Iterator begin() const { return Iterator( this, adjustFilterCursor(m_filterStyle, m_decls.begin(), m_decls.end()), m_decls.end(), m_filterStyle); } Iterator end() const { return Iterator(this, m_decls.end(), m_decls.end(), m_filterStyle); } }; // // type Expressions // // A "type expression" is a term that we expect to resolve to a type during checking. // We store both the original syntax and the resolved type here. FIDDLE() struct TypeExp { FIDDLE(...) typedef TypeExp ThisType; TypeExp() {} TypeExp(TypeExp const& other) : exp(other.exp), type(other.type) { } explicit TypeExp(Expr* exp) : exp(exp) { } explicit TypeExp(Type* type) : type(type) { } TypeExp(Expr* exp, Type* type) : exp(exp), type(type) { } Expr* exp = nullptr; Type* type = nullptr; bool equals(Type* other); Type* Ptr() { return type; } operator Type*() { return type; } Type* operator->() { return Ptr(); } explicit operator bool() const { return type != nullptr; } ThisType& operator=(const ThisType& rhs) = default; /// A global immutable TypeExp, that has no type or exp set. static const TypeExp empty; }; // Masks to be applied when lookup up declarations enum class LookupMask : uint8_t { type = 0x1, Function = 0x2, Value = 0x4, Attribute = 0x8, SyntaxDecl = 0x10, Default = type | Function | Value | SyntaxDecl, }; /// Flags for options to be used when looking up declarations enum class LookupOptions : uint8_t { None = 0, IgnoreBaseInterfaces = 1 << 0, Completion = 1 << 1, ///< Lookup all applicable decls for code completion suggestions NoDeref = 1 << 2, ConsiderAllLocalNamesInScope = 1 << 3, ///^ Normally we rely on the checking state of local names to determine /// if they have been declared. If the scopes are currently /// "under-construction" and not being checked, then it's safe to /// consider all names we've inserted so far. This is used when /// checking to see if a keyword is shadowed. IgnoreInheritance = 1 << 4, ///< Lookup only non inheritance children of a struct (including `extension`) IgnoreTransparentMembers = 1 << 5, }; inline LookupOptions operator&(LookupOptions a, LookupOptions b) { return (LookupOptions)((std::underlying_type_t)a & (std::underlying_type_t)b); } class LookupResultItem_Breadcrumb : public RefObject { public: enum class Kind : uint8_t { // The lookup process looked "through" an in-scope // declaration to the fields inside of it, so that // even if lookup started with a simple name `f`, // it needs to result in a member expression `obj.f`. Member, // The lookup process took a pointer(-like) value, and then // proceeded to derefence it and look at the thing(s) // it points to instead, so that the final expression // needs to have `(*obj)` Deref, // The lookup process saw a value `obj` of type `T` and // took into account an in-scope constraint that says // `T` is a subtype of some other type `U`, so that // lookup was able to find a member through type `U` // instead. SuperType, // The lookup process considered a member of an // enclosing type as being in scope, so that any // reference to that member needs to use a `this` // expression as appropriate. This, }; // The kind of lookup step that was performed Kind kind; // For the `Kind::This` case, what does the implicit // `this` or `This` parameter refer to? // enum class ThisParameterMode : uint8_t { ImmutableValue, // An immutable `this` value MutableValue, // A mutable `this` value Type, // A `This` type Default = ImmutableValue, }; ThisParameterMode thisParameterMode = ThisParameterMode::Default; // As needed, a reference to the declaration that faciliated // the lookup step. // // For a `Member` lookup step, this is the declaration whose // members were implicitly pulled into scope. // // For a `Constraint` lookup step, this is the `ConstraintDecl` // that serves to witness the subtype relationship. // DeclRef declRef; Val* val = nullptr; // The next implicit step that the lookup process took to // arrive at a final value. RefPtr next; LookupResultItem_Breadcrumb( Kind kind, DeclRef declRef, Val* val, RefPtr next, ThisParameterMode thisParameterMode = ThisParameterMode::Default) : kind(kind) , thisParameterMode(thisParameterMode) , declRef(declRef) , val(val) , next(next) { } protected: // Needed for serialization LookupResultItem_Breadcrumb() = default; }; // Represents one item found during lookup struct LookupResultItem { typedef LookupResultItem_Breadcrumb Breadcrumb; // Sometimes lookup finds an item, but there were additional // "hops" taken to reach it. We need to remember these steps // so that if/when we consturct a full expression we generate // appropriate AST nodes for all the steps. // // We build up a list of these "breadcrumbs" while doing // lookup, and store them alongside each item found. // // As an example, suppose we have an HLSL `cbuffer` declaration: // // cbuffer C { float4 f; } // // This is syntax sugar for a global-scope variable of // type `ConstantBuffer` where `T` is a `struct` containing // all the members: // // struct Anon0 { float4 f; }; // __transparent ConstantBuffer anon1; // // The `__transparent` modifier there captures the fact that // when somebody writes `f` in their code, they expect it to // "see through" the `cbuffer` declaration (or the global variable, // in this case) and find the member inside. // // But when the user writes `f` we can't just create a simple // `VarExpr` that refers directly to that field, because that // doesn't actually reflect the required steps in a way that // code generation can use. // // Instead we need to construct an expression like `(*anon1).f`, // where there is are two additional steps in the process: // // 1. We needed to dereference the pointer-like type `ConstantBuffer` // to get at a value of type `Anon0` // 2. We needed to access a sub-field of the aggregate type `Anon0` // // We *could* just create these full-formed expressions during // lookup, but this might mean creating a large number of // AST nodes in cases where the user calls an overloaded function. // At the very least we'd rather not heap-allocate in the common // case where no "extra" steps need to be performed to get to // the declarations. // // This is where "breadcrumbs" come in. A breadcrumb represents // an extra "step" that must be performed to turn a declaration // found by lookup into a valid expression to splice into the // AST. Most of the time lookup result items don't have any // breadcrumbs, so that no extra heap allocation takes place. // When an item does have breadcrumbs, and it is chosen as // the unique result (perhaps by overload resolution), then // we can walk the list of breadcrumbs to create a full // expression. // A properly-specialized reference to the declaration that was found. DeclRef declRef; // Any breadcrumbs needed in order to turn that declaration // reference into a well-formed expression. // // This is unused in the simple case where a declaration // is being referenced directly (rather than through // transparent members). RefPtr breadcrumbs; LookupResultItem() = default; explicit LookupResultItem(DeclRef declRef) : declRef(declRef) { } LookupResultItem(DeclRef declRef, RefPtr breadcrumbs) : declRef(declRef), breadcrumbs(breadcrumbs) { } }; // Result of looking up a name in some lexical/semantic environment. // Can be used to enumerate all the declarations matching that name, // in the case where the result is overloaded. struct LookupResult { // The one item that was found, in the simple case LookupResultItem item; // All of the items that were found, in the complex case. // Note: if there was no overloading, then this list isn't // used at all, to avoid allocation. // // Additionally, if `items` is used, then `item` *must* hold an item that // is also in the items list (typically the first entry), as an invariant. // Otherwise isValid/begin will not function correctly. List items; // Was at least one result found? bool isValid() const { return item.declRef.getDecl() != nullptr; } bool isOverloaded() const { return items.getCount() > 1; } Name* getName() const { return items.getCount() > 1 ? items[0].declRef.getName() : item.declRef.getName(); } LookupResultItem* begin() const { if (isValid()) { if (isOverloaded()) return const_cast(items.begin()); else return const_cast(&item); } else return nullptr; } LookupResultItem* end() const { if (isValid()) { if (isOverloaded()) return const_cast(items.end()); else return const_cast(&item + 1); } else return nullptr; } }; // A helper to avoid having to include slang-check-impl.h in slang-syntax.h struct SemanticsVisitor; ASTBuilder* semanticsVisitorGetASTBuilder(SemanticsVisitor*); struct LookupRequest { SemanticsVisitor* semantics = nullptr; Scope* scope = nullptr; Scope* endScope = nullptr; // A decl to exclude from the lookup, used to exclude the current decl being checked, such // as in typedef Foo Foo; to avoid finding itself. Decl* declToExclude = nullptr; LookupMask mask = LookupMask::Default; LookupOptions options = LookupOptions::None; bool isCompletionRequest() const { return (options & LookupOptions::Completion) != LookupOptions::None; } bool shouldConsiderAllLocalNames() const { return (options & LookupOptions::ConsiderAllLocalNamesInScope) != LookupOptions::None; } }; class WitnessTable; // A value that witnesses the satisfaction of an interface // requirement by a particular declaration or value. struct RequirementWitness { RequirementWitness() : m_flavor(Flavor::none) { } RequirementWitness(DeclRefBase* declRef) : m_flavor(Flavor::declRef), m_declRef(declRef) { } RequirementWitness(Val* val); RequirementWitness(RefPtr witnessTable); enum class Flavor { none, declRef, val, witnessTable, }; Flavor getFlavor() const { return m_flavor; } DeclRef getDeclRef() { SLANG_ASSERT(getFlavor() == Flavor::declRef); return m_declRef; } Val* getVal() { SLANG_ASSERT(getFlavor() == Flavor::val); return m_val; } RefPtr getWitnessTable(); RequirementWitness specialize(ASTBuilder* astBuilder, SubstitutionSet const& subst); Flavor m_flavor; DeclRef m_declRef; RefPtr m_obj; Val* m_val = nullptr; }; typedef OrderedDictionary RequirementDictionary; FIDDLE() class WitnessTable : public RefObject { FIDDLE(...) const RequirementDictionary& getRequirementDictionary() { return m_requirementDictionary; } void add(Decl* decl, RequirementWitness const& witness); // The type that the witness table witnesses conformance to (e.g. an Interface) FIDDLE() Type* baseType; // The type witnessesd by the witness table (a concrete type). FIDDLE() Type* witnessedType; // Whether or not this witness table is an extern declaration. FIDDLE() bool isExtern = false; // Cached dictionary for looking up satisfying values. FIDDLE() RequirementDictionary m_requirementDictionary; RefPtr specialize(ASTBuilder* astBuilder, SubstitutionSet const& subst); }; struct SpecializationParam { enum class Flavor { GenericType, GenericValue, ExistentialType, ExistentialValue, }; Flavor flavor; SourceLoc loc; NodeBase* object = nullptr; }; typedef List SpecializationParams; struct SpecializationArg { Val* val = nullptr; Expr* expr = nullptr; }; typedef List SpecializationArgs; struct ExpandedSpecializationArg : SpecializationArg { Val* witness = nullptr; }; typedef List ExpandedSpecializationArgs; /// A reference-counted object to hold a list of candidate extensions /// that might be applicable to a type based on its declaration. /// FIDDLE() class CandidateExtensionList : public RefObject { FIDDLE(...) List candidateExtensions; }; enum class DeclAssociationKind { ForwardDerivativeFunc, BackwardDerivativeFunc, PrimalSubstituteFunc }; FIDDLE() class DeclAssociation : public RefObject { FIDDLE(...) FIDDLE() DeclAssociationKind kind; FIDDLE() Decl* decl; }; /// A reference-counted object to hold a list of associated decls for a decl. /// FIDDLE() class DeclAssociationList : public RefObject { FIDDLE(...) List> associations; }; /// Represents the "direction" that a parameter is being passed (e.g., `in` or `out` enum class ParamPassingMode { /// Pass a value as input. /// /// Indicated by using the `in` modifier on a parameter, /// or simply using no modifier (on a parameter of /// copyable type). This is the default mode. /// /// Must (almost by definition) be passed a copy /// of the argument. /// /// In, /// Pass a reference to a memory location, to be used for output. /// /// Indicated by using the `out` modifier on a parameter /// (while not also using `in`). /// /// May semantically be implemented by directly passing a /// reference to the argument, or a reference to a temporary /// that is moved out of after the call. /// /// Storage at the passed-in address should be unintialized /// on input, and will be must be initialized by the callee /// on any normal return path. On an error return, the storage /// must be uninitialized. /// Out, /// Pass a reference to a borrowed immutable value. /// /// Indicated by using the `borrow` modifier on a parameter, /// or the combination of `borrow` and `in` (while not also /// using `out`). /// /// May semantically be implemented by directly passing a /// reference to the argument, or a reference to a temporary /// that is moved/copied into before the call. /// /// Storage at the passed-in address must be guaranteed (by /// the caller) to be immutable for the duration of the call. /// BorrowIn, /// Pass a reference to a borrowed mutable value. /// /// Indicated by using the `inout` modifier on a parameter, /// or the combination of `in` and `out`; may also be combined /// with `borrow` to make the semantics more clear. /// /// May semantically be implemented by directly passing a /// reference to the argument, or a reference to a temporary /// that is moved/copied into before the call, and then /// moved/copied out of after the call. /// /// Storage at the passed-in address must be guaranteed (by /// the caller) to not be be accessed (including both reads /// and writes) via any other potentially-aliasing access path. /// Put another way, the callee has exclusive access to the /// memory location for the duration of the call. /// BorrowInOut, /// Pass a reference to a mutable memory location. /// /// Indicated by using the `ref` modifier on a parmater /// (without also using the `readonly` or `writeonly` modifiers). /// /// Must be implemented by directly passing a reference to /// the memory location of the argument. It is an error if /// an argument resolves to a storage location that is not /// a memory location (and cannot be turned into a memory /// location via, e.g., a `ref` accessor). /// /// The memory location at the passed-in address may be /// accessed (for reads, writes, atomics, etc.) during the /// duration of the call, through access paths that alias /// the parameter. The callee does not have a guarantee /// of exclusivity of access to the memory location, and /// must take appropriate precautions to ensure consistency, /// coherency, and synchronization of access. /// /// This parameter-passing mode is more-or-less just syntactic /// sugar for a parameter of an explicit pointer type (`Ptr`). /// Ref, }; void printDiagnosticArg(StringBuilder & sb, ParamPassingMode direction); /// The kind of a builtin interface requirement that can be automatically synthesized. enum class BuiltinRequirementKind { DefaultInitializableConstructor, ///< The `IDefaultInitializable.__init()` method DifferentialType, ///< The `IDifferentiable.Differential` associated type requirement DifferentialPtrType, ///< The `IDifferentiable.DifferentialPtr` associated type requirement DZeroFunc, ///< The `IDifferentiable.dzero` function requirement DAddFunc, ///< The `IDifferentiable.dadd` function requirement DMulFunc, ///< The `IDifferentiable.dmul` function requirement InitLogicalFromInt, ///< The `ILogical.__init` mtehod. Equals, ///< The `ILogical.equals` mtehod. LessThan, ///< The `ILogical.lessThan` mtehod. LessThanOrEquals, ///< The `ILogical.lessThanOrEquals` mtehod. Shl, ///< The `ILogical.shl` mtehod. Shr, ///< The `ILogical.shr` mtehod. BitAnd, ///< The `ILogical.bitAnd` mtehod. BitOr, ///< The `ILogical.bitOr` mtehod. BitXor, ///< The `ILogical.bitXor` mtehod. BitNot, ///< The `ILogical.bitNot` mtehod. And, ///< The `ILogical.and` mtehod. Or, ///< The `ILogical.or` mtehod. Not, ///< The `ILogical.not` mtehod. }; enum class FunctionDifferentiableLevel { None, Forward, Backward }; /// Represents a markup (documentation) associated with a decl. FIDDLE() class MarkupEntry : public RefObject { FIDDLE(...) NodeBase* m_node; ///< The node this documentation is associated with String m_markup; ///< The raw contents of of markup associated with the decoration MarkupVisibility m_visibility = MarkupVisibility::Public; ///< How visible this decl is }; /// Get the inner most expr from an higher order expr chain, e.g. `__fwd_diff(__fwd_diff(f))`'s /// inner most expr is `f`. Expr* getInnerMostExprFromHigherOrderExpr( Expr * expr, FunctionDifferentiableLevel & outDiffLevel); inline Expr* getInnerMostExprFromHigherOrderExpr(Expr * expr) { FunctionDifferentiableLevel level; return getInnerMostExprFromHigherOrderExpr(expr, level); } /// Get the operator name from the higher order invoke expr. UnownedStringSlice getHigherOrderOperatorName(HigherOrderInvokeExpr * expr); enum class DeclVisibility { Private, Internal, Public, Default = Internal, }; } // namespace Slang