https://github.com/shader-slang/slang
Raw File
Tip revision: 5902acdabc4445a65741a7a6a3a95f223e301059 authored by Yong He on 23 January 2024, 07:19:40 UTC
[LSP] Fetch configs directly from didConfigurationChanged message. (#3478)
Tip revision: 5902acd
slang-ast-base.h
// slang-ast-base.h

#pragma once

#include "slang-ast-support-types.h"

#include "slang-generated-ast.h"
#include "slang-ast-reflect.h"

#include "slang-serialize-reflection.h"

// This file defines the primary base classes for the hierarchy of
// AST nodes and related objects. For example, this is where the
// basic `Decl`, `Stmt`, `Expr`, `type`, etc. definitions come from.

namespace Slang
{  

class ASTBuilder;
struct SemanticsVisitor;

class NodeBase 
{
    SLANG_ABSTRACT_AST_CLASS(NodeBase)

        // MUST be called before used. Called automatically via the ASTBuilder.
        // Note that the astBuilder is not stored in the NodeBase derived types by default.
    SLANG_FORCE_INLINE void init(ASTNodeType inAstNodeType, ASTBuilder* inAstBuilder)
    {
        SLANG_UNUSED(inAstBuilder);
        astNodeType = inAstNodeType;
#ifdef _DEBUG
        _initDebug(inAstNodeType, inAstBuilder);
#endif
    }

    void _initDebug(ASTNodeType inAstNodeType, ASTBuilder* inAstBuilder);

        /// Get the class info 
    SLANG_FORCE_INLINE const ReflectClassInfo& getClassInfo() const { return *ASTClassInfo::getInfo(astNodeType); }

    SyntaxClass<NodeBase> getClass() { return SyntaxClass<NodeBase>(&getClassInfo()); }

        /// The type of the node. ASTNodeType(-1) is an invalid node type, and shouldn't appear on any
        /// correctly constructed (through ASTBuilder) NodeBase derived class. 
        /// The actual type is set when constructed on the ASTBuilder. 
    ASTNodeType astNodeType = ASTNodeType(-1);

#ifdef _DEBUG
    SLANG_UNREFLECTED int32_t _debugUID = 0;
#endif
};

// Casting of NodeBase

template<typename T>
SLANG_FORCE_INLINE T* dynamicCast(NodeBase* node)
{
    return (node && ReflectClassInfo::isSubClassOf(uint32_t(node->astNodeType), T::kReflectClassInfo)) ? static_cast<T*>(node) : nullptr;
}

template<typename T>
SLANG_FORCE_INLINE const T* dynamicCast(const NodeBase* node)
{
    return (node && ReflectClassInfo::isSubClassOf(uint32_t(node->astNodeType), T::kReflectClassInfo)) ? static_cast<const T*>(node) : nullptr;
}

template<typename T>
SLANG_FORCE_INLINE T* as(NodeBase* node)
{
    return (node && ReflectClassInfo::isSubClassOf(uint32_t(node->astNodeType), T::kReflectClassInfo)) ? static_cast<T*>(node) : nullptr;
}

template<typename T>
SLANG_FORCE_INLINE const T* as(const NodeBase* node)
{
    return (node && ReflectClassInfo::isSubClassOf(uint32_t(node->astNodeType), T::kReflectClassInfo)) ? static_cast<const T*>(node) : nullptr;
}

// Because DeclRefBase is now a `Val`, we prevent casting it directly into other nodes
// to avoid confusion and bugs. Instead, use the `as<>()` method on `DeclRefBase` to
// get a `DeclRef<T>` for a specific node type.
template<typename T>
T* as(DeclRefBase* declRefBase, typename EnableIf<!IsBaseOf<DeclRefBase, T>::Value, void*>::type arg = nullptr) = delete;

template<typename T>
T* as(DeclRefBase* declRefBase, typename EnableIf<IsBaseOf<DeclRefBase, T>::Value, void*>::type arg = nullptr)
{
    SLANG_UNUSED(arg);
    return dynamicCast<T>(declRefBase);
}

template<typename T, typename U>
DeclRef<T> as(DeclRef<U> declRef) { return DeclRef<T>(declRef); }

struct Scope : public NodeBase
{
    SLANG_AST_CLASS(Scope)
    
    // The container to use for lookup
    //
    // Note(tfoley): This is kept as an unowned pointer
    // so that a scope can't keep parts of the AST alive,
    // but the opposite it allowed.
    ContainerDecl*          containerDecl = nullptr;

    SLANG_UNREFLECTED
    // The parent of this scope (where lookup should go if nothing is found locally)
    Scope*                  parent = nullptr;

    // The next sibling of this scope (a peer for lookup)
    Scope*                  nextSibling = nullptr;
};

// Base class for all nodes representing actual syntax
// (thus having a location in the source code)
class SyntaxNodeBase : public NodeBase
{
    SLANG_ABSTRACT_AST_CLASS(SyntaxNodeBase)

    // The primary source location associated with this AST node
    SourceLoc loc;
};

enum class ValNodeOperandKind
{
    ConstantValue,
    ValNode,
    ASTNode,
};

struct ValNodeOperand
{
    ValNodeOperandKind kind = ValNodeOperandKind::ConstantValue;
    union
    {
        NodeBase* nodeOperand;
        int64_t intOperand;
    } values;

    ValNodeOperand()
    {
        values.intOperand = 0;
    }

    explicit ValNodeOperand(NodeBase* node)
    {
        if constexpr(sizeof(values.nodeOperand) < sizeof(values.intOperand))
            values.intOperand = 0;

        if (as<Val>(node))
        {
            values.nodeOperand = (NodeBase*)node;
            kind = ValNodeOperandKind::ValNode;
        }
        else
        {
            values.nodeOperand = node;
            kind = ValNodeOperandKind::ASTNode;
        }
    }

    template<typename T>
    explicit ValNodeOperand(DeclRef<T> declRef)
    { 
        if constexpr (sizeof(values.nodeOperand) < sizeof(values.intOperand))
            values.intOperand = 0;
        values.nodeOperand = declRef.declRefBase; kind = ValNodeOperandKind::ValNode;
    }

    template<typename T>
    explicit ValNodeOperand(T* node)
    {
        if constexpr (sizeof(values.nodeOperand) < sizeof(values.intOperand))
            values.intOperand = 0;
        if constexpr (std::is_base_of<Val, T>::value)
        {
            values.nodeOperand = (NodeBase*)node;
            kind = ValNodeOperandKind::ValNode;
        }
        else if constexpr (std::is_base_of<NodeBase, T>::value)
        {
            values.nodeOperand = node;
            kind = ValNodeOperandKind::ASTNode;
        }
        else
        {
            static_assert(std::is_base_of<Val, T>::value || std::is_base_of<NodeBase, T>::value, "pointer used as Val operand must be an AST node.");
        }
    }

    template<typename EnumType>
    explicit ValNodeOperand(EnumType intVal)
    {
        static_assert(std::is_trivial<EnumType>::value, "Type to construct NodeOperand must be trivial.");
        static_assert(sizeof(EnumType) <= sizeof(values), "size of operand must be less than pointer size.");
        values.intOperand = 0;
        memcpy(&values, &intVal, sizeof(intVal));
        kind = ValNodeOperandKind::ConstantValue;
    }
};

struct ValNodeDesc
{
private:
    HashCode hashCode = 0;
public:
    ASTNodeType             type;
    ShortList<ValNodeOperand, 8> operands;

    inline bool operator==(ValNodeDesc const& that) const
    {
        if (hashCode != that.hashCode) return false;
        if (type != that.type) return false;
        if (operands.getCount() != that.operands.getCount()) return false;
        for (Index i = 0; i < operands.getCount(); ++i)
        {
            // Note: we are comparing the operands directly for identity
            // (pointer equality) rather than doing the `Val`-level
            // equality check.
            //
            // The rationale here is that nodes that will be created
            // via a `NodeDesc` *should* all be going through the
            // deduplication path anyway, as should their operands.
            // 
            if (operands[i].values.intOperand != that.operands[i].values.intOperand) return false;
        }
        return true;
    }
    HashCode getHashCode() const { return hashCode; }
    void init();

};

template<int N>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>&)
{}

template<int N, typename... Ts>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>& list, ExpandedSpecializationArgs e, Ts... ts)
{
    for (auto arg : e)
    {
        list.add(ValNodeOperand(arg.val));
        list.add(ValNodeOperand(arg.witness));
    }
    addOrAppendToNodeList(list, ts...);
}

