Skip to content

Commit 65db86b

Browse files
authored
Merge pull request #34715 from Jumhyn/SR-13815
[Sema] Always look through optionals for unresolved member lookup
2 parents 8104d5f + 6b0f5da commit 65db86b

File tree

9 files changed

+189
-4
lines changed

9 files changed

+189
-4
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,8 @@ enum ScoreKind {
785785
SK_ForwardTrailingClosure,
786786
/// A use of a disfavored overload.
787787
SK_DisfavoredOverload,
788+
/// A member for an \c UnresolvedMemberExpr found via unwrapped optional base.
789+
SK_UnresolvedMemberViaOptional,
788790
/// An implicit force of an implicitly unwrapped optional value.
789791
SK_ForceUnchecked,
790792
/// A user-defined conversion.

lib/Sema/CSRanking.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ static StringRef getScoreKindName(ScoreKind kind) {
5252
case SK_DisfavoredOverload:
5353
return "disfavored overload";
5454

55+
case SK_UnresolvedMemberViaOptional:
56+
return "unwrapping optional at unresolved member base";
57+
5558
case SK_ForceUnchecked:
5659
return "force of an implicitly unwrapped optional";
5760

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6777,13 +6777,12 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
67776777
// through optional types.
67786778
//
67796779
// FIXME: Unify with the above code path.
6780-
if (result.ViableCandidates.empty() &&
6781-
baseObjTy->is<AnyMetatypeType>() &&
6780+
if (baseObjTy->is<AnyMetatypeType>() &&
67826781
constraintKind == ConstraintKind::UnresolvedValueMember) {
67836782
if (auto objectType = instanceTy->getOptionalObjectType()) {
67846783
// If we don't have a wrapped type yet, we can't look through the optional
67856784
// type.
6786-
if (objectType->getAs<TypeVariableType>()) {
6785+
if (objectType->getAs<TypeVariableType>() && result.ViableCandidates.empty()) {
67876786
MemberLookupResult result;
67886787
result.OverallResult = MemberLookupResult::Unsolved;
67896788
return result;

lib/Sema/ConstraintSystem.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,6 +2802,11 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
28022802
choice.getDecl()->getAttrs().hasAttribute<DisfavoredOverloadAttr>()) {
28032803
increaseScore(SK_DisfavoredOverload);
28042804
}
2805+
2806+
if (choice.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional &&
2807+
locator->isLastElement<LocatorPathElt::UnresolvedMember>()) {
2808+
increaseScore(SK_UnresolvedMemberViaOptional);
2809+
}
28052810
}
28062811

28072812
Type ConstraintSystem::simplifyTypeImpl(Type type,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-typecheck-verify-swift -dump-ast > %t.dump
2+
// RUN: %FileCheck %s < %t.dump
3+
4+
// SR-13815
5+
extension Optional {
6+
func sr13815() -> SR13815? { SR13815() }
7+
static func sr13815_2() -> SR13815? { SR13815() }
8+
static func sr13815_3() -> SR13815? { SR13815() }
9+
static var sr13815_wrongType: Int { 0 }
10+
static var sr13815_overload: SR13815 { SR13815() }
11+
init(overloaded: Void) { self = nil }
12+
}
13+
14+
struct SR13815 {
15+
static var sr13815: SR13815? = SR13815()
16+
static var sr13815_2: SR13815? = SR13815()
17+
static var sr13815_wrongType: SR13815? { SR13815() }
18+
static var p_SR13815: SR13815? { SR13815() }
19+
static func sr13815_3() -> SR13815? { SR13815() }
20+
static var sr13815_overload: SR13815? { SR13815() }
21+
init(overloaded: Void) {}
22+
init?(failable: Void) {}
23+
init() {}
24+
}
25+
26+
protocol P_SR13815 {}
27+
extension Optional: P_SR13815 where Wrapped: Equatable {
28+
static func p_SR13815() {}
29+
}
30+
31+
let _: SR13815? = .sr13815
32+
let _: SR13815? = .sr13815_wrongType
33+
let _: SR13815? = .init()
34+
let _: SR13815? = .sr13815() // expected-error {{instance member 'sr13815' cannot be used on type 'SR13815?'}}
35+
let _: SR13815? = .sr13815_2()
36+
let _: SR13815? = .init(SR13815())
37+
let _: SR13815? = .init(overloaded: ())
38+
// If members exist on Optional and Wrapped, always choose the one on optional
39+
// CHECK: declref_expr {{.*}} location={{.*}}optional_overload.swift:37
40+
// CHECK-SAME: decl=optional_overload.(file).Optional extension.init(overloaded:)
41+
let _: SR13815? = .sr13815_overload
42+
// Should choose the overload from Optional even if the Wrapped overload would otherwise have a better score
43+
// CHECK: member_ref_expr {{.*}} location={{.*}}optional_overload.swift:41
44+
// CHECK-SAME: decl=optional_overload.(file).Optional extension.sr13815_overload
45+
let _: SR13815? = .init(failable: ())
46+
let _: SR13815? = .sr13815_3()
47+
let _: SR13815? = .p_SR13815

unittests/Sema/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
add_swift_unittest(swiftSemaTests
33
SemaFixture.cpp
44
BindingInferenceTests.cpp
5-
ConstraintSimplificationTests.cpp)
5+
ConstraintSimplificationTests.cpp
6+
UnresolvedMemberLookupTests.cpp)
67

78
target_link_libraries(swiftSemaTests
89
PRIVATE

unittests/Sema/SemaFixture.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,39 @@ Type SemaTest::getStdlibType(StringRef name) const {
7676
return Type();
7777
}
7878

79+
NominalTypeDecl *SemaTest::getStdlibNominalTypeDecl(StringRef name) const {
80+
auto typeName = Context.getIdentifier(name);
81+
82+
auto *stdlib = Context.getStdlibModule();
83+
84+
llvm::SmallVector<ValueDecl *, 4> results;
85+
stdlib->lookupValue(typeName, NLKind::UnqualifiedLookup, results);
86+
87+
if (results.size() != 1)
88+
return nullptr;
89+
90+
return dyn_cast<NominalTypeDecl>(results.front());
91+
}
92+
93+
VarDecl *SemaTest::addExtensionVarMember(NominalTypeDecl *decl,
94+
StringRef name, Type type) const {
95+
auto *ext = ExtensionDecl::create(Context, SourceLoc(), nullptr, { }, DC,
96+
nullptr);
97+
decl->addExtension(ext);
98+
ext->setExtendedNominal(decl);
99+
100+
auto *VD = new (Context) VarDecl(/*isStatic=*/ true, VarDecl::Introducer::Var,
101+
/*nameLoc=*/ SourceLoc(),
102+
Context.getIdentifier(name), ext);
103+
104+
ext->addMember(VD);
105+
auto *pat = new (Context) NamedPattern(VD);
106+
VD->setNamingPattern(pat);
107+
pat->setType(type);
108+
109+
return VD;
110+
}
111+
79112
ProtocolType *SemaTest::createProtocol(llvm::StringRef protocolName,
80113
Type parent) {
81114
auto *PD = new (Context)

unittests/Sema/SemaFixture.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ class SemaTest : public SemaTestBase {
6767
protected:
6868
Type getStdlibType(StringRef name) const;
6969

70+
NominalTypeDecl *getStdlibNominalTypeDecl(StringRef name) const;
71+
72+
VarDecl *addExtensionVarMember(NominalTypeDecl *decl, StringRef name,
73+
Type type) const;
74+
7075
ProtocolType *createProtocol(llvm::StringRef protocolName,
7176
Type parent = Type());
7277

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===--- UnresolvedMemberLookupTests.cpp --------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "SemaFixture.h"
14+
#include "swift/Sema/ConstraintSystem.h"
15+
16+
using namespace swift;
17+
using namespace swift::unittest;
18+
using namespace swift::constraints;
19+
20+
TEST_F(SemaTest, TestLookupAlwaysLooksThroughOptionalBase) {
21+
auto *intTypeDecl = getStdlibNominalTypeDecl("Int");
22+
auto *optTypeDecl = getStdlibNominalTypeDecl("Optional");
23+
auto intType = intTypeDecl->getDeclaredType();
24+
auto intOptType = OptionalType::get(intType);
25+
auto stringType = getStdlibType("String");
26+
27+
auto *intMember = addExtensionVarMember(intTypeDecl, "test", intOptType);
28+
addExtensionVarMember(optTypeDecl, "test", stringType);
29+
30+
auto *UME = new (Context)
31+
UnresolvedMemberExpr(SourceLoc(), DeclNameLoc(),
32+
DeclNameRef(Context.getIdentifier("test")), true);
33+
auto *UMCRE = new (Context) UnresolvedMemberChainResultExpr(UME, UME);
34+
35+
ConstraintSystem cs(DC, ConstraintSystemOptions());
36+
cs.generateConstraints(UMCRE, DC);
37+
cs.addConstraint(
38+
ConstraintKind::Conversion, cs.getType(UMCRE), intOptType,
39+
cs.getConstraintLocator(UMCRE, ConstraintLocator::ContextualType));
40+
SmallVector<Solution, 2> solutions;
41+
cs.solve(solutions);
42+
43+
// We should have a solution.
44+
ASSERT_EQ(solutions.size(), 1);
45+
46+
auto &solution = solutions[0];
47+
auto *locator = cs.getConstraintLocator(UME,
48+
ConstraintLocator::UnresolvedMember);
49+
auto choice = solution.getOverloadChoice(locator).choice;
50+
51+
// The `test` member on `Int` should be selected.
52+
ASSERT_EQ(choice.getDecl(), intMember);
53+
}
54+
55+
TEST_F(SemaTest, TestLookupPrefersResultsOnOptionalRatherThanBase) {
56+
auto *intTypeDecl = getStdlibNominalTypeDecl("Int");
57+
auto *optTypeDecl = getStdlibNominalTypeDecl("Optional");
58+
auto intType = intTypeDecl->getDeclaredType();
59+
auto intOptType = OptionalType::get(intType);
60+
61+
addExtensionVarMember(intTypeDecl, "test", intOptType);
62+
auto *optMember = addExtensionVarMember(optTypeDecl, "test", intType);
63+
64+
auto *UME = new (Context)
65+
UnresolvedMemberExpr(SourceLoc(), DeclNameLoc(),
66+
DeclNameRef(Context.getIdentifier("test")), true);
67+
auto *UMCRE = new (Context) UnresolvedMemberChainResultExpr(UME, UME);
68+
69+
ConstraintSystem cs(DC, ConstraintSystemOptions());
70+
cs.generateConstraints(UMCRE, DC);
71+
cs.addConstraint(
72+
ConstraintKind::Conversion, cs.getType(UMCRE), intOptType,
73+
cs.getConstraintLocator(UMCRE, ConstraintLocator::ContextualType));
74+
SmallVector<Solution, 2> solutions;
75+
cs.solve(solutions);
76+
77+
// We should have a solution.
78+
ASSERT_EQ(solutions.size(), 1);
79+
80+
auto &solution = solutions[0];
81+
auto *locator = cs.getConstraintLocator(UME,
82+
ConstraintLocator::UnresolvedMember);
83+
auto choice = solution.getOverloadChoice(locator).choice;
84+
auto score = solution.getFixedScore();
85+
86+
// The `test` member on `Optional` should be chosen over the member on `Int`,
87+
// even though the score is otherwise worse.
88+
ASSERT_EQ(score.Data[SK_ValueToOptional], 1);
89+
ASSERT_EQ(choice.getDecl(), optMember);
90+
}

0 commit comments

Comments
 (0)