Skip to content

[ConstraintSystem] Increase score only if members found on Optional #35381

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 1 commit into from
Jan 14, 2021
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
26 changes: 22 additions & 4 deletions include/swift/Sema/OverloadChoice.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,14 @@ class OverloadChoice {
/// optional context type, turning a "Decl" kind into
/// "DeclViaUnwrappedOptional".
IsDeclViaUnwrappedOptional = 0x02,
/// Indicates that there are viable members found on `Optional`
/// type and its underlying type. And current overload choice
/// is a backup one, which should be picked only if members
/// found directly on `Optional` do not match.
IsFallbackDeclViaUnwrappedOptional = 0x03,
/// Indicates that this declaration was dynamic, turning a
/// "Decl" kind into "DeclViaDynamic" kind.
IsDeclViaDynamic = 0x03,
IsDeclViaDynamic = 0x07,
};

/// The base type to be used when referencing the declaration
Expand Down Expand Up @@ -175,17 +180,23 @@ class OverloadChoice {

/// Retrieve an overload choice for a declaration that was found
/// by unwrapping an optional context type.
///
/// \param isFallback Indicates that this result should be used
/// as a backup, if member found directly on `Optional` doesn't
/// match.
static OverloadChoice
getDeclViaUnwrappedOptional(Type base, ValueDecl *value,
getDeclViaUnwrappedOptional(Type base, ValueDecl *value, bool isFallback,
FunctionRefKind functionRefKind) {
OverloadChoice result;
result.BaseAndDeclKind.setPointer(base);
result.BaseAndDeclKind.setInt(IsDeclViaUnwrappedOptional);
result.BaseAndDeclKind.setInt(isFallback
? IsFallbackDeclViaUnwrappedOptional
: IsDeclViaUnwrappedOptional);
result.DeclOrKind = value;
result.TheFunctionRefKind = functionRefKind;
return result;
}

/// Retrieve an overload choice for a declaration that was found via
/// dynamic member lookup. The `ValueDecl` is a `subscript(dynamicMember:)`
/// method.
Expand Down Expand Up @@ -219,6 +230,7 @@ class OverloadChoice {
case IsDeclViaBridge: return OverloadChoiceKind::DeclViaBridge;
case IsDeclViaDynamic: return OverloadChoiceKind::DeclViaDynamic;
case IsDeclViaUnwrappedOptional:
case IsFallbackDeclViaUnwrappedOptional:
return OverloadChoiceKind::DeclViaUnwrappedOptional;
default: return OverloadChoiceKind::Decl;
}
Expand Down Expand Up @@ -256,6 +268,12 @@ class OverloadChoice {
return getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup;
}

/// Determine whether this member is a backup in case
/// members found directly on `Optional` didn't match.
bool isFallbackMemberOnUnwrappedBase() const {
return BaseAndDeclKind.getInt() == IsFallbackDeclViaUnwrappedOptional;
}

/// Get the name of the overload choice.
DeclName getName() const;

Expand Down
21 changes: 14 additions & 7 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6672,8 +6672,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,

// Local function that turns a ValueDecl into a properly configured
// OverloadChoice.
auto getOverloadChoice = [&](ValueDecl *cand, bool isBridged,
bool isUnwrappedOptional) -> OverloadChoice {
auto getOverloadChoice =
[&](ValueDecl *cand, bool isBridged, bool isUnwrappedOptional,
bool isFallbackUnwrap = false) -> OverloadChoice {
// If we're looking into an existential type, check whether this
// result was found via dynamic lookup.
if (instanceTy->isAnyObject()) {
Expand All @@ -6694,8 +6695,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
auto ovlBaseTy = MetatypeType::get(baseTy->castTo<MetatypeType>()
->getInstanceType()
->getOptionalObjectType());
return OverloadChoice::getDeclViaUnwrappedOptional(ovlBaseTy, cand,
functionRefKind);
return OverloadChoice::getDeclViaUnwrappedOptional(
ovlBaseTy, cand,
/*isFallback=*/isFallbackUnwrap, functionRefKind);
}

// While looking for subscript choices it's possible to find
Expand All @@ -6719,7 +6721,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,

return OverloadChoice(baseTy, cand, functionRefKind);
};

// Add all results from this lookup.
for (auto result : lookup)
addChoice(getOverloadChoice(result.getValueDecl(),
Expand Down Expand Up @@ -6802,11 +6804,16 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
}

if (objectType->mayHaveMembers()) {
// If there are viable members directly on `Optional`, let's
// prioritize them and mark any results found on wrapped type
// as a fallback results.
bool isFallback = !result.ViableCandidates.empty();
LookupResult &optionalLookup = lookupMember(objectType, memberName);
for (auto result : optionalLookup)
addChoice(getOverloadChoice(result.getValueDecl(),
/*bridged*/false,
/*isUnwrappedOptional=*/true));
/*bridged*/ false,
/*isUnwrappedOptional=*/true,
/*isUnwrapFallback=*/isFallback));
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2799,8 +2799,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
increaseScore(SK_DisfavoredOverload);
}

if (choice.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional &&
locator->isLastElement<LocatorPathElt::UnresolvedMember>()) {
if (choice.isFallbackMemberOnUnwrappedBase()) {
increaseScore(SK_UnresolvedMemberViaOptional);
}
}
Expand Down
25 changes: 25 additions & 0 deletions test/Constraints/sr13815.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %target-typecheck-verify-swift

// SR-13815

enum E {
case foo(String)
}

struct Test {
var bar: E?
}

struct S {
func evaluate(_: Test) -> [Test] {
return []
}

func test(set: Set<String>) {
let flattened = set.flatMap { element in
evaluate(Test(bar: .foo(element)))
}

let _: [Test] = flattened // Ok (find .`bar` after unwrap)
}
}