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-check-impl.h
// slang-check-impl.h
#pragma once

// This file provides the private interfaces used by
// the various `slang-check-*` files that provide
// the semantic checking infrastructure.

#include "slang-check.h"
#include "slang-compiler.h"
#include "slang-visitor.h"

namespace Slang
{
        /// Should the given `decl` be treated as a static rather than instance declaration?
    bool isEffectivelyStatic(
        Decl*           decl);

    bool isGlobalDecl(Decl* decl);

    bool isUniformParameterType(Type* type);

    Type* checkProperType(
        Linkage*        linkage,
        TypeExp         typeExp,
        DiagnosticSink* sink);

        /// Get the element type if `type` is Ptr or PtrLike type, otherwise returns null.
        /// Note: this currently does not include PtrTypeBase.
    Type* getPointedToTypeIfCanImplicitDeref(Type* type);

    inline int getIntValueBitSize(IntegerLiteralValue val)
    {
        uint64_t v = val > 0 ? (uint64_t)val : (uint64_t)-val;
        int result = 1;
        while (v >>= 1)
        {
            result++;
        }
        return result;
    }

    // A flat representation of basic types (scalars, vectors and matrices)
    // that can be used as lookup key in caches
    struct BasicTypeKey
    {
        uint32_t baseType : 8;
        uint32_t dim1 : 4;
        uint32_t dim2 : 4;
        uint32_t knownConstantBitCount : 8;
        uint32_t knownNegative : 1;
        uint32_t isLValue : 1;
        uint32_t reserved : 6;
        uint32_t getRaw() const
        {
            uint32_t val;
            memcpy(&val, this, sizeof(uint32_t));
            return val;
        }
        bool operator==(BasicTypeKey other) const
        {
            return getRaw() == other.getRaw();
        }
        static BasicTypeKey invalid() { return BasicTypeKey{ 0xff, 0, 0, 0, 0, 0, 0 }; }
    };

    SLANG_FORCE_INLINE BasicTypeKey makeBasicTypeKey(BaseType baseType, IntegerLiteralValue dim1 = 0, IntegerLiteralValue dim2 = 0, bool inIsLValue = false)
    {
        SLANG_ASSERT(dim1 >= 0 && dim2 >= 0);
        return BasicTypeKey{ uint8_t(baseType), uint8_t(dim1), uint8_t(dim2), 0, 0, (inIsLValue?1u:0u), 0 };
    }

    inline BasicTypeKey makeBasicTypeKey(QualType typeIn, Expr* exprIn = nullptr)
    {
        if (auto basicType = as<BasicExpressionType>(typeIn))
        {
            auto rs = makeBasicTypeKey(basicType->getBaseType());
            if (auto constInt = as<IntegerLiteralExpr>(exprIn))
            {
                if (constInt->value < 0)
                {
                    rs.knownNegative = 1;
                }
                rs.knownConstantBitCount = getIntValueBitSize(constInt->value);
            }
            rs.isLValue = typeIn.isLeftValue ? 1u : 0u;
            return rs;
        }
        else if (auto vectorType = as<VectorExpressionType>(typeIn))
        {
            if (auto elemCount = as<ConstantIntVal>(vectorType->getElementCount()))
            {
                if( auto elemBasicType = as<BasicExpressionType>(vectorType->getElementType()) )
                {
                    return makeBasicTypeKey(elemBasicType->getBaseType(), elemCount->getValue(), 0, typeIn.isLeftValue);
                }
            }
        }
        else if (auto matrixType = as<MatrixExpressionType>(typeIn))
        {
            if (auto elemCount1 = as<ConstantIntVal>(matrixType->getRowCount()))
            {
                if (auto elemCount2 = as<ConstantIntVal>(matrixType->getColumnCount()))
                {
                    if( auto elemBasicType = as<BasicExpressionType>(matrixType->getElementType()) )
                    {
                        return makeBasicTypeKey(elemBasicType->getBaseType(), elemCount1->getValue(), elemCount2->getValue(), typeIn.isLeftValue);
                    }
                }
            }
        }
        return BasicTypeKey::invalid();
    }

    struct BasicTypeKeyPair
    {
        BasicTypeKey type1, type2;
        bool operator==(const BasicTypeKeyPair& rhs) const { return type1 == rhs.type1 && type2 == rhs.type2; }
        bool operator!=(const BasicTypeKeyPair& rhs) const { return !(*this == rhs); }

        bool isValid() const { return type1.getRaw() != BasicTypeKey::invalid().getRaw() && type2.getRaw() != BasicTypeKey::invalid().getRaw(); }

        HashCode getHashCode() const
        {
            return combineHash(type1.getRaw(), type2.getRaw());
        }
    };

    struct OperatorOverloadCacheKey
    {
        intptr_t operatorName;
        BasicTypeKey args[2];
        bool operator == (OperatorOverloadCacheKey key) const
        {
            return operatorName == key.operatorName && args[0] == key.args[0] && args[1] == key.args[1];
        }
        HashCode getHashCode() const
        {
            return combineHash((int)(UInt64)(void*)(operatorName), args[0].getRaw(), args[1].getRaw());
        }
        bool fromOperatorExpr(OperatorExpr* opExpr)
        {
            // First, lets see if the argument types are ones
            // that we can encode in our space of keys.
            args[0] = BasicTypeKey::invalid();
            args[1] = BasicTypeKey::invalid();
            if (opExpr->arguments.getCount() > 2)
                return false;

            for (Index i = 0; i < opExpr->arguments.getCount(); i++)
            {
                auto key = makeBasicTypeKey(opExpr->arguments[i]->type, opExpr->arguments[i]);
                if (key.getRaw() == BasicTypeKey::invalid().getRaw())
                {
                    return false;
                }
                args[i] = key;
            }

            // Next, lets see if we can find an intrinsic opcode
            // attached to an overloaded definition (filtered for
            // definitions that could conceivably apply to us).
            //
            // TODO: This should really be parsed on the operator name
            // plus fixity, rather than the intrinsic opcode...
            //
            // We will need to reject postfix definitions for prefix
            // operators, and vice versa, to ensure things work.
            //
            auto prefixExpr = as<PrefixExpr>(opExpr);
            auto postfixExpr = as<PostfixExpr>(opExpr);

            if (auto overloadedBase = as<OverloadedExpr>(opExpr->functionExpr))
            {
                for(auto item : overloadedBase->lookupResult2 )
                {
                    // Look at a candidate definition to be called and
                    // see if it gives us a key to work with.
                    //
                    Decl* funcDecl = item.declRef.getDecl();
                    if (auto genDecl = as<GenericDecl>(funcDecl))
                        funcDecl = genDecl->inner;

                    // Reject definitions that have the wrong fixity.
                    //
                    if(prefixExpr && !funcDecl->findModifier<PrefixModifier>())
                        continue;
                    if(postfixExpr && !funcDecl->findModifier<PostfixModifier>())
                        continue;

                    if (auto intrinsicOp = funcDecl->findModifier<IntrinsicOpModifier>())
                    {
                        operatorName = intrinsicOp->op;
                        return true;
                    }
                }
            }
            return false;
        }
    };

    struct OverloadCandidate
    {
        enum class Flavor
        {
            Func,
            Generic,
            UnspecializedGeneric,
            Expr,
        };
        Flavor flavor;

        enum class Status
        {
            GenericArgumentInferenceFailed,
            Unchecked,
            ArityChecked,
            FixityChecked,
            TypeChecked,
            DirectionChecked,
            VisibilityChecked,
            Applicable,
        };
        Status status = Status::Unchecked;

        typedef unsigned int Flags;
        enum Flag : Flags
        {
            IsPartiallyAppliedGeneric = 1 << 0,
        };
        Flags flags = 0;

        // Reference to the declaration being applied
        LookupResultItem item;

        // The expression when flavor is Expr.
        Expr* exprVal = nullptr;

        // Type of function being applied (for cases where `item` is not used)
        FuncType* funcType = nullptr;

        // The type of the result expression if this candidate is selected
        Type*	resultType = nullptr;

        // A system for tracking constraints introduced on generic parameters
        //            ConstraintSystem constraintSystem;

        // How much conversion cost should be considered for this overload,
        // when ranking candidates.
        ConversionCost conversionCostSum = kConversionCost_None;

        // When required, a candidate can store a pre-checked list of
        // arguments so that we don't have to repeat work across checking
        // phases. Currently this is only needed for generics.
        SubstitutionSet   subst;
    };

    struct TypeCheckingCache
    {
        Dictionary<OperatorOverloadCacheKey, OverloadCandidate> resolvedOperatorOverloadCache;
        Dictionary<BasicTypeKeyPair, ConversionCost> conversionCostCache;
    };

    enum class CoercionSite
    {
        General,
        Assignment,
        Argument,
        Return,
        Initializer,
    };

    struct FacetImpl;

        /// Information about one "facet" of a type or declaration
        ///
        /// In the simplest terms, a facet represents a grouping of
        /// member declarations that were all originally declared
        /// as part of the same `{}`-enclosed body.
        ///
        /// A given *entity* (a type, type declaration, or `extension`
        /// declaration) may have multiple facets, depending on what it
        /// declares, what it inherits from, or what `extension`s apply to it.
        ///
        /// Broadly, an entity will have:
        ///
        /// * A *self facet*, if it has a body, that contains the members
        ///   the entity directly declares.
        ///
        /// * An *inherited facet* for each base type that it (transitively)
        ///   inherits from. Inherited facets are either *direct*, if the
        ///   original entity stated the inheritance relationship, or
        ///   *indirect* if they arise from the transitive closure of the
        ///   inheritance relationship. Each inherited facet contains the
        ///   members of the entity that was inherited from.
        ///
        /// * An *extension facet* for each `extension` declaration that
        ///   is known to apply to the entity in the context where semantic
        ///   checking is being performed. Each extension facet contains the
        ///   members of the `extension` that applied.
        ///
    struct Facet
    {
    public:
            /// Kinds of facets that can occur
        enum class Kind
        {
            Type,
            Extension,
        };

            /// How many indirections away from the self facet?
        typedef unsigned int DirectnessVal;
        enum class Directness : DirectnessVal
        {
            Self = 0,
            Direct = 1,
        };

            /// The *origin* of a facet is the type and/or declaration
            /// that the facet's members belong to.
            ///
        struct Origin
        {
            /// A `DeclRef` to the declaration this facet corresponds to, if any.
            ///
            /// This might be a type declaration, an `extension` declaration,
            /// or nothing.
            ///
            DeclRef<Decl> declRef;

            /// The type that this facet corresponds to, if any
            Type* type = nullptr;

            Origin()
            {}

            explicit Origin(DeclRef<Decl> declRef, Type* type = nullptr)
                : declRef(declRef)
                , type(type)
            {}
        };

        Facet()
        {}

        typedef FacetImpl Impl;

        Facet(Impl* impl)
            : _impl(impl)
        {}

        Impl* getImpl() const { return _impl; }
        Impl* operator->() const { return _impl; }

    private:
        Impl* _impl = nullptr;
    };


        /// Do the origins of `left` and `right` match,
        /// such that they are both facets for the same
        /// base type or `extension`?
        ///
    bool originsMatch(Facet left, Facet right);