template<int N, typename T, typename... Ts>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>& list, T t, Ts... ts)
{
    list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

template<int N, typename T, typename... Ts>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>& list, const List<T>& l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

template<int N, typename T, typename... Ts>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>& list, ConstArrayView<T> l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

template<int N, typename T, typename... Ts>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>& list, ArrayView<T> l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

inline void addOrAppendToNodeList(List<ValNodeOperand>&)
{}

template<typename... Ts>
static void addOrAppendToNodeList(List<ValNodeOperand>& list, ExpandedSpecializationArgs e, Ts... ts)
{
    for (auto arg : e)
    {
        list.add(ValNodeOperand(arg.val));
        list.add(ValNodeOperand(arg.witness));
    }
    addOrAppendToNodeList(list, ts...);
}

template<typename T, typename... Ts>
static void addOrAppendToNodeList(List<ValNodeOperand>& list, T t, Ts... ts)
{
    list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

template<typename T, typename... Ts>
static void addOrAppendToNodeList(List<ValNodeOperand>& list, const List<T>& l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

template<typename T, typename... Ts>
static void addOrAppendToNodeList(List<ValNodeOperand>& list, ConstArrayView<T> l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

template<typename T, typename... Ts>
static void addOrAppendToNodeList(List<ValNodeOperand>& list, ArrayView<T> l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

// Base class for compile-time values (most often a type).
// These are *not* syntax nodes, because they do not have
// a unique location, and any two `Val`s representing
// the same value should be conceptually equal.

class Val : public NodeBase
{
    SLANG_ABSTRACT_AST_CLASS(Val)
    
    template<typename T>
    struct OperandView
    {
        const Val* val;
        Index offset;
        Index count;
        OperandView()
        {
            val = nullptr;
            offset = 0;
            count = 0;
        }
        OperandView(const Val* val, Index offset, Index count) : val(val), offset(offset), count(count) {}
        Index getCount() { return count; }
        T* operator[](Index index) const
        {
            return as<T>(val->getOperand(index + offset));
        }
        struct ConstIterator
        {
            const Val* val;
            Index i;
            bool operator==(ConstIterator other) const
            {
                return val == other.val && i == other.i;
            }
            bool operator!=(ConstIterator other) const
            {
                return val != other.val || i != other.i;
            }
            T *const & operator*() const
            {
                return *(this->operator->());
            }
            T *const * operator->() const
            {
                return reinterpret_cast<T *const *>(&val->m_operands[i].values.nodeOperand);
            }
            ConstIterator& operator++()
            {
                i++;
                return *this;
            }
        };
        ConstIterator begin() const { return ConstIterator { val, offset }; }
        ConstIterator end() const { return ConstIterator{ val, offset + count }; }
    };

    typedef IValVisitor Visitor;

    void accept(IValVisitor* visitor, void* extra);

    // construct a new value by applying a set of parameter
    // substitutions to this one
    Val* substitute(ASTBuilder* astBuilder, SubstitutionSet subst);

    // Lower-level interface for substitution. Like the basic
    // `Substitute` above, but also takes a by-reference
    // integer parameter that should be incremented when
    // returning a modified value (this can help the caller
    // decide whether they need to do anything).
    Val* substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);

    bool equals(Val* val) const
    {
        return this == val || (val && const_cast<Val*>(this)->resolve() == val->resolve());
    }

    // Appends as text to the end of the builder
    void toText(StringBuilder& out);
    String toString();

    HashCode getHashCode();
    bool operator == (const Val & v) const
    {
        return equals(const_cast<Val*>(&v));
    }

    // Overrides should be public so base classes can access
    Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);
    void _toTextOverride(StringBuilder& out);

    Val* _resolveImplOverride();

    Val* resolveImpl();
    Val* resolve();

    Val* getOperand(Index index) const
    {
        SLANG_ASSERT(m_operands[index].kind == ValNodeOperandKind::ValNode);
        return (Val*)m_operands[index].values.nodeOperand;
    }

    Decl* getDeclOperand(Index index) const
    {
        SLANG_ASSERT(m_operands[index].kind == ValNodeOperandKind::ASTNode);
        return (Decl*)(m_operands[index].values.nodeOperand);
    }

    int64_t getIntConstOperand(Index index) const
    {
        SLANG_ASSERT(m_operands[index].kind == ValNodeOperandKind::ConstantValue);
        return m_operands[index].values.intOperand;
    }

    Index getOperandCount() const { return m_operands.getCount(); }

    template<typename ... TArgs>
    void setOperands(TArgs... args)
    {
        m_operands.clear();
        addOrAppendToNodeList(m_operands, args...);
    }
    template<typename ... TArgs>
    void addOperands(TArgs... args)
    {
        addOrAppendToNodeList(m_operands, args...);
    }
    template<typename T>
    void addOperands(OperandView<T> operands)
    {
        for (auto v : operands)
            m_operands.add(ValNodeOperand(v));
    }
    List<ValNodeOperand> m_operands;

    // Private use by stdlib deserialization only. Since we know the Vals serialized into stdlib is already
    // unique, we can just use `this` pointer as the `m_resolvedVal` so we don't need to resolve them again.
    void _setUnique();
protected:
    Val* defaultResolveImpl();
private:
    mutable Val* m_resolvedVal = nullptr;
    SLANG_UNREFLECTED mutable Index m_resolvedValEpoch = 0;
};

template<int N, typename T, typename... Ts>
static void addOrAppendToNodeList(ShortList<ValNodeOperand, N>& list, Val::OperandView<T> l, Ts... ts)
{
    for (auto t : l)
        list.add(ValNodeOperand(t));
    addOrAppendToNodeList(list, ts...);
}

struct ValSet
{
    struct ValItem
    {
        Val* val = nullptr;
        ValItem() = default;
        ValItem(Val* v) : val(v) {}

        HashCode getHashCode() const
        {
            return val ? val->getHashCode() : 0;
        }
        bool operator==(const ValItem other) const
        {
            if (val == other.val)
                return true;
            if (val)
                return val->equals(other.val);
            else if (other.val)
                return other.val->equals(val);
            return false;
        }
    };
    HashSet<ValItem> set;
    bool add(Val* val)
    {
        return set.add(ValItem(val));
    }
    bool contains(Val* val)
    {
        return set.contains(ValItem(val));
    }
};


SLANG_FORCE_INLINE StringBuilder& operator<<(StringBuilder& io, Val* val) { SLANG_ASSERT(val); val->toText(io); return io; }

    /// Given a `value` that refers to a `param` of some generic, attempt to apply
    /// the `subst` to it and produce a new `Val` as a result.
    ///
    /// If the `subst` does not include anything to replace `value`, then this function
    /// returns null.
    ///
Val* maybeSubstituteGenericParam(Val* value, Decl* param, SubstitutionSet subst, int* ioDiff);

class Type;

template <typename T>
SLANG_FORCE_INLINE T* as(Type* obj);
template <typename T>
SLANG_FORCE_INLINE const T* as(const Type* obj);
    
// A type, representing a classifier for some term in the AST.
//
// Types can include "sugar" in that they may refer to a
// `typedef` which gives them a good name when printed as
// part of diagnostic messages.
//
// In order to operate on types, though, we often want
// to look past any sugar, and operate on an underlying
// "canonical" type. The representation caches a pointer to
// a canonical type on every type, so we can easily
// operate on the raw representation when needed.
class Type: public Val
{
    SLANG_ABSTRACT_AST_CLASS(Type)

    typedef ITypeVisitor Visitor;

    void accept(ITypeVisitor* visitor, void* extra);

        /// Type derived types store the AST builder they were constructed on. The builder calls this function
        /// after constructing.
    SLANG_FORCE_INLINE void init(ASTNodeType inAstNodeType, ASTBuilder* inAstBuilder)
    {
        Val::init(inAstNodeType, inAstBuilder);
        m_astBuilderForReflection = inAstBuilder;
    }

    // Overrides should be public so base classes can access
    Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);
    Type* _createCanonicalTypeOverride();
    Val* _resolveImplOverride();

    Type* getCanonicalType()
    {
        return as<Type>(resolve());
    }

    ASTBuilder* getASTBuilderForReflection() const { return m_astBuilderForReflection; }
protected:
    Type* createCanonicalType();

    // We store the ASTBuilder to support reflection API only.
    // It should not be used for anything else, especially not for constructing new AST nodes during
    // semantic checking, since Val deduplication requires the entire semantic checking process to
    // stick with one ASTBuilder.
    // Call getCurrentASTBuilder() to obtain the right ASTBuilder for semantic checking.
    SLANG_UNREFLECTED ASTBuilder* m_astBuilderForReflection;
};

template <typename T>
SLANG_FORCE_INLINE T* as(Type* obj) { return obj ? dynamicCast<T>(obj->getCanonicalType()) : nullptr; }
template <typename T>
SLANG_FORCE_INLINE const T* as(const Type* obj) { return obj ? dynamicCast<T>(const_cast<Type*>(obj)->getCanonicalType()) : nullptr; }

class Decl;

// A reference to a declaration, which may include
// substitutions for generic parameters.
class DeclRefBase : public Val
{
    SLANG_ABSTRACT_AST_CLASS(DeclRefBase)

    Decl* getDecl() const { return getDeclOperand(0); }

    // Apply substitutions to this declaration reference
    DeclRefBase* substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);

    DeclRefBase* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff)
    {
        SLANG_UNUSED(astBuilder);
        SLANG_UNUSED(subst);
        SLANG_UNUSED(ioDiff);
        SLANG_UNREACHABLE("DeclRefBase::_substituteImplOverride not overrided.");
    }

    void _toTextOverride(StringBuilder& out)
    {
        SLANG_UNUSED(out);
        SLANG_UNREACHABLE("DeclRefBase::_toTextOverride not overrided.");
    }

    Val* _resolveImplOverride()
    {
        SLANG_UNREACHABLE("DeclRefBase::_resolveImplOverride not overrided.");
    }

    DeclRefBase* _getBaseOverride()
    {
        SLANG_UNREACHABLE("DeclRefBase::_getBaseOverride not overrided.");
    }

    // Returns true if 'as' will return a valid cast
    template <typename T>
    bool is() const { return Slang::as<T>(getDecl()) != nullptr; }

    // Convenience accessors for common properties of declarations
    Name* getName() const;
    SourceLoc getNameLoc() const;
    SourceLoc getLoc() const;
    DeclRefBase* getParent();
    String toString() const
    {
        StringBuilder sb;
        const_cast<DeclRefBase*>(this)->toText(sb);
        return sb.produceString();
    }
    DeclRefBase* getBase();
    void toText(StringBuilder& out);
};

