https://github.com/shader-slang/slang
Raw File
Tip revision: d06a78d935b2743494d47ed5cd3f36e38ac9c5ac authored by Yong He on 04 February 2022, 03:17:30 UTC
Add gfx interop to allow more direct D3D12 usage scenarios. (#2117)
Tip revision: d06a78d
slang-lookup.cpp
// slang-lookup.cpp
#include "slang-lookup.h"

#include "../compiler-core/slang-name.h"

namespace Slang {

void ensureDecl(SemanticsVisitor* visitor, Decl* decl, DeclCheckState state);

//

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

//


// Helper for constructing breadcrumb trails during lookup, without unnecessary heap allocaiton
struct BreadcrumbInfo
{
    LookupResultItem::Breadcrumb::Kind kind;
    LookupResultItem::Breadcrumb::ThisParameterMode thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::Default;
    DeclRef<Decl> declRef;
    Val* val = nullptr;
    BreadcrumbInfo* prev = nullptr;
};

//

void buildMemberDictionary(ContainerDecl* decl)
{
    // Don't rebuild if already built
    if (decl->isMemberDictionaryValid())
        return;

    // If it's < 0 it means that the dictionaries are entirely invalid
    if (decl->dictionaryLastCount < 0)
    {
        decl->dictionaryLastCount = 0;
        decl->memberDictionary.Clear();
        decl->transparentMembers.clear();
    }

    // are we a generic?
    GenericDecl* genericDecl = as<GenericDecl>(decl);

    const Index membersCount = decl->members.getCount();

    SLANG_ASSERT(decl->dictionaryLastCount >= 0 && decl->dictionaryLastCount <= membersCount);

    for (Index i = decl->dictionaryLastCount; i < membersCount; ++i)
    {
        Decl* m = decl->members[i];

        auto name = m->getName();

        // Add any transparent members to a separate list for lookup
        if (m->hasModifier<TransparentModifier>())
        {
            TransparentMemberInfo info;
            info.decl = m;
            decl->transparentMembers.add(info);
        }

        // Ignore members with no name
        if (!name)
            continue;

        // Ignore the "inner" member of a generic declaration
        if (genericDecl && m == genericDecl->inner)
            continue;

        m->nextInContainerWithSameName = nullptr;

        Decl* next = nullptr;
        if (decl->memberDictionary.TryGetValue(name, next))
            m->nextInContainerWithSameName = next;

        decl->memberDictionary[name] = m;
    }

    decl->dictionaryLastCount = membersCount;
    SLANG_ASSERT(decl->isMemberDictionaryValid());
}


bool DeclPassesLookupMask(Decl* decl, LookupMask mask)
{
    // type declarations
    if(auto aggTypeDecl = as<AggTypeDecl>(decl))
    {
        return int(mask) & int(LookupMask::type);
    }
    else if(auto simpleTypeDecl = as<SimpleTypeDecl>(decl))
    {
        return int(mask) & int(LookupMask::type);
    }
    // function declarations
    else if(auto funcDecl = as<FunctionDeclBase>(decl))
    {
        return (int(mask) & int(LookupMask::Function)) != 0;
    }
    // attribute declaration
    else if( auto attrDecl = as<AttributeDecl>(decl) )
    {
        return (int(mask) & int(LookupMask::Attribute)) != 0;
    }

    // default behavior is to assume a value declaration
    // (no overloading allowed)

    return (int(mask) & int(LookupMask::Value)) != 0;
}

void AddToLookupResult(
    LookupResult&		result,
    LookupResultItem	item)
{
    if (!result.isValid())
    {
        // If we hadn't found a hit before, we have one now
        result.item = item;
    }
    else if (!result.isOverloaded())
    {
        // We are about to make this overloaded
        result.items.add(result.item);
        result.items.add(item);
    }
    else
    {
        // The result was already overloaded, so we pile on
        result.items.add(item);
    }
}

LookupResult refineLookup(LookupResult const& inResult, LookupMask mask)
{
    if (!inResult.isValid()) return inResult;
    if (!inResult.isOverloaded()) return inResult;

    LookupResult result;
    for (auto item : inResult.items)
    {
        if (!DeclPassesLookupMask(item.declRef.getDecl(), mask))
            continue;

        AddToLookupResult(result, item);
    }
    return result;
}

LookupResultItem CreateLookupResultItem(
    DeclRef<Decl> declRef,
    BreadcrumbInfo* breadcrumbInfos)
{
    LookupResultItem item;
    item.declRef = declRef;

    // breadcrumbs were constructed "backwards" on the stack, so we
    // reverse them here by building a linked list the other way
    RefPtr<LookupResultItem::Breadcrumb> breadcrumbs;
    for (auto bb = breadcrumbInfos; bb; bb = bb->prev)
    {
        breadcrumbs = new LookupResultItem::Breadcrumb(
            bb->kind,
            bb->declRef,
            bb->val,
            breadcrumbs,
            bb->thisParameterMode);
    }
    item.breadcrumbs = breadcrumbs;
    return item;
}

static void _lookUpMembersInValue(
    ASTBuilder*             astBuilder,
    Name*                   name,
    DeclRef<Decl>           valueDeclRef,
    LookupRequest const&    request,
    LookupResult&	        ioResult,
    BreadcrumbInfo*	        breadcrumbs);

    /// Look up direct members (those declared in `containerDeclRef` itself, as well
    /// as transitively through any direct members that are marked "transparent."
    ///
    /// This function does *not* deal with looking up through `extension`s,
    /// inheritance clauses, etc.
    ///
static void _lookUpDirectAndTransparentMembers(
    ASTBuilder*             astBuilder,
    Name*                   name,
    DeclRef<ContainerDecl>  containerDeclRef,
    LookupRequest const&    request,
    LookupResult&           result,
    BreadcrumbInfo*         inBreadcrumbs)
{
    ContainerDecl* containerDecl = containerDeclRef.getDecl();

    // Ensure that the lookup dictionary in the container is up to date
    if (!containerDecl->isMemberDictionaryValid())
    {
        buildMemberDictionary(containerDecl);
    }

    // Look up the declarations with the chosen name in the container.
    Decl* firstDecl = nullptr;
    containerDecl->memberDictionary.TryGetValue(name, firstDecl);

    // Now iterate over those declarations (if any) and see if
    // we find any that meet our filtering criteria.
    // For example, we might be filtering so that we only consider
    // type declarations.
    for (auto m = firstDecl; m; m = m->nextInContainerWithSameName)
    {
        if (!DeclPassesLookupMask(m, request.mask))
            continue;

        // The declaration passed the test, so add it!
        AddToLookupResult(result, CreateLookupResultItem(DeclRef<Decl>(m, containerDeclRef.substitutions), inBreadcrumbs));
    }

    // TODO(tfoley): should we look up in the transparent decls
    // if we already has a hit in the current container?
    for(auto transparentInfo : containerDecl->transparentMembers)
    {
        // The reference to the transparent member should use whatever
        // substitutions we used in referring to its outer container
        DeclRef<Decl> transparentMemberDeclRef(transparentInfo.decl, containerDeclRef.substitutions);

        // We need to leave a breadcrumb so that we know that the result
        // of lookup involves a member lookup step here

        BreadcrumbInfo memberRefBreadcrumb;
        memberRefBreadcrumb.kind = LookupResultItem::Breadcrumb::Kind::Member;
        memberRefBreadcrumb.declRef = transparentMemberDeclRef;
        memberRefBreadcrumb.prev = inBreadcrumbs;

        _lookUpMembersInValue(
            astBuilder,
            name,
            transparentMemberDeclRef,
            request,
            result,
            &memberRefBreadcrumb);
    }
}

    /// Perform "direct" lookup in a container declaration
LookupResult lookUpDirectAndTransparentMembers(
    ASTBuilder*             astBuilder,
    SemanticsVisitor*       semantics,
    Name*                   name,
    DeclRef<ContainerDecl>  containerDeclRef,
    LookupMask              mask)
{
    LookupRequest request;
    request.semantics = semantics;
    request.mask = mask;
    LookupResult result;
    _lookUpDirectAndTransparentMembers(
        astBuilder,
        name,
        containerDeclRef,
        request,
        result,
        nullptr);
    return result;
}

static SubtypeWitness* _makeSubtypeWitness(
    ASTBuilder*                 astBuilder,
    Type*                       subType,
    SubtypeWitness*             subToMidWitness,
    Type*                       superType,
    SubtypeWitness*             midtoSuperWitness)
{
    if(subToMidWitness)
    {
        TransitiveSubtypeWitness* transitiveWitness = astBuilder->create<TransitiveSubtypeWitness>();
        transitiveWitness->subToMid = subToMidWitness;
        transitiveWitness->midToSup = midtoSuperWitness;
        transitiveWitness->sub = subType;
        transitiveWitness->sup = superType;
        return transitiveWitness;
    }
    else
    {
        return midtoSuperWitness;
    }
}

static SubtypeWitness* _makeSubtypeWitness(
    ASTBuilder*                 astBuilder,
    Type*                       subType,
    SubtypeWitness*             subToMidWitness,
    Type*                       superType,
    DeclRef<TypeConstraintDecl> midToSuperConstraint)
{
    DeclaredSubtypeWitness* midToSuperWitness = astBuilder->create<DeclaredSubtypeWitness>();
    midToSuperWitness->declRef = midToSuperConstraint;
    midToSuperWitness->sub = subType;
    midToSuperWitness->sup = superType;
    return _makeSubtypeWitness(astBuilder, subType, subToMidWitness, superType, midToSuperWitness);
}

// Same as the above, but we are specializing a type instead of a decl-ref
static Type* _maybeSpecializeSuperType(
    ASTBuilder*                 astBuilder, 
    Type*                       superType,
    SubtypeWitness*             subIsSuperWitness)
{
    if (auto superDeclRefType = as<DeclRefType>(superType))
    {
        if (auto superInterfaceDeclRef = superDeclRefType->declRef.as<InterfaceDecl>())
        {
            ThisTypeSubstitution* thisTypeSubst = astBuilder->create<ThisTypeSubstitution>();
            thisTypeSubst->interfaceDecl = superInterfaceDeclRef.getDecl();
            thisTypeSubst->witness = subIsSuperWitness;
            thisTypeSubst->outer = superInterfaceDeclRef.substitutions.substitutions;

            auto specializedInterfaceDeclRef = DeclRef<Decl>(superInterfaceDeclRef.getDecl(), thisTypeSubst);

            auto specializedInterfaceType = DeclRefType::create(astBuilder, specializedInterfaceDeclRef);
            return specializedInterfaceType;
        }
    }

    return superType;
}

static void _lookUpMembersInType(
    ASTBuilder*             astBuilder, 
    Name*                   name,
    Type*            type,
    LookupRequest const&    request,
    LookupResult&           ioResult,
    BreadcrumbInfo*         breadcrumbs);

static void _lookUpMembersInSuperTypeImpl(
    ASTBuilder*             astBuilder, 
    Name*                   name,
    Type*                   leafType,
    Type*                   superType,
    SubtypeWitness*         leafIsSuperWitness,
    LookupRequest const&    request,
    LookupResult&           ioResult,
    BreadcrumbInfo*         inBreadcrumbs);

static void _lookUpMembersInSuperType(
    ASTBuilder*                 astBuilder,
    Name*                       name,
    Type*                       leafType,
    Type*                       superType,
    SubtypeWitness*             leafIsSuperWitness,
    LookupRequest const&        request,
    LookupResult&               ioResult,
    BreadcrumbInfo*             inBreadcrumbs)
{
    // If we are looking up through an interface type, then
    // we need to be sure that we add an appropriate
    // "this type" substitution here, since that needs to
    // be applied to any members we look up.
    //
    superType = _maybeSpecializeSuperType(
        astBuilder,
        superType,
        leafIsSuperWitness);

    // We need to track the indirection we took in lookup,
    // so that we can construct an appropriate AST on the other
    // side that includes the "upcast" from sub-type to super-type.
    //
    BreadcrumbInfo breadcrumb;
    breadcrumb.prev = inBreadcrumbs;
    breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::SuperType;
    breadcrumb.val = leafIsSuperWitness;
    breadcrumb.prev = inBreadcrumbs;

    // TODO: Need to consider case where this might recurse infinitely (e.g.,
    // if an inheritance clause does something like `Bad<T> : Bad<Bad<T>>`.
    //
    // TODO: The even simpler thing we need to worry about here is that if
    // there is ever a "diamond" relationship in the inheritance hierarchy,
    // we might end up seeing the same interface via different "paths" and
    // we wouldn't want that to lead to overload-resolution failure.
    //
    _lookUpMembersInSuperTypeImpl(astBuilder, name, leafType, superType, leafIsSuperWitness, request, ioResult, &breadcrumb);
}

static void _lookUpMembersInSuperType(
    ASTBuilder*                 astBuilder, 
    Name*                       name,
    Type*                       leafType,
    SubtypeWitness*             leafIsIntermediateWitness,
    DeclRef<TypeConstraintDecl> intermediateIsSuperConstraint,
    LookupRequest const&        request,
    LookupResult&               ioResult,
    BreadcrumbInfo*             inBreadcrumbs)
{
    if( request.semantics )
    {
        ensureDecl(request.semantics, intermediateIsSuperConstraint, DeclCheckState::CanUseBaseOfInheritanceDecl);
    }

    // The super-type in the constraint (e.g., `Foo` in `T : Foo`)
    // will tell us a type we should use for lookup.
    //
    auto superType = getSup(astBuilder, intermediateIsSuperConstraint);
    //
    // We will go ahead and perform lookup using `superType`,
    // after dealing with some details.

    auto leafIsSuperWitness = _makeSubtypeWitness(
        astBuilder,
        leafType,
        leafIsIntermediateWitness,
        superType,
        intermediateIsSuperConstraint);

    return _lookUpMembersInSuperType(astBuilder, name, leafType, superType, leafIsSuperWitness, request, ioResult, inBreadcrumbs);
}

static void _lookUpMembersInSuperTypeDeclImpl(
    ASTBuilder*             astBuilder,
    Name*                   name,
    Type*                   leafType,
    Type*                   superType,
    SubtypeWitness*         leafIsSuperWitness,
    DeclRef<Decl>           declRef,
    LookupRequest const&    request,
    LookupResult&           ioResult,
    BreadcrumbInfo*         inBreadcrumbs)
{
    auto semantics = request.semantics;
    if( semantics )
    {
        ensureDecl(semantics, declRef.getDecl(), DeclCheckState::ReadyForLookup);
    }

    if (auto genericTypeParamDeclRef = declRef.as<GenericTypeParamDecl>())
    {
        // If the type we are doing lookup in is a generic type parameter,
        // then the members it provides can only be discovered by looking
        // at the constraints that are placed on that type.

        auto genericDeclRef = genericTypeParamDeclRef.getParent().as<GenericDecl>();
        assert(genericDeclRef);

        for(auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
        {
            if( semantics )
            {
                ensureDecl(semantics, constraintDeclRef, DeclCheckState::CanUseBaseOfInheritanceDecl);
            }

            // Does this constraint pertain to the type we are working on?
            //
            // We want constraints of the form `T : Foo` where `T` is the
            // generic parameter in question, and `Foo` is whatever we are
            // constraining it to.
            auto subType = getSub(astBuilder, constraintDeclRef);
            auto subDeclRefType = as<DeclRefType>(subType);
            if(!subDeclRefType)
                continue;
            if(!subDeclRefType->declRef.equals(genericTypeParamDeclRef))
                continue;

            _lookUpMembersInSuperType(
                astBuilder,
                name,
                leafType,
                leafIsSuperWitness,
                constraintDeclRef,
                request,
                ioResult,
                inBreadcrumbs);
        }
    }
    else if (declRef.as<AssocTypeDecl>() || declRef.as<GlobalGenericParamDecl>())
    {
        for (auto constraintDeclRef : getMembersOfType<TypeConstraintDecl>(declRef.as<ContainerDecl>()))
        {
            _lookUpMembersInSuperType(
                astBuilder,
                name,
                leafType,
                leafIsSuperWitness,
                constraintDeclRef,
                request,
                ioResult,
                inBreadcrumbs);
        }
    }
    else if(auto aggTypeDeclBaseRef = declRef.as<AggTypeDeclBase>())
    {
        // In this case we are peforming lookup in the context of an aggregate
        // type or an `extension`, so the first thing to do is to look for
        // matching members declared directly in the body of the type/`extension`.
        //
        _lookUpDirectAndTransparentMembers(astBuilder, name, aggTypeDeclBaseRef, request, ioResult, inBreadcrumbs);

        // There are further lookup steps that we can only perform when a
        // semantic checking context is available to us. That means that
        // during parsing, lookup will fail to find members under `name`
        // if they required following these paths.
        //
        if(semantics)
        {
            if(auto aggTypeDeclRef = aggTypeDeclBaseRef.as<AggTypeDecl>())
            {
                // If the declaration we are looking at is a nominal type declaration,
                // then we want to consider any `extension`s that have been associated
                // directly with that type.
                //
                ensureDecl(request.semantics, aggTypeDeclRef.getDecl(), DeclCheckState::ReadyForLookup);
                for(auto extDecl : getCandidateExtensions(aggTypeDeclRef, semantics))
                {
                    // Note: In this case `extDecl` is an extension that was declared to apply
                    // (conditionally) to `aggTypeDeclRef`, which is the decl-ref part of
                    // `superType`. Thus when looking for a substitution to apply to the
                    // extension, we need to apply it to `superType` and not to `leafType`.
                    //
                    auto extDeclRef = ApplyExtensionToType(request.semantics, extDecl, superType);
                    if (!extDeclRef)
                        continue;

                    // TODO: eventually we need to insert a breadcrumb here so that
                    // the constructed result can somehow indicate that a member
                    // was found through an extension.
                    //
                    _lookUpMembersInSuperTypeDeclImpl(
                        astBuilder,
                        name,
                        leafType,
                        superType,
                        leafIsSuperWitness,
                        extDeclRef,
                        request,
                        ioResult,
                        inBreadcrumbs);
                }
            }

            // For both aggregate types and their `extension`s, we want lookup to follow
            // through the declared inheritance relationships on each declaration.
            //
            ensureDecl(semantics, aggTypeDeclBaseRef.getDecl(), DeclCheckState::CanEnumerateBases);
            for (auto inheritanceDeclRef : getMembersOfType<InheritanceDecl>(aggTypeDeclBaseRef))
            {
                ensureDecl(semantics, inheritanceDeclRef.getDecl(), DeclCheckState::CanUseBaseOfInheritanceDecl);

                // Some things that are syntactically `InheritanceDecl`s don't actually
                // represent a subtype/supertype relationship, and thus we shouldn't
                // include members from the base type when doing lookup in the
                // derived type.
                //
                if(inheritanceDeclRef.getDecl()->hasModifier<IgnoreForLookupModifier>())
                    continue;

                auto baseType = getSup(astBuilder, inheritanceDeclRef);
                if( auto baseDeclRefType = as<DeclRefType>(baseType) )
                {
                    if( auto baseInterfaceDeclRef = baseDeclRefType->declRef.as<InterfaceDecl>() )
                    {
                        if( int(request.options) & int(LookupOptions::IgnoreBaseInterfaces) )
                            continue;
                    }
                }

                _lookUpMembersInSuperType(astBuilder, name, leafType, leafIsSuperWitness, inheritanceDeclRef, request, ioResult, inBreadcrumbs);
            }
        }
    }
}

static void _lookUpMembersInSuperTypeImpl(
    ASTBuilder*             astBuilder,
    Name*                   name,
    Type*                   leafType,
    Type*                   superType,
    SubtypeWitness*         leafIsSuperWitness,
    LookupRequest const&    request,
    LookupResult&           ioResult,
    BreadcrumbInfo*         inBreadcrumbs)
{
    // If the type was pointer-like, then dereference it
    // automatically here.
    if (auto pointerLikeType = as<PointerLikeType>(superType))
    {
        // Need to leave a breadcrumb to indicate that we
        // did an implicit dereference here
        BreadcrumbInfo derefBreacrumb;
        derefBreacrumb.kind = LookupResultItem::Breadcrumb::Kind::Deref;
        derefBreacrumb.prev = inBreadcrumbs;

        // Recursively perform lookup on the result of deref
        _lookUpMembersInType(
            astBuilder, 
            name, pointerLikeType->elementType, request, ioResult, &derefBreacrumb);
        return;
    }

    // Default case: no dereference needed

    if(auto declRefType = as<DeclRefType>(superType))
    {
        auto declRef = declRefType->declRef;

        _lookUpMembersInSuperTypeDeclImpl(astBuilder, name, leafType, superType, leafIsSuperWitness, declRef, request, ioResult, inBreadcrumbs);
    }
    else if (auto extractExistentialType = as<ExtractExistentialType>(superType))
    {
        // We want lookup to be performed on the underlying interface type of the existential,
        // but we need to have a this-type substitution applied to ensure that the result of
        // lookup will have a comparable substitution applied (allowing things like associated
        // types, etc. used in the signature of a method to resolve correctly).
        //
        auto interfaceDeclRef = extractExistentialType->getSpecializedInterfaceDeclRef();
        _lookUpMembersInSuperTypeDeclImpl(astBuilder, name, leafType, superType, leafIsSuperWitness, interfaceDeclRef, request, ioResult, inBreadcrumbs);
    }
    else if( auto thisType = as<ThisType>(superType) )
    {
        // We need to create a witness that represents the next link in the
        // chain. The `leafIsSuperWitness` represents the knowledge that `leafType : superType`
        // (and we know that `superType == thisType`,  but we now need to extend that
        // with the knowledge that `thisType : thisType->interfaceTypeDeclRef`.
        //
        auto interfaceType = DeclRefType::create(astBuilder, thisType->interfaceDeclRef);

        auto superIsInterfaceWitness = astBuilder->create<ThisTypeSubtypeWitness>();
        superIsInterfaceWitness->sub = superType;
        superIsInterfaceWitness->sup = interfaceType;

        auto leafIsInterfaceWitness = _makeSubtypeWitness(
            astBuilder,
            leafType,
            leafIsSuperWitness,
            interfaceType,
            superIsInterfaceWitness);

        _lookUpMembersInSuperType(astBuilder, name, leafType, interfaceType, leafIsInterfaceWitness, request, ioResult, inBreadcrumbs);
    }
    else if( auto andType = as<AndType>(superType) )
    {
        // We have a type of the form `leftType & rightType` and we need to perform
        // lookup in both `leftType` and `rightType`.
        //
        auto leftType = andType->left;
        auto rightType = andType->right;

        // Operationally, we are in a situation where we have a witness
        // that the `leafType` we are doing lookup on is an subtype
        // of `superType` (which is `leftType & rightType`) and now we need
        // to construct a witness that `leafType` is a subtype of
        // the `Left` type.
        //
        // Effectively, we have a witness that `T : X & Y` and we
        // need to extract from it a witness that `T : X`.
        // Fortunately, we have a class of subtype witness that does
        // *precisely* this:
        //
        auto leafIsLeftWitness = astBuilder->create<ExtractFromConjunctionSubtypeWitness>();
        //
        // Our witness will be to the fact that `leafType` is a subtype of `leftType`
        //
        leafIsLeftWitness->sub = leafType;
        leafIsLeftWitness->sup = leftType;
        //
        // The evidence for the subtype relationship will be a witness
        // proving that `leafType : leftType & rightType`:
        //
        leafIsLeftWitness->conunctionWitness = leafIsSuperWitness;
        //
        // ... along with the index of the desired super-type in
        // that conjunction. The index of `leftType` in `leftType & rightType`
        // is zero.
        //
        leafIsLeftWitness->indexInConjunction = 0;

        // The witness for the fact that `leafType : rightType` is the
        // same as for the left case, just with a different index into
        // the conjunction.
        //
        auto leafIsRightWitness = astBuilder->create<ExtractFromConjunctionSubtypeWitness>();
        leafIsRightWitness->conunctionWitness = leafIsSuperWitness;
        leafIsRightWitness->indexInConjunction = 1;
        leafIsRightWitness->sub = leafType;
        leafIsRightWitness->sup = rightType;

        // We then perform lookup on both sides of the conjunction, and
        // accumulate whatever items are found on either/both sides.
        //
        // For each recursive lookup, we pass the appropriate pair of
        // the type to look up in and the witness of the subtype
        // relationship.
        //
        _lookUpMembersInSuperType(astBuilder, name, leafType, leftType, leafIsLeftWitness, request, ioResult, inBreadcrumbs);
        _lookUpMembersInSuperType(astBuilder, name, leafType, rightType, leafIsRightWitness, request, ioResult, inBreadcrumbs);
    }
}

    /// Perform lookup for `name` in the context of `type`.
    ///
    /// This operation does the kind of lookup we'd expect if `name`
    /// was used inside of a member function on `type`, or if the
    /// user wrote `obj.<name>` for a variable `obj` of the given
    /// `type`.
    ///
    /// Looking up members in `type` includes lookup through any
    /// constraints or inheritance relationships that expand the
    /// set of members visible on `type`.
    ///
static void _lookUpMembersInType(
    ASTBuilder*             astBuilder,
    Name*                   name,
    Type*            type,
    LookupRequest const&    request,
    LookupResult&           ioResult,
    BreadcrumbInfo*         breadcrumbs)
{
    if (!type)
    {
        return;
    }

    _lookUpMembersInSuperTypeImpl(astBuilder, name, type, type, nullptr, request, ioResult, breadcrumbs);
}

    /// Look up members by `name` in the given `valueDeclRef`.
    ///
    /// If `valueDeclRef` represents a reference to a variable
    /// or other named and typed value, then this performs the
    /// kind of lookup we'd expect for `valueDeclRef.<name>`.
    ///
static void _lookUpMembersInValue(
    ASTBuilder*             astBuilder,
    Name*                   name,
    DeclRef<Decl>           valueDeclRef,
    LookupRequest const&    request,
    LookupResult&	        ioResult,
    BreadcrumbInfo*	        breadcrumbs)
{
    // Looking up `name` in the context of a value can
    // be reduced to the problem of looking up `name`
    // in the *type* of that value.
    //
    auto valueType = getTypeForDeclRef(astBuilder, valueDeclRef, SourceLoc());

    return _lookUpMembersInType(astBuilder, name, valueType, request, ioResult, breadcrumbs);
}

static void _lookUpInScopes(
    ASTBuilder*             astBuilder,
    Name*                   name,
    LookupRequest const&    request,
    LookupResult&           result)
{
    auto thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::Default;

    auto scope      = request.scope;

    auto endScope   = request.endScope;
    for (;scope != endScope; scope = scope->parent)
    {
        // Note that we consider all "peer" scopes together,
        // so that a hit in one of them does not preclude
        // also finding a hit in another
        for(auto link = scope; link; link = link->nextSibling)
        {
            auto containerDecl = link->containerDecl;

            // It is possible for the first scope in a list of
            // siblings to be a "dummy" scope that only exists
            // to combine the siblings; in that case it will
            // have a null `containerDecl` and needs to be
            // skipped over.
            //
            if(!containerDecl)
                continue;

            // TODO: If we need default substitutions to be applied to
            // the `containerDecl`, then it might make sense to have
            // each `link` in the scope store a decl-ref instead of
            // just a decl.
            //
            DeclRef<ContainerDecl> containerDeclRef =
                DeclRef<Decl>(containerDecl, createDefaultSubstitutions(astBuilder, containerDecl)).as<ContainerDecl>();
            
            // If the container we are looking into represents a type
            // or an `extension` of a type, then we need to treat
            // this step as lookup into the `this` variable (or the
            // `This` type), which means including any `extension`s
            // or inheritance clauses in the lookup process.
            //
            // Note: The `AggTypeDeclBase` class is the common superclass
            // between `AggTypeDecl` and `ExtensionDecl`.
            //
            if (auto aggTypeDeclBaseRef = containerDeclRef.as<AggTypeDeclBase>())
            {
                // When reconstructing the final expression for a result
                // looked up through the type or extension, we will need
                // a `this` expression (or a `This` type expression) to
                // mark the base of the member reference, so we create
                // a "breadcrumb" here to track that fact.
                //
                BreadcrumbInfo breadcrumb;
                breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::This;
                breadcrumb.thisParameterMode = thisParameterMode;
                breadcrumb.declRef = aggTypeDeclBaseRef;
                breadcrumb.prev = nullptr;

                Type* type = nullptr;
                if(auto extDeclRef = aggTypeDeclBaseRef.as<ExtensionDecl>())
                {
                    if( request.semantics )
                    {
                        ensureDecl(request.semantics, extDeclRef.getDecl(), DeclCheckState::CanUseExtensionTargetType);
                    }

                    // If we are doing lookup from inside an `extension`
                    // declaration, then the `this` expression will have
                    // a type that uses the "target type" of the `extension`.
                    //
                    type = getTargetType(astBuilder, extDeclRef);
                }
                else
                {
                    assert(aggTypeDeclBaseRef.as<AggTypeDecl>());
                    type = DeclRefType::create(astBuilder, aggTypeDeclBaseRef);
                }

                _lookUpMembersInType(astBuilder, name, type, request, result, &breadcrumb);
            }
            else
            {
                // The default case is when the scope doesn't represent a
                // type or `extension` declaration, so we can look up members
                // in that scope much more simply.
                //
                _lookUpDirectAndTransparentMembers(astBuilder, name, containerDeclRef, request, result, nullptr);
            }

            // Before we proceed up to the next outer scope to perform lookup
            // again, we need to consider what the current scope tells us
            // about how to interpret uses of implicit `this` or `This`. For
            // example, if we are inside a `[mutating]` method, then the implicit
            // `this` that we use for lookup should be an l-value.
            //
            // Similarly, if we look up a member in a type from the scope
            // of some nested type, then there shouldn't be an implicit `this`
            // expression for the outer type, but instead an implicit `This`.
            //
            if( containerDeclRef.is<ConstructorDecl>() )
            {
                // In the context of an `__init` declaration, the members of
                // the surrounding type are accessible through a mutable `this`.
                //
                thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::MutableValue;
            }
            else if( containerDeclRef.is<SetterDecl>() )
            {
                // In the context of a `set` accessor, the members of the
                // surrounding type are accessible through a mutable `this`.
                //
                // TODO: At some point we may want a way to opt out of this
                // behavior; it is possible to have a setter on a `struct`
                // that actually just sets data into a buffer that is
                // referenced by one of the `struct`'s fields.
                //
                thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::MutableValue;
            }
            else if( auto funcDeclRef = containerDeclRef.as<FunctionDeclBase>() )
            {
                // The implicit `this`/`This` for a function-like declaration
                // depends on modifiers attached to the declaration.
                //
                if( funcDeclRef.getDecl()->hasModifier<HLSLStaticModifier>() )
                {
                    // A `static` method only has access to an implicit `This`,
                    // and does not have a `this` expression available.
                    //
                    thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::Type;
                }
                else if( funcDeclRef.getDecl()->hasModifier<MutatingAttribute>() )
                {
                    // In a non-`static` method marked `[mutating]` there is
                    // an implicit `this` parameter that is mutable.
                    //
                    thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::MutableValue;
                }
                else
                {
                    // In all other cases, there is an implicit `this` parameter
                    // that is immutable.
                    //
                    thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::ImmutableValue;
                }
            }
            else if( containerDeclRef.as<AggTypeDeclBase>() )
            {
                // When lookup moves from a nested typed declaration to an
                // outer scope, there is no ability to use an implicit `this`
                // expression, and we have only the `This` type available.
                //
                thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::Type;
            }
            // TODO: What other cases need to be enumerated here?
        }

        if (result.isValid())
        {
            // If we've found a result in this scope, then there
            // is no reason to look further up (for now).
            return;
        }
    }

    // If we run out of scopes, then we are done.
}

LookupResult lookUp(
    ASTBuilder*         astBuilder, 
    SemanticsVisitor*   semantics,
    Name*               name,
    Scope*              scope,
    LookupMask          mask)
{
    LookupRequest request;
    request.semantics = semantics;
    request.scope = scope;
    request.mask = mask;

    LookupResult result;
    _lookUpInScopes(astBuilder, name, request, result);
    return result;
}

void lookUpMemberImpl(
    ASTBuilder*         astBuilder, 
    SemanticsVisitor*   semantics,
    Name*               name,
    Type*               type,
    LookupResult&       ioResult,
    BreadcrumbInfo*     inBreadcrumbs,
    LookupMask          mask);

LookupResult lookUpMember(
    ASTBuilder*         astBuilder, 
    SemanticsVisitor*   semantics,
    Name*               name,
    Type*               type,
    LookupMask          mask,
    LookupOptions       options)
{
    LookupRequest request;
    request.semantics = semantics;
    request.mask = mask;
    request.options = options;

    LookupResult result;
    _lookUpMembersInType(astBuilder, name, type, request, result, nullptr);
    return result;
}

}
back to top