    inline bool operator!(Facet facet) { return !facet.getImpl(); }

    bool operator==(Facet::Origin left, Facet::Origin right);

    inline bool operator!=(Facet::Origin left, Facet::Origin right)
    {
        return !(left == right);
    }

        /// Heap-allocated implementation of a single facet.
    struct FacetImpl
    {
            /// The kind of this facet
        Facet::Kind kind = Facet::Kind::Type;

            /// How many indirections away from the self facet?
        Facet::Directness directness = Facet::Directness::Self;

            /// The origin of this facet.
            ///
            /// This is the type or declaration that the facet
            /// corresponds to.
            ///
        Facet::Origin origin;

        Type* getType() const { return origin.type; }
        DeclRef<Decl> getDeclRef() const { return origin.declRef; }

            /// A witness that the type this facet belongs to
            /// is a subtype of `origin.type` (if both of those
            /// types exist).
            ///
        SubtypeWitness* subtypeWitness = nullptr;

            /// The next facet in the linearized inheritance list of the entity.
        Facet next;

        FacetImpl()
        {}

        FacetImpl(
            Facet::Kind         kind,
            Facet::Directness   directness,
            DeclRef<Decl>       declRef,
            Type*               type,
            SubtypeWitness*     subtypeWitness)
            : kind(kind)
            , directness(directness)
            , origin(declRef, type)
            , subtypeWitness(subtypeWitness)
        {}
    };

    struct FacetListBuilder;

        /// A singly linked list of facets.
    struct FacetList
    {
    public:
        FacetList()
        {}

        explicit FacetList(Facet head)
            : _head(head)
        {}

        Facet getHead() const { return _head; }
        Facet& getHead() { return _head; }

        Facet advanceHead()
        {
            SLANG_ASSERT(_head.getImpl());
            auto facet = _head;
            _head = facet->next;
            return facet;
        }

        Facet popHead()
        {
            auto facet = advanceHead();
            facet->next = nullptr;
            return facet;
        }

        FacetList getTail() const
        {
            SLANG_ASSERT(_head.getImpl());
            return FacetList(_head->next);
        }

        bool containsMatchFor(Facet facet) const;

        bool isEmpty() const { return _head.getImpl() == nullptr; }

        struct Iterator
        {
        public:
            Iterator()
            {}

            Iterator(Facet::Impl* cursor)
                : _cursor(cursor)
            {}

            bool operator!=(Iterator const& that) const
            {
                return this->_cursor != that._cursor;
            }

            void operator++()
            {
                SLANG_ASSERT(_cursor);
                _cursor = _cursor->next.getImpl();
            }

            Facet operator*() const
            {
                return _cursor;
            }

        private:
            Facet::Impl* _cursor = nullptr;
        };

        Iterator begin() const { return Iterator(_head.getImpl()); }
        Iterator end() const { return Iterator(); }

        struct Appender
        {
        public:
            Appender(FacetList& list)
            {
                _link = &list._head;
            }

            void add(Facet facet)
            {
                *_link = facet;
                _link = &facet->next;
            }

        protected:
            Appender()
            {}

            Facet* _link = nullptr;
        };

        typedef FacetListBuilder Builder;

    protected:

        Facet _head;
    };

    struct FacetListBuilder : FacetList, FacetList::Appender
    {
    public:
        FacetListBuilder()
        {
            _link = &_head;
        }
    };

        /// Information about the inheritance of an entity (type or declaration)
        ///
        /// Currently this is only used to store a linearized list of the
        /// `Facet`s that the type/declaration transitively inherits.
        ///
    struct InheritanceInfo
    {
        FacetList facets;
    };

        /// Shared state for a semantics-checking session.
    struct SharedSemanticsContext
    {
        Linkage*        m_linkage   = nullptr;

            /// The (optional) "primary" module that is the parent to everything that will be checked.
        Module*         m_module    = nullptr;

        DiagnosticSink* m_sink      = nullptr;

            /// (optional) modules that comes from previously processed translation units in the
            /// front-end request that are made visible to the module being checked. This allows
            /// `import` to use them instead of trying to find the files in file system.
        LoadedModuleDictionary* m_environmentModules = nullptr;

            /// (optional) The translation unit that is being checked.
            /// Needed for handling `__include`s.
        TranslationUnitRequest* m_translationUnitRequest = nullptr;

        DiagnosticSink* getSink()
        {
            return m_sink;
        }

        // We need to track what has been `import`ed into
        // the scope of this semantic checking session,
        // and also to avoid importing the same thing more
        // than once.
        //
        List<ModuleDecl*> importedModulesList;
        HashSet<ModuleDecl*> importedModulesSet;
    public:
        SharedSemanticsContext(
            Linkage*        linkage,
            Module*         module,
            DiagnosticSink* sink,
            LoadedModuleDictionary* environmentModules = nullptr,
            TranslationUnitRequest* translationUnit = nullptr)
            : m_linkage(linkage)
            , m_module(module)
            , m_sink(sink)
            , m_environmentModules(environmentModules)
            , m_translationUnitRequest(translationUnit)
        {}

        Session* getSession()
        {
            return m_linkage->getSessionImpl();
        }

        Linkage* getLinkage()
        {
            return m_linkage;
        }

        Module* getModule()
        {
            return m_module;
        }

        TranslationUnitRequest* getTranslationUnitRequest()
        {
            return m_translationUnitRequest;
        }

        bool isInLanguageServer()
        {
            if (m_linkage)
                return m_linkage->isInLanguageServer();
            return false;
        }
            /// Get the list of extension declarations that appear to apply to `decl` in this context
        List<ExtensionDecl*> const& getCandidateExtensionsForTypeDecl(AggTypeDecl* decl);

            /// Register a candidate extension `extDecl` for `typeDecl` encountered during checking.
        void registerCandidateExtension(AggTypeDecl* typeDecl, ExtensionDecl* extDecl);

        void registerAssociatedDecl(Decl* original, DeclAssociationKind assoc, Decl* declaration);

        List<RefPtr<DeclAssociation>> const& getAssociatedDeclsForDecl(Decl* decl);

        bool isDifferentiableFunc(FunctionDeclBase* func);
        bool isBackwardDifferentiableFunc(FunctionDeclBase* func);
        FunctionDifferentiableLevel _getFuncDifferentiableLevelImpl(FunctionDeclBase* func, int recurseLimit);
        FunctionDifferentiableLevel getFuncDifferentiableLevel(FunctionDeclBase* func);

            /// Get the processed inheritance information for `type`, including all its facets
        InheritanceInfo getInheritanceInfo(Type* type);

            /// Get the processed inheritance information for `extension`, including all its facets
        InheritanceInfo getInheritanceInfo(DeclRef<ExtensionDecl> const& extension);

            /// Try get subtype witness from cache, returns true if cache contains a result for the query.
        bool tryGetSubtypeWitnessFromCache(Type* sub, Type* sup, SubtypeWitness*& outWitness)
        {
            auto pair = TypePair{ sub, sup };
            return m_mapTypePairToSubtypeWitness.tryGetValue(pair, outWitness);
        }
        void cacheSubtypeWitness(Type* sub, Type* sup, SubtypeWitness*& outWitness)
        {
            auto pair = TypePair{ sub, sup };
            m_mapTypePairToSubtypeWitness[pair] = outWitness;
        }

    private:
            /// Mapping from type declarations to the known extensiosn that apply to them
        Dictionary<AggTypeDecl*, RefPtr<CandidateExtensionList>> m_mapTypeDeclToCandidateExtensions;

            /// Is the `m_mapTypeDeclToCandidateExtensions` dictionary valid and up to date?
        bool m_candidateExtensionListsBuilt = false;

            /// Add candidate extensions declared in `moduleDecl` to `m_mapTypeDeclToCandidateExtensions`
        void _addCandidateExtensionsFromModule(ModuleDecl* moduleDecl);

            /// Mapping from a decl to additional declarations of the same decl.
            /// The additional declarations provide a location to hold extra decorations.
        OrderedDictionary<Decl*, RefPtr<DeclAssociationList>> m_mapDeclToAssociatedDecls;

            /// Is the `m_mapDeclToAssociatedDecls` dictionary valid and up to date?
        bool m_associatedDeclListsBuilt = false;

            /// Add associated decls declared in `moduleDecl` to `m_mapDeclToAssociatedDecls`
        void _addDeclAssociationsFromModule(ModuleDecl* moduleDecl);

        ASTBuilder* _getASTBuilder() { return m_linkage->getASTBuilder(); }

        InheritanceInfo _getInheritanceInfo(DeclRef<Decl> declRef, DeclRefType* correspondingType);
        InheritanceInfo _calcInheritanceInfo(Type* type);
        InheritanceInfo _calcInheritanceInfo(DeclRef<Decl> declRef, DeclRefType* correspondingType);

        struct DirectBaseInfo
        {
            FacetList facets;

            Facet::Impl facetImpl;

            DirectBaseInfo* next = nullptr;
        };

        struct DirectBaseListBuilder;

        struct DirectBaseList
        {
        public:
            struct Iterator
            {
            public:
                Iterator()
                {}

                Iterator(DirectBaseInfo* cursor)
                    : _cursor(cursor)
                {}

                bool operator!=(Iterator that) const
                {
                    return _cursor != that._cursor;
                }

                void operator++()
                {
                    SLANG_ASSERT(_cursor);
                    _cursor = _cursor->next;
                }

                DirectBaseInfo* operator*()
                {
                    return _cursor;
                }

            private:
                DirectBaseInfo* _cursor = nullptr;
            };

            Iterator begin() const { return Iterator(_head); }
            Iterator end() const { return Iterator(); }

            bool isEmpty() const
            {
                return _head == nullptr;
            }

            bool doesAnyTailContainMatchFor(Facet facet) const;

            void removeEmptyLists();

            typedef DirectBaseListBuilder Builder;

        public:
            DirectBaseInfo* _head = nullptr;
        };

        struct DirectBaseListBuilder : DirectBaseList
        {
        public:
            DirectBaseListBuilder()
            {
                _link = &_head;
            }

            void add(DirectBaseInfo* base)
            {
                *_link = base;
                _link = &base->next;
            }

        private:
            DirectBaseInfo** _link = nullptr;
        };

        void _mergeFacetLists(DirectBaseList bases, FacetList baseFacets, FacetList::Builder& ioMergedFacets);

        struct TypePair
        {
            Type* type0;
            Type* type1;
            HashCode getHashCode() const { return combineHash(Slang::getHashCode(type0), Slang::getHashCode(type1)); }
            bool operator == (const TypePair& other) const { return type0 == other.type0 && type1 == other.type1; }
        };
        Dictionary<Type*, InheritanceInfo> m_mapTypeToInheritanceInfo;
        Dictionary<DeclRef<Decl>, InheritanceInfo> m_mapDeclRefToInheritanceInfo;
        Dictionary<TypePair, SubtypeWitness*> m_mapTypePairToSubtypeWitness;
    };

        /// Local/scoped state of the semantic-checking system
        ///
        /// This type is kept distinct from `SharedSemanticsContext` so that we
        /// can avoid unncessary mutable state being propagated through the
        /// checking process.
        ///
        /// Semantic-checking code should make a new local `SemanticsContext`
        /// in cases where it want to check a sub-entity (expression, statement,
        /// declaration, etc.) in a modified or extended context.
        ///
    struct SemanticsContext
    {
    public:
        friend struct OuterScopeContextRAII;