SLANG_FORCE_INLINE StringBuilder& operator<<(StringBuilder& io, const DeclRefBase* declRef) { if (declRef) const_cast<DeclRefBase*>(declRef)->toText(io); return io; }

SLANG_FORCE_INLINE StringBuilder& operator<<(StringBuilder& io, Decl* decl)
{
    if (decl)
        makeDeclRef(decl).declRefBase->toText(io);
    return io;
}

class SyntaxNode : public SyntaxNodeBase
{
    SLANG_ABSTRACT_AST_CLASS(SyntaxNode);
};

//
// All modifiers are represented as full-fledged objects in the AST
// (that is, we don't use a bitfield, even for simple/common flags).
// This ensures that we can track source locations for all modifiers.
//
class Modifier : public SyntaxNode
{
    SLANG_ABSTRACT_AST_CLASS(Modifier)
    typedef IModifierVisitor Visitor;

    void accept(IModifierVisitor* visitor, void* extra);

    // Next modifier in linked list of modifiers on same piece of syntax
    Modifier* next = nullptr;

    // The keyword that was used to introduce t that was used to name this modifier.
    Name* keywordName = nullptr;

    Name* getKeywordName() { return keywordName; }
    NameLoc getKeywordNameAndLoc() { return NameLoc(keywordName, loc); }
};

