Skip to content

Commit c78ab8f

Browse files
committed
Sema: Tighten ridiculous disambiguation hack for overloaded type declarations
We noticed that a project failed to build with prepared overloads enabled, but built successfully with prepared overloads disabled. It turns out that the code clearly should never have been accepted in the first place, because the type checker was making an arbitrary choice between two nominal type declarations with the same name. Further inspection revealed this was because of a FIXME added in 2013 which was far too broad. Tighten up the logic here to only disambiguate if at least one of the two declarations is a type alias, and it has the same underlying type as the other one. A choice between unrelated nominal type declarations should always be ambiguous. This still isn't perfect, because we might have two generic type aliases that are referenced in both solutions with different substitution maps, in which case we will still erroneously pick the first one. But this is better than the old logic, at least. Fixes rdar://165863775.
1 parent 3e2943f commit c78ab8f

File tree

8 files changed

+65
-20
lines changed

8 files changed

+65
-20
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,19 @@ static bool sameDecl(Decl *decl1, Decl *decl2) {
195195
if (decl1 == decl2)
196196
return true;
197197

198-
// All types considered identical.
199-
// FIXME: This is a hack. What we really want is to have substituted the
200-
// base type into the declaration reference, so that we can compare the
201-
// actual types to which two type declarations resolve. If those types are
202-
// equivalent, then it doesn't matter which declaration is chosen.
203-
if (isa<TypeDecl>(decl1) && isa<TypeDecl>(decl2))
204-
return true;
205-
198+
// Special hack to allow type aliases with same underlying type.
199+
//
200+
// FIXME: Check substituted types instead.
201+
//
202+
// FIXME: Perhaps this should be handled earlier in name lookup.
203+
auto *typeDecl1 = dyn_cast<TypeDecl>(decl1);
204+
auto *typeDecl2 = dyn_cast<TypeDecl>(decl2);
205+
if (typeDecl1 && typeDecl2) {
206+
auto type1 = typeDecl1->getDeclaredInterfaceType();
207+
auto type2 = typeDecl2->getDeclaredInterfaceType();
208+
return type1->isEqual(type2);
209+
}
210+
206211
if (decl1->getKind() != decl2->getKind())
207212
return false;
208213

test/AssociatedTypeInference/type_witness_from_parameterized_protocol.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ struct S4: (P & Q<String>) & R {
4343
}
4444

4545
struct Bad: P<Int, Float> { // expected-error {{type 'Bad' does not conform to protocol 'P'}}
46-
typealias A = String // expected-note {{possibly intended match}}
46+
typealias A = String
47+
// expected-note@-1 {{possibly intended match}}
48+
// expected-note@-2 {{found this candidate}}
4749
}
4850

49-
let x = Bad.A.self
51+
let x = Bad.A.self // expected-error {{ambiguous use of 'A'}}
5052
g(x)
5153

5254
struct Circle: Q<Circle.A> {}

test/Constraints/ambiguous_specialized_name_diagnostics.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ import A
3838
import B
3939

4040
func test() {
41-
_ = S<Int>(t: 42) // expected-error {{ambiguous use of 'init(t:)'}}
41+
_ = S<Int>(t: 42) // expected-error {{ambiguous use of 'S'}}
4242

43-
S<Int>(t: 42).test() // expected-error {{ambiguous use of 'init(t:)'}}
43+
S<Int>(t: 42).test() // expected-error {{ambiguous use of 'S'}}
4444

4545
S<Int>.staticFn()
46-
// expected-error@-1 {{ambiguous use of 'staticFn()'}}
46+
// expected-error@-1 {{ambiguous use of 'S'}}
4747
}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
// RUN: %target-swift-frontend -module-name SomeModule -typecheck -verify -dump-ast -import-objc-header %S/Inputs/imported_type.h %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -module-name SomeModule -typecheck -verify -import-objc-header %S/Inputs/imported_type.h %s -verify-ignore-unrelated
22

33
// REQUIRES: objc_interop
44

55
import Foundation
66

7-
// CHECK: declref_expr type="module<SomeModule>"
8-
// CHECK-NEXT: type_expr type="Data.Type"
9-
let type = SomeModule.Data.self
7+
let type = SomeModule.Data.self // expected-error {{ambiguous use of 'Data'}}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/src)
3+
// RUN: split-file %s %t/src
4+
5+
// RUN: %target-swift-frontend -emit-module %t/src/A.swift \
6+
// RUN: -module-name A \
7+
// RUN: -emit-module-path %t/A.swiftmodule
8+
9+
// RUN: %target-swift-frontend -emit-module %t/src/B.swift \
10+
// RUN: -module-name B \
11+
// RUN: -emit-module-path %t/B.swiftmodule
12+
13+
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated -module-name main -I %t %t/src/main.swift
14+
15+
//--- A.swift
16+
public struct Bag {
17+
}
18+
19+
//--- B.swift
20+
public struct Bag {
21+
}
22+
23+
//--- main.swift
24+
import A
25+
import B
26+
27+
protocol P {
28+
}
29+
30+
struct Test {
31+
func inject<T>(_: T.Type = T.self) {}
32+
33+
func inject<T: P>(_: T) {}
34+
35+
func inject<T>(_: T) async -> T {}
36+
}
37+
38+
func test(t: Test) {
39+
t.inject(Bag.self) // expected-error {{ambiguous use of 'Bag'}}
40+
}

test/NameLookup/member_import_visibility.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ extension X {
8585
_ = (NestedInA, NestedInB, NestedInC).self // expected-member-visibility-error{{struct 'NestedInB' is not available due to missing import of defining module 'members_B'}}
8686
_ = GenericType<NestedInB>.self // expected-member-visibility-error{{struct 'NestedInB' is not available due to missing import of defining module 'members_B'}}
8787
_ = NestedInC.self
88-
_ = AmbiguousNestedType.self
88+
_ = AmbiguousNestedType.self // expected-ambiguity-error{{ambiguous use of 'AmbiguousNestedType'}}
8989
}
9090

9191
var hasNestedInAType: NestedInA { fatalError() }

validation-test/Sema/rdar84879566.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ struct TupleBuilder {
2020

2121
struct MyApp: Tupled {
2222
var tuple: some Any {
23-
MyView() // expected-error {{ambiguous use of 'init()'}}
23+
MyView() // expected-error {{ambiguous use of 'MyView'}}
2424
}
2525
}

validation-test/compiler_crashers_fixed/issue-85364.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct S1 { // expected-note {{found this candidate}} \
99
var c: () -> Void
1010
}
1111

12-
S1 {} // expected-error {{ambiguous use of 'init'}}
12+
S1 {} // expected-error {{ambiguous use of 'S1'}}
1313

1414
struct S1 { // expected-note {{found this candidate}} \
1515
// expected-error {{invalid redeclaration of 'S1'}}

0 commit comments

Comments
 (0)