        explicit SemanticsContext(
            SharedSemanticsContext* shared)
            : m_shared(shared)
            , m_sink(shared->getSink())
            , m_astBuilder(shared->getLinkage()->getASTBuilder())
        {}

        SharedSemanticsContext* getShared() { return m_shared; }
        ASTBuilder* getASTBuilder() { return m_astBuilder; }

        DiagnosticSink* getSink() { return m_sink; }

        Session* getSession() { return m_shared->getSession(); }

        Linkage* getLinkage() { return m_shared->m_linkage; }
        NamePool* getNamePool() { return getLinkage()->getNamePool(); }
        SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }

        SemanticsContext withSink(DiagnosticSink* sink)
        {
            SemanticsContext result(*this);
            result.m_sink = sink;
            return result;
        }

        SemanticsContext withParentFunc(FunctionDeclBase* parentFunc)
        {
            SemanticsContext result(*this);
            result.m_parentFunc = parentFunc;
            result.m_outerStmts = nullptr;
            result.m_parentDifferentiableAttr = parentFunc->findModifier<DifferentiableAttribute>();
            if (parentFunc->ownedScope)
                result.m_outerScope = parentFunc->ownedScope;
            return result;
        }

            /// Information for tracking one or more outer statements.
            ///
            /// During checking of statements, we need to track what
            /// outer statements are in scope, so that we can resolve
            /// the target for a `break` or `continue` statement (and
            /// validate that such statements are only used in contexts
            /// where such a target exists).
            ///
            /// We use a linked list of `OuterStmtInfo` threaded up
            /// through the recursive call stack to track the statements
            /// that are lexically surrounding the one we are checking.
            ///
        struct OuterStmtInfo
        {
            Stmt* stmt = nullptr;
            OuterStmtInfo* next;
        };

        OuterStmtInfo* getOuterStmts() { return m_outerStmts; }

        SemanticsContext withOuterStmts(OuterStmtInfo* outerStmts)
        {
            SemanticsContext result(*this);
            result.m_outerStmts = outerStmts;
            return result;
        }

        TryClauseType getEnclosingTryClauseType() { return m_enclosingTryClauseType; }

        SemanticsContext withEnclosingTryClauseType(TryClauseType tryClauseType)
        {
            SemanticsContext result(*this);
            result.m_enclosingTryClauseType = tryClauseType;
            return result;
        }

        DifferentiableAttribute* getParentDifferentiableAttribute()
        {
            return m_parentDifferentiableAttr;
        }

            /// A scope that is local to a particular expression, and
            /// that can be used to allocate temporary bindings that
            /// might be needed by that expression or its sub-expressions.
            ///
            /// The scope is represented as a sequence of nested `LetExpr`s
            /// that introduce the bindings needed in the scope.
            ///
        struct ExprLocalScope
        {
        public:
            void addBinding(LetExpr* binding);

            LetExpr* getOuterMostBinding() const { return m_outerMostBinding; }

        private:
            LetExpr* m_outerMostBinding = nullptr;
            LetExpr* m_innerMostBinding = nullptr;
        };

        ExprLocalScope* getExprLocalScope() { return m_exprLocalScope; }
        Scope* getOuterScope() { return m_outerScope; }

        SemanticsContext withExprLocalScope(ExprLocalScope* exprLocalScope)
        {
            SemanticsContext result(*this);
            result.m_exprLocalScope = exprLocalScope;
            return result;
        }

        SemanticsContext withOuterScope(Scope* scope)
        {
            SemanticsContext result(*this);
            result.m_outerScope = scope;
            return result;
        }

        SemanticsContext withTreatAsDifferentiable(TreatAsDifferentiableExpr* expr)
        {
            SemanticsContext result(*this);
            result.m_treatAsDifferentiableExpr = expr;
            return result;
        }

        SemanticsContext allowStaticReferenceToNonStaticMember()
        {
            SemanticsContext result(*this);
            result.m_allowStaticReferenceToNonStaticMember = true;
            return result;
        }

    private:
        SharedSemanticsContext* m_shared = nullptr;

        DiagnosticSink* m_sink = nullptr;

        ExprLocalScope* m_exprLocalScope = nullptr;


    protected:
        // TODO: consider making more of this state `private`...

            /// The parent function (if any) that surrounds the statement being checked.
        FunctionDeclBase* m_parentFunc = nullptr;

        DifferentiableAttribute* m_parentDifferentiableAttr = nullptr;

            /// The linked list of lexically surrounding statements.
        OuterStmtInfo* m_outerStmts = nullptr;

            /// The type of a try clause (if any) enclosing current expr.
        TryClauseType m_enclosingTryClauseType = TryClauseType::None;

            /// Whether an expr referencing to a non-static member in static style (e.g. `Type.member`)
            /// is considered valid in the current context.
        bool m_allowStaticReferenceToNonStaticMember = false;

            /// Whether or not we are in a `no_diff` environment (and therefore should treat the call to
            /// a non-differentiable function as differentiable and not issue a diagnostic).
        TreatAsDifferentiableExpr* m_treatAsDifferentiableExpr = nullptr;

        ASTBuilder* m_astBuilder = nullptr;

        Scope* m_outerScope = nullptr;
    };

    struct OuterScopeContextRAII
    {
        SemanticsContext* m_context;
        Scope* m_oldOuterScope;

        OuterScopeContextRAII(SemanticsContext* context, Scope* outerScope)
            : m_context(context)
            , m_oldOuterScope(context->getOuterScope())
        {
            context->m_outerScope = outerScope;
        }

        ~OuterScopeContextRAII()
        {
            m_context->m_outerScope = m_oldOuterScope;
        }
    };

