Skip to content

Commit c6f43f9

Browse files
author
Greg Titus
committed
Track mismatched opened generic params.
Improve diagnoses by forbidding @dynamicMemberLookup use for mismatched opened generic params.
1 parent 0350023 commit c6f43f9

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,10 @@ class ConstraintSystem {
23752375
llvm::SmallMapVector<ConstraintLocator *, ArrayRef<OpenedType>, 4>
23762376
OpenedTypes;
23772377

2378+
/// A set of type variables representing opened generic types that have been
2379+
/// involved in type mismatch fixes.
2380+
llvm::SmallVector<TypeVariableType *, 4> MismatchedOpenedTypes;
2381+
23782382
/// A dictionary of all conformances that have been looked up by the solver.
23792383
llvm::DenseMap<std::pair<TypeBase *, ProtocolDecl *>, ProtocolConformanceRef>
23802384
Conformances;
@@ -2891,6 +2895,9 @@ class ConstraintSystem {
28912895
/// The length of \c OpenedTypes.
28922896
unsigned numOpenedTypes;
28932897

2898+
/// The length of \c MismatchedOpenedTypes.
2899+
unsigned numMismatchedOpenedTypes;
2900+
28942901
/// The length of \c OpenedExistentialTypes.
28952902
unsigned numOpenedExistentialTypes;
28962903

@@ -4315,6 +4322,27 @@ class ConstraintSystem {
43154322
ConstraintLocatorBuilder locator);
43164323

43174324
private:
4325+
/// Record mismatch in opened generic type parameter.
4326+
void recordMismatchedOpenedType(TypeVariableType *type) {
4327+
MismatchedOpenedTypes.push_back(type);
4328+
}
4329+
4330+
/// Check whether type involves any mismatched opened generic type parameters..
4331+
bool typeHasMismatchedOpenedType(Type type) {
4332+
if (MismatchedOpenedTypes.empty())
4333+
return false;
4334+
SmallPtrSet<TypeVariableType *, 2> variables;
4335+
type->getTypeVariables(variables);
4336+
for (auto tv : variables) {
4337+
auto rep = tv->getImpl().getRepresentative(nullptr);
4338+
for (auto mismatch : MismatchedOpenedTypes) {
4339+
if (mismatch->getImpl().getRepresentative(nullptr) == rep)
4340+
return true;
4341+
}
4342+
}
4343+
return false;
4344+
}
4345+
43184346
/// "Open" an opaque archetype type, similar to \c openType.
43194347
Type openOpaqueType(OpaqueTypeArchetypeType *type,
43204348
ConstraintLocatorBuilder locator);

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10413,7 +10413,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1041310413
if ((candidates.empty() ||
1041410414
allFromConditionalConformances(*this, instanceTy, candidates)) &&
1041510415
!isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy,
10416-
memberLocator)) {
10416+
memberLocator) &&
10417+
!typeHasMismatchedOpenedType(instanceTy)) {
1041710418
auto &ctx = getASTContext();
1041810419

1041910420
// Recursively look up `subscript(dynamicMember:)` methods in this type.
@@ -15293,6 +15294,14 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1529315294
impact += 1;
1529415295
}
1529515296

15297+
// Record any opened generic types mismatched
15298+
SmallPtrSet<TypeVariableType *, 2> variables;
15299+
type2->getTypeVariables(variables);
15300+
for (auto tv : variables) {
15301+
if (tv->getImpl().getGenericParameter())
15302+
recordMismatchedOpenedType(tv);
15303+
}
15304+
1529615305
return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
1529715306
}
1529815307

