summaryrefslogtreecommitdiff
path: root/source/slang/slang-lookup.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2020-03-06 11:37:36 -0800
committerGitHub <noreply@github.com>2020-03-06 11:37:36 -0800
commit18be2d81fd2740d3f0c06fc407cff1702b93d468 (patch)
tree2438f0cca4cd3453534d9742c2235cf3771c9194 /source/slang/slang-lookup.cpp
parent0b91ea73fe7eb1cf908fc1f87a9539a6fe48b65a (diff)
Expand range of definitions that can be moved into stdlib (#1259)
The actual definitions that got moved into the stdlib here are pretty few: * `clip()` * `cross()` * `dxx()`, `ddy()` etc. * `degrees()` * `distance()` * `dot()` * `faceforward()` The meat of the change is infrastructure changes required to support these new declarations * Generic versions of the standard operators (e.g., `operator+`) were added that are generic for a type `T` that implements the matching `__Builtin`-prefixed interface. An open question is whether we can now drop the non-generic versions in favor of just having these generic operators. * A `__BuiltinLogicalType` interface was added to capture the commonality between integers and `bool` * `__BuiltinArithmeticType` was extended so that implementations must support initialization from an `int` * `__BuiltinFloatingPointType` was extended to require an accessor that returns the value of pi for the given type, and the concrete floating-point types were extended to provide definitions of this value. * It turns out that our logic for checking if two functions have the same signature (and should thus count as redeclarations/redefinitions) wasn't taking generic constraints into account at all. That was fixed with a stopgap solution that checks if the generic constraints are pairwise identical, but I didn't implement the more "correct" fix that would require canonicalizing the constraints. * When doing overload resolution and considering potential callees, logic was added so that a non-generic candidate should always be selected over a generic one (generally the Right Thing to do), and also so that a generic candidate with fewer parameters will be selected over one with more (an approximation of the much more complicated rule we'd ideally have). * The formatting of declarations/overloads for "ambiguous overload" errors was fleshed out a bit to include more context (the "kind" of declaration where appropriate, the return type for function declarations) and to properly space thing when outputting specialization of operator overloads that end with `<` (so that we print `func < <int>(int, int)` instead of just `func <<int,int>(int,int)`). * The core lookup routines were heavily refactored and reorganized to try to make them bottleneck more effectively so that all paths handle all the nuances of inheritance, extensions, etc. * Because of the refactoring to lookup logic, the semantic checking logic related to checking if a type conforms to an interface was updated to be driven based on the `Type` that is supposed to be conforming, rather than a `DeclRef` to the type's declaration. This allows it to use the type-based lookup entry point and eliminates one special-case entry point for lookup. In addition to the various core changes, this change also refactors some of the existing stdlib code to favor writing more things in actual Slang syntax, and less in C++ code that uses `StringBuilder` to construct the Slang syntax. There is a lot more that could be done along those lines, but even pushing this far is showing that the current approach that `slang-generate` takes for how to separate meta-level C++ and Slang code isn't really ideal, so a revamp of the generator code is probably needed before I continue pushing. One surprising casualty of the refactoring of lookup is that we no longer have the `lookedUpDecls` field in `LookupResult`. That field probably didn't belong there anyway, but the role it served was important. The idea of `lookedUpDecls` was to avoid looking up in the same interface more than once in cases where a type might have a "diamond" inheritance pattern. Removing that field doesn't appear to affect correctness of any of our existing tests, but by adding a specific test for "diamond" inheritance I could see that the refactoring introduced a regression and made looking up a member inherited along multiple paths ambiguous. Rather than add back `lookedUpDecls` I went for a simpler (but arguably even hackier) solution where when ranking candidates from a `LookupResult` we check for identical `DeclRef`s and arbitrarily favor one over the other. One complication that arises here is that when comparing `DeclRef`s inherited along different paths they might have a `ThisTypeSubstitution` for the same type, but with different subtype witnesses (because different inheritance paths could lead to different transitive subtype witnesses: e.g., `A : B : D` and `A : C : D`).
Diffstat (limited to 'source/slang/slang-lookup.cpp')
-rw-r--r--source/slang/slang-lookup.cpp787
1 files changed, 402 insertions, 385 deletions
diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp
index 49f0bbf0c..02f74579c 100644
--- a/source/slang/slang-lookup.cpp
+++ b/source/slang/slang-lookup.cpp
@@ -25,14 +25,6 @@ struct BreadcrumbInfo
BreadcrumbInfo* prev = nullptr;
};
-void DoLocalLookupImpl(
- Session* session,
- Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
- LookupRequest const& request,
- LookupResult& result,
- BreadcrumbInfo* inBreadcrumbs);
-
//
void buildMemberDictionary(ContainerDecl* decl)
@@ -169,146 +161,28 @@ LookupResultItem CreateLookupResultItem(
return item;
}
-void DoMemberLookupImpl(
- Session* session,
- Name* name,
- RefPtr<Type> baseType,
- LookupRequest const& request,
- LookupResult& ioResult,
- BreadcrumbInfo* breadcrumbs)
-{
- if (!baseType)
- {
- return;
- }
-
- // If the type was pointer-like, then dereference it
- // automatically here.
- if (auto pointerLikeType = as<PointerLikeType>(baseType))
- {
- // Need to leave a breadcrumb to indicate that we
- // did an implicit dereference here
- BreadcrumbInfo derefBreacrumb;
- derefBreacrumb.kind = LookupResultItem::Breadcrumb::Kind::Deref;
- derefBreacrumb.prev = breadcrumbs;
-
- // Recursively perform lookup on the result of deref
- return DoMemberLookupImpl(
- session,
- name, pointerLikeType->elementType, request, ioResult, &derefBreacrumb);
- }
-
- // Default case: no dereference needed
-
- if (auto baseDeclRefType = as<DeclRefType>(baseType))
- {
- if (auto baseAggTypeDeclRef = baseDeclRefType->declRef.as<AggTypeDecl>())
- {
- DoLocalLookupImpl(
- session,
- name, baseAggTypeDeclRef, request, ioResult, breadcrumbs);
- }
- }
-
- // TODO(tfoley): any other cases to handle here?
-}
-
-void DoMemberLookupImpl(
+static void _lookUpMembersInValue(
Session* session,
Name* name,
- DeclRef<Decl> baseDeclRef,
+ DeclRef<Decl> valueDeclRef,
LookupRequest const& request,
LookupResult& ioResult,
- BreadcrumbInfo* breadcrumbs)
-{
- auto baseType = getTypeForDeclRef(
- session,
- baseDeclRef,
- SourceLoc());
- return DoMemberLookupImpl(
- session,
- name, baseType, request, ioResult, breadcrumbs);
-}
-
-// If we are about to perform lookup through an interface, then
-// we need to specialize the decl-ref to that interface to include
-// a "this type" subtitution. This function applies that substition
-// when it is required, and returns the existing `declRef` otherwise.
-DeclRef<Decl> maybeSpecializeInterfaceDeclRef(
- RefPtr<Type> subType,
- RefPtr<Type> superType,
- DeclRef<Decl> superTypeDeclRef, // The decl-ref we are going to perform lookup in
- DeclRef<TypeConstraintDecl> constraintDeclRef) // The type constraint that told us our type is a subtype
-{
- if (auto superInterfaceDeclRef = superTypeDeclRef.as<InterfaceDecl>())
- {
- // TODO: This case should probably loop back into the semantic
- // checking logic (when available) in order to ensure that
- // appropriate witness values have been registered for (at least)
- // the associated type requirements of the super-type.
-
- // Create a subtype witness value to note the subtype relationship
- // that makes this specialization valid.
- //
- // Note: this is to ensure that we can specialize the subtype witness
- // later (e.g., by replacing a subtype witness that represents a generic
- // constraint parameter with the concrete generic arguments that
- // are used at a particular call site to the generic).
- RefPtr<DeclaredSubtypeWitness> subtypeWitness = new DeclaredSubtypeWitness();
- subtypeWitness->declRef = constraintDeclRef;
- subtypeWitness->sub = subType;
- subtypeWitness->sup = superType;
-
- RefPtr<ThisTypeSubstitution> thisTypeSubst = new ThisTypeSubstitution();
- thisTypeSubst->interfaceDecl = superInterfaceDeclRef.getDecl();
- thisTypeSubst->witness = subtypeWitness;
- thisTypeSubst->outer = superInterfaceDeclRef.substitutions.substitutions;
-
- auto specializedInterfaceDeclRef = DeclRef<Decl>(superInterfaceDeclRef.getDecl(), thisTypeSubst);
- return specializedInterfaceDeclRef;
- }
-
- return superTypeDeclRef;
-}
-
-// Same as the above, but we are specializing a type instead of a decl-ref
-RefPtr<Type> maybeSpecializeInterfaceDeclRef(
- Session* session,
- RefPtr<Type> subType,
- RefPtr<Type> superType, // The type we are going to perform lookup in
- DeclRef<TypeConstraintDecl> constraintDeclRef) // The type constraint that told us our type is a subtype
-{
- if (auto superDeclRefType = as<DeclRefType>(superType))
- {
- if (auto superInterfaceDeclRef = superDeclRefType->declRef.as<InterfaceDecl>())
- {
- auto specializedInterfaceDeclRef = maybeSpecializeInterfaceDeclRef(
- subType,
- superType,
- superInterfaceDeclRef,
- constraintDeclRef);
- auto specializedInterfaceType = DeclRefType::Create(session, specializedInterfaceDeclRef);
- return specializedInterfaceType;
- }
- }
-
- return superType;
-}
-
-
-// Look for members of the given name in the given container for declarations
-void DoLocalLookupImpl(
+ 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(
Session* session,
Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
+ DeclRef<ContainerDecl> containerDeclRef,
LookupRequest const& request,
- LookupResult& result,
- BreadcrumbInfo* inBreadcrumbs)
+ LookupResult& result,
+ BreadcrumbInfo* inBreadcrumbs)
{
- if (result.lookedupDecls.Contains(containerDeclRef))
- return;
- result.lookedupDecls.Add(containerDeclRef);
-
ContainerDecl* containerDecl = containerDeclRef.getDecl();
// Ensure that the lookup dictionary in the container is up to date
@@ -334,10 +208,8 @@ void DoLocalLookupImpl(
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
@@ -352,7 +224,7 @@ void DoLocalLookupImpl(
memberRefBreadcrumb.declRef = transparentMemberDeclRef;
memberRefBreadcrumb.prev = inBreadcrumbs;
- DoMemberLookupImpl(
+ _lookUpMembersInValue(
session,
name,
transparentMemberDeclRef,
@@ -360,97 +232,363 @@ void DoLocalLookupImpl(
result,
&memberRefBreadcrumb);
}
+}
- // Consider lookup via extension
- if( auto aggTypeDeclRef = containerDeclRef.as<AggTypeDecl>() )
+static RefPtr<SubtypeWitness> _makeSubtypeWitness(
+ Type* subType,
+ SubtypeWitness* subToMidWitness,
+ Type* superType,
+ DeclRef<TypeConstraintDecl> midToSuperConstraint)
+{
+ if(subToMidWitness)
+ {
+ RefPtr<TransitiveSubtypeWitness> transitiveWitness = new TransitiveSubtypeWitness();
+ transitiveWitness->subToMid = subToMidWitness;
+ transitiveWitness->midToSup = midToSuperConstraint;
+ transitiveWitness->sub = subType;
+ transitiveWitness->sup = superType;
+ return transitiveWitness;
+ }
+ else
{
- if (request.semantics)
+ RefPtr<DeclaredSubtypeWitness> declaredWitness = new DeclaredSubtypeWitness();
+ declaredWitness->declRef = midToSuperConstraint;
+ declaredWitness->sub = subType;
+ declaredWitness->sup = superType;
+ return declaredWitness;
+ }
+}
+
+// Same as the above, but we are specializing a type instead of a decl-ref
+static RefPtr<Type> _maybeSpecializeSuperType(
+ Session* session,
+ Type* superType,
+ SubtypeWitness* subIsSuperWitness)
+{
+ if (auto superDeclRefType = as<DeclRefType>(superType))
+ {
+ if (auto superInterfaceDeclRef = superDeclRefType->declRef.as<InterfaceDecl>())
{
- ensureDecl(request.semantics, containerDeclRef.getDecl(), DeclCheckState::ReadyForLookup);
+ RefPtr<ThisTypeSubstitution> thisTypeSubst = new ThisTypeSubstitution();
+ thisTypeSubst->interfaceDecl = superInterfaceDeclRef.getDecl();
+ thisTypeSubst->witness = subIsSuperWitness;
+ thisTypeSubst->outer = superInterfaceDeclRef.substitutions.substitutions;
+
+ auto specializedInterfaceDeclRef = DeclRef<Decl>(superInterfaceDeclRef.getDecl(), thisTypeSubst);
+
+ auto specializedInterfaceType = DeclRefType::Create(session, specializedInterfaceDeclRef);
+ return specializedInterfaceType;
}
+ }
+
+ return superType;
+}
+
+static void _lookUpMembersInType(
+ Session* session,
+ Name* name,
+ RefPtr<Type> type,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* breadcrumbs);
+
+static void _lookUpMembersInSuperTypeImpl(
+ Session* session,
+ Name* name,
+ Type* leafType,
+ Type* superType,
+ SubtypeWitness* leafIsSuperWitness,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* inBreadcrumbs);
- RefPtr<Type> type = DeclRefType::Create(
- session,
- aggTypeDeclRef);
- for (auto ext = GetCandidateExtensions(aggTypeDeclRef); ext; ext = ext->nextCandidateExtension)
+static void _lookUpMembersInSuperType(
+ Session* session,
+ 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(intermediateIsSuperConstraint);
+ //
+ // We will go ahead and perform lookup using `superType`,
+ // after dealing with some details.
+
+ auto leafIsSuperWitness = _makeSubtypeWitness(
+ leafType,
+ leafIsIntermediateWitness,
+ superType,
+ intermediateIsSuperConstraint);
+
+ // 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(
+ session,
+ 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::Constraint;
+ breadcrumb.declRef = intermediateIsSuperConstraint;
+ 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(session, name, leafType, superType, leafIsSuperWitness, request, ioResult, &breadcrumb);
+}
+
+static void _lookUpMembersInSuperTypeDeclImpl(
+ Session* session,
+ 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))
{
- auto extDeclRef = ApplyExtensionToType(request.semantics, ext, type);
- if (!extDeclRef)
- continue;
+ if( semantics )
+ {
+ ensureDecl(semantics, constraintDeclRef, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ }
- // 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.
+ // 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(constraintDeclRef);
+ auto subDeclRefType = as<DeclRefType>(subType);
+ if(!subDeclRefType)
+ continue;
+ if(!subDeclRefType->declRef.Equals(genericTypeParamDeclRef))
+ continue;
- DoLocalLookupImpl(
+ _lookUpMembersInSuperType(
session,
- name, extDeclRef, request, result, inBreadcrumbs);
+ name,
+ leafType,
+ leafIsSuperWitness,
+ constraintDeclRef,
+ request,
+ ioResult,
+ inBreadcrumbs);
}
-
}
- // for interface decls, also lookup in the base interfaces
- if (request.semantics)
+ else if (declRef.as<AssocTypeDecl>() || declRef.as<GlobalGenericParamDecl>())
{
- // TODO:
- // The logic here is a bit gross, because it tries to work in terms of
- // decl-refs instead of types (e.g., it asserts that the target type
- // for an `extension` declaration must be a decl-ref type).
- //
- // This code should be converted to do a type-based lookup
- // through declared bases for *any* aggregate type declaration.
- // I think that logic is present in the type-based lookup path, but
- // it would be needed here for when doing lookup from inside an
- // aggregate declaration.
-
- // if we are looking at an extension, find the target decl that we are extending
- DeclRef<Decl> targetDeclRef = containerDeclRef;
- RefPtr<DeclRefType> targetDeclRefType;
- if (auto extDeclRef = containerDeclRef.as<ExtensionDecl>())
+ for (auto constraintDeclRef : getMembersOfType<TypeConstraintDecl>(declRef.as<ContainerDecl>()))
{
- ensureDecl(request.semantics, extDeclRef.getDecl(), DeclCheckState::CanUseExtensionTargetType);
-
- targetDeclRefType = as<DeclRefType>(extDeclRef.getDecl()->targetType);
- SLANG_ASSERT(targetDeclRefType);
- int diff = 0;
- targetDeclRef = targetDeclRefType->declRef.as<ContainerDecl>().SubstituteImpl(containerDeclRef.substitutions, &diff);
+ _lookUpMembersInSuperType(
+ session,
+ name,
+ leafType,
+ leafIsSuperWitness,
+ constraintDeclRef,
+ request,
+ ioResult,
+ inBreadcrumbs);
}
-
- // When looking up inside a type, we want to also perform lookup via
- // the types it inherits from, in case of them defines the member we are looking for.
+ }
+ 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`.
//
- // TODO: Need to be careful that this doesn't allow a type to satisfy an interface
- // requirement using the original declaration of that requirement...
+ _lookUpDirectAndTransparentMembers(session, 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(!targetDeclRefType)
+ if(auto aggTypeDeclRef = aggTypeDeclBaseRef.as<AggTypeDecl>())
{
- targetDeclRefType = DeclRefType::Create(session, targetDeclRef);
+ // 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); extDecl; extDecl = extDecl->nextCandidateExtension)
+ {
+ // 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(
+ session,
+ name,
+ leafType,
+ superType,
+ leafIsSuperWitness,
+ extDeclRef,
+ request,
+ ioResult,
+ inBreadcrumbs);
+ }
}
- auto baseInterfaces = getMembersOfType<InheritanceDecl>(containerDeclRef);
- for (auto inheritanceDeclRef : baseInterfaces)
+ // 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(request.semantics, inheritanceDeclRef.getDecl(), DeclCheckState::CanUseBaseOfInheritanceDecl);
+ ensureDecl(semantics, inheritanceDeclRef.getDecl(), DeclCheckState::CanUseBaseOfInheritanceDecl);
+ _lookUpMembersInSuperType(session, name, leafType, leafIsSuperWitness, inheritanceDeclRef, request, ioResult, inBreadcrumbs);
+ }
+ }
+ }
+}
+
+static void _lookUpMembersInSuperTypeImpl(
+ Session* session,
+ 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;
- auto baseType = inheritanceDeclRef.getDecl()->base.type.dynamicCast<DeclRefType>();
- SLANG_ASSERT(baseType);
- int diff = 0;
- auto baseInterfaceDeclRef = baseType->declRef.SubstituteImpl(containerDeclRef.substitutions, &diff);
+ // Recursively perform lookup on the result of deref
+ _lookUpMembersInType(
+ session,
+ name, pointerLikeType->elementType, request, ioResult, &derefBreacrumb);
+ return;
+ }
- baseInterfaceDeclRef = maybeSpecializeInterfaceDeclRef(
- targetDeclRefType,
- baseType,
- baseInterfaceDeclRef,
- inheritanceDeclRef);
+ // Default case: no dereference needed
- DoLocalLookupImpl(session, name, baseInterfaceDeclRef.as<ContainerDecl>(), request, result, inBreadcrumbs);
- }
- }
+ if(auto declRefType = as<DeclRefType>(superType))
+ {
+ auto declRef = declRefType->declRef;
+
+ _lookUpMembersInSuperTypeDeclImpl(session, name, leafType, superType, leafIsSuperWitness, declRef, 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(
+ Session* session,
+ Name* name,
+ RefPtr<Type> type,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* breadcrumbs)
+{
+ if (!type)
+ {
+ return;
}
+
+ _lookUpMembersInSuperTypeImpl(session, 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(
+ Session* session,
+ 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(
+ session,
+ valueDeclRef,
+ SourceLoc());
+ return _lookUpMembersInType(
+ session,
+ name, valueType, request, ioResult, breadcrumbs);
}
-void DoLookupImpl(
+static void _lookUpInScopes(
Session* session,
Name* name,
LookupRequest const& request,
@@ -472,52 +610,74 @@ void DoLookupImpl(
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(session, containerDecl)).as<ContainerDecl>();
- BreadcrumbInfo breadcrumb;
- BreadcrumbInfo* breadcrumbs = nullptr;
-
- // Depending on the kind of container we are looking into,
- // we may need to insert something like a `this` expression
- // to resolve the lookup result.
+ // 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: We are checking for `AggTypeDeclBase` here, and not
- // just `AggTypeDecl`, because we want to catch `extension`
- // declarations as well.
+ // Note: The `AggTypeDeclBase` class is the common superclass
+ // between `AggTypeDecl` and `ExtensionDecl`.
//
- if (auto aggTypeDeclRef = containerDeclRef.as<AggTypeDeclBase>())
+ if (auto aggTypeDeclBaseRef = containerDeclRef.as<AggTypeDeclBase>())
{
+ // When reconstructing the final expression for a result
+ // looked up through the tyep 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 trakc that fact.
+ //
+ BreadcrumbInfo breadcrumb;
breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::This;
breadcrumb.thisParameterMode = thisParameterMode;
- breadcrumb.declRef = aggTypeDeclRef;
+ breadcrumb.declRef = aggTypeDeclBaseRef;
breadcrumb.prev = nullptr;
- breadcrumbs = &breadcrumb;
- }
-
- // Now perform "local" lookup in the context of the container,
- // as if we were looking up a member directly.
-
- // if we are currently in an extension decl, perform local lookup
- // in the target decl we are extending
- if (auto extDeclRef = containerDeclRef.as<ExtensionDecl>())
- {
- if (extDeclRef.getDecl()->targetType)
+ RefPtr<Type> type;
+ if(auto extDeclRef = aggTypeDeclBaseRef.as<ExtensionDecl>())
{
- if (auto targetDeclRef = as<DeclRefType>(extDeclRef.getDecl()->targetType))
+ if( request.semantics )
{
- if (auto aggDeclRef = targetDeclRef->declRef.as<AggTypeDecl>())
- {
- containerDeclRef = extDeclRef.Substitute(aggDeclRef);
- }
+ 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(extDeclRef);
}
+ else
+ {
+ assert(aggTypeDeclBaseRef.as<AggTypeDecl>());
+ type = DeclRefType::Create(session, aggTypeDeclBaseRef);
+ }
+
+ _lookUpMembersInType(session, 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(session, name, containerDeclRef, request, result, nullptr);
}
- DoLocalLookupImpl(
- session,
- name, containerDeclRef, request, result, breadcrumbs);
+ // 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 `this`. For example, if
+ // we are inside a `[mutating]` method, then the implicit `this`
+ // that we use for lookup should be an l-value.
+ //
if( containerDeclRef.is<ConstructorDecl>() )
{
thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::Mutating;
@@ -546,16 +706,6 @@ void DoLookupImpl(
// If we run out of scopes, then we are done.
}
-LookupResult DoLookup(
- Session* session,
- Name* name,
- LookupRequest const& request)
-{
- LookupResult result;
- DoLookupImpl(session, name, request, result);
- return result;
-}
-
LookupResult lookUp(
Session* session,
SemanticsVisitor* semantics,
@@ -567,24 +717,9 @@ LookupResult lookUp(
request.semantics = semantics;
request.scope = scope;
request.mask = mask;
- return DoLookup(session, name, request);
-}
-
-// perform lookup within the context of a particular container declaration,
-// and do *not* look further up the chain
-LookupResult lookUpLocal(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
- LookupMask mask)
-{
- LookupRequest request;
- request.semantics = semantics;
- request.mask = mask;
LookupResult result;
- DoLocalLookupImpl(session, name, containerDeclRef, request, result, nullptr);
+ _lookUpInScopes(session, name, request, result);
return result;
}
@@ -597,128 +732,6 @@ void lookUpMemberImpl(
BreadcrumbInfo* inBreadcrumbs,
LookupMask mask);
-// Perform lookup "through" the given constraint decl-ref,
-// which should show that `subType` is a sub-type of some
-// super-type (e.g., an interface).
-//
-void lookUpThroughConstraint(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- Type* subType,
- DeclRef<TypeConstraintDecl> constraintDeclRef,
- LookupResult& ioResult,
- BreadcrumbInfo* inBreadcrumbs,
- LookupMask mask)
-{
- // 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(constraintDeclRef);
- //
- // We will go ahead and perform lookup using `superType`,
- // after dealing with some details.
-
- // 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 = maybeSpecializeInterfaceDeclRef(
- session,
- subType,
- superType,
- constraintDeclRef);
-
- // 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 "upcase" from sub-type to super-type.
- //
- BreadcrumbInfo breadcrumb;
- breadcrumb.prev = inBreadcrumbs;
- breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::Constraint;
- breadcrumb.declRef = constraintDeclRef;
-
- // 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.
- //
- lookUpMemberImpl(session, semantics, name, superType, ioResult, &breadcrumb, mask);
-}
-
-void lookUpMemberImpl(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- Type* type,
- LookupResult& ioResult,
- BreadcrumbInfo* inBreadcrumbs,
- LookupMask mask)
-{
- if (auto declRefType = as<DeclRefType>(type))
- {
- auto declRef = declRefType->declRef;
- if (declRef.as<AssocTypeDecl>() || declRef.as<GlobalGenericParamDecl>())
- {
- for (auto constraintDeclRef : getMembersOfType<TypeConstraintDecl>(declRef.as<ContainerDecl>()))
- {
- lookUpThroughConstraint(
- session,
- semantics,
- name,
- type,
- constraintDeclRef,
- ioResult,
- inBreadcrumbs,
- mask);
- }
- }
- else if (auto aggTypeDeclRef = declRef.as<AggTypeDecl>())
- {
- LookupRequest request;
- request.semantics = semantics;
-
- DoLocalLookupImpl(session, name, aggTypeDeclRef, request, ioResult, inBreadcrumbs);
- }
- else if (auto genericTypeParamDeclRef = declRef.as<GenericTypeParamDecl>())
- {
- auto genericDeclRef = genericTypeParamDeclRef.GetParent().as<GenericDecl>();
- assert(genericDeclRef);
-
- for(auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
- {
- // 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(constraintDeclRef);
- auto subDeclRefType = as<DeclRefType>(subType);
- if(!subDeclRefType)
- continue;
- if(!subDeclRefType->declRef.Equals(genericTypeParamDeclRef))
- continue;
-
- lookUpThroughConstraint(
- session,
- semantics,
- name,
- type,
- constraintDeclRef,
- ioResult,
- inBreadcrumbs,
- mask);
- }
- }
-
- }
-
-}
-
LookupResult lookUpMember(
Session* session,
SemanticsVisitor* semantics,
@@ -726,8 +739,12 @@ LookupResult lookUpMember(
Type* type,
LookupMask mask)
{
+ LookupRequest request;
+ request.semantics = semantics;
+ request.mask = mask;
+
LookupResult result;
- lookUpMemberImpl(session, semantics, name, type, result, nullptr, mask);
+ _lookUpMembersInType(session, name, type, request, result, nullptr);
return result;
}