#define SLANG_OUTER_SCOPE_CONTEXT_RAII(context, scope) OuterScopeContextRAII _outerScopeContextRAII(context, scope)
#define SLANG_OUTER_SCOPE_CONTEXT_DECL_RAII(context, decl) OuterScopeContextRAII _outerScopeContextRAII(context, decl->ownedScope?decl->ownedScope:context->getOuterScope())

    struct SemanticsVisitor : public SemanticsContext
    {
        typedef SemanticsContext Super;

        explicit SemanticsVisitor(
            SharedSemanticsContext* shared)
            : Super(shared)
        {}

        SemanticsVisitor(
            SemanticsContext const& context)
            : Super(context)
        {}


    public:
        // Translate Types


        Expr* TranslateTypeNodeImpl(Expr* node);
        Type* ExtractTypeFromTypeRepr(Expr* typeRepr);
        Type* TranslateTypeNode(Expr* node);
        TypeExp TranslateTypeNodeForced(TypeExp const& typeExp);
        TypeExp TranslateTypeNode(TypeExp const& typeExp);
        Type* getRemovedModifierType(ModifiedType* type, ModifierVal* modifier);
        DeclRefType* getExprDeclRefType(Expr * expr);

            /// Is `decl` usable as a static member?
        bool isDeclUsableAsStaticMember(
            Decl*   decl);

            /// Is `item` usable as a static member?
        bool isUsableAsStaticMember(
            LookupResultItem const& item);

            /// Move `expr` into a temporary variable and execute `func` on that variable.
            ///
            /// Returns an expression that wraps both the creation and initialization of
            /// the temporary, and the computation created by `func`.
            ///
        template<typename F>
        Expr* moveTemp(Expr* const& expr, F const& func);

            /// Execute `func` on a variable with the value of `expr`.
            ///
            /// If `expr` is just a reference to an immutable (e.g., `let`) variable
            /// then this might use the existing variable. Otherwise it will create
            /// a new variable to hold `expr`, using `moveTemp()`.
            ///
        template<typename F>
        Expr* maybeMoveTemp(Expr* const& expr, F const& func);

            /// Return an expression that represents "opening" the existential `expr`.
            ///
            /// The type of `expr` must be an interface type, matching `interfaceDeclRef`.
            ///
            /// If we scope down the PL theory to just the case that Slang cares about,
            /// a value of an existential type like `IMover` is a tuple of:
            ///
            ///  * a concrete type `X`
            ///  * a witness `w` of the fact that `X` implements `IMover`
            ///  * a value `v` of type `X`
            ///
            /// "Opening" an existential value is the process of decomposing a single
            /// value `e : IMover` into the pieces `X`, `w`, and `v`.
            ///
            /// Rather than return all those pieces individually, this operation
            /// returns an expression that logically corresponds to `v`: an expression
            /// of type `X`, where the type carries the knowledge that `X` implements `IMover`.
            ///
        Expr* openExistential(
            Expr*            expr,
            DeclRef<InterfaceDecl>  interfaceDeclRef);

            /// If `expr` has existential type, then open it.
            ///
            /// Returns an expression that opens `expr` if it had existential type.
            /// Otherwise returns `expr` itself.
            ///
            /// See `openExistential` for a discussion of what "opening" an
            /// existential-type value means.
            ///
        Expr* maybeOpenExistential(Expr* expr);

            /// If `expr` has Ref<T> Type, convert it into an l-value expr that has T type.
        Expr* maybeOpenRef(Expr* expr);

        Scope* getScope(SyntaxNode* node);

        // Add a sibling lookup scope for `dest` to refer to `source`.
        void addSiblingScopeForContainerDecl(ContainerDecl* dest, ContainerDecl* source);
        void addSiblingScopeForContainerDecl(Scope* destScope, ContainerDecl* source);


        void diagnoseDeprecatedDeclRefUsage(DeclRef<Decl> declRef, SourceLoc loc, Expr* originalExpr);

        DeclRef<Decl> getDefaultDeclRef(Decl* decl)
        {
            return createDefaultSubstitutionsIfNeeded(m_astBuilder, this, makeDeclRef(decl));
        }

        DeclRef<Decl> getSpecializedDeclRef(DeclRef<Decl> declToSpecialize, DeclRef<Decl> declRefWithSpecializationArgs)
        {
            return declRefWithSpecializationArgs.substitute(m_astBuilder, declToSpecialize);
        }

        DeclRef<Decl> getSpecializedDeclRef(Decl* declToSpecialize, DeclRef<Decl> declRefWithSpecializationArgs)
        {
            return declRefWithSpecializationArgs.substitute(m_astBuilder, getDefaultDeclRef(declToSpecialize));
        }

        DeclRefExpr* ConstructDeclRefExpr(
            DeclRef<Decl>   declRef,
            Expr*    baseExpr,
            SourceLoc loc,
            Expr*    originalExpr);

        Expr* ConstructDerefExpr(
            Expr*    base,
            SourceLoc       loc);

        InvokeExpr* constructUncheckedInvokeExpr(Expr* callee, const List<Expr*>& arguments);

        Expr* maybeUseSynthesizedDeclForLookupResult(
            LookupResultItem const& item,
            Expr* orignalExpr);

        Expr* ConstructLookupResultExpr(
            LookupResultItem const& item,
            Expr*            baseExpr,
            SourceLoc loc,
            Expr* originalExpr);

        Expr* createLookupResultExpr(
            Name*                   name,
            LookupResult const&     lookupResult,
            Expr*            baseExpr,
            SourceLoc loc,
            Expr*    originalExpr);

        DeclVisibility getDeclVisibility(Decl* decl);
        DeclVisibility getTypeVisibility(Type* type);
        bool isDeclVisibleFromScope(DeclRef<Decl> declRef, Scope* scope);
        LookupResult filterLookupResultByVisibility(const LookupResult& lookupResult);
        LookupResult filterLookupResultByVisibilityAndDiagnose(const LookupResult& lookupResult, SourceLoc loc, bool& outDiagnosed);

        Val* resolveVal(Val* val)
        {
            if (!val) return nullptr;
            return val->resolve();
        }
        Type* resolveType(Type* type)
        {
            return (Type*)resolveVal(type);
        }
        DeclRef<Decl> resolveDeclRef(DeclRef<Decl> declRef);

            /// Attempt to "resolve" an overloaded `LookupResult` to only include the "best" results
        LookupResult resolveOverloadedLookup(LookupResult const& lookupResult);

            /// Attempt to resolve `expr` into an expression that refers to a single declaration/value.
            /// If `expr` isn't overloaded, then it will be returned as-is.
            ///
            /// The provided `mask` is used to filter items down to those that are applicable in a given context (e.g., just types).
            ///
            /// If the expression cannot be resolved to a single value then *if* `diagSink` is non-null an
            /// appropriate "ambiguous reference" error will be reported, and an error expression will be returned.
            /// Otherwise, the original expression is returned if resolution fails.
            ///
        Expr* maybeResolveOverloadedExpr(Expr* expr, LookupMask mask, DiagnosticSink* diagSink);

            /// Attempt to resolve `overloadedExpr` into an expression that refers to a single declaration/value.
            ///
            /// Equivalent to `maybeResolveOverloadedExpr` with `diagSink` bound to the sink for the `SemanticsVisitor`.
        Expr* resolveOverloadedExpr(OverloadedExpr* overloadedExpr, LookupMask mask);

            /// Worker reoutine for `maybeResolveOverloadedExpr` and `resolveOverloadedExpr`.
        Expr* _resolveOverloadedExprImpl(OverloadedExpr* overloadedExpr, LookupMask mask, DiagnosticSink* diagSink);

        void diagnoseAmbiguousReference(OverloadedExpr* overloadedExpr, LookupResult const& lookupResult);
        void diagnoseAmbiguousReference(Expr* overloadedExpr);


        Expr* ExpectATypeRepr(Expr* expr);

        Type* ExpectAType(Expr* expr);

        Type* ExtractGenericArgType(Expr* exp);

        IntVal* ExtractGenericArgInteger(Expr* exp, Type* genericParamType, DiagnosticSink* sink);
        IntVal* ExtractGenericArgInteger(Expr* exp, Type* genericParamType);

        Val* ExtractGenericArgVal(Expr* exp);

        // Construct a type representing the instantiation of
        // the given generic declaration for the given arguments.
        // The arguments should already be checked against
        // the declaration.
        Type* InstantiateGenericType(
            DeclRef<GenericDecl>        genericDeclRef,
            List<Expr*> const&   args);

        // These routines are bottlenecks for semantic checking,
        // so that we can add some quality-of-life features for users
        // in cases where the compiler crashes
        //
        void dispatchStmt(Stmt* stmt, SemanticsContext const& context);
        Expr* dispatchExpr(Expr* expr, SemanticsContext const& context);

            /// Ensure that a declaration has been checked up to some state
            /// (aka, a phase of semantic checking) so that we can safely
            /// perform certain operations on it.
            ///
            /// Calling `ensureDecl` may cause the type-checker to recursively
            /// start checking `decl` on top of the stack that is already
            /// doing other semantic checking. Care should be taken when relying
            /// on this function to avoid blowing out the stack or (even worse
            /// creating a circular dependency).
            ///
        void ensureDecl(Decl* decl, DeclCheckState state, SemanticsContext* baseContext = nullptr);

            /// Helper routine allowing `ensureDecl` to be called on a `DeclRef`
        void ensureDecl(DeclRefBase* declRef, DeclCheckState state)
        {
            ensureDecl(declRef->getDecl(), state);
        }

            /// Helper routine allowing `ensureDecl` to be used on a `DeclBase`
            ///
            /// `DeclBase` is the base clas of `Decl` and `DeclGroup`. When
            /// called on a `DeclGroup` this function just calls `ensureDecl()`
            /// on each declaration in the group.
            ///
        void ensureDeclBase(DeclBase* decl, DeclCheckState state, SemanticsContext* baseContext);

        // A "proper" type is one that can be used as the type of an expression.
        // Put simply, it can be a concrete type like `int`, or a generic
        // type that is applied to arguments, like `Texture2D<float4>`.
        // The type `void` is also a proper type, since we can have expressions
        // that return a `void` result (e.g., many function calls).
        //
        // A "non-proper" type is any type that can't actually have values.
        // A simple example of this in C++ is `std::vector` - you can't have
        // a value of this type.
        //
        // Part of what this function does is give errors if somebody tries
        // to use a non-proper type as the type of a variable (or anything
        // else that needs a proper type).
        //
        // The other thing it handles is the fact that HLSL lets you use
        // the name of a non-proper type, and then have the compiler fill
        // in the default values for its type arguments (e.g., a variable
        // given type `Texture2D` will actually have type `Texture2D<float4>`).
        bool CoerceToProperTypeImpl(
            TypeExp const&  typeExp,
            Type**   outProperType,
            DiagnosticSink* diagSink);

        TypeExp CoerceToProperType(TypeExp const& typeExp);

        TypeExp tryCoerceToProperType(TypeExp const& typeExp);

        // Check a type, and coerce it to be proper
        TypeExp CheckProperType(TypeExp typeExp);

        // For our purposes, a "usable" type is one that can be
        // used to declare a function parameter, variable, etc.
        // These turn out to be all the proper types except
        // `void`.
        //
        // TODO(tfoley): consider just allowing `void` as a
        // simple example of a "unit" type, and get rid of
        // this check.
        TypeExp CoerceToUsableType(TypeExp const& typeExp);

        // Check a type, and coerce it to be usable
        TypeExp CheckUsableType(TypeExp typeExp);

        Expr* CheckTerm(Expr* term);

        Expr* _CheckTerm(Expr* term);

        Expr* CreateErrorExpr(Expr* expr);

        bool IsErrorExpr(Expr* expr);

        // Capture the "base" expression in case this is a member reference
        Expr* GetBaseExpr(Expr* expr);

            /// Validate a declaration to ensure that it doesn't introduce a circularly-defined constant
            ///
            /// Circular definition in a constant may lead to infinite looping or stack overflow in
            /// the compiler, so it needs to be protected against.
            ///
            /// Note that this function does *not* protect against circular definitions in general,
            /// and a program that indirectly initializes a global variable using its own value (e.g.,
            /// by calling a function that indirectly reads the variable) will be allowed and then
            /// exhibit undefined behavior at runtime.
            ///
        void _validateCircularVarDefinition(VarDeclBase* varDecl);

        bool shouldSkipChecking(Decl* decl, DeclCheckState state);

        // Auto-diff convenience functions for translating primal types to differential types.
        Type* _toDifferentialParamType(Type* primalType);

        Type* getDifferentialPairType(Type* primalType);

        // Convert a function's original type to it's forward/backward diff'd type.
        Type* getForwardDiffFuncType(FuncType* originalType);
        Type* getBackwardDiffFuncType(FuncType* originalType);

        /// Registers a type as conforming to IDifferentiable, along with a witness 
        /// describing the relationship.
        ///
        void addDifferentiableTypeToDiffTypeRegistry(DeclRefType* type, SubtypeWitness* witness);
        void maybeRegisterDifferentiableTypeImplRecursive(ASTBuilder* builder, Type* type);

        // Construct the differential for 'type', if it exists.
        Type* getDifferentialType(ASTBuilder* builder, Type* type, SourceLoc loc);
        Type* tryGetDifferentialType(ASTBuilder* builder, Type* type);

        
    public:

        bool ValuesAreEqual(
            IntVal* left,
            IntVal* right);

        // Compute the cost of using a particular declaration to
        // perform implicit type conversion.
        ConversionCost getImplicitConversionCost(
            Decl* decl);

        ConversionCost getImplicitConversionCostWithKnownArg(Decl* decl, Type* toType, Expr* arg);


        BuiltinConversionKind getImplicitConversionBuiltinKind(
            Decl* decl);

        bool isEffectivelyScalarForInitializerLists(
            Type*    type);

            /// Should the provided expression (from an initializer list) be used directly to initialize `toType`?
        bool shouldUseInitializerDirectly(
            Type*    toType,
            Expr*    fromExpr);

            /// Read a value from an initializer list expression.
            ///
            /// This reads one or more argument from the initializer list
            /// given as `fromInitializerListExpr` to initialize a value
            /// of type `toType`. This may involve reading one or
            /// more arguments from the initializer list, depending
            /// on whether `toType` is an aggregate or not, and on
            /// whether the next argument in the initializer list is
            /// itself an initializer list.
            ///
            /// This routine returns `true` if it was able to read
            /// arguments that can form a value of type `toType`,
            /// and `false` otherwise.
            ///
            /// If the routine succeeds and `outToExpr` is non-null,
            /// then it will be filled in with an expression
            /// representing the value (or type `toType`) that was read,
            /// or it will be left null to indicate that a default
            /// value should be used.
            ///
            /// If the routine fails and `outToExpr` is non-null,
            /// then a suitable diagnostic will be emitted.
            ///
        bool _readValueFromInitializerList(
            Type*                toType,
            Expr**               outToExpr,
            InitializerListExpr* fromInitializerListExpr,
            UInt                       &ioInitArgIndex);

            /// Read an aggregate value from an initializer list expression.
            ///
            /// This reads one or more arguments from the initializer list
            /// given as `fromInitializerListExpr` to initialize the
            /// fields/elements of a value of type `toType`.
            ///
            /// This routine returns `true` if it was able to read
            /// arguments that can form a value of type `toType`,
            /// and `false` otherwise.
            ///
            /// If the routine succeeds and `outToExpr` is non-null,
            /// then it will be filled in with an expression
            /// representing the value (or type `toType`) that was read,
            /// or it will be left null to indicate that a default
            /// value should be used.
            ///
            /// If the routine fails and `outToExpr` is non-null,
            /// then a suitable diagnostic will be emitted.
            ///
        bool _readAggregateValueFromInitializerList(
            Type*                inToType,
            Expr**               outToExpr,
            InitializerListExpr* fromInitializerListExpr,
            UInt                       &ioArgIndex);

            /// Coerce an initializer-list expression to a specific type.
            ///
            /// This reads one or more arguments from the initializer list
            /// given as `fromInitializerListExpr` to initialize the
            /// fields/elements of a value of type `toType`.
            ///
            /// This routine returns `true` if it was able to read
            /// arguments that can form a value of type `toType`,
            /// with no arguments left over, and `false` otherwise.
            ///
            /// If the routine succeeds and `outToExpr` is non-null,
            /// then it will be filled in with an expression
            /// representing the value (or type `toType`) that was read,
            /// or it will be left null to indicate that a default
            /// value should be used.
            ///
            /// If the routine fails and `outToExpr` is non-null,
            /// then a suitable diagnostic will be emitted.
            ///
        bool _coerceInitializerList(
            Type*                toType,
            Expr**               outToExpr,
            InitializerListExpr* fromInitializerListExpr);

            /// Report that implicit type coercion is not possible.
        bool _failedCoercion(
            Type*    toType,
            Expr**   outToExpr,
            Expr*    fromExpr);

            /// Central engine for implementing implicit coercion logic
            ///
            /// This function tries to find an implicit conversion path from
            /// `fromType` to `toType`. It returns `true` if a conversion
            /// is found, and `false` if not.
            ///
            /// If a conversion is found, then its cost will be written to `outCost`.
            ///
            /// If a `fromExpr` is provided, it must be of type `fromType`,
            /// and represent a value to be converted.
            ///
            /// If `outToExpr` is non-null, and if a conversion is found, then
            /// `*outToExpr` will be set to an expression that performs the
            /// implicit conversion of `fromExpr` (which must be non-null
            /// to `toType`).
            ///
            /// The case where `outToExpr` is non-null is used to identify
            /// when a conversion is being done "for real" so that diagnostics
            /// should be emitted on failure.
            ///
        bool _coerce(
            CoercionSite site,
            Type*    toType,
            Expr**   outToExpr,
            QualType fromType,
            Expr*    fromExpr,
            ConversionCost* outCost);

            /// Check whether implicit type coercion from `fromType` to `toType` is possible.
            ///
            /// If conversion is possible, returns `true` and sets `outCost` to the cost
            /// of the conversion found (if `outCost` is non-null).
            ///
            /// If conversion is not possible, returns `false`.
            ///
        bool canCoerce(
            Type*    toType,
            QualType fromType,
            Expr*    fromExpr,
            ConversionCost* outCost = 0);

        TypeCastExpr* createImplicitCastExpr();

        Expr* CreateImplicitCastExpr(
            Type*	toType,
            Expr*	fromExpr);

            /// Create an "up-cast" from a value to an interface type
            ///
            /// This operation logically constructs an "existential" value,
            /// which packages up the value, its type, and the witness
            /// of its conformance to the interface.
            ///
        Expr* createCastToInterfaceExpr(
            Type*    toType,
            Expr*    fromExpr,
            Val*     witness);

            /// Implicitly coerce `fromExpr` to `toType` and diagnose errors if it isn't possible
        Expr* coerce(
            CoercionSite site,
            Type*    toType,
            Expr*    fromExpr);

        // Fill in default substitutions for the 'subtype' part of a type constraint decl
        void CheckConstraintSubType(TypeExp& typeExp);

        void checkGenericDeclHeader(GenericDecl* genericDecl);

        ConstantIntVal* checkConstantIntVal(
            Expr*    expr);

        ConstantIntVal* checkConstantEnumVal(
            Expr*    expr);

        // Check an expression, coerce it to the `String` type, and then
        // ensure that it has a literal (not just compile-time constant) value.
        bool checkLiteralStringVal(
            Expr*    expr,
            String*         outVal);

        void visitModifier(Modifier*);

        AttributeDecl* lookUpAttributeDecl(Name* attributeName, Scope* scope);

        bool hasIntArgs(Attribute* attr, int numArgs);
        bool hasStringArgs(Attribute* attr, int numArgs);

        bool getAttributeTargetSyntaxClasses(SyntaxClass<NodeBase> & cls, uint32_t typeFlags);

        bool validateAttribute(Attribute* attr, AttributeDecl* attribClassDecl, ModifiableSyntaxNode* attrTarget);

        AttributeBase* checkAttribute(
            UncheckedAttribute*     uncheckedAttr,
            ModifiableSyntaxNode*   attrTarget);

        Modifier* checkModifier(
            Modifier*        m,
            ModifiableSyntaxNode*   syntaxNode);

        void checkModifiers(ModifiableSyntaxNode* syntaxNode);
        void checkVisibility(Decl* decl);

        bool doesSignatureMatchRequirement(
            DeclRef<CallableDecl>   satisfyingMemberDeclRef,
            DeclRef<CallableDecl>   requiredMemberDeclRef,
            RefPtr<WitnessTable>    witnessTable);

        bool doesAccessorMatchRequirement(
            DeclRef<AccessorDecl>   satisfyingMemberDeclRef,
            DeclRef<AccessorDecl>   requiredMemberDeclRef);

        bool doesPropertyMatchRequirement(
            DeclRef<PropertyDecl>   satisfyingMemberDeclRef,
            DeclRef<PropertyDecl>   requiredMemberDeclRef,
            RefPtr<WitnessTable>    witnessTable);

        bool doesSubscriptMatchRequirement(
            DeclRef<SubscriptDecl>  satisfyingMemberDeclRef,
            DeclRef<SubscriptDecl>  requiredMemberDeclRef,
            RefPtr<WitnessTable>    witnessTable);

        bool doesVarMatchRequirement(
            DeclRef<VarDeclBase>   satisfyingMemberDeclRef,
            DeclRef<VarDeclBase>   requiredMemberDeclRef,
            RefPtr<WitnessTable>    witnessTable);

        bool doesGenericSignatureMatchRequirement(
            DeclRef<GenericDecl>        genDecl,
            DeclRef<GenericDecl>        requirementGenDecl,
            RefPtr<WitnessTable>        witnessTable);

        bool doesTypeSatisfyAssociatedTypeConstraintRequirement(
            Type* satisfyingType,
            DeclRef<AssocTypeDecl>  requiredAssociatedTypeDeclRef,
            RefPtr<WitnessTable>    witnessTable);

        bool doesTypeSatisfyAssociatedTypeRequirement(
            Type*            satisfyingType,
            DeclRef<AssocTypeDecl>  requiredAssociatedTypeDeclRef,
            RefPtr<WitnessTable>    witnessTable);

        // Does the given `memberDecl` work as an implementation
        // to satisfy the requirement `requiredMemberDeclRef`
        // from an interface?
        //
        // If it does, then inserts a witness into `witnessTable`
        // and returns `true`, otherwise returns `false`
        bool doesMemberSatisfyRequirement(
            DeclRef<Decl>               memberDeclRef,
            DeclRef<Decl>               requiredMemberDeclRef,
            RefPtr<WitnessTable>        witnessTable);

        // State used while checking if a declaration (either a type declaration
        // or an extension of that type) conforms to the interfaces it claims
        // via its inheritance clauses.
        //
        struct ConformanceCheckingContext
        {
                /// The type for which conformances are being checked
            Type*           conformingType;

                /// The outer declaration for the conformances being checked (either a type or `extension` declaration)
            ContainerDecl*  parentDecl;

            Dictionary<DeclRef<InterfaceDecl>, RefPtr<WitnessTable>>    mapInterfaceToWitnessTable;
        };

        FuncDecl* synthesizeMethodSignatureForRequirementWitness(
            ConformanceCheckingContext* context,
            DeclRef<FuncDecl> requiredMemberDeclRef,
            List<Expr*>& synArgs,
            ThisExpr*& synThis);
        
        GenericDecl* synthesizeGenericSignatureForRequirementWitness(
            ConformanceCheckingContext* context,
            DeclRef<GenericDecl> requiredMemberDeclRef,
            List<Expr*>& synArgs,
            List<Expr*>& synGenericArgs,
            ThisExpr*& synThis);

        void _addMethodWitness(
            WitnessTable* witnessTable,
            DeclRef<CallableDecl> requirement,
            DeclRef<CallableDecl> method);

            /// Attempt to synthesize a method that can satisfy `requiredMemberDeclRef` using `lookupResult`.
            ///
            /// On success, installs the syntethesized method in `witnessTable` and returns `true`.
            /// Otherwise, returns `false`.
        bool trySynthesizeMethodRequirementWitness(
            ConformanceCheckingContext* context,
            LookupResult const&         lookupResult,
            DeclRef<FuncDecl>           requiredMemberDeclRef,
            RefPtr<WitnessTable>        witnessTable);

            /// Attempt to synthesize a property that can satisfy `requiredMemberDeclRef` using `lookupResult`.
            ///
            /// On success, installs the syntethesized method in `witnessTable` and returns `true`.
            /// Otherwise, returns `false`.
            ///
        bool trySynthesizePropertyRequirementWitness(
            ConformanceCheckingContext* context,
            LookupResult const&         lookupResult,
            DeclRef<PropertyDecl>       requiredMemberDeclRef,
            RefPtr<WitnessTable>        witnessTable);

            /// Attempt to synthesize a declartion that can satisfy `requiredMemberDeclRef` using `lookupResult`.
            ///
            /// On success, installs the syntethesized declaration in `witnessTable` and returns `true`.
            /// Otherwise, returns `false`.
        bool trySynthesizeRequirementWitness(
            ConformanceCheckingContext* context,
            LookupResult const&         lookupResult,
            DeclRef<Decl>               requiredMemberDeclRef,
            RefPtr<WitnessTable>        witnessTable);
        

        enum SynthesisPattern
        {
            // Synthesized method inducts over all arguments.
            // T fn(T x, T y, T z, ...) 
            // { 
            //     typeof(T::member0)::fn(x.member0, y.member0, z.member0, ...);
            //     typeof(T::member1)::fn(x.member1, y.member1, z.member1, ...);
            //     ...
            // }
            // 
            AllInductive,

            // Synthesized method inducts over all arguments except the first.
            // T fn(U x, T y, T z)
            // {
            //     typeof(T::member0)::fn(x, y.member0, z.member0, ...);
            //     typeof(T::member1)::fn(x, y.member1, z.member1, ...);
            //     ...
            // }
            FixedFirstArg
        };

            /// Attempt to synthesize `zero`, `dadd` & `dmul` methods for a type that conforms to
            /// `IDifferentiable`.
            /// On success, installs the syntethesized functions and returns `true`.
            /// Otherwise, returns `false`.
        bool trySynthesizeDifferentialMethodRequirementWitness(
            ConformanceCheckingContext* context,
            DeclRef<Decl> requirementDeclRef,
            RefPtr<WitnessTable> witnessTable,
            SynthesisPattern pattern);

            /// Attempt to synthesize an associated `Differential` type for a type that conforms to
            /// `IDifferentiable`.
            ///
            /// On success, installs the syntethesized type in `witnessTable`, injects `[DerivativeMember]`
            /// modifiers on differentiable fields to point to the corresponding field in the synthesized
            /// differential type, and returns `true`.
            /// Otherwise, returns `false`.
        bool trySynthesizeDifferentialAssociatedTypeRequirementWitness(
            ConformanceCheckingContext* context,
            DeclRef<AssocTypeDecl> requirementDeclRef,
            RefPtr<WitnessTable> witnessTable);

        struct DifferentiableMemberInfo
        {
            Decl* memberDecl;
            Type* diffType;
        };

            /// Gather differentiable members from decl.
        List<DifferentiableMemberInfo> collectDifferentiableMemberInfo(ContainerDecl* decl);

        // Check and register a type if it is differentiable.
        void maybeRegisterDifferentiableType(ASTBuilder* builder, Type* type);

        // Find the appropriate member of a declared type to
        // satisfy a requirement of an interface the type
        // claims to conform to.
        //
        // The type declaration `typeDecl` has declared that it
        // conforms to the interface `interfaceDeclRef`, and
        // `requiredMemberDeclRef` is a required member of
        // the interface.
        //
        // If a satisfying value is found, registers it in
        // `witnessTable` and returns `true`, otherwise
        // returns `false`.
        //
        bool findWitnessForInterfaceRequirement(
            ConformanceCheckingContext* context,
            Type*                       subType,
            Type*                       superInterfaceType,
            InheritanceDecl*            inheritanceDecl,
            DeclRef<InterfaceDecl>      superInterfaceDeclRef,
            DeclRef<Decl>               requiredMemberDeclRef,
            RefPtr<WitnessTable>        witnessTable,
            SubtypeWitness*             subTypeConformsToSuperInterfaceWitness);

        // Check that the type declaration `typeDecl`, which
        // declares conformance to the interface `interfaceDeclRef`,
        // (via the given `inheritanceDecl`) actually provides
        // members to satisfy all the requirements in the interface.
        bool checkInterfaceConformance(
            ConformanceCheckingContext* context,
            Type*                       subType,
            Type*                       superInterfaceType,
            InheritanceDecl*            inheritanceDecl,
            DeclRef<InterfaceDecl>      superInterfaceDeclRef,
            SubtypeWitness*             subTypeConformsToSuperInterfaceWitness,
            WitnessTable*               witnessTable);

        RefPtr<WitnessTable> checkInterfaceConformance(
            ConformanceCheckingContext* context,
            Type*                       subType,
            Type*                       superInterfaceType,
            InheritanceDecl*            inheritanceDecl,
            DeclRef<InterfaceDecl>      superInterfaceDeclRef,
            SubtypeWitness*             subTypeConformsToSuperInterfaceWitness);

        bool checkConformanceToType(
            ConformanceCheckingContext* context,
            Type*                       subType,
            InheritanceDecl*            inheritanceDecl,
            Type*                       superType,
            SubtypeWitness*             subIsSuperWitness,
            WitnessTable*               witnessTable);

            /// Check that `type` which has declared that it inherits from (and/or implements)
            /// another type via `inheritanceDecl` actually does what it needs to for that
            /// inheritance to be valid.
        bool checkConformance(
            Type*                       type,
            InheritanceDecl*            inheritanceDecl,
            ContainerDecl*              parentDecl);

        void checkExtensionConformance(ExtensionDecl* decl);

        void checkAggTypeConformance(AggTypeDecl* decl);

        bool isIntegerBaseType(BaseType baseType);

            /// Is `type` a scalar integer type.
        bool isScalarIntegerType(Type* type);

        bool isIntValueInRangeOfType(IntegerLiteralValue value, Type* type);

        // Validate that `type` is a suitable type to use
        // as the tag type for an `enum`
        void validateEnumTagType(Type* type, SourceLoc const& loc);

        void checkStmt(Stmt* stmt, SemanticsContext const& context);

        void getGenericParams(
            GenericDecl*                        decl,
            List<Decl*>&                        outParams,
            List<GenericTypeConstraintDecl*>&   outConstraints);

            /// Determine if `left` and `right` have matching generic signatures.
            /// If they do, then outputs a specialized declRef to `ioSubstRightToLeft` that
            /// represents a reference to `right` with the parameters of `left`.
        bool doGenericSignaturesMatch(
            GenericDecl*                    left,
            GenericDecl*                    right,
            DeclRef<Decl>*                  outSpecializedRightInner);

        // Check if two functions have the same signature for the purposes
        // of overload resolution.
        bool doFunctionSignaturesMatch(
            DeclRef<FuncDecl> fst,
            DeclRef<FuncDecl> snd);

        Result checkRedeclaration(Decl* newDecl, Decl* oldDecl);
        Result checkFuncRedeclaration(FuncDecl* newDecl, FuncDecl* oldDecl);
        void checkForRedeclaration(Decl* decl);

        Expr* checkPredicateExpr(Expr* expr);

        Expr* checkExpressionAndExpectIntegerConstant(Expr* expr, IntVal** outIntVal);

        IntegerLiteralValue GetMinBound(IntVal* val);

        void maybeInferArraySizeForVariable(VarDeclBase* varDecl);

        void validateArraySizeForVariable(VarDeclBase* varDecl);

        IntVal* getIntVal(IntegerLiteralExpr* expr);

        inline IntVal* getIntVal(SubstExpr<IntegerLiteralExpr> expr)
        {
            return getIntVal(expr.getExpr());
        }

        Name* getName(String const& text)
        {
            return getNamePool()->getName(text);
        }

            /// Helper type to detect and catch circular definitions when folding constants,
            /// to prevent the compiler from going into infinite loops or overflowing the stack.
        struct ConstantFoldingCircularityInfo
        {
            ConstantFoldingCircularityInfo(
                Decl*                           decl,
                ConstantFoldingCircularityInfo* next)
                : decl(decl)
                , next(next)
            {}

                /// A declaration whose value is contributing to the constant being folded
            Decl*                           decl = nullptr;

                /// The rest of the links in the chain of declarations being folded
            ConstantFoldingCircularityInfo* next = nullptr;
        };

            /// Try to apply front-end constant folding to determine the value of `invokeExpr`.
        IntVal* tryConstantFoldExpr(
            SubstExpr<InvokeExpr>           invokeExpr,
            ConstantFoldingCircularityInfo* circularityInfo);

            /// Try to apply front-end constant folding to determine the value of `expr`.
        IntVal* tryConstantFoldExpr(
            SubstExpr<Expr>                 expr,
            ConstantFoldingCircularityInfo* circularityInfo);

        bool _checkForCircularityInConstantFolding(
            Decl*                           decl,
            ConstantFoldingCircularityInfo* circularityInfo);

            /// Try to resolve a compile-time constant `IntVal` from the given `declRef`.
        IntVal* tryConstantFoldDeclRef(
            DeclRef<VarDeclBase> const&     declRef,
            ConstantFoldingCircularityInfo* circularityInfo);

            /// Try to extract the value of an integer constant expression, either
            /// returning the `IntVal` value, or null if the expression isn't recognized
            /// as an integer constant.
            ///
        IntVal* tryFoldIntegerConstantExpression(
            SubstExpr<Expr>                 expr,
            ConstantFoldingCircularityInfo* circularityInfo);

        // Enforce that an expression resolves to an integer constant, and get its value
        enum class IntegerConstantExpressionCoercionType
        {
            SpecificType,
            AnyInteger
        };
        IntVal* CheckIntegerConstantExpression(Expr* inExpr, IntegerConstantExpressionCoercionType coercionType, Type* expectedType);
        IntVal* CheckIntegerConstantExpression(Expr* inExpr, IntegerConstantExpressionCoercionType coercionType, Type* expectedType, DiagnosticSink* sink);

        IntVal* CheckEnumConstantExpression(Expr* expr);


        Expr* CheckSimpleSubscriptExpr(
            IndexExpr*   subscriptExpr,
            Type*              elementType);

        // The way that we have designed out type system, pretyt much *every*
        // type is a reference to some declaration in the standard library.
        // That means that when we construct a new type on the fly, we need
        // to make sure that it is wired up to reference the appropriate
        // declaration, or else it won't compare as equal to other types
        // that *do* reference the declaration.
        //
        // This function is used to construct a `vector<T,N>` type
        // programmatically, so that it will work just like a type of
        // that form constructed by the user.
        VectorExpressionType* createVectorType(
            Type*  elementType,
            IntVal*          elementCount);

        //

            /// Given an immutable `expr` used as an l-value emit a special diagnostic if it was derived from `this`.
        void maybeDiagnoseThisNotLValue(Expr* expr);

        // Figure out what type an initializer/constructor declaration
        // is supposed to return. In most cases this is just the type
        // declaration that its declaration is nested inside.
        Type* findResultTypeForConstructorDecl(ConstructorDecl* decl);

            /// Determine what type `This` should refer to in the context of the given parent `decl`.
        Type* calcThisType(DeclRef<Decl> decl);

            /// Determine what type `This` should refer to in an extension of `type`.
        Type* calcThisType(Type* type);


        //

        struct Constraint
        {
            Decl*		decl = nullptr; // the declaration of the thing being constraints
            Val*	val = nullptr; // the value to which we are constraining it
            bool isUsedAsLValue = false;   // If this constraint is for a type parameter, is the type used in an l-value parameter?
            bool satisfied = false; // Has this constraint been met?

            // Is this constraint optional? An optional constraint provides a hint value to a parameter
            // if it is otherwise unconstrained, but doesn't take precedence over a constraint that is not optional.
            bool isOptional = false;
        };

        // A collection of constraints that will need to be satisfied (solved)
        // in order for checking to succeed.
        struct ConstraintSystem
        {
            // A source location to use in reporting any issues
            SourceLoc loc;

            // The generic declaration whose parameters we
            // are trying to solve for.
            GenericDecl* genericDecl = nullptr;

            // Constraints we have accumulated, which constrain
            // the possible arguments for those parameters.
            List<Constraint> constraints;
        };

        Type* TryJoinVectorAndScalarType(
            VectorExpressionType* vectorType,
            BasicExpressionType*  scalarType);

            /// Is the given interface one that a tagged-union type can conform to?
            ///
            /// If a tagged union type `__TaggedUnion(A,B)` is going to be
            /// plugged in for a type parameter `T : IFoo` then we need to
            /// be sure that the interface `IFoo` doesn't have anything
            /// that could lead to unsafe/unsound behavior. This function
            /// checks that all the requirements on the interfaceare safe ones.
            ///
        bool isInterfaceSafeForTaggedUnion(
            DeclRef<InterfaceDecl> interfaceDeclRef);

            /// Is the given interface requirement one that a tagged-union type can satisfy?
            ///
            /// Unsafe requirements include any `static` requirements,
            /// any associated types, and also any requirements that make
            /// use of the `This` type (once we support it).
            ///
        bool isInterfaceRequirementSafeForTaggedUnion(
            DeclRef<InterfaceDecl>  interfaceDeclRef,
            DeclRef<Decl>           requirementDeclRef);

            /// Check whether `subType` is a subtype of `superType`
            ///
            /// If `subType` is a subtype of `superType`, returns
            /// a witness value for the subtype relationship.
            ///
            /// If `subType` is *not* a subtype of `superType`, returns null.
            ///
        SubtypeWitness* isSubtype(
            Type*                   subType,
            Type*                   superType);

        SubtypeWitness* checkAndConstructSubtypeWitness(Type* subType, Type* superType);

        bool isInterfaceType(Type* type);

        bool isTypeDifferentiable(Type* type);

            /// Check whether `subType` is a sub-type of `superTypeDeclRef`,
            /// and return a witness to the sub-type relationship if it holds
            /// (return null otherwise).
            ///
        SubtypeWitness* tryGetSubtypeWitness(
            Type* subType,
            Type* superType)
        {
            return isSubtype(subType, superType);
        }

            /// Check whether `type` conforms to `interfaceDeclRef`,
            /// and return a witness to the conformance if it holds
            /// (return null otherwise).
            ///
            /// This function is equivalent to `tryGetSubtypeWitness()`.
            ///
        SubtypeWitness* tryGetInterfaceConformanceWitness(
            Type* type,
            Type* interfaceType);

        Expr* createCastToSuperTypeExpr(
            Type*    toType,
            Expr*    fromExpr,
            Val*     witness);

        Expr* createModifierCastExpr(
            Type*    toType,
            Expr*    fromExpr);

        /// Does there exist an implicit conversion from `fromType` to `toType`?
        bool canConvertImplicitly(
            Type* toType,
            QualType fromType);

        ConversionCost getConversionCost(Type* toType, QualType fromType);

        Type* _tryJoinTypeWithInterface(
            Type*                   type,
            Type*                   interfaceType);

        // Try to compute the "join" between two types
        Type* TryJoinTypes(
            QualType  left,
            QualType  right);

        // Try to solve a system of generic constraints.
        // The `system` argument provides the constraints.
        // The `varSubst` argument provides the list of constraint
        // variables that were created for the system.
        //
        // Returns a new declref to the inner decl of `genericDeclRef`,
        // representing the specialized generic with the values
        // we solved for along the way.
        DeclRef<Decl> trySolveConstraintSystem(
            ConstraintSystem*       system,
            DeclRef<GenericDecl>    genericDeclRef,
            ArrayView<Val*> knownGenericArgs,
            ConversionCost& outBaseCost);


        // State related to overload resolution for a call
        // to an overloaded symbol
        struct OverloadResolveContext
        {
            enum class Mode
            {
                // We are just checking if a candidate works or not
                JustTrying,

                // We want to actually update the AST for a chosen candidate
                ForReal,
            };

            // Location to use when reporting overload-resolution errors.
            SourceLoc loc;

            // The original expression (if any) that triggered things
            AppExprBase* originalExpr = nullptr;

            // Source location of the "function" part of the expression, if any
            SourceLoc       funcLoc;

            // The source scope of the lookup for performing visibiliity tests.
            Scope* sourceScope = nullptr;

            // The original arguments to the call
            Index argCount = 0;
            Expr** args = nullptr;
            Type** argTypes = nullptr;

            Index getArgCount() { return argCount; }
            Expr*& getArg(Index index) { return args[index]; }
            Type* getArgType(Index index)
            {
                if(argTypes)
                    return argTypes[index];
                else
                    return getArg(index)->type.type;
            }
            Type* getArgTypeForInference(Index index, SemanticsVisitor* semantics)
            {
                if(argTypes)
                    return argTypes[index];
                else
                    return semantics->maybeResolveOverloadedExpr(getArg(index), LookupMask::Default, nullptr)->type;
            }

            bool disallowNestedConversions = false;

            Expr* baseExpr = nullptr;

            // Are we still trying out candidates, or are we
            // checking the chosen one for real?
            Mode mode = Mode::JustTrying;

            // We store one candidate directly, so that we don't
            // need to do dynamic allocation on the list every time
            OverloadCandidate bestCandidateStorage;
            OverloadCandidate*	bestCandidate = nullptr;

            // Full list of all candidates being considered, in the ambiguous case
            List<OverloadCandidate> bestCandidates;
        };

        struct ParamCounts
        {
            Count required;
            Count allowed;
        };

        // count the number of parameters required/allowed for a callable
        ParamCounts CountParameters(FilteredMemberRefList<ParamDecl> params);

        // count the number of parameters required/allowed for a generic
        ParamCounts CountParameters(DeclRef<GenericDecl> genericRef);

        bool TryCheckOverloadCandidateClassNewMatchUp(
            OverloadResolveContext& context,
            OverloadCandidate const& candidate);

        bool TryCheckOverloadCandidateArity(
            OverloadResolveContext&		context,
            OverloadCandidate const&	candidate);

        bool TryCheckOverloadCandidateFixity(
            OverloadResolveContext&		context,
            OverloadCandidate const&	candidate);

        bool TryCheckOverloadCandidateVisibility(
            OverloadResolveContext& context,
            OverloadCandidate const& candidate);

        bool TryCheckGenericOverloadCandidateTypes(
            OverloadResolveContext&	context,
            OverloadCandidate&		candidate);

        bool TryCheckOverloadCandidateTypes(
            OverloadResolveContext&	context,
            OverloadCandidate&		candidate);

        bool TryCheckOverloadCandidateDirections(
            OverloadResolveContext&		/*context*/,
            OverloadCandidate const&	/*candidate*/);

            /// Check if the given `expr` refers to an `in` function
            /// parameter, or part of one (through field reference, etc.).
            ///
            /// If the expression refers into a parameter, returns
            /// the declaration of the parameter. Otherwise returns
            /// null.
            ///
        ParamDecl* isReferenceIntoFunctionInputParameter(
            Expr* expr);

        // Create a witness that attests to the fact that `type`
        // is equal to itself.
        TypeEqualityWitness* createTypeEqualityWitness(
            Type*  type);

        // In the case where we are explicitly applying a generic
        // to arguments (e.g., `G<A,B>`) check that the constraints
        // on those parameters are satisfied.
        //
        // Note: the constraints actually work as additional parameters/arguments
        // of the generic, and so we need to reify them into the final
        // argument list.
        //
        bool TryCheckOverloadCandidateConstraints(
            OverloadResolveContext&		context,
            OverloadCandidate&	candidate);

        // Try to check an overload candidate, but bail out
        // if any step fails
        void TryCheckOverloadCandidate(
            OverloadResolveContext&		context,
            OverloadCandidate&			candidate);

        // Create the representation of a given generic applied to some arguments
        Expr* createGenericDeclRef(
            Expr*            baseExpr,
            Expr*            originalExpr,
            SubstitutionSet  substSet);

        // Take an overload candidate that previously got through
        // `TryCheckOverloadCandidate` above, and try to finish
        // up the work and turn it into a real expression.
        //
        // If the candidate isn't actually applicable, this is
        // where we'd start reporting the issue(s).
        Expr* CompleteOverloadCandidate(
            OverloadResolveContext&		context,
            OverloadCandidate&			candidate);

        // Implement a comparison operation between overload candidates,
        // so that the better candidate compares as less-than the other
        int CompareOverloadCandidates(
            OverloadCandidate*	left,
            OverloadCandidate*	right);

            /// If `declRef` representations a specialization of a generic, returns the number of specialized generic arguments.
            /// Otherwise, returns zero.
            ///
        Int getSpecializedParamCount(DeclRef<Decl> const& declRef);

            /// Compare items `left` and `right` produced by lookup, to see if one should be favored for overloading.
        int CompareLookupResultItems(
            LookupResultItem const& left,
            LookupResultItem const& right);

            /// Compare items `left` and `right` being considered as overload candidates, and determine if one should be favored for structural reasons.
        int compareOverloadCandidateSpecificity(
            LookupResultItem const& left,
            LookupResultItem const& right);

        void AddOverloadCandidateInner(
            OverloadResolveContext& context,
            OverloadCandidate&		candidate);

        void AddOverloadCandidate(
            OverloadResolveContext& context,
            OverloadCandidate&		candidate,
            ConversionCost baseCost);
        
        void AddHigherOrderOverloadCandidates(
            Expr*                   funcExpr,
            OverloadResolveContext& context,
            ConversionCost baseCost);

        void AddFuncOverloadCandidate(
            LookupResultItem			item,
            DeclRef<CallableDecl>             funcDeclRef,
            OverloadResolveContext&		context,
            ConversionCost baseCost);

        void AddFuncOverloadCandidate(
            FuncType*		/*funcType*/,
            OverloadResolveContext&	/*context*/,
            ConversionCost baseCost);

        void AddFuncExprOverloadCandidate(
            FuncType* funcType,
            OverloadResolveContext& context,
            Expr* expr,
            ConversionCost baseCost);

        // Add a candidate callee for overload resolution, based on
        // calling a particular `ConstructorDecl`.
        void AddCtorOverloadCandidate(
            LookupResultItem            typeItem,
            Type*                type,
            DeclRef<ConstructorDecl>    ctorDeclRef,
            OverloadResolveContext&     context,
            Type*                resultType,
            ConversionCost baseCost);

        // If the given declaration has generic parameters, then
        // return the corresponding `GenericDecl` that holds the
        // parameters, etc. This returns the immediate generic parent
        // of `decl`, e.g. the generic for f<T>, and *not* any indirect
        // generic parents, such as P<T>.f().
        GenericDecl* GetOuterGeneric(Decl* decl);

        // If `decl` is inside a generic, return that outer generic,
        // otherwise returns `decl`.
        Decl* getOuterGenericOrSelf(Decl* decl);

        // Find the next outer generic parent of `decl`, including
        // indirect parents.
        GenericDecl* findNextOuterGeneric(Decl* decl);

        // Try to find a unification for two values
        bool TryUnifyVals(
            ConstraintSystem&	constraints,
            Val*			fst,
            bool            fstLVal,
            Val*			snd,
            bool            sndLVal);

        bool tryUnifyDeclRef(
            ConstraintSystem&       constraints,
            DeclRefBase*   fst,
            bool           fstLVal,
            DeclRefBase*   snd,
            bool           sndLVal);

        bool tryUnifyGenericAppDeclRef(
            ConstraintSystem& constraints,
            GenericAppDeclRef* fst,
            bool            fstLVal,
            GenericAppDeclRef* snd,
            bool            sndLVal);

        bool TryUnifyTypeParam(
            ConstraintSystem&     constraints,
            GenericTypeParamDecl* typeParamDecl,
            QualType              type);

        bool TryUnifyIntParam(
            ConstraintSystem&               constraints,
            GenericValueParamDecl*	paramDecl,
            IntVal*                  val);

        bool TryUnifyIntParam(
            ConstraintSystem&       constraints,
            DeclRef<VarDeclBase> const&   varRef,
            IntVal*          val);

        bool TryUnifyTypesByStructuralMatch(
            ConstraintSystem&       constraints,
            QualType fst,
            QualType snd);

        bool TryUnifyTypes(
            ConstraintSystem&       constraints,
            QualType fst,
            QualType snd);

        bool TryUnifyConjunctionType(
            ConstraintSystem&   constraints,
            QualType            fst,
            QualType            snd);

        void maybeUnifyUnconstraintIntParam(
            ConstraintSystem& constraints,
            IntVal* param,
            IntVal* arg,
            bool paramIsLVal);

        // Is the candidate extension declaration actually applicable to the given type
        DeclRef<ExtensionDecl> applyExtensionToType(
            ExtensionDecl*  extDecl,
            Type*    type);

            // Take a generic declaration that is being applied
            // in a context and attempt to infer any missing generic
            // arguments to form a `DeclRef` to the inner declaration
            // that could be applicable in the context of the given
            // overloaded call.
            // Also computes a `baseCost` for the inferred arguments,
            // so that we can prefer a more specialized generic candidate
            // when there is ambiguity. For example, given
            // ```
            //     interface IBase;
            //     interface IDerived : IBase;
            //     struct Derived : IDerived {}
            //     void f1<T:IBase>(T b)
            //     void f2<T:IDerived>(T b);
            // ```
            // We will prefer f2 when seeing f(Derived()), because it takes
            // less steps to upcast `Derived` to  `IDerived` than it does
            // to `IBase`.
            //
        DeclRef<Decl> inferGenericArguments(
            DeclRef<GenericDecl>    genericDeclRef,
            OverloadResolveContext& context,
            ArrayView<Val*>         knownGenericArgs,
            ConversionCost          &outBaseCost,
            List<QualType>          *innerParameterTypes = nullptr);

        void AddTypeOverloadCandidates(
            Type*	        type,
            OverloadResolveContext&	context);

        void AddDeclRefOverloadCandidates(
            LookupResultItem		item,
            OverloadResolveContext&	context,
            ConversionCost          baseCost);

        void AddOverloadCandidates(
            LookupResult const&     result,
            OverloadResolveContext&	context);

        void AddOverloadCandidates(
            Expr*	funcExpr,
            OverloadResolveContext&			context);

        String getCallSignatureString(
            OverloadResolveContext&     context);

        Expr* ResolveInvoke(InvokeExpr * expr);

        void AddGenericOverloadCandidate(
            LookupResultItem		baseItem,
            OverloadResolveContext&	context);

        void AddGenericOverloadCandidates(
            Expr*	baseExpr,
            OverloadResolveContext&			context);

            // Add overload candidates based on use of `genericDeclRef`
            // in an ordinary function-call context (that is, where it
            // has been applied to arguments using `()` and not `<>`).
            //
            // If some or all of the generic arguments to `genericDeclRef`
            // are known at the call site, they should be passed in via
            // `substWithKnownGenericArgs`.
            //
        void addOverloadCandidatesForCallToGeneric(
            LookupResultItem        genericItem,
            OverloadResolveContext& context,
            ArrayView<Val*>     knownGenericArgs);

            /// Check a generic application where the operands have already been checked.
        Expr* checkGenericAppWithCheckedArgs(GenericAppExpr* genericAppExpr);

        Expr* CheckExpr(Expr* expr);


        Expr* CheckInvokeExprWithCheckedOperands(InvokeExpr *expr);
        // Get the type to use when referencing a declaration
        QualType GetTypeForDeclRef(DeclRef<Decl> declRef, SourceLoc loc);

        //
        //
        //

        Expr* MaybeDereference(Expr* inExpr);

        Expr* CheckMatrixSwizzleExpr(
            MemberExpr* memberRefExpr,
            Type*      baseElementType,
            IntegerLiteralValue        baseElementRowCount,
            IntegerLiteralValue        baseElementColCount);

        Expr* CheckMatrixSwizzleExpr(
            MemberExpr* memberRefExpr,
            Type*      baseElementType,
            IntVal*        baseElementRowCount,
            IntVal*        baseElementColCount);

        Expr* CheckSwizzleExpr(
            MemberExpr* memberRefExpr,
            Type*      baseElementType,
            IntegerLiteralValue         baseElementCount);

        Expr* CheckSwizzleExpr(
            MemberExpr*	memberRefExpr,
            Type*		baseElementType,
            IntVal*				baseElementCount);

            /// Perform semantic checking of an assignment where the operands have already been checked.
        Expr* checkAssignWithCheckedOperands(AssignExpr* expr);

        // Look up a static member
        // @param expr Can be StaticMemberExpr or MemberExpr
        // @param baseExpression Is the underlying type expression determined from resolving expr
        Expr* _lookupStaticMember(DeclRefExpr* expr, Expr* baseExpression);

        Expr* visitStaticMemberExpr(StaticMemberExpr* expr);

            /// Perform checking operations required for the "base" expression of a member-reference like `base.someField`
        Expr* checkBaseForMemberExpr(Expr* baseExpr);

        Expr* lookupMemberResultFailure(
            DeclRefExpr*     expr,
            QualType const& baseType,
            bool supressDiagnostic = false);

        SharedSemanticsContext& operator=(const SharedSemanticsContext &) = delete;


        //

        void importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl);
        void importFileDeclIntoScope(Scope* scope, FileDecl* fileDecl);


        void suggestCompletionItems(
            CompletionSuggestions::ScopeKind scopeKind, LookupResult const& lookupResult);
    };


    inline void ensureDecl(SemanticsVisitor* visitor, Decl* decl, DeclCheckState state)
    {
        visitor->ensureDecl(decl, state);
    }

    DeclRef<ExtensionDecl> applyExtensionToType(
        SemanticsVisitor*   semantics,
        ExtensionDecl*      extDecl,
        Type*               type);


    struct SemanticsExprVisitor
        : public SemanticsVisitor
        , ExprVisitor<SemanticsExprVisitor, Expr*>
    {
    public:
        SemanticsExprVisitor(SemanticsContext const& outer)
            : SemanticsVisitor(outer)
        {}

        Expr* visitSizeOfLikeExpr(SizeOfLikeExpr* expr);

        Expr* visitIncompleteExpr(IncompleteExpr* expr);
        Expr* visitBoolLiteralExpr(BoolLiteralExpr* expr);
        Expr* visitNullPtrLiteralExpr(NullPtrLiteralExpr* expr);
        Expr* visitNoneLiteralExpr(NoneLiteralExpr* expr);
        Expr* visitIntegerLiteralExpr(IntegerLiteralExpr* expr);
        Expr* visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr);
        Expr* visitStringLiteralExpr(StringLiteralExpr* expr);

        Expr* visitIndexExpr(IndexExpr* subscriptExpr);

        Expr* visitParenExpr(ParenExpr* expr);

        Expr* visitAssignExpr(AssignExpr* expr);

        Expr* visitGenericAppExpr(GenericAppExpr* genericAppExpr);

        Expr* visitSharedTypeExpr(SharedTypeExpr* expr);

        Expr* visitInvokeExpr(InvokeExpr *expr);

        Expr* visitSelectExpr(SelectExpr* expr);

        Expr* visitVarExpr(VarExpr *expr);

        Expr* visitTypeCastExpr(TypeCastExpr * expr);

        Expr* visitTryExpr(TryExpr* expr);

        Expr* visitIsTypeExpr(IsTypeExpr* expr);

        Expr* visitAsTypeExpr(AsTypeExpr* expr);

        //
        // Some syntax nodes should not occur in the concrete input syntax,
        // and will only appear *after* checking is complete. We need to
        // deal with this cases here, even if they are no-ops.
        //

    #define CASE(NAME)                                                                           \
        Expr* visit##NAME(NAME* expr)                                                            \
        {                                                                                        \
            if (!getShared()->isInLanguageServer())                                              \
                SLANG_DIAGNOSE_UNEXPECTED(getSink(), expr, "should not appear in input syntax"); \
            expr->type = m_astBuilder->getErrorType();                                           \
            return expr;                                                                         \
        }

        CASE(DerefExpr)
        CASE(MakeRefExpr)
        CASE(MatrixSwizzleExpr)
        CASE(SwizzleExpr)
        CASE(OverloadedExpr)
        CASE(OverloadedExpr2)
        CASE(AggTypeCtorExpr)
        CASE(CastToSuperTypeExpr)
        CASE(ModifierCastExpr)
        CASE(LetExpr)
        CASE(ExtractExistentialValueExpr)
        CASE(OpenRefExpr)
        CASE(MakeOptionalExpr)
        CASE(PartiallyAppliedGenericExpr)

    #undef CASE

        Expr* visitStaticMemberExpr(StaticMemberExpr* expr);

        Expr* visitMemberExpr(MemberExpr * expr);

        Expr* visitInitializerListExpr(InitializerListExpr* expr);

        Expr* visitThisExpr(ThisExpr* expr);
        Expr* visitThisTypeExpr(ThisTypeExpr* expr);
        Expr* visitReturnValExpr(ReturnValExpr* expr);
        Expr* visitAndTypeExpr(AndTypeExpr* expr);
        Expr* visitPointerTypeExpr(PointerTypeExpr* expr);
        Expr* visitModifiedTypeExpr(ModifiedTypeExpr* expr);
        Expr* visitFuncTypeExpr(FuncTypeExpr* expr);
        Expr* visitTupleTypeExpr(TupleTypeExpr* expr);

        Expr* visitForwardDifferentiateExpr(ForwardDifferentiateExpr* expr);
        Expr* visitBackwardDifferentiateExpr(BackwardDifferentiateExpr* expr);
        Expr* visitPrimalSubstituteExpr(PrimalSubstituteExpr* expr);
        Expr* visitDispatchKernelExpr(DispatchKernelExpr* expr);

        Expr* visitTreatAsDifferentiableExpr(TreatAsDifferentiableExpr* expr);

        Expr* visitGetArrayLengthExpr(GetArrayLengthExpr* expr);

        Expr* visitSPIRVAsmExpr(SPIRVAsmExpr*);

            /// Perform semantic checking on a `modifier` that is being applied to the given `type`
        Val* checkTypeModifier(Modifier* modifier, Type* type);

    };

    struct SemanticsStmtVisitor
        : public SemanticsVisitor
        , StmtVisitor<SemanticsStmtVisitor>
    {
        SemanticsStmtVisitor(SemanticsContext const& outer)
            : SemanticsVisitor(outer)
        {}

        FunctionDeclBase* getParentFunc() { return m_parentFunc; }

        void checkStmt(Stmt* stmt);

        template<typename T>
        T* FindOuterStmt();

        Stmt* findOuterStmtWithLabel(Name* label);

        void visitDeclStmt(DeclStmt* stmt);

        void visitBlockStmt(BlockStmt* stmt);

        void visitSeqStmt(SeqStmt* stmt);

        void visitLabelStmt(LabelStmt* stmt);

        void visitBreakStmt(BreakStmt *stmt);

        void visitContinueStmt(ContinueStmt *stmt);

        void visitDoWhileStmt(DoWhileStmt *stmt);

        void visitForStmt(ForStmt *stmt);

        void visitCompileTimeForStmt(CompileTimeForStmt* stmt);

        void visitSwitchStmt(SwitchStmt* stmt);

        void visitCaseStmt(CaseStmt* stmt);

        void visitTargetSwitchStmt(TargetSwitchStmt* stmt);

        void visitTargetCaseStmt(TargetCaseStmt* stmt);

        void visitIntrinsicAsmStmt(IntrinsicAsmStmt*) {}

        void visitDefaultStmt(DefaultStmt* stmt);

        void visitIfStmt(IfStmt *stmt);

        void visitUnparsedStmt(UnparsedStmt*);

        void visitEmptyStmt(EmptyStmt*);

        void visitDiscardStmt(DiscardStmt*);

        void visitReturnStmt(ReturnStmt *stmt);

        void visitWhileStmt(WhileStmt *stmt);
        
        void visitGpuForeachStmt(GpuForeachStmt *stmt);

        void visitExpressionStmt(ExpressionStmt *stmt);

        // Try to infer the max number of iterations the loop will run.
        void tryInferLoopMaxIterations(ForStmt* stmt);

        void checkLoopInDifferentiableFunc(Stmt* stmt);
    };

    struct SemanticsDeclVisitorBase
        : public SemanticsVisitor
    {
        SemanticsDeclVisitorBase(SemanticsContext const& outer)
            : SemanticsVisitor(outer)
        {}

        void checkBodyStmt(Stmt* stmt, FunctionDeclBase* parentDecl)
        {
            checkStmt(stmt, withParentFunc(parentDecl));
        }

        void checkModule(ModuleDecl* programNode);
    };

    bool isUnsizedArrayType(Type* type);
}
back to top