mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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.
This commit is contained in:
@@ -195,16 +195,28 @@ static bool sameDecl(Decl *decl1, Decl *decl2) {
|
||||
if (decl1 == decl2)
|
||||
return true;
|
||||
|
||||
// All types considered identical.
|
||||
// FIXME: This is a hack. What we really want is to have substituted the
|
||||
// base type into the declaration reference, so that we can compare the
|
||||
// actual types to which two type declarations resolve. If those types are
|
||||
// equivalent, then it doesn't matter which declaration is chosen.
|
||||
if (isa<TypeDecl>(decl1) && isa<TypeDecl>(decl2))
|
||||
return true;
|
||||
|
||||
if (decl1->getKind() != decl2->getKind())
|
||||
return false;
|
||||
// Special hack to allow type aliases with same underlying type.
|
||||
//
|
||||
// FIXME: Check substituted types instead.
|
||||
//
|
||||
// FIXME: Perhaps this should be handled earlier in name lookup.
|
||||
auto *typeDecl1 = dyn_cast<TypeDecl>(decl1);
|
||||
auto *typeDecl2 = dyn_cast<TypeDecl>(decl2);
|
||||
if (typeDecl1 && typeDecl2) {
|
||||
auto type1 = typeDecl1->getDeclaredInterfaceType();
|
||||
auto type2 = typeDecl2->getDeclaredInterfaceType();
|
||||
|
||||
// Handle unbound generic type aliases, eg
|
||||
//
|
||||
// struct Array<Element> {}
|
||||
// typealias MyArray = Array
|
||||
if (type1->is<UnboundGenericType>())
|
||||
type1 = type1->getAnyNominal()->getDeclaredInterfaceType();
|
||||
if (type2->is<UnboundGenericType>())
|
||||
type2 = type2->getAnyNominal()->getDeclaredInterfaceType();
|
||||
|
||||
return type1->isEqual(type2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ struct S4: (P & Q<String>) & R {
|
||||
}
|
||||
|
||||
struct Bad: P<Int, Float> { // expected-error {{type 'Bad' does not conform to protocol 'P'}}
|
||||
typealias A = String // expected-note {{possibly intended match}}
|
||||
typealias A = String
|
||||
// expected-note@-1 {{possibly intended match}}
|
||||
// expected-note@-2 {{found this candidate}}
|
||||
}
|
||||
|
||||
let x = Bad.A.self
|
||||
let x = Bad.A.self // expected-error {{ambiguous use of 'A'}}
|
||||
g(x)
|
||||
|
||||
struct Circle: Q<Circle.A> {}
|
||||
|
||||
@@ -38,10 +38,10 @@ import A
|
||||
import B
|
||||
|
||||
func test() {
|
||||
_ = S<Int>(t: 42) // expected-error {{ambiguous use of 'init(t:)'}}
|
||||
_ = S<Int>(t: 42) // expected-error {{ambiguous use of 'S'}}
|
||||
|
||||
S<Int>(t: 42).test() // expected-error {{ambiguous use of 'init(t:)'}}
|
||||
S<Int>(t: 42).test() // expected-error {{ambiguous use of 'S'}}
|
||||
|
||||
S<Int>.staticFn()
|
||||
// expected-error@-1 {{ambiguous use of 'staticFn()'}}
|
||||
// expected-error@-1 {{ambiguous use of 'S'}}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// RUN: %target-swift-frontend -module-name SomeModule -typecheck -verify -dump-ast -import-objc-header %S/Inputs/imported_type.h %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -module-name SomeModule -typecheck -verify -import-objc-header %S/Inputs/imported_type.h %s -verify-ignore-unrelated
|
||||
|
||||
// REQUIRES: objc_interop
|
||||
|
||||
import Foundation
|
||||
|
||||
// CHECK: declref_expr type="module<SomeModule>"
|
||||
// CHECK-NEXT: type_expr type="Data.Type"
|
||||
let type = SomeModule.Data.self
|
||||
let type = SomeModule.Data.self // expected-error {{ambiguous use of 'Data'}}
|
||||
|
||||
40
test/Constraints/rdar165863775.swift
Normal file
40
test/Constraints/rdar165863775.swift
Normal file
@@ -0,0 +1,40 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %empty-directory(%t/src)
|
||||
// RUN: split-file %s %t/src
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module %t/src/A.swift \
|
||||
// RUN: -module-name A \
|
||||
// RUN: -emit-module-path %t/A.swiftmodule
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module %t/src/B.swift \
|
||||
// RUN: -module-name B \
|
||||
// RUN: -emit-module-path %t/B.swiftmodule
|
||||
|
||||
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated -module-name main -I %t %t/src/main.swift
|
||||
|
||||
//--- A.swift
|
||||
public struct Bag {
|
||||
}
|
||||
|
||||
//--- B.swift
|
||||
public struct Bag {
|
||||
}
|
||||
|
||||
//--- main.swift
|
||||
import A
|
||||
import B
|
||||
|
||||
protocol P {
|
||||
}
|
||||
|
||||
struct Test {
|
||||
func inject<T>(_: T.Type = T.self) {}
|
||||
|
||||
func inject<T: P>(_: T) {}
|
||||
|
||||
func inject<T>(_: T) async -> T {}
|
||||
}
|
||||
|
||||
func test(t: Test) {
|
||||
t.inject(Bag.self) // expected-error {{ambiguous use of 'Bag'}}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ extension X {
|
||||
_ = (NestedInA, NestedInB, NestedInC).self // expected-member-visibility-error{{struct 'NestedInB' is not available due to missing import of defining module 'members_B'}}
|
||||
_ = GenericType<NestedInB>.self // expected-member-visibility-error{{struct 'NestedInB' is not available due to missing import of defining module 'members_B'}}
|
||||
_ = NestedInC.self
|
||||
_ = AmbiguousNestedType.self
|
||||
_ = AmbiguousNestedType.self // expected-ambiguity-error{{ambiguous use of 'AmbiguousNestedType'}}
|
||||
}
|
||||
|
||||
var hasNestedInAType: NestedInA { fatalError() }
|
||||
|
||||
@@ -20,6 +20,6 @@ struct TupleBuilder {
|
||||
|
||||
struct MyApp: Tupled {
|
||||
var tuple: some Any {
|
||||
MyView() // expected-error {{ambiguous use of 'init()'}}
|
||||
MyView() // expected-error {{ambiguous use of 'MyView'}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ struct S1 { // expected-note {{found this candidate}} \
|
||||
var c: () -> Void
|
||||
}
|
||||
|
||||
S1 {} // expected-error {{ambiguous use of 'init'}}
|
||||
S1 {} // expected-error {{ambiguous use of 'S1'}}
|
||||
|
||||
struct S1 { // expected-note {{found this candidate}} \
|
||||
// expected-error {{invalid redeclaration of 'S1'}}
|
||||
|
||||
Reference in New Issue
Block a user