lib/Sema/CSSolver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
669669
numAppliedDisjunctions = cs.AppliedDisjunctions.size();
670670
numArgumentMatchingChoices = cs.argumentMatchingChoices.size();
671671
numOpenedTypes = cs.OpenedTypes.size();
672+
numMismatchedOpenedTypes = cs.MismatchedOpenedTypes.size();
672673
numOpenedExistentialTypes = cs.OpenedExistentialTypes.size();
673674
numOpenedPackExpansionTypes = cs.OpenedPackExpansionTypes.size();
674675
numPackExpansionEnvironments = cs.PackExpansionEnvironments.size();
@@ -749,6 +750,9 @@ ConstraintSystem::SolverScope::~SolverScope() {
749750
// Remove any opened types.
750751
truncate(cs.OpenedTypes, numOpenedTypes);
751752

753+
// Remove any mismatched opened types.
754+
truncate(cs.MismatchedOpenedTypes, numMismatchedOpenedTypes);
755+
752756
// Remove any conformances solver had to fix along
753757
// the current path.
754758
truncate(cs.FixedRequirements, numFixedRequirements);

test/Constraints/issue-74700.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol Idable<ID> {
4+
associatedtype ID
5+
var id: ID { get }
6+
}
7+
8+
@dynamicMemberLookup struct Binding<C> {
9+
subscript(dynamicMember member: String) -> String { "" }
10+
}
11+
12+
struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID: Hashable {
13+
init(_ data: Data, content: (Data.Element) -> Content) where ID == Data.Element.ID, Data.Element : Idable {}
14+
// expected-note@-1 {{where 'Data.Element' = 'User'}}
15+
init<C>(_ data: Binding<C>, content: (Binding<C.Element>) -> Content) where Data == Array<C>, ID == C.Element.ID, C : MutableCollection, C : RandomAccessCollection, C.Element : Idable, C.Index : Hashable {}
16+
}
17+
18+
struct User {
19+
let name: String // expected-note 3 {{'name' declared here}}
20+
func member() {}
21+
}
22+
23+
struct ContentView {
24+
let users: [User] = []
25+
func body() -> Void {
26+
ForEach(users) { user in // expected-error {{initializer 'init(_:content:)' requires that 'User' conform to 'Idable'}}
27+
return user.nam // expected-error {{value of type 'User' has no member 'nam'; did you mean 'name'?}}
28+
}
29+
}
30+
}
31+
32+
33+
@dynamicMemberLookup struct BindingB {
34+
subscript(dynamicMember member: String) -> String { "" }
35+
}
36+
func test(_: String) {} // expected-note {{candidate expects value of type 'String' for parameter #1 (got 'Int')}}
37+
func test(_: BindingB) {} // expected-note {{candidate expects value of type 'BindingB' for parameter #1 (got 'Int')}}
38+
test(42) // expected-error {{no exact matches in call to global function 'test'}}
39+
40+
41+
func passThrough<T>(_ :T, _: (T) -> Void) {}
42+
func passThrough<T>(_ :Binding<T>, _: (Binding<T>) -> Void) {}
43+
func passThrough2<T>(_ : T, _: (T) -> Void) {}
44+
func takeString(_: String) {}
45+
func two<T>(_: T, _: T, _: (T) -> Void) {}
46+
func two<T>(_: Binding<T>, _: Binding<T>, _: (Binding<T>) -> Void) {}
47+
48+
func f(s: String, i: Int, u: User, b: Binding<User>) {
49+
passThrough(u) {
50+
takeString($0.nam) // expected-error {{value of type 'User' has no member 'nam'}}
51+
passThrough(b) { takeString($0.a) }
52+
}
53+
passThrough(u) {
54+
passThrough2($0) { takeString($0.nam) } // expected-error {{value of type 'User' has no member 'nam'}}
55+
}
56+
passThrough(b) {
57+
takeString($0.name)
58+
passThrough(s) { takeString($0) }
59+
passThrough(i) { takeString($0) } // expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}}
60+
}
61+
62+
two(u, b) { takeString($0.b) } // expected-error {{cannot convert value of type 'User' to expected argument type 'Binding<User>'}}
63+
two(u, b) { $0.member() } // expected-error {{cannot convert value of type 'Binding<User>' to expected argument type 'User'}}
64+
}

0 commit comments

Comments
 (0)