Skip to content

[CS] Resolve callees for key path components #27087

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4243,11 +4243,11 @@ namespace {

auto locator = cs.getConstraintLocator(
E, LocatorPathElt::KeyPathComponent(i));
if (kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) {
locator =
cs.getConstraintLocator(locator,
ConstraintLocator::SubscriptMember);
}

// Adjust the locator such that it includes any additional elements to
// point to the component's callee, e.g a SubscriptMember for a
// subscript component.
locator = cs.getCalleeLocator(locator);

bool isDynamicMember = false;
// If this is an unresolved link, make sure we resolved it.
Expand Down
21 changes: 10 additions & 11 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ Expr *FailureDiagnostic::getBaseExprFor(Expr *anchor) const {
return nullptr;
}

Optional<SelectedOverload> FailureDiagnostic::getChoiceFor(Expr *expr) const {
Optional<SelectedOverload>
FailureDiagnostic::getChoiceFor(ConstraintLocator *locator) const {
auto &cs = getConstraintSystem();
return getOverloadChoiceIfAvailable(cs.getCalleeLocator(expr));
return getOverloadChoiceIfAvailable(cs.getCalleeLocator(locator));
}

Type FailureDiagnostic::resolveInterfaceType(Type type,
Expand Down Expand Up @@ -220,7 +221,7 @@ FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const {

ValueDecl *callee = nullptr;
Type rawFnType;
if (auto overload = getChoiceFor(anchor)) {
if (auto overload = getChoiceFor(argLocator)) {
// If we have resolved an overload for the callee, then use that to get the
// function type and callee.
callee = overload->choice.getDeclOrNull();
Expand Down Expand Up @@ -374,7 +375,7 @@ ValueDecl *RequirementFailure::getDeclRef() const {
if (isFromContextualType())
return getAffectedDeclFromType(cs.getContextualType());

if (auto overload = getChoiceFor(getRawAnchor())) {
if (auto overload = getChoiceFor(getLocator())) {
// If there is a declaration associated with this
// failure e.g. an overload choice of the call
// expression, let's see whether failure is
Expand Down Expand Up @@ -746,7 +747,7 @@ bool LabelingFailure::diagnoseAsNote() {
return "(" + str + ")";
};

auto selectedOverload = getChoiceFor(anchor);
auto selectedOverload = getChoiceFor(getLocator());
if (!selectedOverload)
return false;

Expand Down Expand Up @@ -3120,8 +3121,8 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
return true;
};

auto selection = getChoiceFor(ctorRef->getBase());
if (selection) {
auto *baseLoc = cs.getConstraintLocator(ctorRef->getBase());
if (auto selection = getChoiceFor(baseLoc)) {
OverloadChoice choice = selection->choice;
if (choice.isDecl() && isMutable(choice.getDecl()) &&
!isCallArgument(initCall) &&
Expand Down Expand Up @@ -4281,15 +4282,13 @@ bool MutatingMemberRefOnImmutableBase::diagnoseAsError() {
}

bool InvalidTupleSplatWithSingleParameterFailure::diagnoseAsError() {
auto *anchor = getRawAnchor();

auto selectedOverload = getChoiceFor(anchor);
auto selectedOverload = getChoiceFor(getLocator());
if (!selectedOverload || !selectedOverload->choice.isDecl())
return false;

auto *choice = selectedOverload->choice.getDecl();

auto *argExpr = getArgumentExprFor(anchor);
auto *argExpr = getArgumentExprFor(getRawAnchor());
if (!argExpr)
return false;

Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ class FailureDiagnostic {
/// reference or subscript, nullptr otherwise.
Expr *getArgumentExprFor(Expr *anchor) const;

Optional<SelectedOverload> getChoiceFor(Expr *) const;
/// \returns The overload choice made by the constraint system for the callee
/// of a given locator's anchor, or \c None if no such choice can be found.
Optional<SelectedOverload> getChoiceFor(ConstraintLocator *) const;

/// For a given locator describing a function argument conversion, or a
/// constraint within an argument conversion, returns information about the
Expand Down
6 changes: 5 additions & 1 deletion lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3023,6 +3023,8 @@ namespace {
}

case KeyPathExpr::Component::Kind::TupleElement: {
// Note: If implemented, the logic in `getCalleeLocator` will need
// updating to return the correct callee locator for this.
llvm_unreachable("not implemented");
break;
}
Expand Down Expand Up @@ -3256,7 +3258,9 @@ namespace {
// Record the labels.
if (!labelsArePermanent)
info.Labels = CS.allocateCopy(info.Labels);
CS.ArgumentInfos[CS.getArgumentInfoLocator(expr)] = info;

auto *locator = CS.getConstraintLocator(expr);
CS.ArgumentInfos[CS.getArgumentInfoLocator(locator)] = info;
}
};

Expand Down
21 changes: 21 additions & 0 deletions lib/Sema/ConstraintLocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,27 @@ class ConstraintLocator : public llvm::FoldingSetNode {
/// Determine whether this locator points to the contextual type.
bool isForContextualType() const;

/// Attempts to cast the first path element of the locator to a specific
/// \c LocatorPathElt subclass, returning \c None if either unsuccessful or
/// the locator has no path elements.
template <class T>
Optional<T> getFirstElementAs() const {
auto path = getPath();
if (path.empty())
return None;

return path[0].getAs<T>();
}

/// Casts the first path element of the locator to a specific
/// \c LocatorPathElt subclass, asserting that it has at least one element.
template <class T>
T castFirstElementTo() const {
auto path = getPath();
assert(!path.empty() && "Expected at least one path element!");
return path[0].castTo<T>();
}

/// Check whether the last element in the path of this locator
/// is of a given kind.
bool isLastElement(ConstraintLocator::PathElementKind kind) const;
Expand Down
73 changes: 55 additions & 18 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,14 +413,49 @@ ConstraintLocator *ConstraintSystem::getConstraintLocator(
return getConstraintLocator(anchor, path, builder.getSummaryFlags());
}

ConstraintLocator *ConstraintSystem::getCalleeLocator(Expr *expr) {
ConstraintLocator *
ConstraintSystem::getCalleeLocator(ConstraintLocator *locator) {
auto *anchor = locator->getAnchor();
assert(anchor && "Expected an anchor!");

// If we have a locator that starts with a key path component element, we
// may have a callee given by a property or subscript component.
if (auto componentElt =
locator->getFirstElementAs<LocatorPathElt::KeyPathComponent>()) {
auto *kpExpr = cast<KeyPathExpr>(anchor);
auto component = kpExpr->getComponents()[componentElt->getIndex()];

using ComponentKind = KeyPathExpr::Component::Kind;
switch (component.getKind()) {
case ComponentKind::UnresolvedSubscript:
case ComponentKind::Subscript:
// For a subscript the callee is given by 'component -> subscript member'.
return getConstraintLocator(
anchor, {*componentElt, ConstraintLocator::SubscriptMember});
case ComponentKind::UnresolvedProperty:
case ComponentKind::Property:
// For a property, the choice is just given by the component.
return getConstraintLocator(anchor, *componentElt);
case ComponentKind::TupleElement:
llvm_unreachable("Not implemented by CSGen");
break;
case ComponentKind::Invalid:
case ComponentKind::OptionalForce:
case ComponentKind::OptionalChain:
case ComponentKind::OptionalWrap:
case ComponentKind::Identity:
// These components don't have any callee associated, so just continue.
break;
}
}

// Make sure we handle subscripts before looking at apply exprs. We don't
// want to return a subscript member locator for an expression such as x[](y),
// as its callee is not the subscript, but rather the function it returns.
if (isa<SubscriptExpr>(expr))
return getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
if (isa<SubscriptExpr>(anchor))
return getConstraintLocator(anchor, ConstraintLocator::SubscriptMember);

if (auto *applyExpr = dyn_cast<ApplyExpr>(expr)) {
if (auto *applyExpr = dyn_cast<ApplyExpr>(anchor)) {
auto *fnExpr = applyExpr->getFn();
// For an apply of a metatype, we have a short-form constructor. Unlike
// other locators to callees, these are anchored on the apply expression
Expand All @@ -436,27 +471,27 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator(Expr *expr) {
// Otherwise fall through and look for locators anchored on the function
// expr. For CallExprs, this can look through things like parens and
// optional chaining.
if (auto *callExpr = dyn_cast<CallExpr>(expr)) {
expr = callExpr->getDirectCallee();
if (auto *callExpr = dyn_cast<CallExpr>(anchor)) {
anchor = callExpr->getDirectCallee();
} else {
expr = fnExpr;
anchor = fnExpr;
}
}

if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
return getConstraintLocator(
expr, TC.getSelfForInitDelegationInConstructor(DC, UDE)
anchor, TC.getSelfForInitDelegationInConstructor(DC, UDE)
? ConstraintLocator::ConstructorMember
: ConstraintLocator::Member);
}

if (isa<UnresolvedMemberExpr>(expr))
return getConstraintLocator(expr, ConstraintLocator::UnresolvedMember);
if (isa<UnresolvedMemberExpr>(anchor))
return getConstraintLocator(anchor, ConstraintLocator::UnresolvedMember);

if (isa<MemberRefExpr>(expr))
return getConstraintLocator(expr, ConstraintLocator::Member);
if (isa<MemberRefExpr>(anchor))
return getConstraintLocator(anchor, ConstraintLocator::Member);

return getConstraintLocator(expr);
return getConstraintLocator(anchor);
}

Type ConstraintSystem::openUnboundGenericType(UnboundGenericType *unbound,
Expand Down Expand Up @@ -2462,7 +2497,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
return false;

const auto *fix = fixes.front();
auto *calleeLocator = getCalleeLocator(fix->getAnchor());
auto *calleeLocator = getCalleeLocator(fix->getLocator());
if (commonCalleeLocator && commonCalleeLocator != calleeLocator)
return false;

Expand Down Expand Up @@ -2829,7 +2864,9 @@ void ConstraintSystem::generateConstraints(
}
}

ConstraintLocator *ConstraintSystem::getArgumentInfoLocator(Expr *anchor) {
ConstraintLocator *
ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) {
auto *anchor = locator->getAnchor();
if (!anchor)
return nullptr;

Expand All @@ -2838,12 +2875,12 @@ ConstraintLocator *ConstraintSystem::getArgumentInfoLocator(Expr *anchor) {
return getConstraintLocator(fnExpr);
}

return getCalleeLocator(anchor);
return getCalleeLocator(locator);
}

Optional<ConstraintSystem::ArgumentInfo>
ConstraintSystem::getArgumentInfo(ConstraintLocator *locator) {
if (auto *infoLocator = getArgumentInfoLocator(locator->getAnchor())) {
if (auto *infoLocator = getArgumentInfoLocator(locator)) {
auto known = ArgumentInfos.find(infoLocator);
if (known != ArgumentInfos.end())
return known->second;
Expand Down
31 changes: 24 additions & 7 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1579,9 +1579,10 @@ class ConstraintSystem {
/// that locator.
llvm::DenseMap<ConstraintLocator *, ArgumentInfo> ArgumentInfos;

/// Form a locator with given anchor which then could be used
/// to retrieve argument information cached in the constraint system.
ConstraintLocator *getArgumentInfoLocator(Expr *anchor);
/// Form a locator that can be used to retrieve argument information cached in
/// the constraint system for the callee described by the anchor of the
/// passed locator.
ConstraintLocator *getArgumentInfoLocator(ConstraintLocator *locator);

/// Retrieve the argument info that is associated with a member
/// reference at the given locator.
Expand Down Expand Up @@ -2008,10 +2009,26 @@ class ConstraintSystem {
return e != ExprWeights.end() ? e->second.second : nullptr;
}

/// Returns a locator describing the callee for a given expression. For
/// a function application, this is a locator describing the function expr.
/// For an unresolved dot/member, this is a locator to the member.
ConstraintLocator *getCalleeLocator(Expr *expr);
/// Returns a locator describing the callee for the anchor of a given locator.
///
/// - For an unresolved dot/member anchor, this will be a locator describing
/// the member.
///
/// - For a subscript anchor, this will be a locator describing the subscript
/// member.
///
/// - For a key path anchor with a property/subscript component path element,
/// this will be a locator describing the decl referenced by the component.
///
/// - For a function application anchor, this will be a locator describing the
/// 'direct callee' of the call. For example, for the expression \c x.foo?()
/// the returned locator will describe the member \c foo.
///
/// Note that because this function deals with the anchor, given a locator
/// anchored on \c functionA(functionB()) with path elements pointing to the
/// argument \c functionB(), the returned callee locator will describe
/// \c functionA rather than \c functionB.
ConstraintLocator *getCalleeLocator(ConstraintLocator *locator);

public:

Expand Down
13 changes: 13 additions & 0 deletions test/Constraints/generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -819,3 +819,16 @@ func test_correct_identification_of_requirement_source() {
_ = X(A(), 17)
// expected-error@-1 {{initializer 'init(_:_:)' requires that 'Int' conform to 'P'}}
}

struct SR11435<T> {
subscript<U : P & Hashable>(x x: U) -> U { x } // expected-note {{where 'U' = 'Int'}}
}

extension SR11435 where T : P { // expected-note {{where 'T' = 'Int'}}
var foo: Int { 0 }
}

func test_identification_of_key_path_component_callees() {
_ = \SR11435<Int>.foo // expected-error {{property 'foo' requires that 'Int' conform to 'P'}}
_ = \SR11435<Int>.[x: 5] // expected-error {{subscript 'subscript(x:)' requires that 'Int' conform to 'P'}}
}