https://github.com/shader-slang/slang
Tip revision: 5902acdabc4445a65741a7a6a3a95f223e301059 authored by Yong He on 23 January 2024, 07:19:40 UTC
[LSP] Fetch configs directly from didConfigurationChanged message. (#3478)
[LSP] Fetch configs directly from didConfigurationChanged message. (#3478)
Tip revision: 5902acd
slang-ast-support-types.h
#ifndef SLANG_AST_SUPPORT_TYPES_H
#define SLANG_AST_SUPPORT_TYPES_H
#include "../core/slang-basic.h"
#include "../compiler-core/slang-lexer.h"
#include "../compiler-core/slang-name.h"
#include "../compiler-core/slang-doc-extractor.h"
#include "slang-profile.h"
#include "slang-type-system-shared.h"
#include "../../slang.h"
#include "../core/slang-semantic-version.h"
#include "slang-generated-ast.h"
#include "slang-serialize-reflection.h"
#include "slang-ast-reflect.h"
#include "slang-ref-object-reflect.h"
#include <assert.h>
#include <type_traits>
namespace Slang
{
class Module;
class Name;
class Session;
class SyntaxVisitor;
class FuncDecl;
class Layout;
struct IExprVisitor;
struct IDeclVisitor;
struct IModifierVisitor;
struct IStmtVisitor;
struct ITypeVisitor;
struct IValVisitor;
class Parser;
class SyntaxNode;
class Decl;
struct QualType;
class Type;
struct TypeExp;
class Val;
class NodeBase;
class LookupDeclRef;
class GenericAppDeclRef;
template <typename T>
T* as(NodeBase* node);
template <typename T>
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);
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);
typedef NodeBase* (*SyntaxParseCallback)(Parser* parser, void* userData);
typedef unsigned int ConversionCost;
enum : ConversionCost
{
// No conversion at all
kConversionCost_None = 0,
kConversionCost_GenericParamUpcast = 1,
kConversionCost_UnconstraintGenericParam = 20,
// 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<Foo>`
// from one on `Foo`
kConversionCost_GetRef = 5,
kConversionCost_ImplicitDereference = 10,
kConversionCost_InRangeIntLitConversion = 23,
kConversionCost_InRangeIntLitSignedToUnsignedConversion = 32,
kConversionCost_InRangeIntLitUnsignedToSignedConversion = 81,
// 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,
// 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 = 250,
// Conversion from signed->unsigned integer of same or greater size
kConversionCost_SignedToUnsignedConversion = 300,
// Cost of converting an integer to a floating-point type
kConversionCost_IntegerToFloatConversion = 400,
// Cost of converting a pointer to bool
kConversionCost_PtrToBool = 400,
// 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_ScalarToVector = 1,
// Additional cost when casting an LValue.
kConversionCost_LValueCast = 800,
// Conversion is impossible
kConversionCost_Impossible = 0xFFFFFFFF,
};
typedef unsigned int BuiltinConversionKind;
enum : BuiltinConversionKind
{
kBuiltinConversion_Unknown = 0,
kBuiltinConversion_FloatToDouble = 1,
};
enum class ImageFormat
{
#define FORMAT(NAME, OTHER) NAME,
#include "slang-image-format-defs.h"
};
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);
// 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
struct NameLoc
{
Name* name;
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<typename T>
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<typename T>
FilteredModifierList<T> getModifiersOfType() { return FilteredModifierList<T>(first); }
// Find the first modifier of a given type, or return `nullptr` if none is found.
template<typename T>
T* findModifier()
{
return *getModifiersOfType<T>().begin();
}
template<typename T>
bool hasModifier() { return findModifier<T>() != nullptr; }
/// True if has no modifiers
bool isEmpty() const { return first == nullptr; }
FilteredModifierList<Modifier>::Iterator begin() { return FilteredModifierList<Modifier>::Iterator(first); }
FilteredModifierList<Modifier>::Iterator end() { return FilteredModifierList<Modifier>::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,
/// 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 declaration is fully 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.
///
Checked,
// 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
{
SLANG_VALUE_CLASS(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);
struct QualType
{
SLANG_VALUE_CLASS(QualType)
Type* type = nullptr;
bool isLeftValue;
QualType()
: isLeftValue(false)
{}
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 ASTClassInfo
{
struct Infos
{
const ReflectClassInfo* infos[int(ASTNodeType::CountOf)];
};
SLANG_FORCE_INLINE static const ReflectClassInfo* getInfo(ASTNodeType type) { return kInfos.infos[int(type)]; }
static const Infos kInfos;
};
// A reference to a class of syntax node, that can be
// used to create instances on the fly
struct SyntaxClassBase
{
SyntaxClassBase()
{}
SyntaxClassBase(ReflectClassInfo const* inClassInfo)
: classInfo(inClassInfo)
{}
void* createInstanceImpl(ASTBuilder* astBuilder) const
{
auto ci = classInfo;
if (!ci) return nullptr;
auto cf = ci->m_createFunc;
if (!cf) return nullptr;
return cf(astBuilder);
}
SLANG_FORCE_INLINE bool isSubClassOfImpl(SyntaxClassBase const& super) const
{
return classInfo ? classInfo->isSubClassOf(*super.classInfo) : false;
}
ReflectClassInfo const* classInfo = nullptr;
};
template<typename T>
struct SyntaxClass : SyntaxClassBase
{
SyntaxClass()
{}
template <typename U>
SyntaxClass(SyntaxClass<U> const& other,
typename EnableIf<IsConvertible<T*, U*>::Value, void>::type* = 0)
: SyntaxClassBase(other.classInfo)
{
}
T* createInstance(ASTBuilder* astBuilder) const
{
return (T*)createInstanceImpl(astBuilder);
}
SyntaxClass(const ReflectClassInfo* inClassInfo):
SyntaxClassBase(inClassInfo)
{}
static SyntaxClass<T> getClass()
{
return SyntaxClass<T>(&T::kReflectClassInfo);
}
template<typename U>
bool isSubClassOf(SyntaxClass<U> super)
{
return isSubClassOfImpl(super);
}
template<typename U>
bool isSubClassOf()
{
return isSubClassOf(SyntaxClass<U>::getClass());
}
template<typename U>
bool operator==(const SyntaxClass<U> other) const
{
return classInfo == other.classInfo;
}
template<typename U>
bool operator!=(const SyntaxClass<U> other) const
{
return classInfo != other.classInfo;
}
};
template<typename T>
SyntaxClass<T> getClass()
{
return SyntaxClass<T>::getClass();
}
struct SubstitutionSet
{
DeclRefBase* declRef = nullptr;
SubstitutionSet() = default;
SubstitutionSet(DeclRefBase* declRefBase)
:declRef(declRefBase)
{}
explicit operator bool() const;
template<typename F>
void forEachGenericSubstitution(F func) const;
template<typename F>
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<T>`. Code should prefer to use `SubstExpr<Expr>` 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<typename T>
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 <typename U>
SubstExpr(SubstExpr<U> const& other,
typename EnableIf<IsConvertible<T*, U*>::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<typename U>
SubstExpr<U> as()
{
return SubstExpr<U>(Slang::as<U>(getExpr()), getSubsts());
}
};
SubstExpr<Expr> applySubstitutionToExpr(SubstitutionSet substSet, Expr* expr);
class ASTBuilder;
template<typename T>
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<Decl> declRef);
template<typename T = Decl>
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 <typename U, typename = typename EnableIf<IsConvertible<T*, U*>::Value, void>::type>
DeclRef(DeclRef<U> const& other)
: declRefBase(other.declRefBase)
{}
T* getDecl() const;
Name* getName() const;
SourceLoc getNameLoc() const;
SourceLoc getLoc() const;
DeclRef<ContainerDecl> getParent() const;
HashCode getHashCode() const;
Type* substitute(ASTBuilder* astBuilder, Type* type) const;
SubstExpr<Expr> substitute(ASTBuilder* astBuilder, Expr* expr) const;
// Apply substitutions to a type or declaration
template<typename U>
DeclRef<U> substitute(ASTBuilder* astBuilder, DeclRef<U> declRef) const;
// Apply substitutions to this declaration reference
DeclRef<T> substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) const;
template<typename U>
DeclRef<U> as() const
{
DeclRef<U> result = DeclRef<U>(declRefBase);
return result;
}
template<typename U>
bool is() const
{
return Slang::as<U>(static_cast<NodeBase*>(getDecl())) != nullptr;
}
operator DeclRefBase* () const
{
return declRefBase;
}
operator DeclRef<Decl>() const
{
return DeclRef<Decl>(declRefBase);
}
template<typename U>
bool equals(DeclRef<U> other) const
{
return declRefBase == other.declRefBase;
}
template<typename U>
bool operator == (DeclRef<U> other) const
{
return equals(other);
}
template<typename U>
bool operator != (DeclRef<U> other) const
{
return !equals(other);
}
explicit operator bool() const
{
return declRefBase;
}
};
template<typename T>
inline DeclRef<T> makeDeclRef(T* decl)
{
return DeclRef<T>(decl);
}
SubstExpr<Expr> substituteExpr(SubstitutionSet const& substs, Expr* expr);
DeclRef<Decl> substituteDeclRef(SubstitutionSet const& substs, ASTBuilder* astBuilder, DeclRef<Decl> 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 <typename T>
Decl*const* adjustFilterCursor(MemberFilterStyle filterStyle, Decl*const* ptr, Decl*const* end)
{
return adjustFilterCursorImpl(T::kReflectClassInfo, 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 <typename T>
Decl*const* getFilterCursorByIndex(MemberFilterStyle filterStyle, Decl*const* ptr, Decl*const* end, Index index)
{
return getFilterCursorByIndexImpl(T::kReflectClassInfo, filterStyle, ptr, end, index);
}
template <typename T>
Index getFilterCount(MemberFilterStyle filterStyle, Decl*const* ptr, Decl*const* end)
{
return getFilterCountImpl(T::kReflectClassInfo, filterStyle, ptr, end);
}
template <typename T>
bool isFilterNonEmpty(MemberFilterStyle filterStyle, Decl*const* ptr, Decl*const* end)
{
return adjustFilterCursorImpl(T::kReflectClassInfo, filterStyle, ptr, end) != end;
}
template<typename T>
struct FilteredMemberList
{
typedef Decl* Element;
FilteredMemberList()
: m_begin(nullptr)
, m_end(nullptr)
{}
explicit FilteredMemberList(
List<Element> const& list,
MemberFilterStyle filterStyle = MemberFilterStyle::All)
: m_begin(adjustFilterCursor<T>(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<T>(m_filterStyle, m_cursor + 1, m_end); }
T* operator*() { return static_cast<T*>(*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<T>(m_filterStyle, m_begin, m_end); }
T* operator[](Index index) const
{
Decl*const* ptr = getFilterCursorByIndex<T>(m_filterStyle, m_begin, m_end, index);
SLANG_ASSERT(ptr);
return static_cast<T*>(*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<T*> toList()
{
List<T*> 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;
};
struct TransparentMemberInfo
{
// The declaration of the transparent member
Decl* decl = nullptr;
};
template<typename T>
struct FilteredMemberRefList
{
List<Decl*> const& m_decls;
DeclRef<Decl> m_parent;
MemberFilterStyle m_filterStyle;
ASTBuilder* m_astBuilder;
FilteredMemberRefList(
ASTBuilder* astBuilder,
List<Decl*> const& decls,
DeclRef<Decl> parent,
MemberFilterStyle filterStyle = MemberFilterStyle::All)
: m_decls(decls)
, m_parent(parent)
, m_filterStyle(filterStyle)
, m_astBuilder(astBuilder)
{}
Index getCount() const { return getFilterCount<T>(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<T>(m_filterStyle, m_decls.begin(), m_decls.end()); }
DeclRef<T> getFirstOrNull() { return isEmpty() ? DeclRef<T>() : (*this)[0]; }
DeclRef<T> operator[](Index index) const
{
Decl*const* decl = getFilterCursorByIndex<T>(m_filterStyle, m_decls.begin(), m_decls.end(), index);
SLANG_ASSERT(decl);
return _getMemberDeclRef(m_astBuilder, m_parent, (T*)*decl).template as<T>();
}
List<DeclRef<T>> toArray() const
{
List<DeclRef<T>> 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<T>(m_filterStyle, m_ptr + 1, m_end); }
DeclRef<T> operator*() { return _getMemberDeclRef(m_list->m_astBuilder, m_list->m_parent, (T*)*m_ptr).template as<T>(); }
};
Iterator begin() const { return Iterator(this, adjustFilterCursor<T>(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.
struct TypeExp
{
SLANG_VALUE_CLASS(TypeExp)
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(); }
ThisType& operator=(const ThisType& rhs) = default;
//TypeExp accept(SyntaxVisitor* visitor);
/// 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,
Default = type | Function | Value,
};
/// 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.
};
inline LookupOptions operator&(LookupOptions a, LookupOptions b)
{
return (LookupOptions)((std::underlying_type_t<LookupOptions>)a & (std::underlying_type_t<LookupOptions>)b);
}
class SerialRefObject;
// Make sure C++ extractor can see the base class.
SLANG_PRE_DECLARE(OBJ, class SerialRefObject)
SLANG_TYPE_SET(OBJ, RefObject)
SLANG_TYPE_SET(VALUE, Value)
SLANG_TYPE_SET(AST, ASTNode)
class LookupResultItem_Breadcrumb : public SerialRefObject
{
public:
SLANG_OBJ_CLASS(LookupResultItem_Breadcrumb)
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<Decl> declRef;
Val* val = nullptr;
// The next implicit step that the lookup process took to
// arrive at a final value.
RefPtr<LookupResultItem_Breadcrumb> next;
LookupResultItem_Breadcrumb(
Kind kind,
DeclRef<Decl> declRef,
Val* val,
RefPtr<LookupResultItem_Breadcrumb> 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
{
SLANG_VALUE_CLASS(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<T>` where `T` is a `struct` containing
// all the members:
//
// struct Anon0 { float4 f; };
// __transparent ConstantBuffer<Anon0> 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<Anon0>`
// 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<Decl> 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<LookupResultItem_Breadcrumb> breadcrumbs;
LookupResultItem() = default;
explicit LookupResultItem(DeclRef<Decl> declRef)
: declRef(declRef)
{}
LookupResultItem(DeclRef<Decl> declRef, RefPtr<Breadcrumb> 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<LookupResultItem> 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<LookupResultItem*>(items.begin());
else
return const_cast<LookupResultItem*>(&item);
}
else
return nullptr;
}
LookupResultItem* end() const
{
if (isValid())
{
if (isOverloaded())
return const_cast<LookupResultItem*>(items.end());
else
return const_cast<LookupResultItem*>(&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;
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; }
};
struct WitnessTable;
// A value that witnesses the satisfaction of an interface
// requirement by a particular declaration or value.
struct RequirementWitness
{
SLANG_VALUE_CLASS(RequirementWitness)
RequirementWitness()
: m_flavor(Flavor::none)
{}
RequirementWitness(DeclRefBase* declRef)
: m_flavor(Flavor::declRef)
, m_declRef(declRef)
{}
RequirementWitness(Val* val);
RequirementWitness(RefPtr<WitnessTable> witnessTable);
enum class Flavor
{
none,
declRef,
val,
witnessTable,
};
Flavor getFlavor() const
{
return m_flavor;
}
DeclRef<Decl> getDeclRef()
{
SLANG_ASSERT(getFlavor() == Flavor::declRef);
return m_declRef;
}
Val* getVal()
{
SLANG_ASSERT(getFlavor() == Flavor::val);
return m_val;
}
RefPtr<WitnessTable> getWitnessTable();
RequirementWitness specialize(ASTBuilder* astBuilder, SubstitutionSet const& subst);
Flavor m_flavor;
DeclRef<Decl> m_declRef;
RefPtr<RefObject> m_obj;
Val* m_val = nullptr;
};
typedef OrderedDictionary<Decl*, RequirementWitness> RequirementDictionary;
struct WitnessTable : SerialRefObject
{
SLANG_OBJ_CLASS(WitnessTable)
const RequirementDictionary& getRequirementDictionary()
{
if (m_requirementDictionary.getCount() != m_requirements.getCount())
{
for (Index i = m_requirementDictionary.getCount(); i < m_requirements.getCount(); i++)
{
auto& r = m_requirements[i];
m_requirementDictionary.add(r.key, r.value);
}
}
return m_requirementDictionary;
}
void add(Decl* decl, RequirementWitness const& witness);
// The type that the witness table witnesses conformance to (e.g. an Interface)
Type* baseType;
// The type witnessesd by the witness table (a concrete type).
Type* witnessedType;
// Satisfying values of each requirement.
List<KeyValuePair<Decl*, RequirementWitness>> m_requirements;
// Cached dictionary for looking up satisfying values.
SLANG_UNREFLECTED RequirementDictionary m_requirementDictionary;
RefPtr<WitnessTable> specialize(ASTBuilder* astBuilder, SubstitutionSet const& subst);
};
struct SpecializationParam
{
enum class Flavor
{
GenericType,
GenericValue,
ExistentialType,
ExistentialValue,
};
Flavor flavor;
SourceLoc loc;
NodeBase* object = nullptr;
};
typedef List<SpecializationParam> SpecializationParams;
struct SpecializationArg
{
SLANG_VALUE_CLASS(SpecializationArg)
Val* val = nullptr;
};
typedef List<SpecializationArg> SpecializationArgs;
struct ExpandedSpecializationArg : SpecializationArg
{
SLANG_VALUE_CLASS(ExpandedSpecializationArg)
Val* witness = nullptr;
};
typedef List<ExpandedSpecializationArg> ExpandedSpecializationArgs;
/// A reference-counted object to hold a list of candidate extensions
/// that might be applicable to a type based on its declaration.
///
struct CandidateExtensionList : RefObject
{
List<ExtensionDecl*> candidateExtensions;
};
enum class DeclAssociationKind
{
ForwardDerivativeFunc, BackwardDerivativeFunc, PrimalSubstituteFunc
};
struct DeclAssociation : SerialRefObject
{
SLANG_OBJ_CLASS(DeclAssociation)
DeclAssociationKind kind;
Decl* decl;
};
/// A reference-counted object to hold a list of associated decls for a decl.
///
struct DeclAssociationList : SerialRefObject
{
SLANG_OBJ_CLASS(DeclAssociationList)
List<RefPtr<DeclAssociation>> associations;
};
/// Represents the "direction" that a parameter is being passed (e.g., `in` or `out`
enum ParameterDirection
{
kParameterDirection_In, ///< Copy in
kParameterDirection_Out, ///< Copy out
kParameterDirection_InOut, ///< Copy in, copy out
kParameterDirection_Ref, ///< By-reference
kParameterDirection_ConstRef, ///< By-const-reference
};
/// The kind of a builtin interface requirement that can be automatically synthesized.
enum class BuiltinRequirementKind
{
DifferentialType, ///< The `IDifferentiable.Differential` associated type requirement
DZeroFunc, ///< The `IDifferentiable.dzero` function requirement
DAddFunc, ///< The `IDifferentiable.dadd` function requirement
DMulFunc, ///< The `IDifferentiable.dmul` function requirement
};
enum class FunctionDifferentiableLevel
{
None,
Forward,
Backward
};
/// Represents a markup (documentation) associated with a decl.
struct MarkupEntry : public SerialRefObject
{
SLANG_OBJ_CLASS(MarkupEntry)
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
#endif