From 53b4ce0034301db5064d3ecad95f5c9a2aac9199 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Wed, 24 Jun 2020 15:16:22 +0100 Subject: [PATCH 01/38] fix confusing protocol diagnostic - update the diagnostic error message - add educational notes --- include/swift/AST/DiagnosticsSema.def | 4 +- include/swift/AST/EducationalNotes.def | 1 + .../protocol-type-non-conformance.md | 41 +++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 userdocs/diagnostics/protocol-type-non-conformance.md diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index bbc6763b8db62..b64342e6508cf 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1803,8 +1803,8 @@ ERROR(use_of_equal_instead_of_equality,none, "use of '=' in a boolean context, did you mean '=='?", ()) ERROR(type_cannot_conform, none, - "%select{|value of protocol }0type %1 cannot conform to %2; " - "only struct/enum/class types can conform to protocols", + "%select{|A value of protocol }0type %1 does not conform to the protocol itself (exception: 'Error'); " + "only concrete types such as structs, enums and classes can conform to protocols", (bool, Type, Type)) NOTE(required_by_opaque_return,none, "required by opaque return type of %0 %1", (DescriptiveDeclKind, DeclName)) diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index ca30032f17a5f..ff587eb261343 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -65,5 +65,6 @@ EDUCATIONAL_NOTES(append_interpolation_static, "string-interpolation-conformance.md") EDUCATIONAL_NOTES(append_interpolation_void_or_discardable, "string-interpolation-conformance.md") +EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md") #undef EDUCATIONAL_NOTES diff --git a/userdocs/diagnostics/protocol-type-non-conformance.md b/userdocs/diagnostics/protocol-type-non-conformance.md new file mode 100644 index 0000000000000..b0e42f582c64f --- /dev/null +++ b/userdocs/diagnostics/protocol-type-non-conformance.md @@ -0,0 +1,41 @@ +# Protocol type not conforming to itself +Swift disallows us from using a protocol as a type that conforms to itself as illustrated in the examples below + +```swift +protocol SomeProtocol { + init() +} + +struct SomeStruct: SomeProtocol {} +struct AnotherStruct: SomeProtocol {} + +var arr: [SomeProtocol] = [SomeStruct(), AnotherStruct()] +arr.appendNewItem() + +extension Array where Element: SomeProtocol { + mutating func appendNewItem() { + append(Element()) + } +} +``` + +The code snippet above would not compile because we are using `SomeProtocol` as a type that conforms to itself. There is no concrete implementation for the protocol. + +Consider also the case of using protocol as a type in a generic type - + +```swift +protocol AnotherProtocol { + static func foo() +} + +struct GenericStruct { + func faz() { + T.foo() + } +} + +GenericStruct().faz() +``` +Constructing the instance of the struct `GenericStruct` with type `AnotherProtocol` will not compile because there is no concrete implementation for the static requirement of the protocol. +There is no implementation for for() used above. + From cbdb27464e931056b2021c979b2b86d05b206057 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Fri, 26 Jun 2020 18:06:21 +0100 Subject: [PATCH 02/38] add protocol self conformance exception note --- .../protocol-type-non-conformance.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/userdocs/diagnostics/protocol-type-non-conformance.md b/userdocs/diagnostics/protocol-type-non-conformance.md index b0e42f582c64f..66e7f6d381e31 100644 --- a/userdocs/diagnostics/protocol-type-non-conformance.md +++ b/userdocs/diagnostics/protocol-type-non-conformance.md @@ -39,3 +39,28 @@ GenericStruct().faz() Constructing the instance of the struct `GenericStruct` with type `AnotherProtocol` will not compile because there is no concrete implementation for the static requirement of the protocol. There is no implementation for for() used above. +We, however have an exception for `@objc` protocols that conforms to itself as shown below + +```swift +import Foundation + +@objc protocol SomeProtocol { + func foo() +} + +class SomeClass : SomeProtocol { + func foo() { + print("foo called") + } +} + +func faz(_ t: T) { + t.foo() +} + +let c: SomeProtocol = SomeClass() +faz(c) +``` + +The function `faz` requires that `T` conforms to `SomeProtocol` and we can easily substitute in `SomeProtocol` for `T` because it has no static requirements. + From c642a7f271a13e714e309f3b34f32b650b082625 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Fri, 26 Jun 2020 18:07:57 +0100 Subject: [PATCH 03/38] update diagnostics --- include/swift/AST/DiagnosticsSema.def | 6 +++--- lib/Sema/CSDiagnostics.cpp | 4 ++-- lib/Sema/TypeCheckProtocol.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index b64342e6508cf..d3c79209b5c21 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1801,11 +1801,11 @@ ERROR(type_cannot_conform_to_nsobject,none, ERROR(use_of_equal_instead_of_equality,none, "use of '=' in a boolean context, did you mean '=='?", ()) - ERROR(type_cannot_conform, none, - "%select{|A value of protocol }0type %1 does not conform to the protocol itself (exception: 'Error'); " + "%select{type %1|protocol %1 as a type}0 cannot conform to " + "%select{%3|the protocol itself}2; " "only concrete types such as structs, enums and classes can conform to protocols", - (bool, Type, Type)) + (bool, Type, bool, Type)) NOTE(required_by_opaque_return,none, "required by opaque return type of %0 %1", (DescriptiveDeclKind, DeclName)) NOTE(required_by_decl,none, diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index fe56b2098c15b..4e86faf6d8703 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -474,7 +474,7 @@ bool MissingConformanceFailure::diagnoseTypeCannotConform( } emitDiagnostic(diag::type_cannot_conform, - nonConformingType->isExistentialType(), nonConformingType, + nonConformingType->isExistentialType(), nonConformingType, nonConformingType->isEqual(protocolType), protocolType); if (auto *OTD = dyn_cast(AffectedDecl)) { @@ -2061,7 +2061,7 @@ bool ContextualFailure::diagnoseAsError() { if (CTP == CTP_ForEachStmt) { if (fromType->isAnyExistentialType()) { emitDiagnostic(diag::type_cannot_conform, - /*isExistentialType=*/true, fromType, toType); + /*isExistentialType=*/true, fromType, fromType->isEqual(toType), toType); return true; } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index c7f2082bfd3a6..cd02550166bda 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4256,7 +4256,7 @@ void swift::diagnoseConformanceFailure(Type T, if (!T->isObjCExistentialType()) { diags.diagnose(ComplainLoc, diag::type_cannot_conform, true, - T, Proto->getDeclaredType()); + T, T->isEqual(Proto->getDeclaredType()), Proto->getDeclaredType()); return; } From 434607d0043465cf962818dfe4100bf6bb9b8607 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Fri, 26 Jun 2020 18:08:35 +0100 Subject: [PATCH 04/38] fix broken tests --- test/Constraints/diagnostics.swift | 8 ++++---- test/Constraints/function_builder_diags.swift | 2 +- test/Constraints/generics.swift | 2 +- .../Generics/conditional_conformances_literals.swift | 4 ++-- test/Generics/existential_restrictions.swift | 12 ++++++------ test/Misc/misc_diagnostics.swift | 2 +- test/Parse/confusables.swift | 2 +- .../protocol/conforms/error_self_conformance.swift | 6 +++--- test/stmt/foreach.swift | 2 +- test/type/opaque.swift | 2 +- test/type/subclass_composition.swift | 2 +- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 357f8b65e322a..e85f64b5de0fe 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -51,9 +51,9 @@ f0(i, i, // expected-error@:7 {{cannot convert value of type 'Int' to expected a // Cannot conform to protocols. -f5(f4) // expected-error {{type '(Int) -> Int' cannot conform to 'P2'; only struct/enum/class types can conform to protocols}} -f5((1, "hello")) // expected-error {{type '(Int, String)' cannot conform to 'P2'; only struct/enum/class types can conform to protocols}} -f5(Int.self) // expected-error {{type 'Int.Type' cannot conform to 'P2'; only struct/enum/class types can conform to protocols}} +f5(f4) // expected-error {{type '(Int) -> Int' cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} +f5((1, "hello")) // expected-error {{type '(Int, String)' cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} +f5(Int.self) // expected-error {{type 'Int.Type' cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} // Tuple element not convertible. f0(i, @@ -104,7 +104,7 @@ func f8(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' f8(3, f4) // expected-error {{global function 'f8' requires that 'Int' conform to 'P2'}} typealias Tup = (Int, Double) func f9(_ x: Tup) -> Tup { return x } -f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conform to 'P2'; only struct/enum/class types can conform to protocols}} +f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conform to 'P2'; only concrete types such as structs, enums and classes can conform to protocols}} // QoI: Incorrect diagnostic for calling nonexistent members on literals 1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}} diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index 1a04e05892926..b887c113ab638 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -179,7 +179,7 @@ struct Label : P where L : P { // expected-note 2 {{'L' declared as parameter } func test_51167632() -> some P { - AnyP(G { // expected-error {{type 'Label<_>.Type' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} + AnyP(G { // expected-error {{type 'Label<_>.Type' cannot conform to 'P'; only concrete types such as structs, enums and classes can conform to protocols}} Text("hello") Label // expected-error {{generic parameter 'L' could not be inferred}} // expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} {{10-10=<<#L: P#>>}} diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index 450af7f1212bd..bb4ed1d14f0f1 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -188,7 +188,7 @@ func r22459135() { // QoI: Friendlier error message for "[] as Set" // QoI: "argument for generic parameter 'Element' could not be inferred" lacks context -_ = [] as Set // expected-error {{value of protocol type 'Any' cannot conform to 'Hashable'; only struct/enum/class types can conform to protocols}} +_ = [] as Set // expected-error {{protocol 'Any' as a type cannot conform to 'Hashable'; only concrete types such as structs, enums and classes can conform to protocols}} // expected-note@-1 {{required by generic struct 'Set' where 'Element' = 'Any'}} diff --git a/test/Generics/conditional_conformances_literals.swift b/test/Generics/conditional_conformances_literals.swift index 932a51aa76f76..37f4729f14d17 100644 --- a/test/Generics/conditional_conformances_literals.swift +++ b/test/Generics/conditional_conformances_literals.swift @@ -128,9 +128,9 @@ func combined() { // Needs self conforming protocols: let _: Conforms = [[0: [1 : [works]] as Conforms]] - // expected-error@-1 {{value of protocol type 'Conforms' cannot conform to 'Conforms'; only struct/enum/class types can conform to protocols}} + // expected-error@-1 {{protocol 'Conforms' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} let _: Conforms = [[0: [1 : [fails]] as Conforms]] // expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}} - // expected-error@-2 {{value of protocol type 'Conforms' cannot conform to 'Conforms'; only struct/enum/class types can conform to protocols}} + // expected-error@-2 {{protocol 'Conforms' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} } diff --git a/test/Generics/existential_restrictions.swift b/test/Generics/existential_restrictions.swift index fbf2f29375fc5..425308b798901 100644 --- a/test/Generics/existential_restrictions.swift +++ b/test/Generics/existential_restrictions.swift @@ -23,7 +23,7 @@ func fAOE(_ t: AnyObject) { } func fT(_ t: T) { } func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, ao: AnyObject) { - fP(p) // expected-error{{value of protocol type 'P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} + fP(p) // expected-error{{protocol 'P' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} fAO(p) // expected-error{{global function 'fAO' requires that 'P' be a class type}} fAOE(p) // expected-error{{argument type 'P' expected to be an instance of a class or class-constrained type}} fT(p) @@ -37,8 +37,8 @@ func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, fAOE(cp) fT(cp) - fP(opp) // expected-error{{value of protocol type 'OP & P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} - fOP(opp) // expected-error{{value of protocol type 'OP & P' cannot conform to 'OP'; only struct/enum/class types can conform to protocols}} + fP(opp) // expected-error{{protocol 'OP & P' as a type cannot conform to 'P'; only concrete types such as structs, enums and classes can conform to protocols}} + fOP(opp) // expected-error{{protocol 'OP & P' as a type cannot conform to 'OP'; only concrete types such as structs, enums and classes can conform to protocols}} fAO(opp) // expected-error{{global function 'fAO' requires that 'OP & P' be a class type}} fAOE(opp) fT(opp) @@ -64,9 +64,9 @@ class GAO {} // expected-note 2{{requirement specified as 'T' : ' func blackHole(_ t: Any) {} func testBindExistential() { - blackHole(GP

()) // expected-error{{value of protocol type 'P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} + blackHole(GP

()) // expected-error{{protocol 'P' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} blackHole(GOP()) - blackHole(GCP()) // expected-error{{value of protocol type 'CP' cannot conform to 'CP'; only struct/enum/class types can conform to protocols}} + blackHole(GCP()) // expected-error{{protocol 'CP' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} blackHole(GAO