// A syntax node which can have modifiers applied
class ModifiableSyntaxNode : public SyntaxNode
{
    SLANG_ABSTRACT_AST_CLASS(ModifiableSyntaxNode)

    Modifiers modifiers;

    template<typename T>
    FilteredModifierList<T> getModifiersOfType() { return FilteredModifierList<T>(modifiers.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; }
};


// An intermediate type to represent either a single declaration, or a group of declarations
class DeclBase : public ModifiableSyntaxNode
{
    SLANG_ABSTRACT_AST_CLASS(DeclBase)

    typedef IDeclVisitor Visitor;

    void accept(IDeclVisitor* visitor, void* extra);
};

class Decl : public DeclBase
{
public:
    SLANG_ABSTRACT_AST_CLASS(Decl)

    ContainerDecl* parentDecl = nullptr;

    DeclRefBase* getDefaultDeclRef();

    NameLoc nameAndLoc;

    RefPtr<MarkupEntry> markup;

    Name*     getName() const      { return nameAndLoc.name; }
    SourceLoc getNameLoc() const   { return nameAndLoc.loc ; }
    NameLoc   getNameAndLoc() const { return nameAndLoc     ; }

    DeclCheckStateExt checkState = DeclCheckState::Unchecked;

    // The next declaration defined in the same container with the same name
    Decl* nextInContainerWithSameName = nullptr;

