Skip to content

Commit 6b0f5da

Browse files
committed
[Unit tests] Add unit tests for optional lookup behavior
1 parent a53b1e4 commit 6b0f5da

File tree

4 files changed

+130
-1
lines changed

4 files changed

+130
-1
lines changed

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)