()) // expected-error{{'GAO' requires that 'P' be a class type}} blackHole(GAO()) blackHole(GAO()) // expected-error{{'GAO' requires that 'CP' be a class type}} @@ -92,5 +92,5 @@ func foo() { // generic no overloads error path. The error should actually talk // about the return type, and this can happen in other contexts as well; // tracks improving QoI here. - allMine.takeAll() // expected-error{{value of protocol type 'Mine' cannot conform to 'Mine'; only struct/enum/class types can conform to protocols}} + allMine.takeAll() // expected-error{{protocol 'Mine' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} } diff --git a/test/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index 557b1beec9021..6bf146ba0ff36 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -143,7 +143,7 @@ func test17875634() { func test20770032() { if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} // expected-error@-1 {{expression pattern of type 'ClosedRange' cannot match values of type '(Int, Int)'}} - // expected-error@-2 {{'(Int, Int)' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}} + // expected-error@-2 {{type '(Int, Int)' cannot conform to 'Equatable'; only concrete types such as structs, enums and classes can conform to protocols}} // expected-note@-3 {{required by operator function '~=' where 'T' = '(Int, Int)'}} } } diff --git a/test/Parse/confusables.swift b/test/Parse/confusables.swift index e32d07c56517e..57afcde30dd29 100644 --- a/test/Parse/confusables.swift +++ b/test/Parse/confusables.swift @@ -17,7 +17,7 @@ if (true ꝸꝸꝸ false) {} // expected-note {{identifier 'ꝸꝸꝸ' contains // expected-error @+3 {{invalid character in source file}} // expected-error @+2 {{expected ',' separator}} -// expected-error @+1 {{type '(Int, Int)' cannot conform to 'BinaryInteger'; only struct/enum/class types can conform to protocols}} +// expected-error @+1 {{type '(Int, Int)' cannot conform to 'BinaryInteger'; only concrete types such as structs, enums and classes can conform to protocols}} if (5 ‒ 5) == 0 {} // expected-note {{unicode character '‒' looks similar to '-'; did you mean to use '-'?}} {{7-10=-}} // expected-note @-1 {{operator function '=='}} diff --git a/test/decl/protocol/conforms/error_self_conformance.swift b/test/decl/protocol/conforms/error_self_conformance.swift index a5ceb7947a8c3..b451839b0e5ec 100644 --- a/test/decl/protocol/conforms/error_self_conformance.swift +++ b/test/decl/protocol/conforms/error_self_conformance.swift @@ -11,15 +11,15 @@ func testSimple(error: Error) { protocol ErrorRefinement : Error {} func testErrorRefinment(error: ErrorRefinement) { - wantsError(error) // expected-error {{value of protocol type 'ErrorRefinement' cannot conform to 'Error'; only struct/enum/class types can conform to protocols}} + wantsError(error) // expected-error {{protocol 'ErrorRefinement' as a type cannot conform to 'Error'; only concrete types such as structs, enums and classes can conform to protocols}} } protocol OtherProtocol {} func testErrorComposition(error: Error & OtherProtocol) { - wantsError(error) // expected-error {{value of protocol type 'Error & OtherProtocol' cannot conform to 'Error'; only struct/enum/class types can conform to protocols}} + wantsError(error) // expected-error {{protocol 'Error & OtherProtocol' as a type cannot conform to 'Error'; only concrete types such as structs, enums and classes can conform to protocols}} } class C {} func testErrorCompositionWithClass(error: Error & C) { - wantsError(error) // expected-error {{value of protocol type 'C & Error' cannot conform to 'Error'; only struct/enum/class types can conform to protocols}} + wantsError(error) // expected-error {{protocol 'C & Error' as a type cannot conform to 'Error'; only concrete types such as structs, enums and classes can conform to protocols}} } diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 07441ed39f079..0b4ca6de47112 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -177,7 +177,7 @@ func testOptionalSequence() { // Crash with (invalid) for each over an existential func testExistentialSequence(s: Sequence) { // expected-error {{protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements}} - for x in s { // expected-error {{value of protocol type 'Sequence' cannot conform to 'Sequence'; only struct/enum/class types can conform to protocols}} + for x in s { // expected-error {{protocol 'Sequence' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} _ = x } } diff --git a/test/type/opaque.swift b/test/type/opaque.swift index 276933e611580..12e6b3921ff1f 100644 --- a/test/type/opaque.swift +++ b/test/type/opaque.swift @@ -383,7 +383,7 @@ protocol P_51641323 { func rdar_51641323() { struct Foo: P_51641323 { var foo: some P_51641323 { // expected-note {{required by opaque return type of property 'foo'}} - {} // expected-error {{type '() -> ()' cannot conform to 'P_51641323'; only struct/enum/class types can conform to protocols}} + {} // expected-error {{type '() -> ()' cannot conform to 'P_51641323'; only concrete types such as structs, enums and classes can conform to protocols}} } } } diff --git a/test/type/subclass_composition.swift b/test/type/subclass_composition.swift index 1139c4bf3d34f..a7c52f539bf58 100644 --- a/test/type/subclass_composition.swift +++ b/test/type/subclass_composition.swift @@ -413,7 +413,7 @@ func conformsTo & P2>( // expected-error@-1 {{global function 'conformsToAnyObject' requires that 'P1' be a class type}} conformsToP1(p1) - // expected-error@-1 {{value of protocol type 'P1' cannot conform to 'P1'; only struct/enum/class types can conform to protocols}} + // expected-error@-1 {{protocol 'P1' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} // FIXME: Following diagnostics are not great because when // `conformsTo*` methods are re-typechecked, they loose information From 42b9b48a0630371dbfd65641f30d7600bf3a6a82 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Tue, 30 Jun 2020 22:59:16 +0100 Subject: [PATCH 05/38] update educational notes --- .../protocol-type-non-conformance.md | 71 +++++++------------ 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/userdocs/diagnostics/protocol-type-non-conformance.md b/userdocs/diagnostics/protocol-type-non-conformance.md index 66e7f6d381e31..a852f4b6ddfb8 100644 --- a/userdocs/diagnostics/protocol-type-non-conformance.md +++ b/userdocs/diagnostics/protocol-type-non-conformance.md @@ -1,66 +1,45 @@ # Protocol type not conforming to itself -Swift disallows us from using a protocol as a type that conforms to itself as illustrated in the examples below +Protocols in Swift may be used as types. Protocols as types are sometimes called existential types. -```swift -protocol SomeProtocol { - init() -} -struct SomeStruct: SomeProtocol {} -struct AnotherStruct: SomeProtocol {} +```swift +protocol P {} -var arr: [SomeProtocol] = [SomeStruct(), AnotherStruct()] -arr.appendNewItem() +struct S: P {} -extension Array where Element: SomeProtocol { - mutating func appendNewItem() { - append(Element()) - } -} +var s: P = S() // This creates existential type because the protocol P is used as a type ``` -The code snippet above would not compile because we are using `SomeProtocol` as a type that conforms to itself. There is no concrete implementation for the protocol. - -Consider also the case of using protocol as a type in a generic type - +However, a protocol type does not conform to protocols - not even the protocol itself. +Allowing existential types to conform to protocols is unsound because some protocol with static methods, initializers, or associated types requirements cannot be accessed from the protocol type itself - these kinds of requirements require a concrete type. +Let's walk through the example below: ```swift -protocol AnotherProtocol { - static func foo() +protocol Word: Hashable { + var word: String { get } } -struct GenericStruct { - func faz() { - T.foo() - } +struct Singular: Word { + var word: String } -GenericStruct().faz() -``` -Constructing the instance of the struct `GenericStruct` with type `AnotherProtocol` will not compile because there is no concrete implementation for the static requirement of the protocol. -There is no implementation for for() used above. - -We, however have an exception for `@objc` protocols that conforms to itself as shown below - -```swift -import Foundation - -@objc protocol SomeProtocol { - func foo() +struct Plural: Word { + var word: String } -class SomeClass : SomeProtocol { - func foo() { - print("foo called") - } -} +let singularWord = Singular(word: "mango") +let pluralWord = Plural(word: "mangoes") -func faz(_ t: T) { - t.foo() -} +let wordPairDict: [Word: Word] = [singularWord: pluralWord] // Error +``` + +One workaround to fix this problem is to write a type erasure for the protocol `Word`. Think of type erasure as a way to hide an object's type. Since `Word` is of type `Hashable`, we already have `AnyHashable` type erasure available in the standard library which we can easily use here. -let c: SomeProtocol = SomeClass() -faz(c) +```swift +// The fix +let wordPairDict: [AnyHashable: AnyHashable] = [singularWord: pluralWord] ``` -The function `faz` requires that `T` conforms to `SomeProtocol` and we can easily substitute in `SomeProtocol` for `T` because it has no static requirements. +# Exceptions +`@objc` protocol type with no static requirements however do conform to its own protocol. One example is the `Error` protocol. From 38e2998e50aa3ded09a0179b9e54085133a880e8 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Wed, 1 Jul 2020 04:04:03 +0100 Subject: [PATCH 06/38] implement feedback changes --- lib/Sema/CSDiagnostics.cpp | 7 +++++-- lib/Sema/TypeCheckProtocol.cpp | 3 ++- userdocs/diagnostics/protocol-type-non-conformance.md | 7 ++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 4e86faf6d8703..49af3ba59f8ea 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -474,7 +474,9 @@ bool MissingConformanceFailure::diagnoseTypeCannotConform( } emitDiagnostic(diag::type_cannot_conform, - nonConformingType->isExistentialType(), nonConformingType, nonConformingType->isEqual(protocolType), + nonConformingType->isExistentialType(), + nonConformingType, + nonConformingType->isEqual(protocolType), protocolType); if (auto *OTD = dyn_cast(AffectedDecl)) { @@ -2061,7 +2063,8 @@ bool ContextualFailure::diagnoseAsError() { if (CTP == CTP_ForEachStmt) { if (fromType->isAnyExistentialType()) { emitDiagnostic(diag::type_cannot_conform, - /*isExistentialType=*/true, fromType, fromType->isEqual(toType), toType); + /*isExistentialType=*/true, fromType, + fromType->isEqual(toType), toType); return true; } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index cd02550166bda..fc152163c7840 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4256,7 +4256,8 @@ void swift::diagnoseConformanceFailure(Type T, if (!T->isObjCExistentialType()) { diags.diagnose(ComplainLoc, diag::type_cannot_conform, true, - T, T->isEqual(Proto->getDeclaredType()), Proto->getDeclaredType()); + T, T->isEqual(Proto->getDeclaredType()), + Proto->getDeclaredType()); return; } diff --git a/userdocs/diagnostics/protocol-type-non-conformance.md b/userdocs/diagnostics/protocol-type-non-conformance.md index a852f4b6ddfb8..47cc95193a130 100644 --- a/userdocs/diagnostics/protocol-type-non-conformance.md +++ b/userdocs/diagnostics/protocol-type-non-conformance.md @@ -11,7 +11,8 @@ var s: P = S() // This creates existential type because the protocol P is used a ``` However, a protocol type does not conform to protocols - not even the protocol itself. -Allowing existential types to conform to protocols is unsound because some protocol with static methods, initializers, or associated types requirements cannot be accessed from the protocol type itself - these kinds of requirements require a concrete type. +Allowing existential types to conform to protocols is unsound. For protocols with static method, initializer, or associated type requirements, the implementation of these requirements cannot be accessed from the protocol type - accessing these kinds of requirements must be done using a concrete type. + Let's walk through the example below: ```swift @@ -33,7 +34,7 @@ let pluralWord = Plural(word: "mangoes") let wordPairDict: [Word: Word] = [singularWord: pluralWord] // Error ``` -One workaround to fix this problem is to write a type erasure for the protocol `Word`. Think of type erasure as a way to hide an object's type. Since `Word` is of type `Hashable`, we already have `AnyHashable` type erasure available in the standard library which we can easily use here. +One workaround to fix this problem is to use type erasure for the protocol `Word`. Think of type erasure as a way to hide an object's type. Since `Word` is of type `Hashable`, we already have `AnyHashable` type erasure available in the standard library which we can easily use here. ```swift // The fix @@ -41,5 +42,5 @@ let wordPairDict: [AnyHashable: AnyHashable] = [singularWord: pluralWord] ``` # Exceptions -`@objc` protocol type with no static requirements however do conform to its own protocol. One example is the `Error` protocol. +`@objc` protocol type with no static requirements however do conform to its own protocol. Another exception is the `Error` Swift protocol. From b9e600b0018edb32f7cde739eab2c1171d7185a7 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 24 Jun 2020 14:39:19 -0700 Subject: [PATCH 07/38] test: make `round-trip-syntax-test` Python 3 friendly Make the `round-trip-syntax-test` tool Python 3 friendly by tweaking the open modes and using `reduce` from `functools` as it has been removed in Python 3. --- utils/round-trip-syntax-test | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/utils/round-trip-syntax-test b/utils/round-trip-syntax-test index fc09165211488..53ea39efbfc78 100755 --- a/utils/round-trip-syntax-test +++ b/utils/round-trip-syntax-test @@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals import argparse import difflib +from functools import reduce import logging import os import subprocess @@ -17,7 +18,8 @@ class RoundTripTask(object): def __init__(self, input_filename, action, swift_syntax_test, skip_bad_syntax): assert action == '-round-trip-parse' or action == '-round-trip-lex' - assert type(input_filename) == unicode + if sys.version_info[0] < 3: + assert type(input_filename) == unicode assert type(swift_syntax_test) == str assert os.path.isfile(input_filename), \ @@ -51,9 +53,9 @@ class RoundTripTask(object): self.output_file.close() self.stderr_file.close() - with open(self.output_file.name, 'r') as stdout_in: + with open(self.output_file.name, 'rb') as stdout_in: self.stdout = stdout_in.read() - with open(self.stderr_file.name, 'r') as stderr_in: + with open(self.stderr_file.name, 'rb') as stderr_in: self.stderr = stderr_in.read() os.remove(self.output_file.name) @@ -75,7 +77,7 @@ class RoundTripTask(object): raise RuntimeError() contents = ''.join(map(lambda l: l.decode('utf-8', errors='replace'), - open(self.input_filename).readlines())) + open(self.input_filename, 'rb').readlines())) stdout_contents = self.stdout.decode('utf-8', errors='replace') if contents == stdout_contents: @@ -92,7 +94,7 @@ def swift_files_in_dir(d): swift_files = [] for root, dirs, files in os.walk(d): for basename in files: - if not basename.decode('utf-8').endswith('.swift'): + if not basename.endswith('.swift'): continue abs_file = os.path.abspath(os.path.join(root, basename)) swift_files.append(abs_file) @@ -149,7 +151,8 @@ This driver invokes swift-syntax-test using -round-trip-lex and all_input_files = [filename for dir_listing in dir_listings for filename in dir_listing] all_input_files += args.individual_input_files - all_input_files = [f.decode('utf-8') for f in all_input_files] + if sys.version_info[0] < 3: + all_input_files = [f.decode('utf-8') for f in all_input_files] if len(all_input_files) == 0: logging.error('No input files!') From 14f72b7f0eb40d89874a11c57c275be5d5a8b22c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 1 Jul 2020 10:17:47 -0700 Subject: [PATCH 08/38] test: appease python linter (NFC) --- utils/round-trip-syntax-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/round-trip-syntax-test b/utils/round-trip-syntax-test index 53ea39efbfc78..c8a9fed9d0ccd 100755 --- a/utils/round-trip-syntax-test +++ b/utils/round-trip-syntax-test @@ -4,8 +4,8 @@ from __future__ import print_function, unicode_literals import argparse import difflib -from functools import reduce import logging +from functools import reduce import os import subprocess import sys From 93df7f4276f8b6507e61e2d51fc4c0d18fc6ae6a Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 29 Jun 2020 15:43:33 -0700 Subject: [PATCH 09/38] PathSanitizingFileCheck: improve Python3 compatibility Adjust the regex match to do a better job of sanitizing the paths. This improves the test coverage pass rate with Python 3. Furthermore, handle the unicode conversion properly that breaks with Python 3. This further improves the Python 3 test coverage pass rate. --- utils/PathSanitizingFileCheck | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/utils/PathSanitizingFileCheck b/utils/PathSanitizingFileCheck index 55cc4a5d286ac..3496895ec0d07 100755 --- a/utils/PathSanitizingFileCheck +++ b/utils/PathSanitizingFileCheck @@ -67,20 +67,21 @@ constants.""") if args.enable_windows_compatibility: if args.enable_yaml_compatibility: - slashes_re = b'(/|\\\\\\\\|\\\\\\\\\\\\\\\\)' + slashes_re = r'(/|\\\\|\\\\\\\\)' else: - slashes_re = b'(/|\\\\\\\\)' + slashes_re = r'(/|\\\\)' else: - slashes_re = b'/' + slashes_re = r'/' - stdin = io.open(sys.stdin.fileno(), 'rb').read() + stdin = io.open(sys.stdin.fileno(), 'r', encoding='utf-8', errors='ignore').read() for s in args.sanitize_strings: - replacement, pattern = s.encode(encoding="utf-8").split(b'=', 1) + replacement, pattern = s.split('=', 1) # Since we want to use pattern as a regex in some platforms, we need # to escape it first, and then replace the escaped slash # literal (r'\\/') for our platform-dependent slash regex. - stdin = re.sub(re.sub(b'\\\\/', slashes_re, re.escape(pattern)), + stdin = re.sub(re.sub('\\\\/' if sys.version_info[0] < 3 else r'[/\\]', + slashes_re, re.escape(pattern)), replacement, stdin) @@ -90,7 +91,7 @@ constants.""") else: p = subprocess.Popen( [args.file_check_path] + unknown_args, stdin=subprocess.PIPE) - stdout, stderr = p.communicate(stdin) + stdout, stderr = p.communicate(stdin.encode('utf-8')) if stdout is not None: print(stdout) if stderr is not None: From 96127ca3ce6c7766fa3aed4c589a0430b621ed0c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 1 Jul 2020 13:28:22 -0700 Subject: [PATCH 10/38] Update round-trip-syntax-test Try harder to appease the python linter --- utils/round-trip-syntax-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/round-trip-syntax-test b/utils/round-trip-syntax-test index c8a9fed9d0ccd..18b374544c5a8 100755 --- a/utils/round-trip-syntax-test +++ b/utils/round-trip-syntax-test @@ -5,11 +5,11 @@ from __future__ import print_function, unicode_literals import argparse import difflib import logging -from functools import reduce import os import subprocess import sys import tempfile +from functools import reduce logging.basicConfig(format='%(message)s', level=logging.INFO) From a7679c09cdaaec8d97b4f79365a6e7099e998faa Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Mon, 23 Mar 2020 19:55:25 -0400 Subject: [PATCH 11/38] [test] Add OpenBSD to stable ABI testing targets. See #30391, #30296. Neither IRGen/conditional_conformances.swift nor IRGen/class_resilience.swift pass on OpenBSD unless target_mandates_stable_abi = TRUE. --- test/lit.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit.cfg b/test/lit.cfg index 1cdd4b7ee9c08..86de98d04b108 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -699,7 +699,7 @@ if (run_os == 'maccatalyst'): target_os_abi = 'macosx' target_os_is_maccatalyst = "TRUE" config.available_features.add("OS=ios") -if (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'windows-gnu', 'windows-msvc', 'linux-android', 'linux-androideabi']): +if (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'openbsd', 'windows-cygnus', 'windows-gnu', 'windows-msvc', 'linux-android', 'linux-androideabi']): target_mandates_stable_abi = "TRUE" config.available_features.add('swift_only_stable_abi') config.substitutions.append(('%target-os-abi', target_os_abi)) From cacfb01f9794c45c3d408c09178d93732faa2bd9 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 1 Jul 2020 15:47:18 -0700 Subject: [PATCH 12/38] Revert subset of "[Gardening] Clean Up OS-Test Patterns Across The Codebase" Removes usage of #canImport(Darwin) from stdlib/public. This reverts a subset of commit cddf73ecdbd79edf456fe17a7dba19a8adff9186. --- .../TgmathDerivatives.swift.gyb | 4 ++-- stdlib/public/Platform/MachError.swift | 4 ++-- stdlib/public/Platform/POSIXError.swift | 2 +- stdlib/public/Platform/Platform.swift | 20 +++++++++---------- stdlib/public/Platform/TiocConstants.swift | 4 ++-- stdlib/public/Platform/tgmath.swift.gyb | 2 +- stdlib/public/core/Availability.swift | 2 +- stdlib/public/core/CTypes.swift | 4 ++-- .../public/core/UnicodeScalarProperties.swift | 2 +- stdlib/public/core/VarArgs.swift | 4 ++-- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb index dec835649ce5c..1a770907f5a55 100644 --- a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb +++ b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb @@ -14,9 +14,9 @@ import Swift -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin.C.tgmath -#elseif canImport(Glibc) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/public/Platform/MachError.swift b/stdlib/public/Platform/MachError.swift index 8ab16b50d943c..5d506dcefcbc5 100644 --- a/stdlib/public/Platform/MachError.swift +++ b/stdlib/public/Platform/MachError.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) /// Enumeration describing Mach error codes. @objc public enum MachErrorCode : Int32 { @@ -202,4 +202,4 @@ public enum MachErrorCode : Int32 { /// The requested property cannot be changed at this time. case policyStatic = 51 } -#endif // canImport(Darwin) +#endif // os(macOS) || os(iOS) || os(tvOS) || os(watchOS) diff --git a/stdlib/public/Platform/POSIXError.swift b/stdlib/public/Platform/POSIXError.swift index 2e3b5fd333e9f..5e644ae7a7237 100644 --- a/stdlib/public/Platform/POSIXError.swift +++ b/stdlib/public/Platform/POSIXError.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) /// Enumeration describing POSIX error codes. @objc diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index f4f9b3236f560..5243b516379f9 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -17,7 +17,7 @@ import SwiftOverlayShims import ucrt #endif -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) //===----------------------------------------------------------------------===// // MacTypes.h //===----------------------------------------------------------------------===// @@ -103,7 +103,7 @@ public var errno : Int32 { // stdio.h //===----------------------------------------------------------------------===// -#if canImport(Darwin) || os(FreeBSD) || os(PS4) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(FreeBSD) || os(PS4) public var stdin : UnsafeMutablePointer { get { return __stdinp @@ -248,7 +248,7 @@ public var S_IFBLK: mode_t { return mode_t(0o060000) } public var S_IFREG: mode_t { return mode_t(0o100000) } public var S_IFLNK: mode_t { return mode_t(0o120000) } public var S_IFSOCK: mode_t { return mode_t(0o140000) } -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) public var S_IFWHT: mode_t { return mode_t(0o160000) } #endif @@ -271,7 +271,7 @@ public var S_ISUID: mode_t { return mode_t(0o004000) } public var S_ISGID: mode_t { return mode_t(0o002000) } public var S_ISVTX: mode_t { return mode_t(0o001000) } -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) public var S_ISTXT: mode_t { return S_ISVTX } public var S_IREAD: mode_t { return S_IRUSR } public var S_IWRITE: mode_t { return S_IWUSR } @@ -315,7 +315,7 @@ public func ioctl( // unistd.h //===----------------------------------------------------------------------===// -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @available(*, unavailable, message: "Please use threads or posix_spawn*()") public func fork() -> Int32 { fatalError("unavailable function can't be called") @@ -331,7 +331,7 @@ public func vfork() -> Int32 { // signal.h //===----------------------------------------------------------------------===// -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) public var SIG_DFL: sig_t? { return nil } public var SIG_IGN: sig_t { return unsafeBitCast(1, to: sig_t.self) } public var SIG_ERR: sig_t { return unsafeBitCast(-1, to: sig_t.self) } @@ -395,10 +395,10 @@ public typealias Semaphore = UnsafeMutablePointer /// The value returned by `sem_open()` in the case of failure. public var SEM_FAILED: Semaphore? { -#if canImport(Darwin) - // The value is ABI. Value verified to be correct for macOS, iOS, watchOS, tvOS. +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + // The value is ABI. Value verified to be correct for OS X, iOS, watchOS, tvOS. return Semaphore(bitPattern: -1) -#elseif canImport(Glibc) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) // The value is ABI. Value verified to be correct on Glibc. return Semaphore(bitPattern: 0) #else @@ -429,7 +429,7 @@ public func sem_open( //===----------------------------------------------------------------------===// // Some platforms don't have `extern char** environ` imported from C. -#if canImport(Darwin) || os(FreeBSD) || os(OpenBSD) || os(PS4) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(FreeBSD) || os(OpenBSD) || os(PS4) public var environ: UnsafeMutablePointer?> { return _swift_stdlib_getEnviron() } diff --git a/stdlib/public/Platform/TiocConstants.swift b/stdlib/public/Platform/TiocConstants.swift index 3bc51f955e93a..272f7e242e64d 100644 --- a/stdlib/public/Platform/TiocConstants.swift +++ b/stdlib/public/Platform/TiocConstants.swift @@ -13,7 +13,7 @@ // Tty ioctl request constants, needed only on Darwin and FreeBSD. // Constants available on all platforms, also available on Linux. -#if canImport(Darwin) || os(FreeBSD) || os(Haiku) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(FreeBSD) || os(Haiku) /// Set exclusive use of tty. public var TIOCEXCL: UInt { return 0x2000740d } @@ -115,7 +115,7 @@ public var TIOCGLTC: UInt { return 0x40067474 } // Darwin only constants, also available on Linux. -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) /// Get termios struct. public var TIOCGETA: UInt { return 0x40487413 } diff --git a/stdlib/public/Platform/tgmath.swift.gyb b/stdlib/public/Platform/tgmath.swift.gyb index 1a06f3b75503f..cf8c9fa88069b 100644 --- a/stdlib/public/Platform/tgmath.swift.gyb +++ b/stdlib/public/Platform/tgmath.swift.gyb @@ -234,7 +234,7 @@ public func ${ufunc}(_ x: ${T}) -> ${T} { % end -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) // Unary intrinsic functions // Note these have a corresponding LLVM intrinsic % for T, ufunc in TypedUnaryIntrinsicFunctions(): diff --git a/stdlib/public/core/Availability.swift b/stdlib/public/core/Availability.swift index dd8b5f1d025af..55da2e8314a77 100644 --- a/stdlib/public/core/Availability.swift +++ b/stdlib/public/core/Availability.swift @@ -24,7 +24,7 @@ public func _stdlib_isOSVersionAtLeast( _ minor: Builtin.Word, _ patch: Builtin.Word ) -> Builtin.Int1 { -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) if Int(major) == 9999 { return true._value } diff --git a/stdlib/public/core/CTypes.swift b/stdlib/public/core/CTypes.swift index a615263d77089..b1f2432f29adb 100644 --- a/stdlib/public/core/CTypes.swift +++ b/stdlib/public/core/CTypes.swift @@ -67,7 +67,7 @@ public typealias CFloat = Float public typealias CDouble = Double /// The C 'long double' type. -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) // On Darwin, long double is Float80 on x86, and Double otherwise. #if arch(x86_64) || arch(i386) public typealias CLongDouble = Float80 @@ -231,7 +231,7 @@ extension UInt { } /// A wrapper around a C `va_list` pointer. -#if arch(arm64) && !(canImport(Darwin) || os(Windows)) +#if arch(arm64) && !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Windows)) @frozen public struct CVaListPointer { @usableFromInline // unsafe-performance diff --git a/stdlib/public/core/UnicodeScalarProperties.swift b/stdlib/public/core/UnicodeScalarProperties.swift index e0f17727cb8c3..d0c39c2efee3b 100644 --- a/stdlib/public/core/UnicodeScalarProperties.swift +++ b/stdlib/public/core/UnicodeScalarProperties.swift @@ -590,7 +590,7 @@ extension Unicode.Scalar.Properties { return _hasBinaryProperty(__swift_stdlib_UCHAR_CHANGES_WHEN_NFKC_CASEFOLDED) } -#if canImport(Darwin) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) // FIXME: These properties were introduced in ICU 57, but Ubuntu 16.04 comes // with ICU 55 so the values won't be correct there. Exclude them on // non-Darwin platforms for now; bundling ICU with the toolchain would resolve diff --git a/stdlib/public/core/VarArgs.swift b/stdlib/public/core/VarArgs.swift index bba7b96c3c700..8576a86568b01 100644 --- a/stdlib/public/core/VarArgs.swift +++ b/stdlib/public/core/VarArgs.swift @@ -91,7 +91,7 @@ internal let _countGPRegisters = 16 @usableFromInline internal let _registerSaveWords = _countGPRegisters -#elseif arch(arm64) && !(canImport(Darwin) || os(Windows)) +#elseif arch(arm64) && !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Windows)) // ARM Procedure Call Standard for aarch64. (IHI0055B) // The va_list type may refer to any parameter in a parameter list may be in one // of three memory locations depending on its type and position in the argument @@ -419,7 +419,7 @@ extension Float80: CVarArg, _CVarArgAligned { } #endif -#if (arch(x86_64) && !os(Windows)) || arch(s390x) || (arch(arm64) && !(canImport(Darwin) || os(Windows))) +#if (arch(x86_64) && !os(Windows)) || arch(s390x) || (arch(arm64) && !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Windows))) /// An object that can manage the lifetime of storage backing a /// `CVaListPointer`. From ff26a062574708614c0d1e2aa8fc563cf59c5797 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Thu, 2 Jul 2020 03:13:10 +0100 Subject: [PATCH 13/38] fix broken test --- ...00017-llvm-foldingset-llvm-attributesetnode-nodeequals.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation-test/compiler_crashers_fixed/00017-llvm-foldingset-llvm-attributesetnode-nodeequals.swift b/validation-test/compiler_crashers_fixed/00017-llvm-foldingset-llvm-attributesetnode-nodeequals.swift index d58f6e86f203e..6f574b0e9dcac 100644 --- a/validation-test/compiler_crashers_fixed/00017-llvm-foldingset-llvm-attributesetnode-nodeequals.swift +++ b/validation-test/compiler_crashers_fixed/00017-llvm-foldingset-llvm-attributesetnode-nodeequals.swift @@ -20,4 +20,4 @@ extension Bool : BooleanProtocol { func f(_ b: T) {} // expected-note@-1 {{required by global function 'f' where 'T' = 'BooleanProtocol'}} -f(true as BooleanProtocol) // expected-error {{value of protocol type 'BooleanProtocol' cannot conform to 'BooleanProtocol'; only struct/enum/class types can conform to protocols}} +f(true as BooleanProtocol) // expected-error {{protocol 'BooleanProtocol' as a type cannot conform to the protocol itself; only concrete types such as structs, enums and classes can conform to protocols}} From c69baa87311ee831c2f4b908395bb01f2ad3b7c7 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Sat, 6 Jun 2020 19:23:12 -0700 Subject: [PATCH 14/38] [EduNotes] Explain some opaque type diagnostics --- include/swift/AST/EducationalNotes.def | 11 +++ test/type/opaque.swift | 8 +-- userdocs/diagnostics/opaque-type-inference.md | 68 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 userdocs/diagnostics/opaque-type-inference.md diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index ca30032f17a5f..cb2dde613a448 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -59,6 +59,17 @@ EDUCATIONAL_NOTES(property_wrapper_failable_init, EDUCATIONAL_NOTES(property_wrapper_type_requirement_not_accessible, "property-wrapper-requirements.md") +EDUCATIONAL_NOTES(opaque_type_var_no_init, "opaque-type-inference.md") +EDUCATIONAL_NOTES(opaque_type_no_underlying_type_candidates, + "opaque-type-inference.md") +EDUCATIONAL_NOTES(opaque_type_mismatched_underlying_type_candidates, + "opaque-type-inference.md") +EDUCATIONAL_NOTES(opaque_type_self_referential_underlying_type, + "opaque-type-inference.md") +EDUCATIONAL_NOTES(opaque_type_var_no_underlying_type, + "opaque-type-inference.md") + + EDUCATIONAL_NOTES(missing_append_interpolation, "string-interpolation-conformance.md") EDUCATIONAL_NOTES(append_interpolation_static, diff --git a/test/type/opaque.swift b/test/type/opaque.swift index 276933e611580..e97f5ba1b64c6 100644 --- a/test/type/opaque.swift +++ b/test/type/opaque.swift @@ -13,7 +13,7 @@ class C {} class D: C, P, Q { func paul() {}; func priscilla() {}; func quinn() {} } let property: some P = 1 -let deflessLet: some P // expected-error{{has no initializer}} +let deflessLet: some P // expected-error{{has no initializer}} {{educational-notes=opaque-type-inference}} var deflessVar: some P // expected-error{{has no initializer}} struct GenericProperty { @@ -173,13 +173,13 @@ func recursion(x: Int) -> some P { return recursion(x: x - 1) } -func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}} +func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}} {{educational-notes=opaque-type-inference}} func returnUninhabited() -> some P { // expected-note {{opaque return type declared here}} fatalError() // expected-error{{return type of global function 'returnUninhabited()' requires that 'Never' conform to 'P'}} } -func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}} +func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}} {{educational-notes=opaque-type-inference}} if x { return y // expected-note{{underlying type 'Int'}} } else { @@ -209,7 +209,7 @@ func jan() -> some P { return [marcia(), marcia(), marcia()] } func marcia() -> some P { - return [marcia(), marcia(), marcia()] // expected-error{{defines the opaque type in terms of itself}} + return [marcia(), marcia(), marcia()] // expected-error{{defines the opaque type in terms of itself}} {{educational-notes=opaque-type-inference}} } protocol R { diff --git a/userdocs/diagnostics/opaque-type-inference.md b/userdocs/diagnostics/opaque-type-inference.md new file mode 100644 index 0000000000000..ebf63955530f5 --- /dev/null +++ b/userdocs/diagnostics/opaque-type-inference.md @@ -0,0 +1,68 @@ +# Underlying Type Inference for Opaque Result Types + +Opaque result types are a useful tool for abstracting the return type of a function or subscript, or type of a property. Although the concrete underlying type of an opaque type is hidden from clients, it is still inferred by the compiler, which enforces certain usage requirements: + +- Property declarations with opaque types must have an initializer expression or getter, and functions or subscripts returning opaque types must have at least one `return` statement: + +```swift +let x: some Equatable // error: property declares an opaque return type, but has no initializer expression from which to infer an underlying type +let y: some Equatable = 42 // OK +let z: some Equatable { // Also OK + return "hello, " + "world!" +} + +func foo() -> some Equatable { // error: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type + fatalError("Unimplemented") +} + +func bar() -> some Equatable { // OK + fatalError("Unimplemented") + return 42 +} +``` + +- The underlying type of an opaque type must be unique. In other words, if a function or subscript returns an opaque type, it must return values of the same underlying type from every `return` statement in its body. + +```swift +func foo(bar: Bool) -> some Equatable { // error: function declares an opaque return type, but the return statements in its body do not have matching underlying types + if bar { + return "hello, world!" // note: return statement has underlying type 'String' + } else { + return 1 // note: return statement has underlying type 'Int' + } +} + +func bar(baz: Bool) -> some Equatable { // OK, both branches of the if statement return a value of the same underlying type, Int. + if baz { + return 100 + } else { + return 200 + } +} +``` + +- Functions returning opaque types may be recursive. However, such functions must have at least one `return` statement that returns a concrete underlying type as opposed to the function's own opaque result type. Additionally, recursive calls may not be used to create an infinitely recursive opaque type. + +```swift +func foo(_ x: Int) -> some Equatable { // error: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type + // Not allowed because there aren't any non-recursive returns to infer the underlying type from. + return foo(x+1) +} + +struct EquatableWrapper: Equatable { var value: T } +func foo() -> some Equatable { // error: function opaque return type was inferred as 'EquatableWrapper', which defines the opaque type in terms of itself + // Not allowed because the use of EquatableWrapper creates an infinitely recursive underlying type: EquatableWrapper>>...> + return EquatableWrapper(value: foo()) +} + +func bar(_ x: Int) -> some Equatable { // OK, the underlying type can be inferred from the second return statement. + if x > 0 { + return bar(x-1) + } else { + return x + } +} +``` + +To learn more about opaque result types, see the [Opaque Types](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html) section of _The Swift Programming Language_. + From 7b89eae82e0718a02f5c474526aceda9716cf79e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 2 Jul 2020 00:15:47 -0400 Subject: [PATCH 15/38] AST: Remove unused ArchetypeType::getAllNestedTypes() method --- include/swift/AST/Types.h | 20 +------------------- lib/AST/Type.cpp | 25 ++++++------------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 4eec20e76b166..023dbb1158a65 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -5571,25 +5571,7 @@ class ArchetypeType : public SubstitutableType, /// find a particular nested type by name, directly, or look at the /// protocols to which this archetype conforms. ArrayRef> - getKnownNestedTypes(bool resolveTypes = true) const { - return getAllNestedTypes(/*resolveTypes=*/false); - } - - /// Retrieve the nested types of this archetype. - /// - /// \param resolveTypes Whether to eagerly resolve the nested types - /// (defaults to \c true). Otherwise, the nested types might be - /// null. - /// - /// FIXME: This operation should go away, because it breaks recursive - /// protocol constraints. - ArrayRef> - getAllNestedTypes(bool resolveTypes = true) const; - - /// Set the nested types to a copy of the given array of - /// archetypes. - void setNestedTypes(ASTContext &Ctx, - ArrayRef> Nested); + getKnownNestedTypes() const; /// Register a nested type with the given name. void registerNestedType(Identifier name, Type nested); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index ac5d26304f91d..eca5a301f56b9 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3210,7 +3210,11 @@ void ArchetypeType::populateNestedTypes() const { // Record the nested types. auto mutableThis = const_cast(this); - mutableThis->setNestedTypes(mutableThis->getASTContext(), nestedTypes); + + std::sort(nestedTypes.begin(), nestedTypes.end(), OrderArchetypeByName()); + auto &Ctx = mutableThis->getASTContext(); + mutableThis->NestedTypes = Ctx.AllocateCopy(nestedTypes); + mutableThis->Bits.ArchetypeType.ExpandedNestedTypes = true; } Type ArchetypeType::getNestedType(Identifier Name) const { @@ -3250,28 +3254,11 @@ bool ArchetypeType::hasNestedType(Identifier Name) const { } ArrayRef> -ArchetypeType::getAllNestedTypes(bool resolveTypes) const { +ArchetypeType::getKnownNestedTypes() const { populateNestedTypes(); - - if (resolveTypes) { - for (auto &nested : NestedTypes) { - if (!nested.second) - resolveNestedType(nested); - } - } - return NestedTypes; } -void ArchetypeType::setNestedTypes( - ASTContext &Ctx, - ArrayRef> Nested) { - assert(!Bits.ArchetypeType.ExpandedNestedTypes && "Already expanded"); - NestedTypes = Ctx.AllocateCopy(Nested); - std::sort(NestedTypes.begin(), NestedTypes.end(), OrderArchetypeByName()); - Bits.ArchetypeType.ExpandedNestedTypes = true; -} - void ArchetypeType::registerNestedType(Identifier name, Type nested) { populateNestedTypes(); From a08f421c1a649539bcf289379d339f7497fc2b69 Mon Sep 17 00:00:00 2001 From: Onyekachi Ezeoke Date: Thu, 2 Jul 2020 12:09:07 +0100 Subject: [PATCH 16/38] fix failing tests --- .../Sema/type_checker_crashers_fixed/rdar27830834.swift | 2 +- .../compiler_crashers_2_fixed/0196-rdar48937223.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift index 364998bae311b..f7be7a54079b2 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar27830834.swift @@ -2,5 +2,5 @@ var d = [String:String]() _ = "\(d.map{ [$0 : $0] })" -// expected-error@-1 {{type 'Dictionary.Element' (aka '(key: String, value: String)') cannot conform to 'Hashable'; only struct/enum/class types can conform to protocols}} +// expected-error@-1 {{type 'Dictionary.Element' (aka '(key: String, value: String)') cannot conform to 'Hashable'; only concrete types such as structs, enums and classes can conform to protocols}} // expected-note@-2 {{required by generic struct 'Dictionary' where 'Key' = 'Dictionary.Element' (aka '(key: String, value: String)')}} diff --git a/validation-test/compiler_crashers_2_fixed/0196-rdar48937223.swift b/validation-test/compiler_crashers_2_fixed/0196-rdar48937223.swift index c8e9979569714..2414c14a4442d 100644 --- a/validation-test/compiler_crashers_2_fixed/0196-rdar48937223.swift +++ b/validation-test/compiler_crashers_2_fixed/0196-rdar48937223.swift @@ -6,7 +6,7 @@ func fn(_ arg1: T, arg2: (T) -> U) {} // expected-note@-1 {{required by global function 'fn(_:arg2:)' where 'U' = '()'}} func test(str: String) { - fn(str) { arg in // expected-error {{type '()' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} + fn(str) { arg in // expected-error {{type '()' cannot conform to 'P'; only concrete types such as structs, enums and classes can conform to protocols}} <#FOO#> // expected-error {{editor placeholder in source file}} } } From 49d4893ef6cca689f811157b01733f00c9da0774 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 2 Jul 2020 14:49:47 +0100 Subject: [PATCH 17/38] Remove BooleanType from docs/StdlibRationales.rst --- docs/StdlibRationales.rst | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/docs/StdlibRationales.rst b/docs/StdlibRationales.rst index 3b8503b5a92c8..1a13f5a8109c4 100644 --- a/docs/StdlibRationales.rst +++ b/docs/StdlibRationales.rst @@ -174,21 +174,6 @@ call an API with a different name, say ``lazyEnumerate()`` to opt into laziness. The problem is that the eager API, which would have a shorter and less obscure name, would be less efficient for the common case. -Use of ``BooleanType`` in library APIs --------------------------------------- - -Use ``Bool`` instead of a generic function over a ``BooleanType``, unless there -are special circumstances (for example, ``func &&`` is designed to work on all -boolean values so that ``&&`` feels like a part of the language). - -``BooleanType`` is a protocol to which only ``Bool`` and ``ObjCBool`` conform. -Users don't usually interact ``ObjCBool`` instances, except when using certain -specific APIs (for example, APIs that operate on pointers to ``BOOL``). If -someone already has an ``ObjCBool`` instance for whatever strange reason, they -can just convert it to ``Bool``. We think this is the right tradeoff: -simplifying function signatures is more important than making a marginal -usecase a bit more convenient. - Possible future directions ========================== From 3952715bedeae0c450fbd6dca99970e0c8e14bb1 Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Thu, 2 Jul 2020 10:58:45 -0400 Subject: [PATCH 18/38] [SIL] NFC: Move #include of CanTypeVisitor.h This improves incremental rebuild performance. --- include/swift/SIL/SILType.h | 1 - lib/IRGen/MetadataRequest.cpp | 1 + lib/SIL/IR/SILFunctionType.cpp | 1 + lib/SILGen/SILGenExpr.cpp | 1 + lib/SILGen/SILGenProlog.cpp | 1 + 5 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index f151ad217ed4d..d263631a62525 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -18,7 +18,6 @@ #ifndef SWIFT_SIL_SILTYPE_H #define SWIFT_SIL_SILTYPE_H -#include "swift/AST/CanTypeVisitor.h" #include "swift/AST/SILLayout.h" #include "swift/AST/Types.h" #include "llvm/ADT/PointerIntPair.h" diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index e69c72d0914ac..4d607e6f885ed 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -33,6 +33,7 @@ #include "IRGenMangler.h" #include "IRGenModule.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/CanTypeVisitor.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 0091d35779c08..ab739599157cf 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -18,6 +18,7 @@ #define DEBUG_TYPE "libsil" #include "swift/AST/AnyFunctionRef.h" +#include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/ForeignInfo.h" diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 631dd9f88bbf4..bdc577179ceae 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -28,6 +28,7 @@ #include "Varargs.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/Expr.h" diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index b6b564fa6a9d1..269c301fc3a14 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -15,6 +15,7 @@ #include "ManagedValue.h" #include "Scope.h" #include "swift/SIL/SILArgument.h" +#include "swift/AST/CanTypeVisitor.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" From f1bba3e1bfbc85b1cd8864ebc69c1a429bc86490 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Thu, 2 Jul 2020 08:47:55 -0700 Subject: [PATCH 19/38] Disable a RUN line in osx-targets.swift Darwin 55 is now translated to macOS 46 after the LLVM changes landed that added support for macOS 11. This change temporarily disables the RUN line in the test that uses the `darwin55` triple until the appropriate fix is upstreamed on the swift side. --- test/IRGen/osx-targets.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/IRGen/osx-targets.swift b/test/IRGen/osx-targets.swift index 32d0f9e9cf2c9..deb62e65ff4c3 100644 --- a/test/IRGen/osx-targets.swift +++ b/test/IRGen/osx-targets.swift @@ -1,6 +1,8 @@ // RUN: %swift %s -emit-ir | %FileCheck %s // RUN: %swift -target x86_64-apple-macosx10.51 %s -emit-ir | %FileCheck -check-prefix=CHECK-SPECIFIC %s -// RUN: %swift -target x86_64-apple-darwin55 %s -emit-ir | %FileCheck -check-prefix=CHECK-SPECIFIC %s + +// disable this test until macOS 11 support lands in Swift. +// : %swift -target x86_64-apple-darwin55 %s -emit-ir | %FileCheck -check-prefix=CHECK-SPECIFIC %s // REQUIRES: OS=macosx From 0b6a21b85f3a16896031001e3dbce775e669c309 Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Thu, 2 Jul 2020 11:47:15 -0400 Subject: [PATCH 20/38] [SIL] NFC: Add more paranoia checks to SILType --- include/swift/SIL/SILType.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index f151ad217ed4d..30c37b703b993 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -622,10 +622,19 @@ template<> Can##ID##Type SILType::getAs() const = delete; \ template<> Can##ID##Type SILType::castTo() const = delete; \ template<> bool SILType::is() const = delete; NON_SIL_TYPE(Function) +NON_SIL_TYPE(GenericFunction) NON_SIL_TYPE(AnyFunction) NON_SIL_TYPE(LValue) +NON_SIL_TYPE(InOut) #undef NON_SIL_TYPE +#define TYPE(ID, PARENT) +#define UNCHECKED_TYPE(ID, PARENT) \ +template<> Can##ID##Type SILType::getAs() const = delete; \ +template<> Can##ID##Type SILType::castTo() const = delete; \ +template<> bool SILType::is() const = delete; +#include "swift/AST/TypeNodes.def" + inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SILType T) { T.print(OS); return OS; From 5f219cdc11d119320d231fdcfda194dd9fa37343 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 2 Jul 2020 09:51:55 -0700 Subject: [PATCH 21/38] Front-end: disable interface file locking for the -compile-module-from-interface action The situations where we use this action, e.g. explicit module building and generating prebuilt module cache, don't need synchronization. We should avoid using lock files for them. rdar://65005528 --- lib/Frontend/ArgsToFrontendOptionsConverter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index c22f154e969e1..812890d7aec58 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -102,8 +102,6 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.RemarkOnRebuildFromModuleInterface |= Args.hasArg(OPT_Rmodule_interface_rebuild); - Opts.DisableInterfaceFileLock |= Args.hasArg(OPT_disable_interface_lockfile); - computePrintStatsOptions(); computeDebugTimeOptions(); computeTBDOptions(); @@ -149,6 +147,15 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.RequestedAction = determineRequestedAction(Args); } + if (Opts.RequestedAction == FrontendOptions::ActionType::CompileModuleFromInterface) { + // The situations where we use this action, e.g. explicit module building and + // generating prebuilt module cache, don't need synchronization. We should avoid + // using lock files for them. + Opts.DisableInterfaceFileLock = true; + } else { + Opts.DisableInterfaceFileLock |= Args.hasArg(OPT_disable_interface_lockfile); + } + if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate && Opts.InputsAndOutputs.hasPrimaryInputs()) { Diags.diagnose(SourceLoc(), diag::error_immediate_mode_primary_file); From 406fb2324382e4e745ebdccc1d7c4850a92a88dd Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 2 Jul 2020 12:51:54 -0700 Subject: [PATCH 22/38] test: `iteritems` -> `items` This allows the lit configuration to be python 2 and 3 compatible. --- test/lit.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit.cfg b/test/lit.cfg index 1cdd4b7ee9c08..e36f380e3a87e 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1564,7 +1564,7 @@ source_compiler_rt_libs(compiler_rt_dir) def check_runtime_libs(features_to_check): for lib in config.compiler_rt_libs: - for (libname, feature) in features_to_check.iteritems(): + for (libname, feature) in features_to_check.items(): if lib.startswith("libclang_rt." + libname + "_"): config.available_features.add(feature) From f989aa2b22fd935713b707a39bd362ddf41a87a5 Mon Sep 17 00:00:00 2001 From: tbkka Date: Thu, 2 Jul 2020 13:10:01 -0700 Subject: [PATCH 23/38] SR-12486: `T.self is Any.Protocol` is broken (#31662) * SR-12486: `T.self is Any.Protocol` is broken This turned out to be fallout from https://github.com/apple/swift/pull/27572 which was in turn motivated by our confusing metatype syntax when generic variables are bound to protocols. In particular, the earlier PR was an attempt to make the expression `x is T.Type` (where `T` is a generic type variable bound to a protocol `P`) behave the same as `x is P.Type` (where `P` is a protocol). Unfortunately, the generic `T.Type` actually binds to `P.Protocol` in this case (not `P.Type`), so the original motivation was flawed, and as it happens, `x is T.Type` already behaved the same as `x is P.Protocol` in this situation. This PR reverts that earlier change and beefs up some of the tests around these behaviors. Resolves SR-12486 Resolves rdar://62201613 Reverts PR#27572 --- stdlib/public/runtime/Casting.cpp | 7 - test/Interpreter/generic_casts.swift | 241 ++++++++++++---------- test/Interpreter/generic_casts_objc.swift | 35 +++- test/stdlib/Casts.swift | 61 ++++++ 4 files changed, 219 insertions(+), 125 deletions(-) diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index c5d33eb10e580..374d0dfe745bf 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -1183,13 +1183,6 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, } break; - case MetadataKind::Existential: { - auto targetTypeAsExistential = static_cast(targetType); - if (_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, nullptr)) - return origSourceType; - return nullptr; - } - default: return nullptr; } diff --git a/test/Interpreter/generic_casts.swift b/test/Interpreter/generic_casts.swift index ecace2ee16136..73f1837d20ab9 100644 --- a/test/Interpreter/generic_casts.swift +++ b/test/Interpreter/generic_casts.swift @@ -1,9 +1,10 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -Onone %s -o %t/a.out -// RUN: %target-run %t/a.out | %FileCheck --check-prefix CHECK --check-prefix CHECK-ONONE %s // RUN: %target-build-swift -O %s -o %t/a.out.optimized // RUN: %target-codesign %t/a.out.optimized -// RUN: %target-run %t/a.out.optimized | %FileCheck %s +// +// RUN: %target-run %t/a.out | %FileCheck --check-prefix CHECK %s +// RUN: %target-run %t/a.out.optimized | %FileCheck --check-prefix CHECK %s // REQUIRES: executable_test // FIXME: rdar://problem/19648117 Needs splitting objc parts out @@ -140,42 +141,50 @@ class PC: P {} class PCSub: PC {} // `is` checks -func nongenericAnyIsPConforming(type: Any.Type) -> Bool { +func nongenericAnyIsPType(type: Any.Type) -> Bool { // `is P.Type` tests whether the argument conforms to `P` // Note: this can only be true for a concrete type, never a protocol return type is P.Type } -func nongenericAnyIsPSubtype(type: Any.Type) -> Bool { +func nongenericAnyIsPProtocol(type: Any.Type) -> Bool { + // `P.Protocol` is the metatype for `P` (the type of `P.self`) // `is P.Protocol` tests whether the argument is a subtype of `P` // In particular, it is true for `P.self` return type is P.Protocol } -func nongenericAnyIsPAndAnyObjectConforming(type: Any.Type) -> Bool { +func nongenericAnyIsPAndAnyObjectType(type: Any.Type) -> Bool { return type is (P & AnyObject).Type } -func nongenericAnyIsPAndPCSubConforming(type: Any.Type) -> Bool { +func nongenericAnyIsPAndAnyObjectProtocol(type: Any.Type) -> Bool { + return type is (P & AnyObject).Protocol +} +func nongenericAnyIsPAndPCSubType(type: Any.Type) -> Bool { return type is (P & PCSub).Type } func genericAnyIs(type: Any.Type, to: T.Type, expected: Bool) -> Bool { // If we're testing against a runtime that doesn't have the fix this tests, // just pretend we got it right. if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { + // Remember: If `T` is bound to `P`, then `T.Type` is `P.Protocol` return type is T.Type } else { return expected } } // `as?` checks -func nongenericAnyAsConditionalPConforming(type: Any.Type) -> Bool { +func nongenericAnyAsConditionalPType(type: Any.Type) -> Bool { return (type as? P.Type) != nil } -func nongenericAnyAsConditionalPSubtype(type: Any.Type) -> Bool { +func nongenericAnyAsConditionalPProtocol(type: Any.Type) -> Bool { return (type as? P.Protocol) != nil } -func nongenericAnyAsConditionalPAndAnyObjectConforming(type: Any.Type) -> Bool { +func nongenericAnyAsConditionalPAndAnyObjectType(type: Any.Type) -> Bool { return (type as? (P & AnyObject).Type) != nil } -func nongenericAnyAsConditionalPAndPCSubConforming(type: Any.Type) -> Bool { +func nongenericAnyAsConditionalPAndAnyObjectProtocol(type: Any.Type) -> Bool { + return (type as? (P & AnyObject).Protocol) != nil +} +func nongenericAnyAsConditionalPAndPCSubType(type: Any.Type) -> Bool { return (type as? (P & PCSub).Type) != nil } func genericAnyAsConditional(type: Any.Type, to: T.Type, expected: Bool) -> Bool { @@ -190,19 +199,19 @@ func genericAnyAsConditional(type: Any.Type, to: T.Type, expected: Bool) -> B // `as!` checks func blackhole(_ : T) { } -func nongenericAnyAsUnconditionalPConforming(type: Any.Type) -> Bool { +func nongenericAnyAsUnconditionalPType(type: Any.Type) -> Bool { blackhole(type as! P.Type) return true } -func nongenericAnyAsUnconditionalPSubtype(type: Any.Type) -> Bool { +func nongenericAnyAsUnconditionalPProtocol(type: Any.Type) -> Bool { blackhole(type as! P.Protocol) return true } -func nongenericAnyAsUnconditionalPAndAnyObjectConforming(type: Any.Type) -> Bool { +func nongenericAnyAsUnconditionalPAndAnyObjectType(type: Any.Type) -> Bool { blackhole(type as! (P & AnyObject).Type) return true } -func nongenericAnyAsUnconditionalPAndPCSubConforming(type: Any.Type) -> Bool { +func nongenericAnyAsUnconditionalPAndPCSubType(type: Any.Type) -> Bool { blackhole(type as! (P & PCSub).Type) return true } @@ -215,135 +224,153 @@ func genericAnyAsUnconditional(type: Any.Type, to: T.Type, expected: Bool) -> // CHECK-LABEL: casting types to protocols with generics: print("casting types to protocols with generics:") -print(nongenericAnyIsPConforming(type: P.self)) // CHECK: false -print(nongenericAnyIsPSubtype(type: P.self)) // CHECK: true -print(genericAnyIs(type: P.self, to: P.self, expected: true)) // CHECK: true -print(nongenericAnyIsPConforming(type: PS.self)) // CHECK: true -print(genericAnyIs(type: PS.self, to: P.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyIsPConforming(type: PE.self)) // CHECK: true -print(genericAnyIs(type: PE.self, to: P.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyIsPConforming(type: PC.self)) // CHECK: true -print(genericAnyIs(type: PC.self, to: P.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyIsPConforming(type: PCSub.self)) // CHECK: true -print(genericAnyIs(type: PCSub.self, to: P.self, expected: true)) // CHECK-ONONE: true +print(#line, nongenericAnyIsPType(type: P.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPProtocol(type: P.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyIs(type: P.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPType(type: PS.self)) // CHECK: [[@LINE]] true +print(#line, PS() is P) // CHECK: [[@LINE]] true +// One candidate for a Swift type theory holds that +// `A is a subtype of B iff A.self is metatype` +// In that theory, `PS() is P` above would imply that +// `PS.self is P.Protocol` below must also be true. +// But that theory is not the one that Swift currently +// implements. +print(#line, nongenericAnyIsPProtocol(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PS.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPType(type: PE.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPProtocol(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PE.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPType(type: PC.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPProtocol(type: PC.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PC.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPProtocol(type: PCSub.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PCSub.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false // CHECK-LABEL: conditionally casting types to protocols with generics: -print("conditionally casting types to protocols with generics:") -print(nongenericAnyAsConditionalPConforming(type: P.self)) // CHECK: false -print(nongenericAnyAsConditionalPSubtype(type: P.self)) // CHECK: true -print(genericAnyAsConditional(type: P.self, to: P.self, expected: true)) // CHECK: true -print(nongenericAnyAsConditionalPConforming(type: PS.self)) // CHECK: true -print(genericAnyAsConditional(type: PS.self, to: P.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyAsConditionalPConforming(type: PE.self)) // CHECK: true -print(genericAnyAsConditional(type: PE.self, to: P.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyAsConditionalPConforming(type: PC.self)) // CHECK: true -print(genericAnyAsConditional(type: PC.self, to: P.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyAsConditionalPConforming(type: PCSub.self)) // CHECK: true -print(genericAnyAsConditional(type: PCSub.self, to: P.self, expected: true)) // CHECK-ONONE: true +print(#line, "conditionally casting types to protocols with generics:") +print(#line, nongenericAnyAsConditionalPType(type: P.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPProtocol(type: P.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsConditional(type: P.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsConditionalPType(type: PS.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsConditionalPProtocol(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PS.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPType(type: PE.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsConditionalPProtocol(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PE.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPType(type: PC.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsConditionalPProtocol(type: PC.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PC.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsConditionalPProtocol(type: PCSub.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PCSub.self, to: P.self, expected: false)) // CHECK: [[@LINE]] false // CHECK-LABEL: unconditionally casting types to protocols with generics: -print("unconditionally casting types to protocols with generics:") -//print(nongenericAnyAsUnconditionalPConforming(type: P.self)) // expected to trap -print(nongenericAnyAsUnconditionalPSubtype(type: P.self)) // CHECK: true -print(genericAnyAsUnconditional(type: P.self, to: P.self, expected: true)) // CHECK: true -print(nongenericAnyAsUnconditionalPConforming(type: PS.self)) // CHECK: true -print(genericAnyAsUnconditional(type: PS.self, to: P.self, expected: true)) // CHECK: true -print(nongenericAnyAsUnconditionalPConforming(type: PE.self)) // CHECK: true -print(genericAnyAsUnconditional(type: PE.self, to: P.self, expected: true)) // CHECK: true -print(nongenericAnyAsUnconditionalPConforming(type: PC.self)) // CHECK: true -print(genericAnyAsUnconditional(type: PC.self, to: P.self, expected: true)) // CHECK: true -print(nongenericAnyAsUnconditionalPConforming(type: PCSub.self)) // CHECK: true -print(genericAnyAsUnconditional(type: PCSub.self, to: P.self, expected: true)) // CHECK: true +print(#line, "unconditionally casting types to protocols with generics:") +//print(#line, nongenericAnyAsUnconditionalPType(type: P.self)) // expected to trap +print(#line, nongenericAnyAsUnconditionalPProtocol(type: P.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsUnconditional(type: P.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsUnconditionalPType(type: PS.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsUnconditional(type: PS.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsUnconditionalPType(type: PE.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsUnconditional(type: PE.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsUnconditionalPType(type: PC.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsUnconditional(type: PC.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsUnconditionalPType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsUnconditional(type: PCSub.self, to: P.self, expected: true)) // CHECK: [[@LINE]] true // CHECK-LABEL: casting types to protocol & AnyObject existentials: -print("casting types to protocol & AnyObject existentials:") -print(nongenericAnyIsPAndAnyObjectConforming(type: PS.self)) // CHECK: false -print(genericAnyIs(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: false -print(nongenericAnyIsPAndAnyObjectConforming(type: PE.self)) // CHECK: false -print(genericAnyIs(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: false -print(nongenericAnyIsPAndAnyObjectConforming(type: PC.self)) // CHECK: true -print(genericAnyIs(type: PC.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyIsPAndAnyObjectConforming(type: PCSub.self)) // CHECK: true -print(genericAnyIs(type: PCSub.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PS.self)) // CHECK: false -print(genericAnyAsConditional(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: false -print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PE.self)) // CHECK: false -print(genericAnyAsConditional(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: false -print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PC.self)) // CHECK: true -print(genericAnyAsConditional(type: PC.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PCSub.self)) // CHECK: true -print(genericAnyAsConditional(type: PCSub.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true +print(#line, "casting types to protocol & AnyObject existentials:") +print(#line, nongenericAnyIsPAndAnyObjectType(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPAndAnyObjectType(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPAndAnyObjectType(type: PC.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPAndAnyObjectProtocol(type: PC.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PC.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPAndAnyObjectType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPAndAnyObjectProtocol(type: PCSub.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PCSub.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndAnyObjectType(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndAnyObjectType(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndAnyObjectType(type: PC.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyAsConditionalPAndAnyObjectProtocol(type: PC.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PC.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndAnyObjectType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsConditional(type: PCSub.self, to: (P & AnyObject).self, expected: false)) // CHECK: [[@LINE]] false // CHECK-LABEL: casting types to protocol & class existentials: -print("casting types to protocol & class existentials:") -print(nongenericAnyIsPAndPCSubConforming(type: PS.self)) // CHECK: false -print(genericAnyIs(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: false -print(nongenericAnyIsPAndPCSubConforming(type: PE.self)) // CHECK: false -print(genericAnyIs(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: false -//print(nongenericAnyIsPAndPCSubConforming(type: PC.self)) // CHECK-SR-11565: false -- FIXME: reenable this when SR-11565 is fixed -print(genericAnyIs(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: false -print(nongenericAnyIsPAndPCSubConforming(type: PCSub.self)) // CHECK: true -print(genericAnyIs(type: PCSub.self, to: (P & PCSub).self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyAsConditionalPAndPCSubConforming(type: PS.self)) // CHECK: false -print(genericAnyAsConditional(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: false -print(nongenericAnyAsConditionalPAndPCSubConforming(type: PE.self)) // CHECK: false -print(genericAnyAsConditional(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: false -//print(nongenericAnyAsConditionalPAndPCSubConforming(type: PC.self)) // CHECK-SR-11565: false -- FIXME: reenable this when SR-11565 is fixed -print(genericAnyAsConditional(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: false -print(nongenericAnyAsConditionalPAndPCSubConforming(type: PCSub.self)) // CHECK: true -print(genericAnyAsConditional(type: PCSub.self, to: (P & PCSub).self, expected: true)) // CHECK-ONONE: true +print(#line, "casting types to protocol & class existentials:") +print(#line, nongenericAnyIsPAndPCSubType(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPAndPCSubType(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +//print(#line, nongenericAnyIsPAndPCSubType(type: PC.self)) // CHECK-SR-11565: [[@LINE]] false -- FIXME: reenable this when SR-11565 is fixed +print(#line, genericAnyIs(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPAndPCSubType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyIs(type: PCSub.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndPCSubType(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndPCSubType(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, genericAnyAsConditional(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +// print(#line, nongenericAnyAsConditionalPAndPCSubType(type: PC.self)) // CHECK-SR-11565: [[@LINE]] false -- FIXME: reenable this when SR-11565 is fixed +print(#line, genericAnyAsConditional(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyAsConditionalPAndPCSubType(type: PCSub.self)) // CHECK: [[@LINE]] true +print(#line, genericAnyAsConditional(type: PCSub.self, to: (P & PCSub).self, expected: false)) // CHECK: [[@LINE]] false // CHECK-LABEL: type comparisons: -print("type comparisons:\n") -print(allMetasToAllMetas(Int.self, Int.self)) // CHECK: true -print(allMetasToAllMetas(Int.self, Float.self)) // CHECK: false -print(allMetasToAllMetas(C.self, C.self)) // CHECK: true -print(allMetasToAllMetas(D.self, C.self)) // CHECK: true -print(allMetasToAllMetas(C.self, D.self)) // CHECK: false -print(C.self is D.Type) // CHECK: false -print((D.self as C.Type) is D.Type) // CHECK: true +print(#line, "type comparisons:\n") +print(#line, allMetasToAllMetas(Int.self, Int.self)) // CHECK: [[@LINE]] true +print(#line, allMetasToAllMetas(Int.self, Float.self)) // CHECK: [[@LINE]] false +print(#line, allMetasToAllMetas(C.self, C.self)) // CHECK: [[@LINE]] true +print(#line, allMetasToAllMetas(D.self, C.self)) // CHECK: [[@LINE]] true +print(#line, allMetasToAllMetas(C.self, D.self)) // CHECK: [[@LINE]] false +print(#line, C.self is D.Type) // CHECK: [[@LINE]] false +print(#line, (D.self as C.Type) is D.Type) // CHECK: [[@LINE]] true let t: Any.Type = type(of: 1 as Any) -print(t is Int.Type) // CHECK: true -print(t is Float.Type) // CHECK: false -print(t is C.Type) // CHECK: false +print(#line, t is Int.Type) // CHECK: [[@LINE]] true +print(#line, t is Float.Type) // CHECK: [[@LINE]] false +print(#line, t is C.Type) // CHECK: [[@LINE]] false let u: Any.Type = type(of: (D() as Any)) -print(u is C.Type) // CHECK: true -print(u is D.Type) // CHECK: true -print(u is E.Type) // CHECK: false -print(u is Int.Type) // CHECK: false +print(#line, u is C.Type) // CHECK: [[@LINE]] true +print(#line, u is D.Type) // CHECK: [[@LINE]] true +print(#line, u is E.Type) // CHECK: [[@LINE]] false +print(#line, u is Int.Type) // CHECK: [[@LINE]] false // FIXME: Can't spell AnyObject.Protocol // CHECK-LABEL: AnyObject casts: -print("AnyObject casts:") -print(allToAll(C(), AnyObject.self)) // CHECK: true +print(#line, "AnyObject casts:") +print(#line, allToAll(C(), AnyObject.self)) // CHECK: [[@LINE]] true // On Darwin, the object will be the ObjC-runtime-class object; // out of Darwin, this should not succeed. -print(allToAll(type(of: C()), AnyObject.self)) +print(#line, allToAll(type(of: C()), AnyObject.self)) // CHECK-objc: true // CHECK-native: false // Bridging // NSNumber on Darwin, __SwiftValue on Linux. -print(allToAll(0, AnyObject.self)) // CHECK: true +print(#line, allToAll(0, AnyObject.self)) // CHECK: [[@LINE]] true // This will get bridged using __SwiftValue. struct NotBridged { var x: Int } -print(allToAll(NotBridged(x: 0), AnyObject.self)) // CHECK: true +print(#line, allToAll(NotBridged(x: 0), AnyObject.self)) // CHECK: [[@LINE]] true #if canImport(Foundation) // This requires Foundation (for NSCopying): -print(allToAll(NotBridged(x: 0), NSCopying.self)) // CHECK-objc: true +print(#line, allToAll(NotBridged(x: 0), NSCopying.self)) // CHECK-objc: [[@LINE]] true #endif // On Darwin, these casts fail (intentionally) even though __SwiftValue does // technically conform to these protocols through NSObject. // Off Darwin, it should not conform at all. -print(allToAll(NotBridged(x: 0), CustomStringConvertible.self)) // CHECK: false -print(allToAll(NotBridged(x: 0), (AnyObject & CustomStringConvertible).self)) // CHECK: false +print(#line, allToAll(NotBridged(x: 0), CustomStringConvertible.self)) // CHECK: [[@LINE]] false +print(#line, allToAll(NotBridged(x: 0), (AnyObject & CustomStringConvertible).self)) // CHECK: [[@LINE]] false #if canImport(Foundation) // This requires Foundation (for NSArray): @@ -362,5 +389,5 @@ func swiftOptimizesThisFunctionIncorrectly() -> Bool { } let result = swiftOptimizesThisFunctionIncorrectly() -print("Bridge cast result: \(result)") // CHECK-NEXT-objc: Bridge cast result: true +print(#line, "Bridge cast result: \(result)") // CHECK-NEXT-objc: Bridge cast result: true #endif diff --git a/test/Interpreter/generic_casts_objc.swift b/test/Interpreter/generic_casts_objc.swift index f7e9ccd80c01e..e9561d2e2e047 100644 --- a/test/Interpreter/generic_casts_objc.swift +++ b/test/Interpreter/generic_casts_objc.swift @@ -16,9 +16,12 @@ enum PE: P {} class PC: P, PObjC {} class PCSub: PC {} -func nongenericAnyIsPObjC(type: Any.Type) -> Bool { +func nongenericAnyIsPObjCType(type: Any.Type) -> Bool { return type is PObjC.Type } +func nongenericAnyIsPObjCProtocol(type: Any.Type) -> Bool { + return type is PObjC.Protocol +} func genericAnyIs(type: Any.Type, to: T.Type, expected: Bool) -> Bool { // If we're testing against a runtime that doesn't have the fix this tests, // just pretend we got it right. @@ -29,13 +32,23 @@ func genericAnyIs(type: Any.Type, to: T.Type, expected: Bool) -> Bool { } } -// CHECK-LABEL: casting types to ObjC protocols with generics: -print("casting types to ObjC protocols with generics:") -print(nongenericAnyIsPObjC(type: PS.self)) // CHECK: false -print(genericAnyIs(type: PS.self, to: PObjC.self, expected: false)) // CHECK: false -print(nongenericAnyIsPObjC(type: PE.self)) // CHECK: false -print(genericAnyIs(type: PE.self, to: PObjC.self, expected: false)) // CHECK: false -print(nongenericAnyIsPObjC(type: PC.self)) // CHECK: true -print(genericAnyIs(type: PC.self, to: PObjC.self, expected: true)) // CHECK-ONONE: true -print(nongenericAnyIsPObjC(type: PCSub.self)) // CHECK: true -print(genericAnyIs(type: PCSub.self, to: PObjC.self, expected: true)) // CHECK-ONONE: true +// CHECK-LABEL: casting types to ObjC protocol existential metatype: +print("casting types to ObjC protocol existential metatype:") +print(#line, nongenericAnyIsPObjCType(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPObjCType(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPObjCType(type: PC.self)) // CHECK: [[@LINE]] true +print(#line, nongenericAnyIsPObjCType(type: PCSub.self)) // CHECK: [[@LINE]] true + +// CHECK-LABEL: casting types to ObjC protocol metatype: +print("casting types to ObjC protocol metatype:") +print(#line, nongenericAnyIsPObjCProtocol(type: PS.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPObjCProtocol(type: PE.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPObjCProtocol(type: PC.self)) // CHECK: [[@LINE]] false +print(#line, nongenericAnyIsPObjCProtocol(type: PCSub.self)) // CHECK: [[@LINE]] false + +// CHECK-LABEL: casting types to ObjC protocol metatype via generic: +print("casting types to ObjC protocol metatype via generic:") +print(#line, genericAnyIs(type: PS.self, to: PObjC.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PE.self, to: PObjC.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PC.self, to: PObjC.self, expected: false)) // CHECK: [[@LINE]] false +print(#line, genericAnyIs(type: PCSub.self, to: PObjC.self, expected: false)) // CHECK: [[@LINE]] false diff --git a/test/stdlib/Casts.swift b/test/stdlib/Casts.swift index 95bfd67ceb3e1..e29d94c043f88 100644 --- a/test/stdlib/Casts.swift +++ b/test/stdlib/Casts.swift @@ -21,6 +21,8 @@ import StdlibUnittest import Foundation #endif +private func blackhole(_ t: T) {} + let CastsTests = TestSuite("Casts") // Test for SR-426: missing release for some types after failed conversion @@ -161,4 +163,63 @@ CastsTests.test("Dynamic casts of CF types to protocol existentials") } #endif +CastsTests.test("Any.Protocol") { + class C {} + struct S {} + func isAnyProtocol(_ type: T.Type) -> Bool { + let result = T.self is Any.Protocol + if result { + // `as!` should succeed if `is` does + blackhole(T.self as! Any.Protocol) + } + return result + } + func isAnyType(_ type: T.Type) -> Bool { + return T.self is Any.Type + } + func isType(_ type: T.Type, to: U.Type) -> Bool { + return T.self is U.Type + } + + expectTrue(Int.self is Any.Type) + expectNotNil(Int.self as? Any.Type) + expectTrue(isAnyType(Int.self)) + expectFalse(Int.self is Any.Protocol) + expectNil(Int.self as? Any.Protocol) + expectFalse(isAnyProtocol(Int.self)) + expectFalse(isType(Int.self, to: Any.self)) + + expectTrue(C.self is Any.Type) + expectNotNil(C.self as? Any.Type) + expectTrue(isAnyType(C.self)) + expectFalse(C.self is Any.Protocol) + expectNil(C.self as? Any.Protocol) + expectFalse(isAnyProtocol(C.self)) + expectFalse(isType(C.self, to: Any.self)) + + expectTrue(S.self is Any.Type) + expectNotNil(S.self as? Any.Type) + expectTrue(isAnyType(S.self)) + expectFalse(S.self is Any.Protocol) + expectNil(S.self as? Any.Protocol) + expectFalse(isAnyProtocol(S.self)) + expectFalse(isType(S.self, to: Any.self)) + + expectTrue(Any.self is Any.Type) + expectNotNil(Any.self as? Any.Type) + expectTrue(isAnyType(Any.self)) + expectTrue(Any.self is Any.Protocol) + expectNotNil(Any.self as? Any.Protocol) + expectTrue(isAnyProtocol(Any.self)) + expectTrue(isType(Any.self, to: Any.self)) + + expectTrue(Any?.self is Any.Type) + expectNotNil(Any?.self as? Any.Type) + expectTrue(isAnyType(Any?.self)) + expectFalse(Any?.self is Any.Protocol) + expectNil(Any?.self as? Any.Protocol) + expectFalse(isAnyProtocol(Any?.self)) + expectFalse(isType(Any?.self, to: Any.self)) +} + runAllTests() From 23e797422a4ff6379d7308d70ffeeb04d4e05146 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 2 Jul 2020 13:16:56 -0700 Subject: [PATCH 24/38] [CS] Avoid checking RHS of one-way constraint for reactivation Previously we would only gather one-way constraints if they were found through a type variable in their right hand side. However we would incorrectly check this against the type variable that we started the search at. This meant that if the constraint was found through a fixed binding, we would never return it, and could therefore fail to re-activate it, leaving it unsolved. Fix this issue by simply removing the check for the RHS, and letting the constraint system handle it instead. Resolves rdar://64890308. --- lib/Sema/ConstraintGraph.cpp | 23 +++++++-------------- test/Constraints/rdar64890308.swift | 31 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 test/Constraints/rdar64890308.swift diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 7037e3b9c32c7..b85048cf9d255 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -442,24 +442,15 @@ llvm::TinyPtrVector ConstraintGraph::gatherConstraints( llvm::function_ref acceptConstraintFn) { llvm::TinyPtrVector constraints; // Whether we should consider this constraint at all. - auto rep = CS.getRepresentative(typeVar); auto shouldConsiderConstraint = [&](Constraint *constraint) { - // For a one-way constraint, only consider it when the type variable - // is on the right-hand side of the the binding, and the left-hand side of - // the binding is one of the type variables currently under consideration. + // For a one-way constraint, only consider it when the left-hand side of + // the binding is one of the type variables currently under consideration, + // as only such constraints need solving for this component. Note that we + // don't perform any other filtering, as the constraint system should be + // responsible for checking any other conditions. if (constraint->isOneWayConstraint()) { - auto lhsTypeVar = - constraint->getFirstType()->castTo(); - if (!CS.isActiveTypeVariable(lhsTypeVar)) - return false; - - SmallVector rhsTypeVars; - constraint->getSecondType()->getTypeVariables(rhsTypeVars); - for (auto rhsTypeVar : rhsTypeVars) { - if (CS.getRepresentative(rhsTypeVar) == rep) - return true; - } - return false; + auto lhsTypeVar = constraint->getFirstType()->castTo(); + return CS.isActiveTypeVariable(lhsTypeVar); } return true; diff --git a/test/Constraints/rdar64890308.swift b/test/Constraints/rdar64890308.swift new file mode 100644 index 0000000000000..b745850068f5d --- /dev/null +++ b/test/Constraints/rdar64890308.swift @@ -0,0 +1,31 @@ +// RUN: %target-typecheck-verify-swift -parse-stdlib + +// rdar://64890308: Make sure we don't leave one-way constraints unsolved. + +import Swift + +@_functionBuilder +class ArrayBuilder { + static func buildBlock() -> [Element] { [] } + static func buildBlock(_ elt: Element) -> [Element] { [elt] } + static func buildBlock(_ elts: Element...) -> [Element] { elts } +} + +func foo(@ArrayBuilder fn: () -> [T]) {} + +// FIXME(SR-13132): This should compile. +foo { // expected-error {{type of expression is ambiguous without more context}} + "" +} + +struct S { + init(_: T.Type) {} + func overloaded() -> [T] { [] } + func overloaded(_ x: T) -> [T] { [x] } + func overloaded(_ x: T...) -> [T] { x } +} + +func bar(_ x: T, _ fn: (T, T.Type) -> [T]) {} +bar("") { x, ty in + (Builtin.one_way(S(ty).overloaded(x))) +} From 732698e268d8ba3cb532a2507022521be21bce3f Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Thu, 2 Jul 2020 13:21:38 -0700 Subject: [PATCH 25/38] Fix symbol visibility test for CentOS 7 --- test/stdlib/symbol-visibility-linux.test-sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/stdlib/symbol-visibility-linux.test-sh b/test/stdlib/symbol-visibility-linux.test-sh index b4e5a7df71e50..62be057f4d919 100644 --- a/test/stdlib/symbol-visibility-linux.test-sh +++ b/test/stdlib/symbol-visibility-linux.test-sh @@ -30,6 +30,7 @@ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA80_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA88_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA104_cEEEvv \ +// RUN: -e _ZN9__gnu_cxx12__to_xstringISscEET_PFiPT0_mPKS2_P13__va_list_tagEmS5_z \ // RUN: > %t/swiftCore-all.txt // RUN: %llvm-nm --defined-only --extern-only --no-weak %platform-dylib-dir/%target-library-name(swiftCore) > %t/swiftCore-no-weak.txt // RUN: diff -u %t/swiftCore-all.txt %t/swiftCore-no-weak.txt @@ -51,6 +52,7 @@ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA80_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA88_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA104_cEEEvv \ +// RUN: -e _ZN9__gnu_cxx12__to_xstringISscEET_PFiPT0_mPKS2_P13__va_list_tagEmS5_z \ // RUN: > %t/swiftRemoteMirror-all.txt // RUN: %llvm-nm --defined-only --extern-only --no-weak %platform-dylib-dir/%target-library-name(swiftRemoteMirror) > %t/swiftRemoteMirror-no-weak.txt // RUN: diff -u %t/swiftRemoteMirror-all.txt %t/swiftRemoteMirror-no-weak.txt From e02438c260083a0ab1239b88ba7d1127bf10c002 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 2 Jul 2020 14:05:29 -0700 Subject: [PATCH 26/38] test: repair the Windows builders after #32666 Adjust the test to fail properly --- test/Prototypes/BigInt.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Prototypes/BigInt.swift b/test/Prototypes/BigInt.swift index 7b8db3d181447..c84158a0d4b57 100644 --- a/test/Prototypes/BigInt.swift +++ b/test/Prototypes/BigInt.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // RUN: %empty-directory(%t) -// RUN: %target-build-swift -swift-version 4 -o %t/a.out %s +// RUN: not --crash %target-build-swift -swift-version 4 -o %t/a.out %s // RUN: %target-run %t/a.out // REQUIRES: executable_test // REQUIRES: CPU=x86_64 From e80b1882135720b50d6cc88c10da7c6db7ea204b Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Thu, 2 Jul 2020 14:44:37 -0700 Subject: [PATCH 27/38] [build] Add LTO flag to link options (#32683) Following the refactoring in #31612, LTO flag was only added to compile flags -- we need to pass this only when linking. Addresses rdar://65037856 --- cmake/modules/AddSwift.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index d2873612366d5..1d97caa2e09bd 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -121,6 +121,7 @@ function(_add_host_variant_c_compile_link_flags name) _compute_lto_flag("${SWIFT_TOOLS_ENABLE_LTO}" _lto_flag_out) if (_lto_flag_out) target_compile_options(${name} PRIVATE ${_lto_flag_out}) + target_link_options(${name} PRIVATE ${_lto_flag_out}) endif() endfunction() From 4b144873456fbacf0ed3b9dd716e2dae3dd0d9f8 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 1 Jul 2020 10:49:42 -0700 Subject: [PATCH 28/38] [NFC] [cxx-interop] Add SILGen tests for synthesized initializers. Adds a basic test that the synthesized C++ constructor was generated correctly. --- .../Interop/Cxx/class/Inputs/module.modulemap | 4 ++ .../class/Inputs/synthesized-initializers.h | 5 ++ .../Cxx/class/synthesized-initializers.swift | 46 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 test/Interop/Cxx/class/Inputs/synthesized-initializers.h create mode 100644 test/Interop/Cxx/class/synthesized-initializers.swift diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index cb61582d4b941..d4460da89db64 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -21,3 +21,7 @@ module MemberVariables { module ProtocolConformance { header "protocol-conformance.h" } + +module SynthesizedInitializers { + header "synthesized-initializers.h" +} diff --git a/test/Interop/Cxx/class/Inputs/synthesized-initializers.h b/test/Interop/Cxx/class/Inputs/synthesized-initializers.h new file mode 100644 index 0000000000000..da20bf036bd10 --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/synthesized-initializers.h @@ -0,0 +1,5 @@ +struct EmptyStruct {}; + +struct IntBox { + int x; +}; diff --git a/test/Interop/Cxx/class/synthesized-initializers.swift b/test/Interop/Cxx/class/synthesized-initializers.swift new file mode 100644 index 0000000000000..ea74f8ab4eecb --- /dev/null +++ b/test/Interop/Cxx/class/synthesized-initializers.swift @@ -0,0 +1,46 @@ +// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s + +import SynthesizedInitializers + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo11EmptyStructVABycfC : $@convention(method) (@thin EmptyStruct.Type) -> EmptyStruct +// CHECK: bb0(%{{[0-9]+}} : $@thin EmptyStruct.Type): +// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var EmptyStruct } +// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var EmptyStruct } +// CHECK-NEXT: [[PTR:%.*]] = project_box [[UNINIT]] : ${ var EmptyStruct }, 0 +// CHECK-NEXT: [[OBJ:%.*]] = builtin "zeroInitializer"() : $EmptyStruct +// CHECK-NEXT: [[PA:%.*]] = begin_access [modify] [unknown] [[PTR]] : $*EmptyStruct +// CHECK-NEXT: assign [[OBJ]] to [[PA]] +// CHECK-NEXT: end_access [[PA]] +// CHECK-NEXT: [[OUT:%.*]] = load [trivial] [[PTR]] +// CHECK-NEXT: destroy_value [[UNINIT]] +// CHECK-NEXT: return [[OUT]] +// CHECK-LABEL: end sil function '$sSo11EmptyStructVABycfC' +public func emptyTypeNoArgInit() { + let e = EmptyStruct() +} + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo6IntBoxVABycfC : $@convention(method) (@thin IntBox.Type) -> IntBox +// CHECK: bb0(%{{[0-9]+}} : $@thin IntBox.Type): +// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var IntBox } +// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var IntBox } +// CHECK-NEXT: [[PTR:%.*]] = project_box [[UNINIT]] : ${ var IntBox }, 0 +// CHECK-NEXT: [[OBJ:%.*]] = builtin "zeroInitializer"() : $IntBox +// CHECK-NEXT: [[PA:%.*]] = begin_access [modify] [unknown] [[PTR]] : $*IntBox +// CHECK-NEXT: assign [[OBJ]] to [[PA]] +// CHECK-NEXT: end_access [[PA]] +// CHECK-NEXT: [[OUT:%.*]] = load [trivial] [[PTR]] +// CHECK-NEXT: destroy_value [[UNINIT]] +// CHECK-NEXT: return [[OUT]] +// CHECK-LABEL: end sil function '$sSo6IntBoxVABycfC' +public func singleMemberTypeNoArgInit() { + let i = IntBox() +} + +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo6IntBoxV1xABs5Int32V_tcfC : $@convention(method) (Int32, @thin IntBox.Type) -> IntBox +// CHECK: bb0([[I:%[0-9]+]] : $Int32, %{{[0-9]+}} : $@thin IntBox.Type): +// CHECK-NEXT: [[S:%.*]] = struct $IntBox ([[I]] : $Int32) +// CHECK-NEXT: return [[S]] +// CHECK-LABEL: end sil function '$sSo6IntBoxV1xABs5Int32V_tcfC' +public func singleMemberTypeValueInit() { + let i = IntBox(x: 42) +} From 28fb66cbaf706d0bcdc17153e32ac0e8cef57755 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Thu, 2 Jul 2020 22:06:29 -0300 Subject: [PATCH 29/38] [SR-13088] Fix false positive downcast unrelated of types that cannot be statically known (#32592) * [TypeCheckConstraints] Adjusting cases where checked casts that cannot be determined statically were producing misleading warnings * [tests] Adding regression tests for SR-13088 * [TypeCheckConstraints] Adjusting comment and adding an extra test case for SR13035 * [TypeCheckConstraints] Fixing typos in comments * [AST] Moving implementation of isCollection from ConstraintSystem to AST TypeBase * [TypeCheckConstraints] Adjusting logic to verify specific conformance to stdlib collection type before emit an downcast warning * [TypeCheckConstraints] Creating new CheckedCastContextKind::CollectionElement to be able to verify special cases within typeCheckCheckedCast for collection elements * [TypeCheckConstraints] Adjusting logic around generic substitution to check both subtype and supertype * [Sema] Adding isKnownStdlibCollectionType and replacing all usages contraint system method * [TypeChecker] Reverting fixes around array element types * [TypeChecker] Abstract logic of check for conditional requirements on TypeChecker::couldDynamicallyConformToProtocol * [TypeChecker] Ajdustinc can conformDynamically conform and adjust review comments * [TypeChecker] Ajusting comments and fixing typos * [TypeChecker] Adjusting existential and archetype logic to check inside couldDynamicConform * [TypeChecker] Adjusting minor and adding existential check into couldDynamically conform. * [TypeChecker] Adjusting comments --- include/swift/AST/Types.h | 4 ++ lib/AST/Type.cpp | 10 +++++ lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSDiagnostics.cpp | 2 +- lib/Sema/CSDiagnostics.h | 5 --- lib/Sema/CSSimplify.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 12 ------ lib/Sema/ConstraintSystem.h | 3 -- lib/Sema/TypeCheckConstraints.cpp | 45 +++++++++---------- lib/Sema/TypeCheckProtocol.cpp | 34 +++++++++++++++ lib/Sema/TypeChecker.h | 11 +++++ test/Constraints/casts.swift | 72 ++++++++++++++++++++++++++++++- 12 files changed, 155 insertions(+), 47 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 023dbb1158a65..34d48b6e27622 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -785,6 +785,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// Check if this is a nominal type defined at the top level of the Swift module bool isStdlibType(); + + /// Check if this is either an Array, Set or Dictionary collection type defined + /// at the top level of the Swift module + bool isKnownStdlibCollectionType(); /// If this is a class type or a bound generic class type, returns the /// (possibly generic) class. diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index eca5a301f56b9..4c59a9f812fa5 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -760,6 +760,16 @@ bool TypeBase::isStdlibType() { return false; } +bool TypeBase::isKnownStdlibCollectionType() { + if (auto *structType = getAs()) { + auto &ctx = getASTContext(); + auto *decl = structType->getDecl(); + return decl == ctx.getArrayDecl() || decl == ctx.getDictionaryDecl() || + decl == ctx.getSetDecl(); + } + return false; +} + /// Remove argument labels from the function type. Type TypeBase::removeArgumentLabels(unsigned numArgumentLabels) { // If there is nothing to remove, don't. diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 10b3a0d932eaa..a3e7ce416b79e 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1035,7 +1035,7 @@ bool TypeVarBindingProducer::computeNext() { auto srcLocator = binding.getLocator(); if (srcLocator && srcLocator->isLastElement() && - !type->hasTypeVariable() && CS.isCollectionType(type)) { + !type->hasTypeVariable() && type->isKnownStdlibCollectionType()) { // If the type binding comes from the argument conversion, let's // instead of binding collection types directly, try to bind // using temporary type variables substituted for element diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index d249ca2f4d273..33551376f2312 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3376,7 +3376,7 @@ bool MissingMemberFailure::diagnoseInLiteralCollectionContext() const { auto parentType = getType(parentExpr); - if (!isCollectionType(parentType) && !parentType->is()) + if (!parentType->isKnownStdlibCollectionType() && !parentType->is()) return false; if (isa(parentExpr)) { diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 0c21f1de32997..16fa57735e282 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -208,11 +208,6 @@ class FailureDiagnostic { llvm::function_ref substitution = [](GenericTypeParamType *, Type) {}); - bool isCollectionType(Type type) const { - auto &cs = getConstraintSystem(); - return cs.isCollectionType(type); - } - bool isArrayType(Type type) const { auto &cs = getConstraintSystem(); return bool(cs.isArrayType(type)); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 6341a41ea7237..e8a6e7091d2ca 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3708,7 +3708,7 @@ bool ConstraintSystem::repairFailures( // func foo(_: [T]) {} // foo(1) // expected '[Int]', got 'Int' // ``` - if (isCollectionType(rhs)) { + if (rhs->isKnownStdlibCollectionType()) { std::function getArrayOrSetType = [&](Type type) -> Type { if (auto eltTy = isArrayType(type)) return getArrayOrSetType(*eltTy); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 940a4deb6923f..701aeac560264 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -878,18 +878,6 @@ Optional ConstraintSystem::isSetType(Type type) { return None; } -bool ConstraintSystem::isCollectionType(Type type) { - if (auto *structType = type->getAs()) { - auto &ctx = type->getASTContext(); - auto *decl = structType->getDecl(); - if (decl == ctx.getArrayDecl() || decl == ctx.getDictionaryDecl() || - decl == ctx.getSetDecl()) - return true; - } - - return false; -} - bool ConstraintSystem::isAnyHashableType(Type type) { if (auto st = type->getAs()) { auto &ctx = type->getASTContext(); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 348fe54a66baa..e6370f4b260ca 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3348,9 +3348,6 @@ class ConstraintSystem { /// element type of the set. static Optional isSetType(Type t); - /// Determine if the type in question is one of the known collection types. - static bool isCollectionType(Type t); - /// Determine if the type in question is AnyHashable. static bool isAnyHashableType(Type t); diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 86fe5774348aa..5152ff23e24c8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3357,7 +3357,14 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, const auto &fromElt = fromTuple->getElement(i); const auto &toElt = toTuple->getElement(i); - if (fromElt.getName() != toElt.getName()) + // We should only perform name validation if both element have a label, + // because unlabeled tuple elements can be converted to labeled ones + // e.g. + // + // let tup: (Any, Any) = (1, 1) + // _ = tup as! (a: Int, Int) + if ((!fromElt.getName().empty() && !toElt.getName().empty()) && + fromElt.getName() != toElt.getName()) return failed(); auto result = checkElementCast(fromElt.getType(), toElt.getType(), @@ -3527,8 +3534,9 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, (toType->isAnyObject() || fromType->isAnyObject())) return CheckedCastKind::ValueCast; - // A cast from an existential type to a concrete type does not succeed. For - // example: + // If we have a cast from an existential type to a concrete type that we + // statically know doesn't conform to the protocol, mark the cast as always + // failing. For example: // // struct S {} // enum FooError: Error { case bar } @@ -3541,19 +3549,10 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // } // } // - // Note: we relax the restriction if the type we're casting to is a - // non-final class because it's possible that we might have a subclass - // that conforms to the protocol. - if (fromExistential && !toExistential) { - if (auto NTD = toType->getAnyNominal()) { - if (!toType->is() || NTD->isFinal()) { - auto protocolDecl = - dyn_cast_or_null(fromType->getAnyNominal()); - if (protocolDecl && - !conformsToProtocol(toType, protocolDecl, dc)) { - return failed(); - } - } + if (auto *protocolDecl = + dyn_cast_or_null(fromType->getAnyNominal())) { + if (!couldDynamicallyConformToProtocol(toType, protocolDecl, dc)) { + return failed(); } } @@ -3616,10 +3615,12 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, return CheckedCastKind::ValueCast; } - // If the destination type can be a supertype of the source type, we are - // performing what looks like an upcast except it rebinds generic - // parameters. - if (fromType->isBindableTo(toType)) + // We perform an upcast while rebinding generic parameters if it's possible + // to substitute the generic arguments of the source type with the generic + // archetypes of the destination type. Or, if it's possible to substitute + // the generic arguments of the destination type with the generic archetypes + // of the source type, we perform a downcast instead. + if (toType->isBindableTo(fromType) || fromType->isBindableTo(toType)) return CheckedCastKind::ValueCast; // Objective-C metaclasses are subclasses of NSObject in the ObjC runtime, @@ -3636,8 +3637,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, } } - // We can conditionally cast from NSError to an Error-conforming - // type. This is handled in the runtime, so it doesn't need a special cast + // We can conditionally cast from NSError to an Error-conforming type. + // This is handled in the runtime, so it doesn't need a special cast // kind. if (Context.LangOpts.EnableObjCInterop) { auto nsObject = Context.getNSObjectType(); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 3ff1ad85ee0f9..f955e19b4d7a7 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4527,6 +4527,40 @@ TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, return lookupResult; } +bool +TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto, + DeclContext *DC) { + // An existential may have a concrete underlying type with protocol conformances + // we cannot know statically. + if (type->isExistentialType()) + return true; + + // A generic archetype may have protocol conformances we cannot know + // statically. + if (type->is()) + return true; + + // A non-final class might have a subclass that conforms to the protocol. + if (auto *classDecl = type->getClassOrBoundGenericClass()) { + if (!classDecl->isFinal()) + return true; + } + + ModuleDecl *M = DC->getParentModule(); + // For standard library collection types such as Array, Set or Dictionary + // which have custom casting machinery implemented in situations like: + // + // func encodable(_ value: Encodable) { + // _ = value as! [String : Encodable] + // } + // we are skipping checking conditional requirements using lookupConformance, + // as an intermediate collection cast can dynamically change if the conditions + // are met or not. + if (type->isKnownStdlibCollectionType()) + return !M->lookupConformance(type, Proto).isInvalid(); + return !conformsToProtocol(type, Proto, DC).isInvalid(); +} + /// Exposes TypeChecker functionality for querying protocol conformance. /// Returns a valid ProtocolConformanceRef only if all conditional /// requirements are successfully resolved. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index f4c1c6ca61cf4..bfe306236c17d 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -884,6 +884,17 @@ ProtocolConformanceRef conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, SourceLoc ComplainLoc = SourceLoc()); +/// This is similar to \c conformsToProtocol, but returns \c true for cases where +/// the type \p T could be dynamically cast to \p Proto protocol, such as a non-final +/// class where a subclass conforms to \p Proto. +/// +/// \param DC The context in which to check conformance. This affects, for +/// example, extension visibility. +/// +/// +/// \returns True if \p T conforms to the protocol \p Proto, false otherwise. +bool couldDynamicallyConformToProtocol(Type T, ProtocolDecl *Proto, + DeclContext *DC); /// Completely check the given conformance. void checkConformance(NormalProtocolConformance *conformance); diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index 0c84b6cbf1b70..02d6a29224ac0 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -235,8 +235,8 @@ func test_tuple_casts_no_warn() { _ = arr as! [(Foo, Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(Foo, Foo, Foo)]' always fails}} _ = tup as! (Foo, Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(Foo, Foo, Foo)' always fails}} - _ = arr as! [(a: Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(a: Foo, Foo)]' always fails}} - _ = tup as! (a: Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(a: Foo, Foo)' always fails}} + _ = arr as! [(a: Foo, Foo)] // Ok + _ = tup as! (a: Foo, Foo) // Ok } infix operator ^^^ @@ -335,3 +335,71 @@ func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [Strin // The array can also be inferred to be [Any]. _ = ([] ?? []) as Array // expected-warning {{left side of nil coalescing operator '??' has non-optional type '[Any]', so the right side is never used}} } + +// SR-13088 +protocol JSON { } +protocol JSONLeaf: JSON {} +extension Int: JSONLeaf { } +extension Array: JSON where Element: JSON { } + +protocol SR13035Error: Error {} +class ChildError: SR13035Error {} + +protocol AnyC { + func foo() +} + +protocol AnyEvent {} + +protocol A { + associatedtype C: AnyC +} + +protocol EventA: A { + associatedtype Event +} + +typealias Container + = (event: Namespace.Event, c: Namespace.C) where Namespace: EventA + +enum ConcreteA: EventA { + struct C: AnyC { + func foo() {} + } + + enum Event: AnyEvent { + case test + } +} + +func tests_SR13088_false_positive_always_fail_casts() { + // SR-13081 + let x: JSON = [4] // [4] + _ = x as? [Any] // Ok + + // SR-13035 + func SR13035(_ child: Result, _: Result) { + let _ = child as? Result // Ok + } + + func SR13035_1(_ child: Result, parent: Result) { + _ = child as? Result // Ok + _ = parent as? Result // OK + } + + // SR-11434 and SR-12321 + func encodable(_ value: Encodable) { + _ = value as! [String : Encodable] // Ok + _ = value as? [String: Encodable] // Ok + } + + // SR-13025 + func coordinate(_ event: AnyEvent, from c: AnyC) { + switch (event, c) { + case let container as Container: // OK + container.c.foo() + default: + break + } + } +} From 26358c4588dae074521f73cf23f65f8650f564cc Mon Sep 17 00:00:00 2001 From: Michael Forster Date: Fri, 3 Jul 2020 11:06:22 +0200 Subject: [PATCH 30/38] Import member operator functions as static members (#32293) This adds support to `ClangImporter` to import C++ member function operators as static methods into Swift, which is part of SR-12748. The left-hand-side operand, which gets passed as the `this` pointer to the C++ function is represented as an additional first parameter in the Swift method. It gets mapped back in SILGen. Two of the tests are disabled on Windows because we can't yet call member functions correctly on Windows (SR-13129). --- include/swift/SIL/AbstractionPattern.h | 91 +++++++++++++++++-- lib/ClangImporter/ClangImporter.cpp | 12 +++ lib/ClangImporter/ImportDecl.cpp | 12 ++- lib/ClangImporter/ImportName.cpp | 9 +- lib/ClangImporter/ImportType.cpp | 26 ++++++ lib/ClangImporter/SwiftLookupTable.cpp | 30 ++++++ lib/ClangImporter/SwiftLookupTable.h | 4 + lib/SIL/IR/AbstractionPattern.cpp | 71 ++++++++++++++- lib/SIL/IR/SILFunctionType.cpp | 29 +++++- .../Cxx/operators/Inputs/member-inline.h | 9 ++ .../Cxx/operators/Inputs/module.modulemap | 4 + .../Cxx/operators/member-inline-irgen.swift | 11 +++ .../member-inline-module-interface.swift | 5 + .../Cxx/operators/member-inline-silgen.swift | 14 +++ .../operators/member-inline-typechecker.swift | 8 ++ .../Interop/Cxx/operators/member-inline.swift | 22 +++++ 16 files changed, 341 insertions(+), 16 deletions(-) create mode 100644 test/Interop/Cxx/operators/Inputs/member-inline.h create mode 100644 test/Interop/Cxx/operators/member-inline-irgen.swift create mode 100644 test/Interop/Cxx/operators/member-inline-module-interface.swift create mode 100644 test/Interop/Cxx/operators/member-inline-silgen.swift create mode 100644 test/Interop/Cxx/operators/member-inline-typechecker.swift create mode 100644 test/Interop/Cxx/operators/member-inline.swift diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 22f550d39a741..ca64fd6022f14 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -179,15 +179,26 @@ class AbstractionPattern { /// type. ObjCMethod is valid. OtherData is an encoded foreign /// error index. ObjCMethodType, - /// The uncurried imported type of a C++ method. OrigType is valid and is a - /// function type. CXXMethod is valid. + /// The uncurried imported type of a C++ non-operator non-static member + /// function. OrigType is valid and is a function type. CXXMethod is valid. CXXMethodType, - /// The curried imported type of a C++ method. OrigType is valid and is a - /// function type. CXXMethod is valid. + /// The curried imported type of a C++ non-operator non-static member + /// function. OrigType is valid and is a function type. CXXMethod is valid. CurriedCXXMethodType, - /// The partially-applied curried imported type of a C++ method. OrigType is - /// valid and is a function type. CXXMethod is valid. + /// The partially-applied curried imported type of a C++ non-operator + /// non-static member function. OrigType is valid and is a function type. + /// CXXMethod is valid. PartialCurriedCXXMethodType, + /// The uncurried imported type of a C++ operator non-static member + /// function. OrigType is valid and is a function type. CXXMethod is valid. + CXXOperatorMethodType, + /// The curried imported type of a C++ operator non-static member function. + /// OrigType is valid and is a function type. CXXMethod is valid. + CurriedCXXOperatorMethodType, + /// The partially-applied curried imported type of a C++ operator non-static + /// member function. OrigType is valid and is a function type. CXXMethod is + /// valid. + PartialCurriedCXXOperatorMethodType, /// A Swift function whose parameters and results are opaque. This is /// like `AP::Type((T) -> T)`, except that the number of parameters is /// unspecified. @@ -341,6 +352,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: return true; default: @@ -465,6 +479,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: return true; case Kind::Invalid: case Kind::Opaque: @@ -541,6 +558,10 @@ class AbstractionPattern { static AbstractionPattern getCurriedCXXMethod(CanType origType, const AbstractFunctionDecl *function); + static AbstractionPattern + getCurriedCXXOperatorMethod(CanType origType, + const AbstractFunctionDecl *function); + /// Return an abstraction pattern for the uncurried type of a C++ method. /// /// For example, if the original function is: @@ -556,6 +577,15 @@ class AbstractionPattern { return pattern; } + static AbstractionPattern + getCXXOperatorMethod(CanType origType, const clang::CXXMethodDecl *method) { + assert(isa(origType)); + AbstractionPattern pattern; + pattern.initCXXMethod(nullptr, origType, method, + Kind::CXXOperatorMethodType); + return pattern; + } + /// Return an abstraction pattern for the curried type of a C++ method. /// /// For example, if the original function is: @@ -572,6 +602,16 @@ class AbstractionPattern { return pattern; } + static AbstractionPattern + getCurriedCXXOperatorMethod(CanType origType, + const clang::CXXMethodDecl *method) { + assert(isa(origType)); + AbstractionPattern pattern; + pattern.initCXXMethod(nullptr, origType, method, + Kind::CurriedCXXOperatorMethodType); + return pattern; + } + /// For a C-function-as-method pattern, /// get the index of the C function parameter that was imported as the /// `self` parameter of the imported method, or None if this is a static @@ -678,6 +718,17 @@ class AbstractionPattern { return pattern; } + static AbstractionPattern + getPartialCurriedCXXOperatorMethod(CanGenericSignature signature, + CanType origType, + const clang::CXXMethodDecl *method) { + assert(isa(origType)); + AbstractionPattern pattern; + pattern.initCXXMethod(signature, origType, method, + Kind::PartialCurriedCXXOperatorMethodType); + return pattern; + } + public: /// Return an abstraction pattern for the type of an Objective-C method. static AbstractionPattern @@ -813,6 +864,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::Type: case Kind::Discard: return OrigType; @@ -849,6 +903,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::Type: case Kind::Discard: assert(signature || !type->hasTypeParameter()); @@ -886,6 +943,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: return true; } llvm_unreachable("bad kind"); @@ -923,7 +983,9 @@ class AbstractionPattern { /// If so, it is legal to call getCXXMethod(). bool isCXXMethod() const { return (getKind() == Kind::CXXMethodType || - getKind() == Kind::CurriedCXXMethodType); + getKind() == Kind::CurriedCXXMethodType || + getKind() == Kind::CXXOperatorMethodType || + getKind() == Kind::CurriedCXXOperatorMethodType); } const clang::CXXMethodDecl *getCXXMethod() const { @@ -958,6 +1020,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: return false; @@ -994,6 +1059,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::Type: case Kind::Discard: return dyn_cast(getType()); @@ -1022,6 +1090,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: // We assume that the Clang type might provide additional structure. @@ -1051,6 +1122,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: return false; @@ -1078,6 +1152,9 @@ class AbstractionPattern { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: llvm_unreachable("pattern is not a tuple"); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index e9b2e9b6fb8e6..f18f8fcd9b967 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3591,6 +3591,18 @@ void ClangImporter::Implementation::lookupValue( auto &clangCtx = getClangASTContext(); auto clangTU = clangCtx.getTranslationUnitDecl(); + // For operators we have to look up static member functions in addition to the + // top-level function lookup below. + if (name.isOperator()) { + for (auto entry : table.lookupMemberOperators(name.getBaseName())) { + if (isVisibleClangEntry(entry)) { + if (auto decl = dyn_cast( + importDeclReal(entry->getMostRecentDecl(), CurrentVersion))) + consumer.foundDecl(decl, DeclVisibilityKind::VisibleAtTopLevel); + } + } + } + for (auto entry : table.lookup(name.getBaseName(), clangTU)) { // If the entry is not visible, skip it. if (!isVisibleClangEntry(entry)) continue; diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index c2ef408111f93..bddc7536ba997 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3801,14 +3801,20 @@ namespace { decl->isVariadic(), isInSystemModule(dc), name, bodyParams); if (auto *mdecl = dyn_cast(decl)) { - if (!mdecl->isStatic()) { + if (mdecl->isStatic() || + // C++ operators that are implemented as non-static member + // functions get imported into Swift as static member functions + // that use an additional parameter for the left-hand side operand + // instead of the receiver object. + mdecl->getDeclName().getNameKind() == + clang::DeclarationName::CXXOperatorName) { + selfIdx = None; + } else { selfIdx = 0; // Workaround until proper const support is handled: Force // everything to be mutating. This implicitly makes the parameter // indirect. selfIsInOut = true; - } else { - selfIdx = None; } } } diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 3641f2b40236d..1611d20507369 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -32,6 +32,7 @@ #include "swift/ClangImporter/ClangImporterOptions.h" #include "swift/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/Module.h" #include "clang/Basic/OperatorKinds.h" @@ -1439,7 +1440,13 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (auto FD = dyn_cast(D)) { baseName = clang::getOperatorSpelling(op); isFunction = true; - argumentNames.resize(FD->param_size()); + argumentNames.resize( + FD->param_size() + + // C++ operators that are implemented as non-static member functions + // get imported into Swift as static member functions that use an + // additional parameter for the left-hand side operand instead of + // the receiver object. + (isa(D) ? 1 : 0)); } else { // This can happen for example for templated operators functions. // We don't support those, yet. diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 87c5518c3e1e3..91c9ead25f758 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -27,11 +27,13 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Parse/Token.h" #include "swift/Strings.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/TypeVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Lex/Preprocessor.h" @@ -1708,6 +1710,30 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( unsigned index = 0; SmallBitVector nonNullArgs = getNonNullArgs(clangDecl, params); + // C++ operators that are implemented as non-static member functions get + // imported into Swift as static methods that have an additional + // parameter for the left-hand side operand instead of the receiver object. + if (auto CMD = dyn_cast(clangDecl)) { + if (clangDecl->isOverloadedOperator()) { + auto param = new (SwiftContext) + ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), + SwiftContext.getIdentifier("lhs"), dc); + + auto parent = CMD->getParent(); + auto parentType = importType( + parent->getASTContext().getRecordType(parent), + ImportTypeKind::Parameter, allowNSUIntegerAsInt, Bridgeability::None); + + param->setInterfaceType(parentType.getType()); + + // Workaround until proper const support is handled: Force everything to + // be mutating. This implicitly makes the parameter indirect. + param->setSpecifier(ParamSpecifier::InOut); + + parameters.push_back(param); + } + } + for (auto param : params) { auto paramTy = param->getType(); if (paramTy->isVoidType()) { diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index fa034ee544627..74a64dcb025d5 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -20,6 +20,7 @@ #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/Version.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" @@ -795,6 +796,35 @@ SwiftLookupTable::lookupObjCMembers(SerializedSwiftName baseName) { return result; } +SmallVector +SwiftLookupTable::lookupMemberOperators(SerializedSwiftName baseName) { + SmallVector result; + + // Find the lookup table entry for this base name. + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); + if (known == LookupTable.end()) + return result; + + // Walk each of the entries. + for (auto &entry : known->second) { + // We're only looking for C++ operators + if (entry.Context.first != ContextKind::Tag) { + continue; + } + + // Map each of the declarations. + for (auto &stored : entry.DeclsOrMacros) { + assert(isDeclEntry(stored) && "Not a declaration?"); + result.push_back(mapStoredDecl(stored)); + } + } + + return result; +} + ArrayRef SwiftLookupTable::categories() { if (!Categories.empty() || !Reader) return Categories; diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index fe980625c0ff8..0a5afe26e0d4d 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -528,6 +528,10 @@ class SwiftLookupTable { SmallVector lookupObjCMembers(SerializedSwiftName baseName); + /// Lookup member operators with the given base name, regardless of context. + SmallVector + lookupMemberOperators(SerializedSwiftName baseName); + /// Retrieve the set of Objective-C categories and extensions. ArrayRef categories(); diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 4e58d38f021bf..98b35f470a549 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -168,6 +168,12 @@ AbstractionPattern::getCurriedCXXMethod(CanType origType, return getCurriedCXXMethod(origType, clangMethod); } +AbstractionPattern AbstractionPattern::getCurriedCXXOperatorMethod( + CanType origType, const AbstractFunctionDecl *function) { + auto clangMethod = cast(function->getClangDecl()); + return getCurriedCXXOperatorMethod(origType, clangMethod); +} + AbstractionPattern AbstractionPattern::getOptional(AbstractionPattern object) { switch (object.getKind()) { @@ -183,6 +189,9 @@ AbstractionPattern::getOptional(AbstractionPattern object) { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: llvm_unreachable("cannot add optionality to non-type abstraction"); @@ -290,6 +299,9 @@ bool AbstractionPattern::matchesTuple(CanTupleType substType) { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: return false; @@ -364,6 +376,9 @@ AbstractionPattern::getTupleElementType(unsigned index) const { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: llvm_unreachable("function types are not tuples"); @@ -470,6 +485,8 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const { } case Kind::CXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: return AbstractionPattern(getGenericSignatureForFunctionComponent(), getResultType(getType()), getCXXMethod()->getReturnType().getTypePtr()); @@ -488,6 +505,10 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const { case Kind::CurriedCXXMethodType: return getPartialCurriedCXXMethod(getGenericSignatureForFunctionComponent(), getResultType(getType()), getCXXMethod()); + case Kind::CurriedCXXOperatorMethodType: + return getPartialCurriedCXXOperatorMethod( + getGenericSignatureForFunctionComponent(), getResultType(getType()), + getCXXMethod()); case Kind::PartialCurriedObjCMethodType: case Kind::ObjCMethodType: return AbstractionPattern(getGenericSignatureForFunctionComponent(), @@ -525,6 +546,16 @@ AbstractionPattern::getFunctionParamType(unsigned index) const { assert(params.size() == 1); return getCXXMethodSelfPattern(params[0].getParameterType()); } + case Kind::CurriedCXXOperatorMethodType: { + auto params = cast(getType()).getParams(); + assert(params.size() == 1); + + // The formal metatype parameter to a C++ member operator function imported + // as a static method is dropped on the floor. Leave it untransformed. + return AbstractionPattern::getDiscard( + getGenericSignatureForFunctionComponent(), + params[0].getParameterType()); + } case Kind::CFunctionAsMethodType: case Kind::PartialCurriedCFunctionAsMethodType: { auto params = cast(getType()).getParams(); @@ -579,6 +610,26 @@ AbstractionPattern::getFunctionParamType(unsigned index) const { paramType, getClangFunctionParameterType(methodType, index)); } + case Kind::CXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: { + auto params = cast(getType()).getParams(); + auto paramType = params[index].getParameterType(); + + // The first parameter holds the left-hand-side operand, which gets passed + // to the C++ function as the this pointer. + if (index == 0) + return getCXXMethodSelfPattern(paramType); + + // A parameter of type () does not correspond to a Clang parameter. + if (paramType->isVoid()) + return AbstractionPattern(paramType); + + // Otherwise, we're talking about the formal parameter clause. + auto methodType = getCXXMethod()->getType().getTypePtr(); + return AbstractionPattern( + getGenericSignatureForFunctionComponent(), paramType, + getClangFunctionParameterType(methodType, index - 1)); + } case Kind::CurriedObjCMethodType: { auto params = cast(getType()).getParams(); assert(params.size() == 1); @@ -660,6 +711,9 @@ AbstractionPattern AbstractionPattern::getOptionalObjectType() const { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::Tuple: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: @@ -703,6 +757,9 @@ AbstractionPattern AbstractionPattern::getReferenceStorageReferentType() const { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::Tuple: case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: @@ -795,7 +852,16 @@ void AbstractionPattern::print(raw_ostream &out) const { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: - out << (getKind() == Kind::CXXMethodType + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: + out << (getKind() == Kind::CXXOperatorMethodType + ? "AP::CXXOperatorMethodType(" : + getKind() == Kind::CurriedCXXOperatorMethodType + ? "AP::CurriedCXXOperatorMethodType(" : + getKind() == Kind::PartialCurriedCXXOperatorMethodType + ? "AP::PartialCurriedCXXOperatorMethodType(" : + getKind() == Kind::CXXMethodType ? "AP::CXXMethodType(" : getKind() == Kind::CurriedCXXMethodType ? "AP::CurriedCXXMethodType(" @@ -927,6 +993,9 @@ const { case Kind::CXXMethodType: case Kind::CurriedCXXMethodType: case Kind::PartialCurriedCXXMethodType: + case Kind::CXXOperatorMethodType: + case Kind::CurriedCXXOperatorMethodType: + case Kind::PartialCurriedCXXOperatorMethodType: case Kind::ClangType: case Kind::Type: case Kind::Discard: diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 0091d35779c08..9994b4e74be80 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2783,7 +2783,8 @@ getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, } if (auto method = dyn_cast(clangDecl)) { - AbstractionPattern origPattern = + AbstractionPattern origPattern = method->isOverloadedOperator() ? + AbstractionPattern::getCXXOperatorMethod(origType, method): AbstractionPattern::getCXXMethod(origType, method); auto conventions = CXXMethodConventions(method); return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, @@ -4039,7 +4040,10 @@ getAbstractionPatternForConstant(ASTContext &ctx, SILDeclRef constant, assert(numParameterLists == 2); if (auto method = dyn_cast(clangDecl)) { // C++ method. - return AbstractionPattern::getCurriedCXXMethod(fnType, bridgedFn); + return method->isOverloadedOperator() + ? AbstractionPattern::getCurriedCXXOperatorMethod(fnType, + bridgedFn) + : AbstractionPattern::getCurriedCXXMethod(fnType, bridgedFn); } else { // C function imported as a method. return AbstractionPattern::getCurriedCFunctionAsMethod(fnType, @@ -4123,8 +4127,25 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, } auto partialFnPattern = bridgingFnPattern.getFunctionResultType(); - getBridgedParams(rep, partialFnPattern, methodParams, bridgedParams, - bridging); + for (unsigned i : indices(methodParams)) { + // C++ operators that are implemented as non-static member functions get + // imported into Swift as static methods that have an additional + // parameter for the left-hand-side operand instead of the receiver + // object. These are inout parameters and don't get bridged. + // TODO: Undo this if we stop using inout. + if (auto method = dyn_cast_or_null( + constant.getDecl()->getClangDecl())) { + if (i==0 && method->isOverloadedOperator()) { + bridgedParams.push_back(methodParams[0]); + continue; + } + } + + auto paramPattern = partialFnPattern.getFunctionParamType(i); + auto bridgedParam = + getBridgedParam(rep, paramPattern, methodParams[i], bridging); + bridgedParams.push_back(bridgedParam); + } bridgedResultType = getBridgedResultType(rep, diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h new file mode 100644 index 0000000000000..8204aa0644848 --- /dev/null +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -0,0 +1,9 @@ +#ifndef TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H +#define TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H + +struct IntBox { + int value; + IntBox operator+(IntBox rhs) { return IntBox{.value = value + rhs.value}; } +}; + +#endif diff --git a/test/Interop/Cxx/operators/Inputs/module.modulemap b/test/Interop/Cxx/operators/Inputs/module.modulemap index 2cc3fa14cd834..a811dbe61fa0e 100644 --- a/test/Interop/Cxx/operators/Inputs/module.modulemap +++ b/test/Interop/Cxx/operators/Inputs/module.modulemap @@ -1,3 +1,7 @@ +module MemberInline { + header "member-inline.h" +} + module NonMemberInline { header "non-member-inline.h" } diff --git a/test/Interop/Cxx/operators/member-inline-irgen.swift b/test/Interop/Cxx/operators/member-inline-irgen.swift new file mode 100644 index 0000000000000..3d5f445d07864 --- /dev/null +++ b/test/Interop/Cxx/operators/member-inline-irgen.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s +// +// We can't yet call member functions correctly on Windows. +// XFAIL: OS=windows-msvc + +import MemberInline + +public func add(_ lhs: inout IntBox, _ rhs: IntBox) -> IntBox { lhs + rhs } + +// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN6IntBoxplES_|"\?\?HIntBox@@QEAA\?AU0@U0@@Z")]](%struct.IntBox* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.IntBox\* byval align 4}} {{%[0-9]+}}) +// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.IntBox* %this, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.IntBox\* byval\(%struct.IntBox\) align 4 %rhs}}) diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift new file mode 100644 index 0000000000000..aef362f01c207 --- /dev/null +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -0,0 +1,5 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: struct IntBox { +// CHECK: static func + (lhs: inout IntBox, rhs: IntBox) -> IntBox +// CHECK: } diff --git a/test/Interop/Cxx/operators/member-inline-silgen.swift b/test/Interop/Cxx/operators/member-inline-silgen.swift new file mode 100644 index 0000000000000..96d89a4a4fa86 --- /dev/null +++ b/test/Interop/Cxx/operators/member-inline-silgen.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import MemberInline + +public func add(_ lhs: inout IntBox, _ rhs: IntBox) -> IntBox { lhs + rhs } + +// CHECK: bb0([[SELF:%.*]] : $*IntBox, [[RHS:%.*]] : $IntBox): + +// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*IntBox +// CHECK: [[OP:%.*]] = function_ref [[NAME:@(_ZN6IntBoxplES_|\?\?HIntBox@@QEAA\?AU0@U0@@Z)]] : $@convention(c) (@inout IntBox, IntBox) -> IntBox +// CHECK: apply [[OP]]([[SELFACCESS]], [[RHS]]) : $@convention(c) (@inout IntBox, IntBox) -> IntBox +// CHECK: end_access [[SELFACCESS]] : $*IntBox + +// CHECK: sil [clang IntBox."+"] [[NAME]] : $@convention(c) (@inout IntBox, IntBox) -> IntBox diff --git a/test/Interop/Cxx/operators/member-inline-typechecker.swift b/test/Interop/Cxx/operators/member-inline-typechecker.swift new file mode 100644 index 0000000000000..635875a05c71b --- /dev/null +++ b/test/Interop/Cxx/operators/member-inline-typechecker.swift @@ -0,0 +1,8 @@ +// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-cxx-interop + +import MemberInline + +var lhs = IntBox(value: 42) +let rhs = IntBox(value: 23) + +let resultPlus = lhs + rhs diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift new file mode 100644 index 0000000000000..d18ddbcfbac69 --- /dev/null +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -0,0 +1,22 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop) +// +// REQUIRES: executable_test +// +// We can't yet call member functions correctly on Windows. +// XFAIL: OS=windows-msvc + +import MemberInline +import StdlibUnittest + +var OperatorsTestSuite = TestSuite("Operators") + +OperatorsTestSuite.test("plus") { + var lhs = IntBox(value: 42) + let rhs = IntBox(value: 23) + + let result = lhs + rhs + + expectEqual(65, result.value) +} + +runAllTests() From a443327efe6d8013de38ed3f0e4910d7de4b2cf2 Mon Sep 17 00:00:00 2001 From: Michael Forster Date: Fri, 3 Jul 2020 11:16:06 +0200 Subject: [PATCH 31/38] Add bug reference to XFAIL operator tests --- test/Interop/Cxx/operators/member-inline-irgen.swift | 2 +- test/Interop/Cxx/operators/member-inline.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Interop/Cxx/operators/member-inline-irgen.swift b/test/Interop/Cxx/operators/member-inline-irgen.swift index 3d5f445d07864..797682073a393 100644 --- a/test/Interop/Cxx/operators/member-inline-irgen.swift +++ b/test/Interop/Cxx/operators/member-inline-irgen.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s // -// We can't yet call member functions correctly on Windows. +// We can't yet call member functions correctly on Windows (SR-13129). // XFAIL: OS=windows-msvc import MemberInline diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index d18ddbcfbac69..0313d849da70b 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -2,7 +2,7 @@ // // REQUIRES: executable_test // -// We can't yet call member functions correctly on Windows. +// We can't yet call member functions correctly on Windows (SR-13129). // XFAIL: OS=windows-msvc import MemberInline From 1f9da381dcdef98df2a7313d8729750cbac54f45 Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Fri, 3 Jul 2020 09:37:31 -0400 Subject: [PATCH 32/38] Update README for new target names --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 166feb1aa67fc..f1d8839474d15 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ various products. These incremental builds are a big timesaver when developing and debugging. cd ${SWIFT_BUILD_DIR} - ninja swift + ninja compiler This will build the Swift compiler, but will not rebuild the standard library or any other target. Building the `swift-stdlib` target as an additional layer of @@ -250,6 +250,10 @@ library, run: ninja swift-stdlib +To build just the compiler frontend, run: + + ninja swift-frontend + It is always a good idea to do a full build after using `update-checkout`. #### Using Xcode @@ -258,7 +262,7 @@ To open the Swift project in Xcode, open `${SWIFT_BUILD_DIR}/Swift.xcodeproj`. It will auto-create a *lot* of schemes for all of the available targets. A common debug flow would involve: - - Select the 'swift' scheme. + - Select the 'swift-frontend' scheme. - Pull up the scheme editor (⌘⇧<). - Select the 'Arguments' tab and click the '+'. - Add the command line options. From 79913b95959a35011ee1c887bceef1c5f6bc454d Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Jul 2020 15:29:01 +0200 Subject: [PATCH 33/38] Don't "finalize" the empty-array singleton. Array literals only need to be finalized, if the array is really allocated. In case of zero elements, no allocation is done, but the empty-array singleton is used. "Finalization" means to emit an end_cow_mutation instruction on the array. As the empty-array singleton is a read-only and shared object, it's not legal to do a end_cow_mutation on it. --- lib/SILGen/SILGenExpr.cpp | 13 +++++++++++-- lib/SILGen/Varargs.h | 3 ++- lib/SILOptimizer/Mandatory/OSLogOptimization.cpp | 16 +++++++++------- test/SILGen/keypaths.swift | 4 +--- test/SILGen/scalar_to_tuple_args.swift | 4 +--- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index bdc577179ceae..dfd28c4511918 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2109,7 +2109,8 @@ VarargsInfo Lowering::emitBeginVarargs(SILGenFunction &SGF, SILLocation loc, } ManagedValue Lowering::emitEndVarargs(SILGenFunction &SGF, SILLocation loc, - VarargsInfo &&varargs) { + VarargsInfo &&varargs, + unsigned numElements) { // Kill the abort cleanup. SGF.Cleanups.setCleanupState(varargs.getAbortCleanup(), CleanupState::Dead); @@ -2118,6 +2119,14 @@ ManagedValue Lowering::emitEndVarargs(SILGenFunction &SGF, SILLocation loc, if (array.hasCleanup()) SGF.Cleanups.setCleanupState(array.getCleanup(), CleanupState::Active); + // Array literals only need to be finalized, if the array is really allocated. + // In case of zero elements, no allocation is done, but the empty-array + // singleton is used. "Finalization" means to emit an end_cow_mutation + // instruction on the array. As the empty-array singleton is a read-only and + // shared object, it's not legal to do a end_cow_mutation on it. + if (numElements == 0) + return array; + return SGF.emitUninitializedArrayFinalization(loc, std::move(array)); } @@ -3870,7 +3879,7 @@ RValue RValueEmitter::visitCollectionExpr(CollectionExpr *E, SGFContext C) { SGF.Cleanups.setCleanupState(destCleanup, CleanupState::Dead); RValue array(SGF, loc, arrayType, - emitEndVarargs(SGF, loc, std::move(varargsInfo))); + emitEndVarargs(SGF, loc, std::move(varargsInfo), E->getNumElements())); array = scope.popPreservingValue(std::move(array)); diff --git a/lib/SILGen/Varargs.h b/lib/SILGen/Varargs.h index 334b2baf9587c..0497c632101d2 100644 --- a/lib/SILGen/Varargs.h +++ b/lib/SILGen/Varargs.h @@ -68,7 +68,8 @@ VarargsInfo emitBeginVarargs(SILGenFunction &SGF, SILLocation loc, /// Successfully end a varargs emission sequence. ManagedValue emitEndVarargs(SILGenFunction &SGF, SILLocation loc, - VarargsInfo &&varargs); + VarargsInfo &&varargs, + unsigned numElements); } // end namespace Lowering } // end namespace swift diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 9b0ded1b091ed..7f5551a01686c 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -540,13 +540,15 @@ static SILValue emitCodeForConstantArray(ArrayRef elements, assert(arrayAllocateFun); SILFunction *arrayFinalizeFun = nullptr; - if (FuncDecl *arrayFinalizeDecl = astContext.getFinalizeUninitializedArray()) { - std::string finalizeMangledName = - SILDeclRef(arrayFinalizeDecl, SILDeclRef::Kind::Func).mangle(); - arrayFinalizeFun = - module.findFunction(finalizeMangledName, SILLinkage::SharedExternal); - assert(arrayFinalizeFun); - module.linkFunction(arrayFinalizeFun); + if (numElements != 0) { + if (FuncDecl *arrayFinalizeDecl = astContext.getFinalizeUninitializedArray()) { + std::string finalizeMangledName = + SILDeclRef(arrayFinalizeDecl, SILDeclRef::Kind::Func).mangle(); + arrayFinalizeFun = + module.findFunction(finalizeMangledName, SILLinkage::SharedExternal); + assert(arrayFinalizeFun); + module.linkFunction(arrayFinalizeFun); + } } // Call the _allocateUninitializedArray function with numElementsSIL. The diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 7c1e88cff14a4..42a7605d881b6 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -486,9 +486,7 @@ func test_variadics() { // CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]([[ARR_COUNT]]) // CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array, Builtin.RawPointer) - // CHECK: [[FIN_REF:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF - // CHECK: [[FIN_ARR:%[0-9]+]] = apply [[FIN_REF]]([[ARR]]) - // CHECK: keypath $KeyPath, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(thin) (@in_guaranteed SubscriptVariadic1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Array : $Array], indices_equals @$sSaySiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSaySiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[FIN_ARR]]) + // CHECK: keypath $KeyPath, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(thin) (@in_guaranteed SubscriptVariadic1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Array : $Array], indices_equals @$sSaySiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSaySiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[ARR]]) _ = \SubscriptVariadic1.[] _ = \SubscriptVariadic2.["", "1"] diff --git a/test/SILGen/scalar_to_tuple_args.swift b/test/SILGen/scalar_to_tuple_args.swift index 67888cc061196..004d6cade6d5d 100644 --- a/test/SILGen/scalar_to_tuple_args.swift +++ b/test/SILGen/scalar_to_tuple_args.swift @@ -68,8 +68,6 @@ variadicFirst(x) // CHECK: [[X:%.*]] = load [trivial] [[READ]] // CHECK: [[ALLOC_ARRAY:%.*]] = apply {{.*}} -> (@owned Array<τ_0_0>, Builtin.RawPointer) // CHECK: ([[ARRAY:%.*]], [[MEMORY:%.*]]) = destructure_tuple [[ALLOC_ARRAY]] -// CHECK: [[FIN_FN:%.*]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF -// CHECK: [[FIN_ARR:%.*]] = apply [[FIN_FN]]([[ARRAY]]) // CHECK: [[VARIADIC_SECOND:%.*]] = function_ref @$s20scalar_to_tuple_args14variadicSecondyySi_SidtF -// CHECK: apply [[VARIADIC_SECOND]]([[X]], [[FIN_ARR]]) +// CHECK: apply [[VARIADIC_SECOND]]([[X]], [[ARRAY]]) variadicSecond(x) From bc27356d73a5b6ee6233f1604e0225c89e0d9563 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 15 Jun 2020 18:59:39 +0200 Subject: [PATCH 34/38] stdlib: enable runtime checking for COW support by default in assert builds. This was blocked by an LLDB problem, which is now fixed (https://github.com/apple/llvm-project/pull/1333) --- stdlib/public/core/ContiguousArrayBuffer.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index 0519e44821b95..4b6ac4a5ee6fc 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -454,18 +454,12 @@ internal struct _ContiguousArrayBuffer: _ArrayBufferProtocol { @_alwaysEmitIntoClient internal var isImmutable: Bool { get { -// TODO: Enable COW runtime checks by default (when INTERNAL_CHECKS_ENABLED -// is set). Currently there is a problem with remote AST which needs to be -// fixed. -#if ENABLE_COW_RUNTIME_CHECKS if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { return capacity == 0 || _swift_isImmutableCOWBuffer(_storage) } -#endif return true } nonmutating set { -#if ENABLE_COW_RUNTIME_CHECKS if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { if newValue { if capacity > 0 { @@ -481,17 +475,14 @@ internal struct _ContiguousArrayBuffer: _ArrayBufferProtocol { "re-setting mutable array buffer to mutable") } } -#endif } } @_alwaysEmitIntoClient internal var isMutable: Bool { -#if ENABLE_COW_RUNTIME_CHECKS if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { return !_swift_isImmutableCOWBuffer(_storage) } -#endif return true } #endif From 1a0c4d07a63ef789255b4c874f97989bb5f23f95 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Jul 2020 16:05:39 +0200 Subject: [PATCH 35/38] Reinstate "SILCombine: Constant-fold MemoryLayout.offset(of: \.literalKeyPath)" This reinstates commit d7d829c0596ea376a0c19acbc4689b28bb984da9 with a fix for C tail-allocated arrays. Replace a call of the getter of AnyKeyPath._storedInlineOffset with a "constant" offset, in case of a keypath literal. "Constant" offset means a series of struct_element_addr and tuple_element_addr instructions with a 0-pointer as base address. These instructions can then be lowered to "real" constants in IRGen for concrete types, or to metatype offset lookups for generic or resilient types. Replace: %kp = keypath ... %offset = apply %_storedInlineOffset_method(%kp) with: %zero = integer_literal $Builtin.Word, 0 %null_ptr = unchecked_trivial_bit_cast %zero to $Builtin.RawPointer %null_addr = pointer_to_address %null_ptr %projected_addr = struct_element_addr %null_addr ... // other address projections %offset_ptr = address_to_pointer %projected_addr %offset_builtin_int = unchecked_trivial_bit_cast %offset_ptr %offset_int = struct $Int (%offset_builtin_int) %offset = enum $Optional, #Optional.some!enumelt, %offset_int rdar://problem/53309403 --- lib/SILOptimizer/SILCombiner/SILCombiner.h | 7 +- .../SILCombiner/SILCombinerApplyVisitors.cpp | 169 +++++++++++++-- .../Inputs/struct_with_fields.swift | 5 + test/SILOptimizer/keypath_offset.swift | 197 ++++++++++++++++++ 4 files changed, 358 insertions(+), 20 deletions(-) create mode 100644 test/SILOptimizer/Inputs/struct_with_fields.swift create mode 100644 test/SILOptimizer/keypath_offset.swift diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index cfd8207d089fe..6630010e5b7ae 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -246,8 +246,11 @@ class SILCombiner : bool tryOptimizeKeypath(ApplyInst *AI); bool tryOptimizeInoutKeypath(BeginApplyInst *AI); bool tryOptimizeKeypathApplication(ApplyInst *AI, SILFunction *callee); - bool tryOptimizeKeypathKVCString(ApplyInst *AI, SILDeclRef callee); - + bool tryOptimizeKeypathOffsetOf(ApplyInst *AI, FuncDecl *calleeFn, + KeyPathInst *kp); + bool tryOptimizeKeypathKVCString(ApplyInst *AI, FuncDecl *calleeFn, + KeyPathInst *kp); + // Optimize concatenation of string literals. // Constant-fold concatenation of string literals known at compile-time. SILInstruction *optimizeConcatenationOfStringLiterals(ApplyInst *AI); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 67090d1886a97..e71bba15b5f6c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -271,6 +271,130 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI, return true; } +/// Replaces a call of the getter of AnyKeyPath._storedInlineOffset with a +/// "constant" offset, in case of a keypath literal. +/// +/// "Constant" offset means a series of struct_element_addr and +/// tuple_element_addr instructions with a 0-pointer as base address. +/// These instructions can then be lowered to "real" constants in IRGen for +/// concrete types, or to metatype offset lookups for generic or resilient types. +/// +/// Replaces: +/// %kp = keypath ... +/// %offset = apply %_storedInlineOffset_method(%kp) +/// with: +/// %zero = integer_literal $Builtin.Word, 0 +/// %null_ptr = unchecked_trivial_bit_cast %zero to $Builtin.RawPointer +/// %null_addr = pointer_to_address %null_ptr +/// %projected_addr = struct_element_addr %null_addr +/// ... // other address projections +/// %offset_ptr = address_to_pointer %projected_addr +/// %offset_builtin_int = unchecked_trivial_bit_cast %offset_ptr +/// %offset_int = struct $Int (%offset_builtin_int) +/// %offset = enum $Optional, #Optional.some!enumelt, %offset_int +bool SILCombiner::tryOptimizeKeypathOffsetOf(ApplyInst *AI, + FuncDecl *calleeFn, + KeyPathInst *kp) { + auto *accessor = dyn_cast(calleeFn); + if (!accessor || !accessor->isGetter()) + return false; + + AbstractStorageDecl *storage = accessor->getStorage(); + DeclName name = storage->getName(); + if (!name.isSimpleName() || + (name.getBaseIdentifier().str() != "_storedInlineOffset")) + return false; + + KeyPathPattern *pattern = kp->getPattern(); + SubstitutionMap patternSubs = kp->getSubstitutions(); + CanType rootTy = pattern->getRootType().subst(patternSubs)->getCanonicalType(); + CanType parentTy = rootTy; + + // First check if _storedInlineOffset would return an offset or nil. Basically + // only stored struct and tuple elements produce an offset. Everything else + // (e.g. computed properties, class properties) result in nil. + bool hasOffset = true; + for (const KeyPathPatternComponent &component : pattern->getComponents()) { + switch (component.getKind()) { + case KeyPathPatternComponent::Kind::StoredProperty: { + + // Handle the special case of C tail-allocated arrays. IRGen would + // generate an undef offset for struct_element_addr of C tail-allocated + // arrays. + VarDecl *propDecl = component.getStoredPropertyDecl(); + if (propDecl->hasClangNode() && propDecl->getType()->isVoid()) + return false; + + if (!parentTy.getStructOrBoundGenericStruct()) + hasOffset = false; + break; + } + case KeyPathPatternComponent::Kind::TupleElement: + break; + case KeyPathPatternComponent::Kind::GettableProperty: + case KeyPathPatternComponent::Kind::SettableProperty: + // We cannot predict the offset of fields in resilient types, because it's + // unknown if a resilient field is a computed or stored property. + if (component.getExternalDecl()) + return false; + hasOffset = false; + break; + case KeyPathPatternComponent::Kind::OptionalChain: + case KeyPathPatternComponent::Kind::OptionalForce: + case KeyPathPatternComponent::Kind::OptionalWrap: + hasOffset = false; + break; + } + parentTy = component.getComponentType(); + } + + SILLocation loc = AI->getLoc(); + SILValue result; + + if (hasOffset) { + SILType rootAddrTy = SILType::getPrimitiveAddressType(rootTy); + SILValue rootAddr = Builder.createBaseAddrForOffset(loc, rootAddrTy); + + auto projector = KeyPathProjector::create(kp, rootAddr, loc, Builder); + if (!projector) + return false; + + // Create the address projections of the keypath. + SILType ptrType = SILType::getRawPointerType(Builder.getASTContext()); + SILValue offsetPtr; + projector->project(KeyPathProjector::AccessType::Get, [&](SILValue addr) { + offsetPtr = Builder.createAddressToPointer(loc, addr, ptrType); + }); + + // The result of the _storedInlineOffset call should be Optional. If + // not, something is wrong with the stdlib. Anyway, if it's not like we + // expect, bail. + SILType intType = AI->getType().getOptionalObjectType(); + if (!intType) + return false; + StructDecl *intDecl = intType.getStructOrBoundGenericStruct(); + if (!intDecl || intDecl->getStoredProperties().size() != 1) + return false; + VarDecl *member = intDecl->getStoredProperties()[0]; + CanType builtinIntTy = member->getType()->getCanonicalType(); + if (!isa(builtinIntTy)) + return false; + + // Convert the projected address back to an optional integer. + SILValue offset = Builder.createUncheckedBitCast(loc, offsetPtr, + SILType::getPrimitiveObjectType(builtinIntTy)); + SILValue offsetInt = Builder.createStruct(loc, intType, { offset }); + result = Builder.createOptionalSome(loc, offsetInt, AI->getType()); + } else { + // The keypath has no offset. + result = Builder.createOptionalNone(loc, AI->getType()); + } + AI->replaceAllUsesWith(result); + eraseInstFromFunction(*AI); + ++NumOptimizedKeypaths; + return true; +} + /// Try to optimize a keypath KVC string access on a literal key path. /// /// Replace: @@ -279,17 +403,8 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI, /// With: /// %string = string_literal "blah" bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI, - SILDeclRef callee) { - if (AI->getNumArguments() != 1) { - return false; - } - if (!callee.hasDecl()) { - return false; - } - auto calleeFn = dyn_cast(callee.getDecl()); - if (!calleeFn) - return false; - + FuncDecl *calleeFn, + KeyPathInst *kp) { if (!calleeFn->getAttrs() .hasSemanticsAttr(semantics::KEYPATH_KVC_KEY_PATH_STRING)) return false; @@ -300,11 +415,6 @@ bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI, if (!objTy || objTy.getStructOrBoundGenericStruct() != C.getStringDecl()) return false; - KeyPathInst *kp - = KeyPathProjector::getLiteralKeyPath(AI->getArgument(0)); - if (!kp || !kp->hasPattern()) - return false; - auto objcString = kp->getPattern()->getObjCString(); SILValue literalValue; @@ -357,10 +467,33 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) { return tryOptimizeKeypathApplication(AI, callee); } - if (auto method = dyn_cast(AI->getCallee())) { - return tryOptimizeKeypathKVCString(AI, method->getMember()); + // Try optimize keypath method calls. + auto *methodInst = dyn_cast(AI->getCallee()); + if (!methodInst) + return false; + + if (AI->getNumArguments() != 1) { + return false; + } + + SILDeclRef callee = methodInst->getMember(); + if (!callee.hasDecl()) { + return false; } + auto *calleeFn = dyn_cast(callee.getDecl()); + if (!calleeFn) + return false; + + KeyPathInst *kp = KeyPathProjector::getLiteralKeyPath(AI->getArgument(0)); + if (!kp || !kp->hasPattern()) + return false; + if (tryOptimizeKeypathOffsetOf(AI, calleeFn, kp)) + return true; + + if (tryOptimizeKeypathKVCString(AI, calleeFn, kp)) + return true; + return false; } diff --git a/test/SILOptimizer/Inputs/struct_with_fields.swift b/test/SILOptimizer/Inputs/struct_with_fields.swift new file mode 100644 index 0000000000000..7652d39100139 --- /dev/null +++ b/test/SILOptimizer/Inputs/struct_with_fields.swift @@ -0,0 +1,5 @@ + +public struct TestStruct { + public var x: Int + public var y: Int +} diff --git a/test/SILOptimizer/keypath_offset.swift b/test/SILOptimizer/keypath_offset.swift new file mode 100644 index 0000000000000..bb0ffca7249f1 --- /dev/null +++ b/test/SILOptimizer/keypath_offset.swift @@ -0,0 +1,197 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %S/Inputs/struct_with_fields.swift -parse-as-library -wmo -enable-library-evolution -module-name=Test -emit-module -emit-module-path=%t/Test.swiftmodule -c -o %t/test.o + +// RUN: %target-build-swift -O %s -module-name=test -Xfrontend -sil-verify-all -I%t -emit-sil | %FileCheck %s + +// RUN: %target-build-swift -Onone %s -I%t %t/test.o -o %t/Onone.out +// RUN: %target-build-swift -O %s -I%t %t/test.o -o %t/O.out +// RUN: %target-run %t/Onone.out > %t/Onone.txt +// RUN: %target-run %t/O.out > %t/O.txt +// RUN: diff %t/Onone.txt %t/O.txt + +// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib + +import Test + +final class C { + var x: Int + var z: T + let immutable: String + private(set) var secretlyMutable: String + + init(x: Int, z: T) { + self.x = x + self.z = z + self.immutable = "somestring" + self.secretlyMutable = immutable + } +} + +struct Point { + var x: Double + var y: Double +} + +struct S { + var x: Int + var y: Int? + var z: T + var p: Point + var op: Point? + var c: C +} + +struct NonOffsetableProperties { + // observers + var x: Int { didSet {} } + // reabstracted + var y: () -> () + // computed + var z: Int { return 0 } +} + +struct TupleProperties { + // unlabeled + var a: (Int, String) + // labeled + let b: (x: String, y: Int) + // reference writable + let c: (m: C, n: C) +} + +typealias Tuple = (S, C) + +func getIdentityKeyPathOfType(_: T.Type) -> KeyPath { + return \.self +} + + +@inline(never) +func printOffset(_ o: Int?) { + print(o as Any) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A13StructOffsetsyyF +// CHECK-NOT: _storedInlineOffset +// CHECK-NOT: class_method +// CHECK: } // end sil function '$s4test0A13StructOffsetsyyF' +@inline(never) +func testStructOffsets() { + let SLayout = MemoryLayout>.self + printOffset(SLayout.offset(of: \S.x)) + printOffset(SLayout.offset(of: \S.y)) + printOffset(SLayout.offset(of: \S.z)) + printOffset(SLayout.offset(of: \S.p)) + printOffset(SLayout.offset(of: \S.p.x)) + printOffset(SLayout.offset(of: \S.p.y)) + printOffset(SLayout.offset(of: \S.c)) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A20GenericStructOffsetsyyxmlF +// CHECK-NOT: _storedInlineOffset +// CHECK-NOT: class_method +// CHECK: } // end sil function '$s4test0A20GenericStructOffsetsyyxmlF' +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testGenericStructOffsets(_ t: T.Type) { + let SLayout = MemoryLayout>.self + printOffset(SLayout.offset(of: \S.x)) + printOffset(SLayout.offset(of: \S.y)) + printOffset(SLayout.offset(of: \S.z)) + printOffset(SLayout.offset(of: \S.p)) + printOffset(SLayout.offset(of: \S.p.x)) + printOffset(SLayout.offset(of: \S.p.y)) + printOffset(SLayout.offset(of: \S.c)) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A10NonOffsetsyyF +// CHECK-NOT: _storedInlineOffset +// CHECK-NOT: class_method +// CHECK: } // end sil function '$s4test0A10NonOffsetsyyF' +@inline(never) +func testNonOffsets() { + let NOPLayout = MemoryLayout.self + printOffset(NOPLayout.offset(of: \NonOffsetableProperties.x)) + printOffset(NOPLayout.offset(of: \NonOffsetableProperties.y)) + printOffset(NOPLayout.offset(of: \NonOffsetableProperties.z)) + printOffset(MemoryLayout>.offset(of: \C.x)) + let SLayout = MemoryLayout>.self + printOffset(SLayout.offset(of: \S.c.x)) + printOffset(SLayout.offset(of: \S.op!.x)) + printOffset(SLayout.offset(of: \S.op?.x)) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A11SelfOffsetsyyF +// CHECK-NOT: _storedInlineOffset +// CHECK-NOT: class_method +// CHECK: } // end sil function '$s4test0A11SelfOffsetsyyF' +@inline(never) +func testSelfOffsets() { + let SLayout = MemoryLayout>.self + printOffset(SLayout.offset(of: \.self)) + printOffset(SLayout.offset(of: getIdentityKeyPathOfType(S.self))) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A12TupleOffsetsyyF +// CHECK-NOT: _storedInlineOffset +// CHECK-NOT: class_method +// CHECK: } // end sil function '$s4test0A12TupleOffsetsyyF' +@inline(never) +func testTupleOffsets() { + let TPLayout = MemoryLayout.self + printOffset(TPLayout.offset(of: \TupleProperties.self)) + printOffset(TPLayout.offset(of: \TupleProperties.a)) + printOffset(TPLayout.offset(of: \TupleProperties.a.0)) + printOffset(TPLayout.offset(of: \TupleProperties.a.1)) + printOffset(TPLayout.offset(of: \TupleProperties.b)) + printOffset(TPLayout.offset(of: \TupleProperties.b.x)) + printOffset(TPLayout.offset(of: \TupleProperties.b.y)) + printOffset(TPLayout.offset(of: \TupleProperties.c)) + printOffset(TPLayout.offset(of: \TupleProperties.c.m)) + printOffset(TPLayout.offset(of: \TupleProperties.c.n)) + + let TLayout = MemoryLayout>.self + printOffset(TLayout.offset(of: \Tuple.self)) + printOffset(TLayout.offset(of: \Tuple.0)) + printOffset(TLayout.offset(of: \Tuple.0.x)) + printOffset(TLayout.offset(of: \Tuple.1)) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A19GenericTupleOffsetsyyxmlF +// CHECK-NOT: _storedInlineOffset +// CHECK-NOT: class_method +// CHECK: } // end sil function '$s4test0A19GenericTupleOffsetsyyxmlF' +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testGenericTupleOffsets(_ t: T.Type) { + let TLayout = MemoryLayout>.self + printOffset(TLayout.offset(of: \Tuple.self)) + printOffset(TLayout.offset(of: \Tuple.0)) + printOffset(TLayout.offset(of: \Tuple.0.x)) + printOffset(TLayout.offset(of: \Tuple.1)) +} + +// CHECK-LABEL: sil {{.*}} @$s4test0A16ResilientOffsetsyyF +// CHECK: class_method {{.*}}_storedInlineOffset +// CHECK: } // end sil function '$s4test0A16ResilientOffsetsyyF' +@inline(never) +func testResilientOffsets() { + let TLayout = MemoryLayout.self + printOffset(TLayout.offset(of: \TestStruct.x)) +} + +print("### testStructOffsets") +testStructOffsets() +print("### testGenericStructOffsets") +testGenericStructOffsets(Int.self) +print("### testNonOffsets") +testNonOffsets() +print("### testSelfOffsets") +testSelfOffsets() +print("### testTupleOffsets") +testTupleOffsets() +print("### testGenericTupleOffsets") +testGenericTupleOffsets(Int.self) +print("### testResilientOffsets") +testResilientOffsets() + From 7332619472647334f6b1355d06f4eaf420af3a2d Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 3 Jul 2020 16:26:49 -0400 Subject: [PATCH 36/38] Remove `ninja compiler` command --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index f1d8839474d15..8b090a70578d4 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ various products. These incremental builds are a big timesaver when developing and debugging. cd ${SWIFT_BUILD_DIR} - ninja compiler + ninja swift-frontend This will build the Swift compiler, but will not rebuild the standard library or any other target. Building the `swift-stdlib` target as an additional layer of @@ -250,10 +250,6 @@ library, run: ninja swift-stdlib -To build just the compiler frontend, run: - - ninja swift-frontend - It is always a good idea to do a full build after using `update-checkout`. #### Using Xcode From d365616a58bea7bdb7d856f43e0e80c9607cf495 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 4 Jul 2020 11:39:31 +0000 Subject: [PATCH 37/38] [WASM] Add WASI OS condition for stdlib/public/Differentiation/TgmathDerivatives.swift.gyb --- stdlib/public/Differentiation/TgmathDerivatives.swift.gyb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb index 1a770907f5a55..e5f40d3ce9335 100644 --- a/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb +++ b/stdlib/public/Differentiation/TgmathDerivatives.swift.gyb @@ -16,7 +16,7 @@ import Swift #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin.C.tgmath -#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT From b4457808fd5a0e1e0ecccd3c46fcfd13d721ac03 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 4 Jul 2020 14:51:34 +0000 Subject: [PATCH 38/38] [WASM] Add SWIFT_CC(swift) annotation for _swift_setImmutableCOWBuffer and _swift_isImmutableCOWBuffer to sync calling convention --- stdlib/public/runtime/HeapObject.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index 5357d5b30dd75..50177c633e933 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -901,6 +901,7 @@ WeakReference *swift::swift_weakTakeAssign(WeakReference *dest, /// Returns true if the "immutable" flag is set on \p object. /// /// Used for runtime consistency checking of COW buffers. +SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT bool _swift_isImmutableCOWBuffer(HeapObject *object) { return object->refCounts.isImmutableCOWBuffer(); @@ -910,6 +911,7 @@ bool _swift_isImmutableCOWBuffer(HeapObject *object) { /// value of the flag. /// /// Used for runtime consistency checking of COW buffers. +SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT bool _swift_setImmutableCOWBuffer(HeapObject *object, bool immutable) { return object->refCounts.setIsImmutableCOWBuffer(immutable);