    bool isChecked(DeclCheckState state) const { return checkState >= state; }
    void setCheckState(DeclCheckState state)
    {
        SLANG_RELEASE_ASSERT(state >= checkState.getState());
        checkState.setState(state);
    }
    bool isChildOf(Decl* other) const;

private:
    SLANG_UNREFLECTED DeclRefBase* m_defaultDeclRef = nullptr;
    SLANG_UNREFLECTED Index m_defaultDeclRefEpoch = -1;
};

class Expr : public SyntaxNode
{
    SLANG_ABSTRACT_AST_CLASS(Expr)

    typedef IExprVisitor Visitor;

    QualType type;

    void accept(IExprVisitor* visitor, void* extra);
};

class Stmt : public ModifiableSyntaxNode
{
    SLANG_ABSTRACT_AST_CLASS(Stmt)

    typedef IStmtVisitor Visitor;

    void accept(IStmtVisitor* visitor, void* extra);
};

template<typename T>
void DeclRef<T>::init(DeclRefBase* base)
{
    if (base && !Slang::as<T>(base->getDecl()))
        declRefBase = nullptr;
    else
        declRefBase = base;
}

template<typename T>
DeclRef<T>::DeclRef(Decl* decl)
{
    DeclRefBase* declRef = nullptr;
    if (decl)
    {
        declRef = decl->getDefaultDeclRef();
    }
    init(declRef);
}

template<typename T>
T* DeclRef<T>::getDecl() const
{
    return declRefBase ? (T*)declRefBase->getDecl() : nullptr;
}

template<typename T>
Name* DeclRef<T>::getName() const
{
    if (declRefBase)
        return declRefBase->getName();
    return nullptr;
}

template<typename T>
SourceLoc DeclRef<T>::getNameLoc() const
{
    if (declRefBase) return declRefBase->getNameLoc();
    return SourceLoc();
}

template<typename T>
SourceLoc DeclRef<T>::getLoc() const
{
    if (declRefBase) return declRefBase->getLoc();
    return SourceLoc();
}

template<typename T>
DeclRef<ContainerDecl> DeclRef<T>::getParent() const
{
    if (declRefBase) return DeclRef<ContainerDecl>(declRefBase->getParent());
    return DeclRef<ContainerDecl>((DeclRefBase*)nullptr);
}

template<typename T>
HashCode DeclRef<T>::getHashCode() const
{
    if (declRefBase) return declRefBase->getHashCode();
    return 0;
}

template<typename T>
Type* DeclRef<T>::substitute(ASTBuilder* astBuilder, Type* type) const
{
    SLANG_UNUSED(astBuilder);
    if (!declRefBase) return type;
    return SubstitutionSet(*this).applyToType(astBuilder, type);
}

template<typename T>
SubstExpr<Expr> DeclRef<T>::substitute(ASTBuilder* astBuilder, Expr* expr) const
{
    SLANG_UNUSED(astBuilder);
    if (!declRefBase) return expr;
    return applySubstitutionToExpr(SubstitutionSet(*this), expr);
}

// Apply substitutions to a type or declaration
template<typename T>
template<typename U>
DeclRef<U> DeclRef<T>::substitute(ASTBuilder* astBuilder, DeclRef<U> declRef) const
{
    SLANG_UNUSED(astBuilder);
    if (!declRefBase) return declRef;
    return DeclRef<U>(SubstitutionSet(*this).applyToDeclRef(astBuilder, declRef.declRefBase));
}

// Apply substitutions to this declaration reference
template<typename T>
DeclRef<T> DeclRef<T>::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) const
{
    SLANG_UNUSED(astBuilder);
    if (!declRefBase) return *this;
    return DeclRef<T>(declRefBase->substituteImpl(astBuilder, subst, ioDiff));
}

Val::OperandView<Val> tryGetGenericArguments(SubstitutionSet substSet, Decl* genericDecl);


} // namespace Slang
back to top