From a95b3204e1b21cb2bc446b71e5d73165407b0b93 Mon Sep 17 00:00:00 2001 From: George Karpenkov Date: Thu, 15 Jun 2017 11:29:59 -0700 Subject: [PATCH 001/283] gitignore ctags and vim configuration Vim users often rely on ctags, which create a "tags" file in the project root. Additionally, by convention vim project-specific settings are often stored in a `.vimrc` file, also stored in a project root. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 6b9ce48e3ab00..5cb3959d4122f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ #==============================================================================# cscope.files cscope.out +.vimrc +tags #==============================================================================# # Directories to ignore (do not add trailing '/'s, they skip symlinks). From 787736d6b8a890aa1e369fbdde7b6fa7eaf1076c Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 23 Jun 2017 13:21:48 -0700 Subject: [PATCH 002/283] [Sema] Suggest #selector(self.foo) instead of #selector(TypeName.foo) when possible. When inside a declaration or extension of TypeName, humans usually don't write the full typename like #selector(TypeName.foo), but instead prefer the neater form #selector(self.foo). The compiler has enough information to do this too. Fixes rdar://problem/25284692 . --- lib/Sema/MiscDiagnostics.cpp | 17 ++++- test/FixCode/fixits-apply-objc.swift | 80 ++++++++++++++++++++ test/FixCode/fixits-apply-objc.swift.result | 84 ++++++++++++++++++++- 3 files changed, 178 insertions(+), 3 deletions(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 0563f19f210ed..43131ba28d202 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -3332,7 +3332,22 @@ class ObjCSelectorWalker : public ASTWalker { name = bestMethod->getFullName(); } - out << nominal->getName().str() << "." << name.getBaseName(); + auto typeName = nominal->getName().str(); + // If we're inside a type Foo (or an extension of it) and the suggestion + // is going to be #selector(Foo.bar) (or #selector(SuperclassOfFoo.bar), + // then suggest the more natural #selector(self.bar) instead. + if (auto containingTypeContext = DC->getInnermostTypeContext()) { + auto methodNominalType = nominal->getDeclaredType(); + auto containingNominalType = + containingTypeContext + ->getAsNominalTypeOrNominalTypeExtensionContext() + ->getDeclaredType(); + if (methodNominalType->isEqual(containingNominalType) || + methodNominalType->isExactSuperclassOf(containingNominalType)) + typeName = "self"; + } + + out << typeName << "." << name.getBaseName(); auto argNames = name.getArgumentNames(); // Only print the parentheses if there are some argument diff --git a/test/FixCode/fixits-apply-objc.swift b/test/FixCode/fixits-apply-objc.swift index 1fe81fa6d3348..bff0784d0b32f 100644 --- a/test/FixCode/fixits-apply-objc.swift +++ b/test/FixCode/fixits-apply-objc.swift @@ -13,6 +13,86 @@ import ObjectiveC } } +@objc class OtherClass { + func test(s: Selectors) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + } +} + +@objc class Base { + func baseSel() {} +} + +@objc class Outer { + func takeSel(_: Selector) {} + func outerSel() {} + + @objc class Inner: Base { + func takeSel(_: Selector) {} + + func innerSel() {} + + func test(s: Selectors, o: Outer) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + takeSel("innerSel") + takeSel(Selector("innerSel")) + + takeSel("baseSel") + takeSel(Selector("baseSel")) + + o.takeSel("outerSel") + o.takeSel(Selector("outerSel")) + } + } + + func test(s: Selectors, i: Inner) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + i.takeSel("innerSel") + i.takeSel(Selector("innerSel")) + + i.takeSel("baseSel") + i.takeSel(Selector("baseSel")) + + takeSel("outerSel") + takeSel(Selector("outerSel")) + } +} + +extension Outer { + func test2(s: Selectors, i: Inner) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + i.takeSel("innerSel") + i.takeSel(Selector("innerSel")) + + i.takeSel("baseSel") + i.takeSel(Selector("baseSel")) + + takeSel("outerSel") + takeSel(Selector("outerSel")) + } +} + +func freeTest(s: Selectors, o: Outer, i: Outer.Inner) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + i.takeSel("innerSel") + i.takeSel(Selector("innerSel")) + + i.takeSel("baseSel") + i.takeSel(Selector("baseSel")) + + o.takeSel("outerSel") + o.takeSel(Selector("outerSel")) +} + func foo(an : Any) { let a1 : AnyObject a1 = an diff --git a/test/FixCode/fixits-apply-objc.swift.result b/test/FixCode/fixits-apply-objc.swift.result index 4373fddd05aa1..7defb5ee888b9 100644 --- a/test/FixCode/fixits-apply-objc.swift.result +++ b/test/FixCode/fixits-apply-objc.swift.result @@ -8,11 +8,91 @@ import ObjectiveC func takeSel(_: Selector) {} func mySel() {} func test() { - takeSel(#selector(Selectors.mySel)) - takeSel(#selector(Selectors.mySel)) + takeSel(#selector(self.mySel)) + takeSel(#selector(self.mySel)) } } +@objc class OtherClass { + func test(s: Selectors) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + } +} + +@objc class Base { + func baseSel() {} +} + +@objc class Outer { + func takeSel(_: Selector) {} + func outerSel() {} + + @objc class Inner: Base { + func takeSel(_: Selector) {} + + func innerSel() {} + + func test(s: Selectors, o: Outer) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + takeSel(#selector(self.innerSel)) + takeSel(#selector(self.innerSel)) + + takeSel(#selector(self.baseSel)) + takeSel(#selector(self.baseSel)) + + o.takeSel(#selector(Outer.outerSel)) + o.takeSel(#selector(Outer.outerSel)) + } + } + + func test(s: Selectors, i: Inner) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + i.takeSel(#selector(Inner.innerSel)) + i.takeSel(#selector(Inner.innerSel)) + + i.takeSel(#selector(Base.baseSel)) + i.takeSel(#selector(Base.baseSel)) + + takeSel(#selector(self.outerSel)) + takeSel(#selector(self.outerSel)) + } +} + +extension Outer { + func test2(s: Selectors, i: Inner) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + i.takeSel(#selector(Inner.innerSel)) + i.takeSel(#selector(Inner.innerSel)) + + i.takeSel(#selector(Base.baseSel)) + i.takeSel(#selector(Base.baseSel)) + + takeSel(#selector(self.outerSel)) + takeSel(#selector(self.outerSel)) + } +} + +func freeTest(s: Selectors, o: Outer, i: Outer.Inner) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + i.takeSel(#selector(Inner.innerSel)) + i.takeSel(#selector(Inner.innerSel)) + + i.takeSel(#selector(Base.baseSel)) + i.takeSel(#selector(Base.baseSel)) + + o.takeSel(#selector(Outer.outerSel)) + o.takeSel(#selector(Outer.outerSel)) +} + func foo(an : Any) { let a1 : AnyObject a1 = an as AnyObject From 62572e9402565b3ea43c3c38b082a70008a24b64 Mon Sep 17 00:00:00 2001 From: Amr Aboelela Date: Fri, 20 Oct 2017 12:17:20 -0700 Subject: [PATCH 003/283] Added Build Android Toolchain section to docs/Android.md --- docs/Android.md | 20 ++++++++++++++++++-- utils/android/README.md | 16 ---------------- 2 files changed, 18 insertions(+), 18 deletions(-) delete mode 100644 utils/android/README.md diff --git a/docs/Android.md b/docs/Android.md index ff5ea1f960f0d..ffd867ec1aeb4 100644 --- a/docs/Android.md +++ b/docs/Android.md @@ -42,7 +42,7 @@ To follow along with this guide, you'll need: turn on remote debugging by following the official instructions: https://developer.chrome.com/devtools/docs/remote-debugging. -## Part One: "Hello, world" on Android +## "Hello, world" on Android ### 1. Downloading (or building) the Swift Android stdlib dependencies @@ -309,7 +309,7 @@ Hello, Android Congratulations! You've just run your first Swift program on Android. -## Part Two: Running the Swift test suite hosted on an Android device +## Running the Swift test suite hosted on an Android device When running the test suite, build products are automatically pushed to your device. As in part one, you'll need to connect your Android device via USB: @@ -333,3 +333,19 @@ $ utils/build-script \ --android-icu-i18n ~/libicu-android/armeabi-v7a/libicui18n.so \ --android-icu-i18n-include ~/libicu-android/armeabi-v7a/icu/source/i18n/ ``` + +## Build Android Toolchain + +This toolchain will generate the .so and .swiftmodule files of the Swift standard library and Foundation framework for the Android environment, armv7 architecture. Those files are needed when building any Swift library to be included in an application for Android. + +To build the toolchain run: + +``` +$ utils/android/build-toolchain +``` + +It will be built on: + +``` +path/to/swift-source/swift-android-toolchain +``` diff --git a/utils/android/README.md b/utils/android/README.md deleted file mode 100644 index 28ad49b365caa..0000000000000 --- a/utils/android/README.md +++ /dev/null @@ -1,16 +0,0 @@ - -# Build Android Toolchain - -This toolchain will generate the .so and .swiftmodule files of the Swift standard library and Foundation framework for the Android environment, armv7 architecture. Those files are needed when building any Swift library to be included in an application for Android. - -To build the toolchain run: - -``` -android$ ./build-toolchain -``` - -It will be built on: - -``` -path/to/swift-source/swift-android-toolchain -``` From 468acd3b3887c73bfe8e951b6645416ee6613371 Mon Sep 17 00:00:00 2001 From: Matt Gallagher Date: Thu, 16 Nov 2017 23:00:34 +1100 Subject: [PATCH 004/283] Fixed faulty boolean logic This is a trivial logic error. The "abort" condition would be followed only if `c == 0` with other clauses having no effect. --- lib/Demangling/Demangler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index b40fe64c57226..405e007dffe0f 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1397,7 +1397,7 @@ std::string Demangler::demangleBridgedMethodParams() { while (!nextIf('_')) { auto c = nextChar(); - if (!c && c != 'n' && c != 'b') + if (c != 'n' && c != 'b') return std::string(); Str.push_back(c); } From 2cff06dcd4173679638e6192a566690905b4e75d Mon Sep 17 00:00:00 2001 From: Matthew Johnson Date: Sat, 18 Nov 2017 09:23:08 -0800 Subject: [PATCH 005/283] Add generic associatedtypes and generalized supertype constraints --- docs/GenericsManifesto.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/GenericsManifesto.md b/docs/GenericsManifesto.md index faed021f07fee..705ff5b640d29 100644 --- a/docs/GenericsManifesto.md +++ b/docs/GenericsManifesto.md @@ -88,6 +88,30 @@ var d1 = StringDictionary() var d2: Dictionary = d1 // okay: d1 and d2 have the same type, Dictionary ``` +### Generic associatedtypes + +Associatedtypes could be allowed to carry generic parameters. + +```Swift + protocol Wrapper { + associatedtype Wrapped + + static func wrap(_ t: T) -> Wrapped + } +``` + +Generic associatedtypes would support all constraints supported by the language including where clauses. As with non-generic associatedtypes conforming types would be required to provide a nested type or typealias matching the name of the associatedtype. However, in this case the nested type or typealias would be generic. + +```Swift + enum OptionalWrapper { + typealias Wrapped = Optional + + static func wrap(_ t: T) -> Optional + } +``` + +Note: generic associatedtypes address many use cases also addressed by higher-kinded types but with lower implementation complexity. + ### Generic subscripts *This feature has been accepted in [SE-0148](https://github.com/apple/swift-evolution/blob/master/proposals/0148-generic-subscripts.md), was tracked by [SR-115](https://bugs.swift.org/browse/SR-115) and was released with Swift 4.* @@ -249,6 +273,21 @@ typealias AnyObject = protocol See the "Existentials" section, particularly "Generalized existentials", for more information. +### Generalized supertype constraints + +Currently, supertype constraints may only be specified using a concrete class or protocol type. This prevents us from abstracting over the supertype. + +```Swift +protocol P { + associatedtype Base + associatedtype Derived: Base +} +``` + +In the above example `Base` may be any type. `Derived` may be the same as `Base` or may be _any_ subtype of `Base`. All subtype relationships supported by Swift should be supported in this context including (but not limited to) classes and subclasses, existentials and conforming concrete types or refining existentials, `T?` and `T`, `((Base) -> Void)` and `((Derived) -> Void)`, etc. + +Generalized supertype constraints would be accepted in all syntactic locations where generic constraints are accepted. + ### Allowing subclasses to override requirements satisfied by defaults (*) When a superclass conforms to a protocol and has one of the protocol's requirements satisfied by a member of a protocol extension, that member currently cannot be overridden by a subclass. For example: From 80045ad7af4b2b9c3176f8e8cf3c15158ba9803e Mon Sep 17 00:00:00 2001 From: Noah Gilmore Date: Thu, 18 Jan 2018 12:08:01 -0800 Subject: [PATCH 006/283] Fix a typo in DerivedConformances --- lib/Sema/DerivedConformances.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 610db73e59c8d..c8e37af9abf69 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -63,7 +63,7 @@ bool DerivedConformance::derivesProtocolConformance(TypeChecker &tc, } // hasOnlyCasesWithoutAssociatedValues will return true for empty enums; - // empty enumas are allowed to conform as well. + // empty enums are allowed to conform as well. return enumDecl->hasOnlyCasesWithoutAssociatedValues(); } From 021763a1c9464c25f8535d7d5496029da13ca258 Mon Sep 17 00:00:00 2001 From: Artem Mukhin Date: Tue, 13 Mar 2018 16:11:50 +0300 Subject: [PATCH 007/283] Some documentation fixes Replace   with   fix TokenKinds.def path; fix Child documentation --- lib/Syntax/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Syntax/README.md b/lib/Syntax/README.md index 55d0f42463f86..86e4619ebdcb3 100644 --- a/lib/Syntax/README.md +++ b/lib/Syntax/README.md @@ -260,7 +260,7 @@ pieces of syntax that aren't really relevant to the semantics of the program, such as whitespace and comments. These are modeled as collections and, with the exception of comments, are sort of "run-length" encoded. For example, a sequence of four spaces is represented by `{ Kind: TriviaKind::Space, Count: 4 }`, not -the literal text `" "`. +the literal text `"    "`. Some examples of the "atoms" of `Trivia`: @@ -502,7 +502,7 @@ following fields: | Key | Type | Description | | --- | ---- | ----------- | -| `kind` | `String` | The `SyntaxKind` of this child. This must have a corresponding `Node` with that kind. | +| `kind` | `String` | The `SyntaxKind` of this child. This must have a corresponding `Node` with that kind (or corresponding `Token` in both `include/swift/Syntax/TokenKinds.def` and `SYNTAX_TOKENS`). | | `is_optional` | `Bool?` | Whether this child is required in a fully-formed object, or if it is allowed to remain `missing`. Defaults to `false` if not present. | `token_choices` | `[String]?` | A list of `Token`s which are considered "valid" values for `Token` children. | | `text_choices` | `[String]?` | A list of valid textual values for tokens. If this is not provided, any textual value is accepted for tokens like `IdentifierToken`. | @@ -510,7 +510,7 @@ following fields: #### Tokens A `Token` represents one of the `tok::` enums in -`include/Syntax/TokenKinds.def`. `Token.py` has a top-level array of token +`include/swift/Syntax/TokenKinds.def`. `Token.py` has a top-level array of token declarations. The `Token` class has the following fields. | Key | Type | Description | From ae8a506791f1041db28478292ba42853423d4f4a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Aug 2018 11:28:36 -0700 Subject: [PATCH 008/283] [AST] BridgeObject uses ReferenceCounting::Bridge even without ObjC interop. --- lib/AST/Type.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 0159e35c51c1e..b3ed0621436e9 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4012,9 +4012,12 @@ ReferenceCounting TypeBase::getReferenceCounting() { ASTContext &ctx = type->getASTContext(); // In the absence of Objective-C interoperability, everything uses native - // reference counting. - if (!ctx.LangOpts.EnableObjCInterop) - return ReferenceCounting::Native; + // reference counting or is the builtin BridgeObject. + if (!ctx.LangOpts.EnableObjCInterop) { + return type->getKind() == TypeKind::BuiltinBridgeObject + ? ReferenceCounting::Bridge + : ReferenceCounting::Native; + } switch (type->getKind()) { #define SUGARED_TYPE(id, parent) case TypeKind::id: From 388879fc8acc27656cb65591321ca2bc3a1528a3 Mon Sep 17 00:00:00 2001 From: ezura Date: Sun, 19 Aug 2018 10:21:58 +0900 Subject: [PATCH 009/283] [stdlib] Use RRC.filter in removeAll(where: _) --- stdlib/public/core/RangeReplaceableCollection.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/public/core/RangeReplaceableCollection.swift b/stdlib/public/core/RangeReplaceableCollection.swift index e42a205e01a9b..06e56bae38f16 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift +++ b/stdlib/public/core/RangeReplaceableCollection.swift @@ -1136,8 +1136,6 @@ extension RangeReplaceableCollection { public mutating func removeAll( where shouldBeRemoved: (Element) throws -> Bool ) rethrows { - // FIXME: Switch to using RRC.filter once stdlib is compiled for 4.0 - // self = try filter { try !predicate($0) } - self = try Self(self.lazy.filter { try !shouldBeRemoved($0) }) + self = try filter { try !shouldBeRemoved($0) } } } From b6950fc38bcabab6142c0d17074be311f086d83b Mon Sep 17 00:00:00 2001 From: kitasuke Date: Wed, 29 Aug 2018 23:32:33 +1000 Subject: [PATCH 010/283] Fix typo to mark_uninitialized --- lib/SILOptimizer/Transforms/AllocBoxToStack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index f71d09e35353b..2057e53274b5f 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -328,7 +328,7 @@ findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, (!inAppliedFunction && isa(User))) continue; - // If our user instruction is a copy_value or a marked_uninitialized, visit + // If our user instruction is a copy_value or a mark_uninitialized, visit // the users recursively. if (isa(User) || isa(User)) { copy(cast(User)->getUses(), From 3a52e8e29cda90c6d0c49b95f891e8dbb45f36b1 Mon Sep 17 00:00:00 2001 From: Ricardo Nunez Date: Mon, 1 Oct 2018 15:57:40 -0700 Subject: [PATCH 011/283] Update OptimizationTips.rst with consistent colon syntax * Cleans up inconsistencies within the doc concerning inconsistent spacing with colons * According to [most swift style guides](https://github.com/raywenderlich/swift-style-guide#spacing), the there should be no space to the left of the colon and one space to the right of the colon. --- docs/OptimizationTips.rst | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/OptimizationTips.rst b/docs/OptimizationTips.rst index 9a6312c355ebd..9f924a309431b 100644 --- a/docs/OptimizationTips.rst +++ b/docs/OptimizationTips.rst @@ -82,7 +82,7 @@ in the following code snippet, ``a.aProperty``, ``a.doSomething()`` and dynamic doSomethingElse() { ... } } - class B : A { + class B: A { override var aProperty { get { ... } set { ... } @@ -155,7 +155,7 @@ assuming ``E``, ``F`` do not have any overriding declarations in the same file: } class F { - fileprivate var myPrivateVar : Int + fileprivate var myPrivateVar: Int } func usingE(_ e: E) { @@ -193,11 +193,11 @@ Array. // Don't use a class here. struct PhonebookEntry { - var name : String - var number : [Int] + var name: String + var number: [Int] } - var a : [PhonebookEntry] + var a: [PhonebookEntry] Keep in mind that there is a trade-off between using large value types and using reference types. In certain cases, the overhead of copying and moving around @@ -277,9 +277,9 @@ safe. :: - a : [Int] - b : [Int] - c : [Int] + a: [Int] + b: [Int] + c: [Int] // Precondition: for all a[i], b[i]: a[i] + b[i] does not overflow! for i in 0 ... n { @@ -368,12 +368,12 @@ represented as values, so this example is somewhat realistic. :: protocol P {} - struct Node : P { - var left, right : P? + struct Node: P { + var left, right: P? } struct Tree { - var node : P? + var node: P? init() { ... } } @@ -402,8 +402,8 @@ argument drops from being O(n), depending on the size of the tree to O(1). :: - struct Tree : P { - var node : [P?] + struct Tree: P { + var node: [P?] init() { node = [thing] } @@ -435,13 +435,13 @@ construct such a data structure: :: final class Ref { - var val : T - init(_ v : T) {val = v} + var val: T + init(_ v: T) {val = v} } struct Box { - var ref : Ref - init(_ x : T) { ref = Ref(x) } + var ref: Ref + init(_ x: T) { ref = Ref(x) } var value: T { get { return ref.val } @@ -506,7 +506,7 @@ alive. withExtendedLifetime(Head) { // Create an Unmanaged reference. - var Ref : Unmanaged = Unmanaged.passUnretained(Head) + var Ref: Unmanaged = Unmanaged.passUnretained(Head) // Use the unmanaged reference in a call/variable access. The use of // _withUnsafeGuaranteedRef allows the compiler to remove the ultimate @@ -540,7 +540,7 @@ protocols as class-only protocols to get better runtime performance. :: - protocol Pingable : AnyObject { func ping() -> Int } + protocol Pingable: AnyObject { func ping() -> Int } .. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html From 087b9893da12253cbb296dea0e473b807fc016aa Mon Sep 17 00:00:00 2001 From: Adam Thayer Date: Sun, 14 Oct 2018 13:00:52 -0700 Subject: [PATCH 012/283] Force AttributeBase Alignment to 8-byte On 32-bit ARM, if an AvailableAttr was getting 4-byte aligned, it would trigger a LLVM assert that the PointerIntPair wasn't aligned enough. Forcing 8-byte alignment for AttributeBase addresses the problem. However, the assert is mentioning it wants 2 bits, so I'm not 100% sure where the extra bit is coming from. This is a little messy, since if you are using the custom new operator on a child class of AttributeBase, you get the implementation on AttributeBase, which forces alignment to be that of AttributeBase. This is problematic if any child class requires more strict alignment than AttributeBase in general. --- include/swift/AST/Attr.h | 2 +- include/swift/AST/TypeAlignments.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 417f9249d8db3..e1fa02fc862cf 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -135,7 +135,7 @@ class TypeAttributes { static const char *getAttrName(TypeAttrKind kind); }; -class AttributeBase { +class alignas(1 << AttrAlignInBits) AttributeBase { public: /// The location of the '@'. const SourceLoc AtLoc; diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index f360a10befd34..8ceb776d26152 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -30,6 +30,7 @@ namespace swift { class ArchetypeType; class AssociatedTypeDecl; class ASTContext; + class AttributeBase; class BraceStmt; class Decl; class DeclContext; @@ -51,6 +52,7 @@ namespace swift { class ValueDecl; /// We frequently use three tag bits on all of these types. + constexpr size_t AttrAlignInBits = 3; constexpr size_t DeclAlignInBits = 3; constexpr size_t DeclContextAlignInBits = 3; constexpr size_t ExprAlignInBits = 3; @@ -114,6 +116,8 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::Pattern, LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunction, swift::SILFunctionAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::AttributeBase, swift::AttrAlignInBits) + static_assert(alignof(void*) >= 2, "pointer alignment is too small"); #endif From 577ffabc0bb6ccb0fe426b891d38138e6def9b39 Mon Sep 17 00:00:00 2001 From: Han Sang-jin Date: Sat, 10 Nov 2018 05:19:46 +0900 Subject: [PATCH 013/283] [IRGen] Modifications for Cygwin - Cygwin 64 bit uses the LP64 model and differs from the LLP64 model used on Windows 64 bit. On Cygwin 64 bit, CLong is imported as Int. Therefore, no manual mapping is required. - On Cygwin 64 bit, dlls are loaded above the max address for 32 bits. This means that the default CodeModel causes generated code to segfault when run. --- lib/IRGen/GenClangType.cpp | 4 +++- lib/IRGen/IRGen.cpp | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index d36aad402e946..e79bb3aeab587 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -318,7 +318,9 @@ ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, // On 64-bit Windows, no C type is imported as an Int or UInt; CLong is // imported as an Int32 and CLongLong as an Int64. Therefore, manually // add mappings to C for Int and UInt. - if (IGM.Triple.isOSWindows() && IGM.Triple.isArch64Bit()) { + // On 64-bit Cygwin, no manual mapping is required. + if (IGM.Triple.isOSWindows() && !IGM.Triple.isWindowsCygwinEnvironment() && + IGM.Triple.isArch64Bit()) { // Map UInt to uintptr_t auto swiftUIntType = getNamedSwiftType(stdlib, "UInt"); auto clangUIntPtrType = ctx.getCanonicalType(ctx.getUIntPtrType()); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 589edae6f3c18..fe2685ab59a2a 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -593,10 +593,17 @@ swift::createTargetMachine(IRGenOptions &Opts, ASTContext &Ctx) { } + // On Cygwin 64 bit, dlls are loaded above the max address for 32 bits. + // This means that the default CodeModel causes generated code to segfault + // when run. + Optional cmodel = None; + if (EffectiveTriple.isArch64Bit() && EffectiveTriple.isWindowsCygwinEnvironment()) + cmodel = CodeModel::Large; + // Create a target machine. llvm::TargetMachine *TargetMachine = Target->createTargetMachine( EffectiveTriple.str(), CPU, targetFeatures, TargetOpts, Reloc::PIC_, - None, OptLevel); + cmodel, OptLevel); if (!TargetMachine) { Ctx.Diags.diagnose(SourceLoc(), diag::no_llvm_target, EffectiveTriple.str(), "no LLVM target machine"); From d3d5a9465ccf83d5a5621244112ace0a95fab336 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 26 Nov 2018 21:46:50 -0800 Subject: [PATCH 014/283] [Test] Add tests from SR-8767 / rdar://problem/44522298. Slava fixed this recently (see 8acc11f80c5421540f16ecf8ae0575c429877306), but add the fairly-extensive test cases from this bug report as regression tests. --- test/decl/protocol/sr8767.swift | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 test/decl/protocol/sr8767.swift diff --git a/test/decl/protocol/sr8767.swift b/test/decl/protocol/sr8767.swift new file mode 100644 index 0000000000000..6b0cb653af89b --- /dev/null +++ b/test/decl/protocol/sr8767.swift @@ -0,0 +1,64 @@ +// RUN: %target-typecheck-verify-swift + +// SR-8767: a number of related problems with unqualified lookup of +// associated type names. + + +// #1 +public protocol PA { + associatedtype F +} + +public protocol PDA : PA { +} + +public protocol PrB { + associatedtype F +} + +extension PDA where Self : PrB { + public init(first: F?) { + fatalError() + } +} + +// #2 +public protocol S { associatedtype F } +public protocol AM : S {} +public protocol AL { associatedtype F } +extension AM where Self : AL { + public init(first: F?) { fatalError() } +} + +// #3 +public protocol S2 { associatedtype F } +public protocol A2 : S2 {} +public protocol Z2 { associatedtype F } +extension A2 where Self : Z2 { + public init(first: F?) { fatalError() } +} + +// #4 +public protocol BM { associatedtype F } +public protocol C : BM {} +public protocol BL { associatedtype F } +extension C where Self : BL { public init(first: F?) { fatalError() } } + +// #5 +public protocol AZ { associatedtype F } +public protocol ZA : AZ {} +public protocol AA { associatedtype F } +extension ZA where Self : AA { public init(first: F?) { fatalError() } } + +// #6 +public protocol AZ2 { associatedtype F } +public protocol ZA2 : AZ2 {} +public protocol ZZ2 { associatedtype F } +extension ZA2 where Self : ZZ2 { public init(first: F?) { fatalError() } } + +// #7 +public protocol ZA3 { associatedtype F } +public protocol AZ3 : ZA3 {} +public protocol ZZ3 { associatedtype F } +extension AZ3 where Self : ZZ3 { public init(first: F?) { fatalError() } } + From 9c733ed67da99e7df9ec7840bbf8274297a11638 Mon Sep 17 00:00:00 2001 From: Valeriy Van Date: Wed, 26 Dec 2018 18:35:49 +0200 Subject: [PATCH 015/283] Fixes example snippet of AnyHashable Otherwise, `print(descriptions[AnyHashable(45)])` prints `an Int8`, not `nil`. --- stdlib/public/core/AnyHashable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 30ea97a1ac342..82e34416281db 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -120,7 +120,7 @@ internal struct _ConcreteHashableBox : _AnyHashableBox { /// AnyHashable(Set(["a", "b"])): "a set of strings" /// ] /// print(descriptions[AnyHashable(42)]!) // prints "an Int" -/// print(descriptions[AnyHashable(43)]) // prints "nil" +/// print(descriptions[AnyHashable(45)]) // prints "nil" /// print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" /// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings" @_fixed_layout From 4b250b2ae4dee484898863e478fb6b13f4caea7d Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 4 Mar 2019 10:47:56 -0800 Subject: [PATCH 016/283] [cast-opt] Fix obvious bug. Found via inspection. The code is supposed to check if either the source or the target is NSError and in such a case bail. --- lib/SILOptimizer/Utils/CastOptimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Utils/CastOptimizer.cpp b/lib/SILOptimizer/Utils/CastOptimizer.cpp index 83f0394f3253d..7b8eca7ef4d26 100644 --- a/lib/SILOptimizer/Utils/CastOptimizer.cpp +++ b/lib/SILOptimizer/Utils/CastOptimizer.cpp @@ -654,7 +654,7 @@ CastOptimizer::optimizeBridgedCasts(SILDynamicCastInst dynamicCast) { if ((CanBridgedSourceTy && CanBridgedSourceTy->getAnyNominal() == M.getASTContext().getNSErrorDecl()) || - (CanBridgedTargetTy && CanBridgedSourceTy->getAnyNominal() == + (CanBridgedTargetTy && CanBridgedTargetTy->getAnyNominal() == M.getASTContext().getNSErrorDecl())) { // FIXME: Can't optimize bridging with NSError. return nullptr; From 8a4c4edfd2d7427ebcf35799c1bb3a494cda6443 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 3 Apr 2019 12:25:12 +0200 Subject: [PATCH 017/283] Fix typo in ErrorHandlingRationale.rst The error case of Haskell's Maybe type is called `Nothing`. --- docs/ErrorHandlingRationale.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ErrorHandlingRationale.rst b/docs/ErrorHandlingRationale.rst index 71dd3e4de5fc3..a55956b868639 100644 --- a/docs/ErrorHandlingRationale.rst +++ b/docs/ErrorHandlingRationale.rst @@ -1180,7 +1180,7 @@ Haskell Haskell provides three different common error-propagation mechanisms. The first is that, like many other functional languages, it supports -manual propagation with a ``Maybe`` type. A function can return ``None`` +manual propagation with a ``Maybe`` type. A function can return ``Nothing`` to indicate that it couldn't produce a more useful result. This is the most common failure method for functions in the functional subset of the library. From e96f61c8bc44610ba71a9fa01cbac93fa395e76f Mon Sep 17 00:00:00 2001 From: simon gladman Date: Thu, 2 May 2019 11:27:43 +0100 Subject: [PATCH 018/283] Use `elementsAlmostEqual` to resolve suspected alignment-induced failures `DoublePrecisionInterpolateBetweenNeighbours ` was occasionally faliing on tvOS simulator. Not using exact equality appears to have fixed this issue. The code I've used for `isAlmostEqual` is based on SE-0259 --- test/stdlib/Accelerate.swift | 164 ++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 68 deletions(-) diff --git a/test/stdlib/Accelerate.swift b/test/stdlib/Accelerate.swift index e52cd03f3f21e..53847861f0489 100644 --- a/test/stdlib/Accelerate.swift +++ b/test/stdlib/Accelerate.swift @@ -76,8 +76,9 @@ if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 4.0, *) { if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { + let n = 1024 + AccelerateTests.test("vDSP/DiscreteCosineTransform") { - let n = 1024 let source = (0 ..< n).map{ i in return sin(Float(i) * 0.05) + sin(Float(i) * 0.025) @@ -109,19 +110,16 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { source, &legacyDestination) - expectTrue(destination.elementsEqual(legacyDestination)) - expectTrue(destination.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(destination, legacyDestination)) + expectTrue(elementsAlmostEqual(destination, returnedResult)) } } -} - -//===----------------------------------------------------------------------===// -// -// Sliding window summation -// -//===----------------------------------------------------------------------===// -if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { + //===----------------------------------------------------------------------===// + // + // Sliding window summation + // + //===----------------------------------------------------------------------===// AccelerateTests.test("vDSP/SinglePrecisionSlidingWindowSum") { let source: [Float] = [1, 10, 12, 9, 3, 7, 2, 6] @@ -134,7 +132,7 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.slidingWindowSum(source, usingWindowLength: 3) - expectTrue(destination.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(destination, returnedResult)) expectTrue(destination.map{ Int($0) }.elementsEqual([23, 31, 24, 19, 12, 15])) } @@ -149,22 +147,15 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.slidingWindowSum(source, usingWindowLength: 3) - expectTrue(destination.elementsEqual(returnedResult)) - + expectTrue(elementsAlmostEqual(destination, returnedResult)) expectTrue(destination.map{ Int($0) }.elementsEqual([23, 31, 24, 19, 12, 15])) } -} - -//===----------------------------------------------------------------------===// -// -// Linear interpolation -// -//===----------------------------------------------------------------------===// - -if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { - - let n = 1024 + //===----------------------------------------------------------------------===// + // + // Linear interpolation + // + //===----------------------------------------------------------------------===// AccelerateTests.test("vDSP/SinglePrecisionInterpolateBetweenVectors") { var result = [Float](repeating: 0, count: n) @@ -193,8 +184,8 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.linearInterpolate(a, b, using: interpolationConstant) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/SinglePrecisionInterpolateBetweenNeighbours") { @@ -229,8 +220,8 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.linearInterpolate(elementsOf: shortSignal, using: controlVector) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DoublePrecisionInterpolateBetweenVectors") { @@ -260,8 +251,8 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.linearInterpolate(a, b, using: interpolationConstant) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DoublePrecisionInterpolateBetweenNeighbours") { @@ -296,19 +287,16 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.linearInterpolate(elementsOf: shortSignal, using: controlVector) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } -} -//===----------------------------------------------------------------------===// -// -// vDSP difference equation -// -//===----------------------------------------------------------------------===// + //===----------------------------------------------------------------------===// + // + // vDSP difference equation + // + //===----------------------------------------------------------------------===// -if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { - AccelerateTests.test("vDSP/DifferenceEquationSinglePrecision") { let n = 256 @@ -343,8 +331,8 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { coefficients[3], coefficients[4])) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DifferenceEquationDoublePrecision") { @@ -381,18 +369,16 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { coefficients[3], coefficients[4])) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } -} -//===----------------------------------------------------------------------===// -// -// vDSP downsampling -// -//===----------------------------------------------------------------------===// + //===----------------------------------------------------------------------===// + // + // vDSP downsampling + // + //===----------------------------------------------------------------------===// -if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { AccelerateTests.test("vDSP/DownsampleSinglePrecision") { let decimationFactor = 2 let filterLength: vDSP_Length = 2 @@ -429,8 +415,8 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { decimationFactor: decimationFactor, filter: filter) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DownsampleDoublePrecision") { @@ -469,19 +455,16 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { decimationFactor: decimationFactor, filter: filter) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } -} -//===----------------------------------------------------------------------===// -// -// vDSP polynomial evaluation. -// -//===----------------------------------------------------------------------===// + //===----------------------------------------------------------------------===// + // + // vDSP polynomial evaluation. + // + //===----------------------------------------------------------------------===// -if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { - AccelerateTests.test("vDSP/PolynomialEvaluationSinglePrecision") { let coefficients: [Float] = [2, 3, 4, 5, 6, 7, 8, 9, 10] let variables = (0 ... 100).map { return Float($0) } @@ -502,8 +485,8 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.evaluatePolynomial(usingCoefficients: coefficients, withVariables: variables) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/PolynomialEvaluationDoublePrecision") { @@ -526,8 +509,53 @@ if #available(iOS 9999, macOS 9999, tvOS 9999, watchOS 9999, *) { let returnedResult = vDSP.evaluatePolynomial(usingCoefficients: coefficients, withVariables: variables) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) + } + + //===----------------------------------------------------------------------===// + // + // Array almost equal. + // + //===----------------------------------------------------------------------===// + + func elementsAlmostEqual(_ lhs: [T], _ rhs: [T]) -> Bool { + var returnValue = true + zip(lhs, rhs).forEach { + if !isAlmostEqual($0.0, $0.1) { + returnValue = false + return + } + } + return returnValue + } + + func isAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T = T.ulpOfOne.squareRoot()) -> Bool { + assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") + guard lhs.isFinite && rhs.isFinite else { + return rescaledAlmostEqual(lhs, rhs, tolerance: tolerance) + } + let scale = max(abs(lhs), abs(rhs), .leastNormalMagnitude) + return abs(lhs - rhs) < scale*tolerance + } + + func rescaledAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T) -> Bool { + if lhs.isNaN || rhs.isNaN { return false } + if lhs.isInfinite { + if rhs.isInfinite { return lhs == rhs } + let scaledLhs = T(sign: lhs.sign, + exponent: T.greatestFiniteMagnitude.exponent, + significand: 1) + let scaledRhs = T(sign: .plus, + exponent: -1, + significand: rhs) + return isAlmostEqual(scaledLhs, scaledRhs, tolerance: tolerance) + } + return rescaledAlmostEqual(rhs, lhs, tolerance: tolerance) } } From 869bcfb364b868648745faac5befb619f76e7daf Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 30 May 2019 15:55:30 -0700 Subject: [PATCH 019/283] docs: describe where the build system is evolving to Add a manifesto which describes where the build infrastructure is slowly evolving to. Although a good chunk of the necessary work to enable this is already in the tree, this helps explain the desired state to everyone working on the project. --- docs/BuildManifesto.md | 96 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/BuildManifesto.md diff --git a/docs/BuildManifesto.md b/docs/BuildManifesto.md new file mode 100644 index 0000000000000..f8ca34b81127a --- /dev/null +++ b/docs/BuildManifesto.md @@ -0,0 +1,96 @@ +# Build System + +## Introduction + +Modularising the build of Swift is a large undertaking that will simplify the +infrastructure used to build the compiler, runtime, standard library, as well +as the core libraries. Additionally, the work will enable a unified, uniform +build system that reduces the barrier to entry for new contributors. + +## Current State + +Currently, the Swift build is setup similar to the way that the GCC build was +setup. This requires the understanding of the terms `build`, `host`, `target` +as used by autotools. + +
+
build
+
the system where the package is being configured and compiled
+ x86_64-unknown-linux-gnu +
+
host
+
the system where the package runs (by default the same as build)
+ x86-64-unknown-windows-msvc +
+
target
+
the system for which the compiler tools generate code
+ aarch64-unknown-linux-android
+ NOTE this is not really supported properly in the original + build system +
+
+ +## Desired State + +It is preferable to instead consider the full package build as a set of builds +where you are doing normal builds with a differing set of `build` and `host` +values. That is, you can consider the regular host build (e.g. Linux) as: + +```Swift +[ + /* compiler */ (build: "x86_64-unknown-linux-gnu", host: "x86_64-unknown-linux-gnu"), + /* runtime */ (build: "x86_64-unknown-linux-gnu", host: "x86_64-unknown-linux-gnu"), + /* stdlib */ (build: "x86_64-unknown-linux-gnu", host: "x86_64-unknown-linux-gnu"), +] +``` + +Or for more complicated scenarios such as Darwin where you may be compiling for +Darwin and iOS as: + +```Swift +[ + /* compiler */ (build: "x86_64-apple-macosx10.14", host: "x86_64-apple-macosx10.14"), + /* runtime */ (build: "x86_64-apple-macosx10.14", host: "x86_64-apple-macosx10.14"), + /* stdlib */ (build: "x86_64-apple-macosx10.14", host: "x86_64-apple-macosx10.14"), + /* runtime */ (build: "x86_64-apple-macosx10.14", host: "armv7-apple-ios12.3"), + /* stdlib */ (build: "x86_64-apple-macosx10.14", host: "armv7-apple-ios12.3"), +] +``` + +This simplifies the build terminology by having to only deal with the terms +`build` and `host` which removes some of the confusion caused by the original +build system's implementation creating multiple terms and being designed around +the needs of the Darwin build. + +This also generalises to allow for multiple cross-compilations in a single build +invocation which allows the functionality currently available only on Darwin to +be applied to all the targets that Swift supports (and may support in the +future). + +Doing so also enables the build system to be trimmed significantly as we can use +CMake as it was designed to while retaining the ability to cross-compile. The +cross-compilation model for CMake involves invoking `cmake` multiple times with +`CMAKE_SYSTEM_NAME` and `CMAKE_SYSTEM_PROCESSOR` set appropriately to the host +that you would like to build for. This ensures that host specific behaviour is +explicitly handled properly (e.g. import libraries on Windows - something which +was shoehorned into the original CMake implementation in CMake) and corrects the +behaviour of the cmake `install` command. + +Another bit of functional flexibility that this system enables is the ability to +allow developers to focus entirely on the area that they are working in. The +runtime and standard library can be separated and built with a prebuilt +(nightly) release of the compiler toolchain, or focus entirely on building the +standard library for a certain set of targets. + +By organising the standard library build as a regular library, we enable the +ability to use the LLVM runtimes build to cross-compile the standard library for +multiple targets as long as the dependent system libraries are available at +build time. + +The ability to build the runtime components for different hosts allows building +the Swift SDK for various hosts on a single machine. This enables the build of +the android SDK and Linux SDKs on Windows and vice versa. + +Note that none of the changes described here prevent the workflows that are +possible with build-script today. + From 0f25b88bae65fb186e13479a570d688f0c650974 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 23 Jul 2019 09:31:06 -0700 Subject: [PATCH 020/283] docs: add initial documentation for android SDK This adds documentation to build the android SDK (standard library, libdispatch, foundation, and XCTest) for Android on Windows. This is pretty bare abut provides a line in the sand from where we can improve the documentation for cross-compilation, building just the SDK, and android specific items. --- docs/AndroidBuild.md | 181 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 docs/AndroidBuild.md diff --git a/docs/AndroidBuild.md b/docs/AndroidBuild.md new file mode 100644 index 0000000000000..275d98a843d90 --- /dev/null +++ b/docs/AndroidBuild.md @@ -0,0 +1,181 @@ +# Building Swift SDK for Android on Windows + +Visual Studio 2019 or newer is needed to build the Swift SDK for Android on +Windows. + +## 1. Install Dependencies +- Install the latest version of [Visual Studio](https://www.visualstudio.com/downloads/) +- Make sure to include the android NDK in your installation. + +## 1. Clone the repositories +1. Configure git to work with Unix file endings +1. Clone `apple/swift-llvm` into a directory named `llvm` +1. Clone `apple/swift-corelibs-libdispatch` into a directory named `swift-corelibs-libdispatch` +1. Clone `apple/swift-corelibs-foundation` into a directory named `swift-corelibs-foundation`G +1. Clone `apple/swift-corelibs-xctest` into a directory named `swift-corelibs-xctest` +1. Clone `compnerd/swift-windows` into a directory named `swift-windows` + +- Currently, other repositories in the Swift project have not been tested and + may not be supported. + +This guide assumes that your sources live at the root of `S:`. If your sources +live elsewhere, you can create a subsitution for this: + +```cmd +subst S: +``` + +```cmd +S: +git clone https://github.com/apple/swift-llvm llvm +git clone https://github.com/apple/swift-corelibs-libdispatch swift-corelibs-libdispatch +git clone https://github.com/apple/swift-corelibs-foundation swift-corelibs-foundation +git clone https://github.com/apple/swift-corelibs-xctest swift-corelibs-xctest +git clone https://github.com/compnerd/swift-windows swift-windows +``` + +## 1. Acquire the lastest toolchain and dependencies + +1. Download the toolchain, ICU, libxml2, and curl for android from + [Azure](https://dev.azure.com/compnerd/windows-swift) into `S:\b\a\Library`. + +- You can alternatively use `Download-AndroidArtifacts.ps1` from + [compnerd/windows-swift](https://www.github.com/compnerd/windows-swift) under + the utilities directory. This will implicitly setup the requisite directory + structure. + +## 1. Configure LLVM + +```cmd +md S:\b\a\llvm +cd S:\b\a\llvm +cmake -C S:\swift-windows\cmake\caches\android-armv7.cmake ^ + -G Ninja ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-windows\cmake\toolchains\android.toolchain.cmake ^ + -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ + -DLLVM_HOST_TRIPLE=armv7-unknown-linux-androideabi ^ + S:/llvm +``` + +## 1. Build and install the standard library + +- We must build and install the standard library to build the remainder of the + SDK + +```cmd +md S:\b\a\stdlib +cd S:\b\a\stdlib +cmake -C S:\windows-swift\cmake\caches\android-armv7.cmake ^ + -C S:\windows-swift\cmake\caches\swift-stdlib-android-armv7.cmake ^ + -G Ninja ^ + -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ + -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ + -DCMAKE_TOOLCHAIN_FILE=S:\windows-swift\cmake\toolchains\android.toolchain.cmake ^ + -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ + -DLLVM_DIR=S:/b/a/llvm/lib/cmake/llvm ^ + -DSWIFT_NATIVE_SWIFT_TOOLS_PATH=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin ^ + -DSWIFT_ANDROID_armv7_ICU_UC_INCLUDE=S:/b/a/Library/icu-64/usr/include/unicode ^ + -DSWIFT_ANDROID_armv7_ICU_UC=S:/b/a/Library/icu-64/usr/lib/libicuuc64.so ^ + -DSWIFT_ANDROID_armv7_ICU_I18N_INCLUDE=S:/b/a/Library/icu-64/usr/include ^ + -DSWIFT_ANDROID_armv7_ICU_I18N=S:/b/a/Library/icu-64/usr/lib/libicuin64.so ^ + S:/swift +ninja +ninja install +``` + +## 1. Build libdispatch + +- We *cannot* install libdispatch until after all builds are complete as that + will cause the Dispatch module to be imported twice and fail to build. + +```cmd +md S:\b\a\libdispatch +cd S:\b\a\libdispatch +cmake -C S:\windows-swift\cmake\caches\android-armv7.cmake ^ + -DSWIFT_ANDROID_SDK=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk ^ + -C S:\windows-swift\cmake\caches\android-armv7-swift-flags.cmake ^ + -G Ninja ^ + -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ + -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ + -DCMAKE_SWIFT_COMPILER=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/swiftc.exe ^ + -DCMAKE_TOOLCHAIN_FILE=S:\windows-swift\cmake\toolchains\android.toolchain.cmake ^ + -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ + -DENABLE_SWIFT=YES ^ + -DENABLE_TESTING=NO ^ + S:/swift-corelibs-libdispatch +ninja +``` + +## 1. Build foundation + +```cmd +md S:\b\a\foundation +cd S:\b\a\foundation +cmake -C S:\windows-swift\cmake\caches\android-armv7.cmake ^ + -DSWIFT_ANDROID_SDK=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk ^ + -C S:\windows-swift\cmake\caches\android-armv7-swift-flags.cmake ^ + -G Ninja ^ + -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ + -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ + -DCMAKE_SWIFT_COMPILER=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/swiftc.exe ^ + -DCMAKE_TOOLCHAIN_FILE=S:\windows-swift\cmake\toolchains\android.toolchain.cmake ^ + -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ + -DCURL_LIBRARY=S:/b/a/Library/libcurl-development/usr/lib/libcurl.a ^ + -DCURL_INCLUDE_DIR=S:/b/a/Library/libcurl-development/usr/include ^ + -DICU_INCLUDE_DIR=S:/b/a/Library/icu-64/usr/include ^ + -DICU_UC_LIBRARY=S:/b/a/Library/icu-64/usr/lib/libicuuc64.so ^ + -DICU_UC_LIBRARY_RELEASE=S:/b/a/Library/icu-64/usr/lib/libicuuc64.so ^ + -DICU_I18N_LIBRARY=S:/b/a/Library/icu-64/usr/lib/libiucin64.so ^ + -DICU_I18N_LIBRARY_RELEASE=S:/b/a/Library/icu-64/usr/lib/libicuin64.so ^ + -DLIBXML2_LIBRARY=S:/b/a/Library/libxml2-development/usr/lib/libxml2.a ^ + -DLIBXML2_INCLUDE_DIR=S:/b/a/Library/libxml2-development/usr/include/libxml2 ^ + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=S:/swift-corelibs-libdispatch ^ + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=S:/b/a/libdispatch ^ + S:/swift-corelibs-foundation +ninja +``` + +## 1. Build XCTest + +```cmd +md S:\b\a\xctest +cd S:\b\a\xctest +cmake -C S:\swift-windows\cmake\caches\android-armv7.cmake ^ + -C S:\swift-windows\cmake\caches\android-armv7-swift-flags.cmake ^ + -G Ninja ^ + -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ + -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ + -DCMAKE_SWIFT_COMPILER=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/swiftc.exe ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-windows\cmake\toolchains\android.toolchain.cmake ^ + -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ + -DSWIFT_ANDROID_SDK=S:/b/a/Library/Developer/Platforms/andrfoid.platform/Developer/SDKs/android.sdk ^ + -DXCTEST_PATH_TO_FOUNDATION_BUILD=S:/b/a/foundation ^ + -DXCTEST_PATH_TO_LIBDISPATCH_SOURCE=S:/swift-corelibs-libdispatch ^ + -DXCTEST_PATH_TO_LIBDISPATCH_BUILD=S:/b/a/libdispatch ^ + -DENABLE_TESTING=NO ^ + S:/swift-corelibs-foundation +ninja +``` + +## 1. Install libdispatch + +```cmd +cd S:\b\a\libdispatch +ninja install +``` + +## 1. Install Foundation + +```cmd +cd S:\b\a\foundation +ninja install +``` + +## 1. Install XCTest + +```cmd +cd S:\b\a\xctest +ninja install +``` + From b059fd5b54110c701b2c3edf5227b31b4cca721e Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Thu, 1 Aug 2019 14:19:38 -0700 Subject: [PATCH 021/283] Remove build-script-impl target calculation It's duplicated between the build-script and the -impl, removing code from build-script-impl is good. --- utils/build-script-impl | 222 ---------------------------------------- 1 file changed, 222 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 293e87c5237d0..2e0652da1e588 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1320,234 +1320,12 @@ function get_host_specific_variable() { } function calculate_targets_for_host() { - local host=$1 - - SWIFT_STDLIB_TARGETS=() - SWIFT_SDKS=() - SWIFT_BENCHMARK_TARGETS=() - SWIFT_RUN_BENCHMARK_TARGETS=() - SWIFT_TEST_TARGETS=() - - # Get the list of Target platforms for the Host - local stdlib_targets=($(get_stdlib_targets_for_host ${host})) - - for stdlib_deployment_target in "${stdlib_targets[@]}"; do - local swift_sdk= - local is_in_build_list=$(should_build_stdlib_target ${stdlib_deployment_target} ${host}) - local build_for_this_target=1 - local test_this_target=1 - local test_host_only= - local build_benchmark_this_target= - local build_external_benchmark_this_target= - local test_benchmark_this_target= - - case ${stdlib_deployment_target} in - linux-*) - swift_sdk="LINUX" - build_for_this_target=$(not ${SKIP_BUILD_LINUX}) - test_this_target=$(not ${SKIP_TEST_LINUX}) - ;; - freebsd-*) - swift_sdk="FREEBSD" - build_for_this_target=$(not ${SKIP_BUILD_FREEBSD}) - test_this_target=$(not ${SKIP_TEST_FREEBSD}) - ;; - cygwin-*) - swift_sdk="CYGWIN" - build_for_this_target=$(not ${SKIP_BUILD_CYGWIN}) - test_this_target=$(not ${SKIP_TEST_CYGWIN}) - ;; - haiku-*) - swift_sdk="HAIKU" - build_for_this_target=$(not ${SKIP_BUILD_HAIKU}) - test_this_target=$(not ${SKIP_TEST_HAIKU}) - ;; - macosx-*) - swift_sdk="OSX" - build_for_this_target=$(not ${SKIP_BUILD_OSX}) - test_this_target=$(not ${SKIP_TEST_OSX}) - build_benchmark_this_target=$(not ${SKIP_BUILD_OSX}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_OSX}) - test_benchmark_this_target=$(not ${SKIP_BUILD_OSX}) - ;; - iphoneos-*) - swift_sdk="IOS" - build_for_this_target=$(not ${SKIP_BUILD_IOS_DEVICE}) - if [[ ! "${SKIP_TEST_IOS_HOST}" ]] ; then - test_host_only=1 - else - test_this_target= - fi - build_benchmark_this_target=$(not ${SKIP_BUILD_IOS_DEVICE}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_IOS_DEVICE}) - - # Never build iOS armv7s benchmarks. - if [[ "${stdlib_deployment_target}" == "iphoneos-armv7s" ]]; then - build_benchmark_this_target= - build_external_benchmark_this_target= - fi - ;; - iphonesimulator-x86_64) - swift_sdk="IOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_IOS_SIMULATOR}) - test_this_target=$(not ${SKIP_TEST_IOS_SIMULATOR}) - ;; - iphonesimulator-i386) - swift_sdk="IOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_IOS_SIMULATOR}) - if [[ "${SKIP_TEST_IOS_SIMULATOR}" == "1" ]] ; then - SKIP_TEST_IOS_32BIT_SIMULATOR="${SKIP_TEST_IOS_SIMULATOR}" - fi - test_this_target=$(not ${SKIP_TEST_IOS_32BIT_SIMULATOR}) - ;; - appletvos-*) - swift_sdk="TVOS" - build_for_this_target=$(not ${SKIP_BUILD_TVOS_DEVICE}) - if [[ ! "${SKIP_TEST_TVOS_HOST}" ]] ; then - test_host_only=1 - else - test_this_target= - fi - build_benchmark_this_target=$(not ${SKIP_BUILD_TVOS_DEVICE}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_TVOS_DEVICE}) - ;; - appletvsimulator-*) - swift_sdk="TVOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_TVOS_SIMULATOR}) - test_this_target=$(not ${SKIP_TEST_TVOS_SIMULATOR}) - ;; - watchos-*) - swift_sdk="WATCHOS" - build_for_this_target=$(not ${SKIP_BUILD_WATCHOS_DEVICE}) - if [[ ! "${SKIP_TEST_WATCHOS_HOST}" ]] ; then - test_host_only=1 - else - test_this_target= - fi - build_benchmark_this_target=$(not ${SKIP_BUILD_WATCHOS_DEVICE}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_WATCHOS_DEVICE}) - ;; - watchsimulator-*) - swift_sdk="WATCHOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_WATCHOS_SIMULATOR}) - test_this_target=$(not ${SKIP_TEST_WATCHOS_SIMULATOR}) - ;; - android-*) - swift_sdk="ANDROID" - build_for_this_target=$(not ${SKIP_BUILD_ANDROID}) - if [[ ! "${SKIP_TEST_ANDROID_HOST}" ]] ; then - test_host_only=1 - else - test_this_target=$(not ${SKIP_TEST_ANDROID}) - fi - ;; - *) - echo "Unknown compiler deployment target: ${stdlib_deployment_target}" - exit 1 - ;; - esac - - SWIFT_SDKS+=("${swift_sdk}") - - if [[ "${build_for_this_target}" ]] && [[ "${is_in_build_list}" ]]; then - - if [[ "${BUILD_SWIFT_STDLIB_UNITTEST_EXTRA}" == "1" ]] ; then - SWIFT_STDLIB_TARGETS+=("swift-stdlib-${stdlib_deployment_target}") - else - if [[ "${VALIDATION_TEST}" == "1" || "${LONG_TEST}" == "1" ]] ; then - SWIFT_STDLIB_TARGETS+=("swift-stdlib-${stdlib_deployment_target}") - else - SWIFT_STDLIB_TARGETS+=("swift-test-stdlib-${stdlib_deployment_target}") - fi - fi - fi - if [[ "${build_benchmark_this_target}" ]] && [[ "${is_in_build_list}" ]]; then - SWIFT_BENCHMARK_TARGETS+=("swift-benchmark-${stdlib_deployment_target}") - if [[ $(not ${SKIP_TEST_BENCHMARKS}) ]] ; then - SWIFT_RUN_BENCHMARK_TARGETS+=("check-swift-benchmark-${stdlib_deployment_target}") - fi - fi - - if [[ "$(true_false ${SKIP_BUILD_EXTERNAL_BENCHMARKS})" == "FALSE" ]] && - [[ "${build_external_benchmark_this_target}" ]] && - [[ "${is_in_build_list}" ]] ; then - SWIFT_BENCHMARK_TARGETS+=("swift-benchmark-${stdlib_deployment_target}-external") - if [[ $(not ${SKIP_TEST_BENCHMARKS}) ]] ; then - SWIFT_RUN_BENCHMARK_TARGETS+=("check-swift-benchmark-${stdlib_deployment_target}-external") - fi - fi - - if [[ "${test_this_target}" ]] && [[ "${is_in_build_list}" ]]; then - test_target_suffix="" - if [[ -n "${test_host_only}" ]] ; then - test_target_suffix="-only_non_executable" - elif [[ -n "${ONLY_EXECUTABLE_TEST}" ]] ; then - test_target_suffix="-only_executable" - fi - - test_subset_target_suffix="" - if [[ "${VALIDATION_TEST}" == "1" ]] ; then - if [[ "${LONG_TEST}" == "1" ]] ; then - test_subset_target_suffix="-all" - else - test_subset_target_suffix="-validation" - fi - else - if [[ "${LONG_TEST}" == "1" ]] ; then - test_subset_target_suffix="-only_long" - fi - fi - - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}${test_target_suffix}-${stdlib_deployment_target}") - if [[ $(not ${SKIP_TEST_OPTIMIZED}) && ! -n "${test_host_only}" ]] ; then - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}-optimize-${stdlib_deployment_target}") - fi - if [[ $(not ${SKIP_TEST_OPTIMIZE_FOR_SIZE}) && ! -n "${test_host_only}" ]] ; then - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}-optimize_size-${stdlib_deployment_target}") - fi - if [[ $(not ${SKIP_TEST_OPTIMIZE_NONE_WITH_IMPLICIT_DYNAMIC}) && ! -n "${test_host_only}" ]] ; then - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}-optimize_none_with_implicit_dynamic-${stdlib_deployment_target}") - fi - fi - done - - # Filter duplicate SWIFT_SDKs - # We will get them if building for multiple architecture variants - SWIFT_SDKS=($(echo "${SWIFT_SDKS[@]}" | tr " " "\n" | sort -u | tr "\n" " ")) - # Get the values passed by `build-script`. - LEGACY_SWIFT_STDLIB_TARGETS=(${SWIFT_STDLIB_TARGETS[@]}) - LEGACY_SWIFT_SDKS=(${SWIFT_SDKS[@]}) - LEGACY_SWIFT_BENCHMARK_TARGETS=(${SWIFT_BENCHMARK_TARGETS[@]}) - LEGACY_SWIFT_RUN_BENCHMARK_TARGETS=(${SWIFT_RUN_BENCHMARK_TARGETS[@]}) - LEGACY_SWIFT_TEST_TARGETS=(${SWIFT_TEST_TARGETS[@]}) SWIFT_STDLIB_TARGETS=($(get_host_specific_variable ${host} SWIFT_STDLIB_TARGETS)) SWIFT_SDKS=($(get_host_specific_variable ${host} SWIFT_SDKS)) SWIFT_BENCHMARK_TARGETS=($(get_host_specific_variable ${host} SWIFT_BENCHMARK_TARGETS)) SWIFT_RUN_BENCHMARK_TARGETS=($(get_host_specific_variable ${host} SWIFT_RUN_BENCHMARK_TARGETS)) SWIFT_TEST_TARGETS=($(get_host_specific_variable ${host} SWIFT_TEST_TARGETS)) - - # Validate the parameters match. - if [[ "${SWIFT_STDLIB_TARGETS[*]}" != "${LEGACY_SWIFT_STDLIB_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_STDLIB_TARGETS': '%s' vs '%s'\n" "${SWIFT_STDLIB_TARGETS[*]}" "${LEGACY_SWIFT_STDLIB_TARGETS[*]}" - exit 1 - fi - if [[ "${SWIFT_SDKS[*]}" != "${LEGACY_SWIFT_SDKS[*]}" ]]; then - printf "error: invalid build-script for 'SWIFT_SDKS' refactor: '%s' vs '%s'\n" "${SWIFT_SDKS[*]}" "${LEGACY_SWIFT_SDKS[*]}" - exit 1 - fi - if [[ "${SWIFT_BENCHMARK_TARGETS[*]}" != "${LEGACY_SWIFT_BENCHMARK_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_BENCHMARK_TARGETS': '%s' vs '%s'\n" "${SWIFT_BENCHMARK_TARGETS[*]}" "${LEGACY_SWIFT_BENCHMARK_TARGETS[*]}" - exit 1 - fi - if [[ "${SWIFT_RUN_BENCHMARK_TARGETS[*]}" != "${LEGACY_SWIFT_RUN_BENCHMARK_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_RUN_BENCHMARK_TARGETS': '%s' vs '%s'\n" "${SWIFT_RUN_BENCHMARK_TARGETS[*]}" "${LEGACY_SWIFT_RUN_BENCHMARK_TARGETS[*]}" - exit 1 - fi - if [[ "${SWIFT_TEST_TARGETS[*]}" != "${LEGACY_SWIFT_TEST_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_TEST_TARGETS': '%s' vs '%s'\n" "${SWIFT_TEST_TARGETS[*]}" "${LEGACY_SWIFT_TEST_TARGETS[*]}" - exit 1 - fi } From a38c147152059be404a6f99947278b72181a20a3 Mon Sep 17 00:00:00 2001 From: Azoy Date: Mon, 7 Oct 2019 21:45:06 -0400 Subject: [PATCH 022/283] Missing type metadata arg in comments --- include/swift/ABI/ValueWitness.def | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/swift/ABI/ValueWitness.def b/include/swift/ABI/ValueWitness.def index d07eca212993c..646deb4bd0d78 100644 --- a/include/swift/ABI/ValueWitness.def +++ b/include/swift/ABI/ValueWitness.def @@ -160,7 +160,8 @@ FUNCTION_VALUE_WITNESS(assignWithTake, MUTABLE_VALUE_TYPE, (MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE)) -/// unsigned (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases) +/// unsigned (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases, +/// M *self); /// Given an instance of valid single payload enum with a payload of this /// witness table's type (e.g Optional) , get the tag of the enum. FUNCTION_VALUE_WITNESS(getEnumTagSinglePayload, @@ -169,7 +170,7 @@ FUNCTION_VALUE_WITNESS(getEnumTagSinglePayload, (IMMUTABLE_VALUE_TYPE, UINT_TYPE, TYPE_TYPE)) /// void (*storeEnumTagSinglePayload)(T* enum, UINT_TYPE whichCase, -/// UINT_TYPE emptyCases) +/// UINT_TYPE emptyCases, M *self); /// Given uninitialized memory for an instance of a single payload enum with a /// payload of this witness table's type (e.g Optional), store the /// tag. From f67510277ff24f5be8179b3882d3e05c5501103f Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Tue, 29 Oct 2019 20:04:16 +0000 Subject: [PATCH 023/283] ICU: Update to version 65.1 on Linux --- utils/update_checkout/update-checkout-config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 4dcf29635eea3..c374271b6ffa6 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -61,7 +61,7 @@ "swift-integration-tests": "master", "swift-xcode-playground-support": "master", "ninja": "release", - "icu": "release-61-1", + "icu": "release-65-1", "cmake": "v3.15.1", "indexstore-db": "master", "sourcekit-lsp": "master", @@ -86,7 +86,7 @@ "swift-integration-tests": "master", "swift-xcode-playground-support": "master", "ninja": "release", - "icu": "release-61-1", + "icu": "release-65-1", "cmake": "v3.15.1", "indexstore-db": "master", "sourcekit-lsp": "master", From 628d7bf1e920829941e4fefc4547f08fbce907df Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 29 Oct 2019 14:44:34 -0700 Subject: [PATCH 024/283] [docs] LibraryEvolution: Remove discussion of versioning... ...as well as the "Open Issues" section and some future directions. I'll put this back in a different form soon. --- docs/LibraryEvolution.rst | 624 ++------------------------------------ 1 file changed, 26 insertions(+), 598 deletions(-) diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index a66868a9bf4fe..731141160c4b8 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -91,8 +91,8 @@ library, as used by the `Swift Package Manager`_). Because a client always uses a particular version of such a library, there is no need to worry about backwards- or forwards-compatibility at the binary level. Just as developers with a single app target are not forced to think about access control, anyone -writing a bundled library should not be required to use any of the annotations -described below in order to achieve full performance. +writing a bundled library should (ideally) not be required to use any of the +annotations described below in order to achieve full performance. .. _Swift Package Manager: https://swift.org/package-manager/ @@ -112,164 +112,6 @@ changing the fields in a struct will not automatically cause problems for existing clients, so we say the struct is "resilient". -Using Versioned API -=================== - -References to a versioned API must always be guarded with the appropriate -availability checks. This means that any client entities that rely on certain -APIs from a library must themselves be restricted to contexts in which those -APIs are available. This is accomplished using the ``@available`` attribute, by -specifying the name of the client library along with the required version:: - - // Client code - @available(Magician 1.5) - class CrystalBallView : MagicView { /*...*/ } - -Library versions can also be checked dynamically using ``#available``, allowing -for fallback behavior when the requested library version is not present:: - - func scareMySiblings() { - if #available(Magician 1.2) { - summonDemons() - } else { - print("BOO!!") - } - } - -.. note:: - - Possible implementations include generating a hidden symbol into a library, - or putting the version number in some kind of metadata, like the Info.plist - in a framework bundle on Darwin platforms. - -This is essentially the same model as the availability checking released in -Swift 2.0, but generalized for checking library versions instead of just OS -versions. - - -Declaring Library Version Dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Swift's current availability model includes the notion of a *minimum deployment -target,* the version of an OS that must be present for the program being -compiled to run at all. For example, a program compiled with a minimum -deployment target of iOS 9.2 will not launch on iOS 9.0. - -The generalized model above suggests being able to make similar guarantees for -individual libraries. For example, a client program may depend on version 1.1 -of the "Magician" library; trying to run using version 1.0 will result in -errors. By declaring this at compile-time, the client code can omit -``@available`` and ``#available`` checks that are satisfied by the minimum -library version. - -Both the syntax and enforcement of this feature are not covered by this -document. - - -Publishing Versioned API -======================== - -A library's API is already marked with the ``public`` modifier, but if a -client wants to work with multiple releases of the library, the API needs -versioning information as well. A *versioned entity* represents anything with a -run-time presence that a client may rely on; its version records when the entity -was first exposed publicly in its library. Put another way, it is the oldest -version of the library where the entity may be used. - -- Classes, structs, enums, and protocols may all be versioned entities. -- Methods, properties, subscripts, and initializers may be versioned entities. -- Top-level functions, variables, and constants may be versioned entities. -- Protocol conformances may be versioned entities, despite not explicitly having - a declaration in Swift, because a client may depend on them. - See `New Conformances`_, below. -- Typealiases are treated as versioned entities for the purpose of verifying - availability, even though they have no run-time presence. - -In a versioned library, any top-level public entity from the list above may not -be made ``public`` (or ``open``) without an appropriate version. A public -entity declared within a versioned type (or an extension of a versioned type) -will default to having the same version as the type. - -In this document, the term "public" includes classes and members marked -``open``. - -Code within a library may generally use all other entities declared within the -library (barring their own availability checks), since the entire library is -shipped as a unit. That is, even if a particular API was introduced in v1.0, -its (non-public) implementation may refer to APIs introduced in later versions. - -Certain uses of ``internal`` entities require them to be part of a library's -binary interface, which means they need to be versioned as well. See -`Versioning Internal Declarations`_ below. - -In addition to versioned entities, there are also attributes that are safe to -add to declarations when releasing a new version of a library. In most cases, -clients can only take advantage of the attributes when using the new release of -the library, and therefore the attributes also need to record the version in -which they were introduced; these are called *versioned attributes.* If the -version is omitted, it is assumed to be the version of the declaration to which -the attribute is attached. - -The syntax for marking an entity as versioned has not yet been decided, but the -rest of this document will use syntax #1 described below. - - -Syntax #1: Attributes -~~~~~~~~~~~~~~~~~~~~~ - -:: - - @available(1.2) - public func summonDemons() - - @available(1.0) @inlinable(1.2) - public func summonElves() - -Using the same attribute for both publishing and using versioned APIs helps tie -the feature together and enforces a consistent set of rules. However, there are -several other annotations described later in this document that also need -versioning information, and it may not be obvious what the version number means -outside the context of ``available``. - - -Syntax #2: Version Blocks -~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - #version(1.2) - public func summonDemons() - - #version(1.0) {} - #version(1.2) { @inlinable } - public func summonElves() - -Since there are potentially many annotations on a declaration that need -versioning information, it may make sense to group them together in some way. -Only certain annotations would support being versioned in this way. - - -Syntax #3: The ``public`` modifier -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - public(1.2) func summonDemons() - - /* @inlinable ?? */ - public(1.0) func summonElves() - -Putting the version on the public modifier is the most concise option. However, -there's no obvious syntax here for adding versions to other annotations that -may apply to a declaration. - -(Also, at one point there was a proposal to tag API only intended for certain -clients using a similar syntax: ``public("Foundation")``, for example, for APIs -only meant to be used by Foundation. These could then be stripped out of the -public interface for a framework before being widely distributed. But that -could easily use an alternate syntax.) - - Supported Evolution =================== @@ -1173,115 +1015,37 @@ clients, they cannot recompile their code and get correct behavior. Top-level typealiases only exist at compile-time, so changing the underlying type of one is a `binary-compatible source-breaking change`. However, if the -typealias is *used* in the type of any `versioned entity` in a library, it +typealias is *used* in the type of any ABI-public declaration in a library, it may be an actual breaking change and would not be permitted. It is always permitted to change the *use* of a public typealias to its underlying type, and vice versa, at any location in the program. -Typealiases are `versioned ` despite being compile-time +Typealiases require availability annotations despite being compile-time constructs in order to verify the availability of their underlying types. -A Unifying Theme -~~~~~~~~~~~~~~~~ - -So far this document has talked about ways to give up flexibility for several -different kinds of declarations: namely ``@inlinable`` for functions, and -``@frozen`` for enums and structs. Each of these has a different set of -constraints it enforces on the library author and promises it makes to clients. -However, they follow a common theme of giving up the flexibility of future -changes in exchange for improved performance and perhaps some semantic -guarantees. Therefore, these attributes are informally referred to as -"fragility attributes". - - -Versioning Internal Declarations -================================ - -The initial discussion on versioning focused on public APIs, making sure -that a client knows what features they can use when a specific version of a -library is present. Inlinable functions have much the same constraints, except -the inlinable function is the client and the entities being used may not be -public. - -Adding a versioning annotation to an ``internal`` entity promises that the -entity will be available at link time in the containing module's binary. This -makes it safe to refer to such an entity from an inlinable function. If the -entity is ever made ``public`` or ``open``, its availability should not be -changed; not only is it safe for new clients to rely on it, but *existing* -clients require its presence as well. - -.. note:: - - Why isn't this a special form of ``public``? Because we don't want it to - imply everything that ``public`` does, such as requiring overrides to be - ``public``. - -In libraries without binary compatibility concerns, the equivalent annotation -is ``@usableFromInline``, since inlinable functions are the only way that a -non-public entity can be referenced from outside of a module. - -Because a versioned class member may eventually be made ``open``, it must be -assumed that new overrides may eventually appear from outside the module if the -class is marked ``open`` unless the member is marked ``final``. - -Non-public conformances are never considered versioned, even if both the -conforming type and the protocol are versioned. A conformance is considered -public if and only if both the conforming type and protocol are public. - -Entities declared ``private`` or ``fileprivate`` may not be versioned; the -mangled name of such an entity includes an identifier based on the containing -file, which means moving the declaration to another file changes the entity's -mangled name. This implies that a client would not be able to find the entity -at run time if the source code is reorganized, which is unacceptable. - -.. note:: - - There are ways around this limitation, the most simple being that versioned - ``private`` entities are subject to the same cross-file redeclaration rules - as ``internal`` entities. However, this is a purely additive feature, so to - keep things simple we'll stick with the basics. - -We could do away with the entire feature if we restricted inlinable functions -and frozen structs to only refer to public entities. However, this -removes one of the primary reasons to make something inlinable: to allow -efficient access to a type while still protecting its invariants. - - -"Backdating" -============ - -*Backdating* refers to releasing a new version of a library that contains -changes, but pretending those changes were made in a previous version of the -library. For example, you might want to release version 1.2 of the "Magician" -library, but pretend that the "SpellIncantation" struct was frozen -since its introduction in version 1.0. - -**This is not safe.** +``@usableFromInline`` +===================== -Backdating the availability a versioned entity that was previously non-public -is clearly not safe: older versions of the library will not expose the entity -as part of their ABI. What may be less obvious is that the fragility attributes -likewise are not safe to backdate, even if you know the attributes could have -been added in the past. To give one example, the presence of ``@frozen`` may -affect the layout and calling conventions for an enum or struct. +Adding ``@usableFromInline`` to an ``internal`` entity promises that the entity +will be available at link time in the containing module's binary. This makes it +safe to refer to such an entity from an inlinable function or in the stored +properties of a frozen struct. ``@usableFromInline`` declarations shipped as +part of an OS should have availability just like ``public`` declarations; if +the entity is ever made ``public`` or ``open``, its availability should not be +changed. .. note:: - If we add an "SPI" feature, such that the use of specific public entities - is limited to certain clients, it *will* be safe to change the set of - clients, or remove the restriction altogether. In fact, in such cases the - library author is *required* to *not* change the availability info that was - originally presented for the limited set of clients, since as mentioned - above this may affect how those existing clients use the entities declared - in the library. + Why isn't ``@usableFromInline`` a special form of ``public``? Because we + don't want it to imply everything that ``public`` does, such as requiring + overrides to be ``public``. -The one exception is ``@inlinable``, which does not change how a function is -called or otherwise used at the ABI level. If the implementation being provided -is compatible with a previous version of a library, and the function was -present and public (or `versioned `) there, then the library -author may choose to backdate the ``@inlinable`` annotation. +Because a ``@usableFromInline`` class member may eventually be made ``open``, +the compiler must assume that new overrides may eventually appear from outside +the module if the class is marked ``open`` unless the member is marked +``final``. Optimization @@ -1299,324 +1063,16 @@ several ways. For example: - A struct may have additional members in the future, so client code must not assume it fits in any fixed-sized allocation. -In order to make sure client code doesn't make unsafe assumptions, queries -about properties that may change between library versions must be parameterized -with the `availability context` that is using the entity. An availability -context is a set of minimum platform and library versions that can be assumed -present for code executing within the context. (See `Declaring Library Version -Dependencies`_.) This allows the compiler to answer the question, "Given what I -know about where this code will be executed, what can I assume about a -particular entity being used?". - If the entity is declared within the same module as the code that's using it, then the code is permitted to know all the details of how the entity is declared. After all, if the entity is changed, the code that's using it will be -recompiled. - -However, if the entity is declared in another module, then the code using it -must be more conservative, and will therefore receive more conservative answers -to its queries. For example, a stored property may report itself as computed. - -The presence of versioned fragility attributes makes the situation more -complicated. Within a client function that requires version 1.5 of a particular -library, the compiler should be able to take advantage of any fragility -information (and performance assertions) introduced prior to version 1.5. - - -Inlinable Code -~~~~~~~~~~~~~~ - -By default, the availability context for a library always includes the latest -version of the library itself, since that code is always distributed as a unit. -However, this is not true for functions that have been marked inlinable (see -`Inlinable Functions`_ above). Inlinable code must be treated as if it is -outside the current module, since once it's inlined it will be. - -For inlinable code, the availability context is exactly the same as the -equivalent non-inlinable code except that the assumed version of the -containing library is the version attached to the ``@inlinable`` attribute, or -the version of the library in which the entity was introduced, and any `library -version dependencies <#declaring-library-version-dependencies>`_ or minimum -deployment target must be specified explicitly using ``@available``. Code -within this context must be treated as if the containing library were just a -normal dependency. - -A versioned inlinable function still has an exported symbol in the library -binary, which may be used when the function is referenced from a client rather -than called. This version of the function is not subject to the same -restrictions as the version that may be inlined, and so it may be desirable to -compile a function twice: once for inlining, once for maximum performance. - -If the body of an inlinable function is used in any way by a client module -(say, to determine that it does not read any global variables), that module -must take care to emit and use its own copy of the function. This is because -analysis of the function body may not apply to the version of the function -currently in the library. - - -Local Availability Contexts -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Swift availability contexts aren't just at the declaration level; they also -cover specific regions of code inside function bodies as well. These "local" -constructs are formed using the ``#available`` construct, which performs a -dynamic check. - -In theory, it would be legal to allow code dominated by a ``#available`` check -to take advantage of additional fragility information introduced by the more -restrictive dependencies that were checked for. However, this is an additional -optimization that may be complicated to implement (and even to represent -properly in SIL), and so it is not a first priority. - - -Other Promises About Types -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Advanced users may want to promise more specific things about various types. -These are similar to the internal ``effects`` attribute we have for functions, -except that they can be enforced by the compiler. - -- ``trivial``: Promises that assignment just requires a fixed-size bit-for-bit - copy without any indirection or reference-counting operations. - -- ``maximumFootprint(sizeInBits: N, alignmentInBits: A)``: Promises that the - type's size and required alignment are at most N bits and A bits, - respectively. (Both may be smaller.) - -- ``fixedSize``: Promises that the type has *some* size known at compile-time, - allowing optimizations like promoting allocations to the stack. Only applies - to fixed-contents structs and closed enums, which can already infer this - information; the explicit annotation allows it to be enforced. - -Collectively these features are known as "performance assertions", to -underscore the fact that they do not affect how a type is used at the source -level, but do allow for additional optimizations. We may also expose some of -these qualities to static or dynamic queries for performance-sensitive code. - -.. note:: Previous revisions of this document contained a ``noPayload`` - assertion for enums. However, this doesn't actually offer any additional - optimization opportunities over combining ``trivial`` with - ``maximumFootprint``, and the latter is more flexible. - -.. note:: None of these names / spellings are final. The name "trivial" comes - from C++, though Swift's trivial is closer to C++'s "`trivially - copyable`__". - -All of these features need to be versioned, just like the more semantic -fragility attributes above. The exact spelling is not proposed by this document. - -__ http://en.cppreference.com/w/cpp/types/is_trivially_copyable - - -Resilience Domains -================== - -As described in the `Introduction`_, the features and considerations discussed -in this document do not apply to libraries distributed in a bundle with their -clients. In this case, a client can rely on all the current implementation -details of its libraries when compiling, since the same version of the library -is guaranteed to be present at run time. This allows more optimization than -would otherwise be possible. - -In some cases, a collection of libraries may be built and delivered together, -even though their clients may be packaged separately. (For example, the ICU -project is usually built into several library binaries, but these libraries are -always distributed together.) While the *clients* cannot rely on a particular -version of any library being present, the various libraries in the collection -should be able to take advantage of the implementations of their dependencies -also in the collection---that is, it should treat all entities as if marked -with the appropriate fragility attributes. Modules in this sort of collection -are said to be in the same *resilience domain.* - -Exactly how resilience domains are specified is not covered by this document, -and indeed they are an additive feature. One possibility is that a library's -resilience domain defaults to the name of the module, but can be overridden. If -a client has the same resilience domain name as a library it is using, it may -assume that version of the library will be present at run time. - - -Deployments -~~~~~~~~~~~ - -Related to the concept of a resilience domain is a *deployment.* While a -resilience domain allows related libraries to be compiled more efficiently, -a deployment groups related libraries together to present semantic version -information to clients. The simplest example of this might be an OS release: -OS X 10.10.0 contains Foundation version 1151.16 and AppKit version 1343. A -deployment thus acts as a "virtual dependency": clients that depend on -OS X 10.10 can rely on the presence of both of the library versions above. - -The use of deployments allows clients to only have to think about aggregate -dependencies, instead of listing every library they might depend on. It also -allows library authors to build `many versions of a library`__ within a larger -release cycle, as well as allowing a vendor to bundle together many libraries -with uncoordinated release schedules and release them as a logical unit. - -__ https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/doc/constant_group/Foundation_Framework_Version_Numbers - -There are lots of details to figure out here, including how to distribute this -information. In particular, just like libraries publish the history of their -own APIs, a deployment must publish the history of their included library -versions, i.e. not just that OS X 10.10 contains Foundation 1151.16 and AppKit -1343, but also that OS X 10.9 contains Foundation 1056 and AppKit 1265, and that -OS X 10.8 contains Foundation 945.0 and AppKit 1187, and so on, back to the -earliest version of the deployment that is supported. - - - -Checking Binary Compatibility -============================= - -With this many manual controls, it's important that library owners be able to -check their work. Therefore, we intend to build a tool that can compare two -versions of a library's public interface, and present any suspect differences -for verification. Important cases include but are not limited to: - -- Removal of versioned entities. - -- Incompatible modifications to versioned entities, such as added protocol - conformances lacking versioning information. - -- Unsafe `backdating <#backdating>`_. - -- Unsafe modifications to entities marked with fragility attributes, such as - adding a stored property to a ``@frozen`` struct. - -Wherever possible, this tool should also check for `binary-compatible -source-breaking changes `, such as -changing a default argument from ``false`` to ``true``. - - -Automatic Versioning -~~~~~~~~~~~~~~~~~~~~ - -A possible extension of this "checker" would be a tool that *automatically* -generates versioning information for entities in a library, given the previous -public interface of the library. This would remove the need for versions on any -of the fragility attributes, and declaring versioned API would be as simple as -marking an entity ``public``. Obviously this would also remove the possibility -of human error in managing library versions. - -However, making this tool has a number of additional difficulties beyond the -simple checker tool: - -- The tool must be able to read past library interface formats. This is true - for a validation tool as well, but the cost of failure is much higher. - Similarly, the past version of a library *must* be available to correctly - compile a new version. - -- Because the information goes into a library's public interface, the - versioning tool must either be part of the compilation process, modify the - interface generated by compilation, or produce a sidecar file that can be - loaded when compiling the client. In any case, it must *produce* information - in addition to *consuming* it. - -- Occasionally a library owner may want to override the inferred versions. This - can be accomplished by providing explicit versioning information, as - described above. - -- Bugs in the tool manifest as bugs in client programs. - -Because this tool would require a fair amount of additional work, it is not -part of this initial model. It is something we may decide to add in the future. - - -Open Issues -=========== - -There are still a number of known issues with the model described in this -document. We should endeavor to account for each of them, and if we can't come -up with a satisfactory implementation we should at least make sure that they -will not turn into pitfalls for library or client developers. - +recompiled. However, if the entity is declared in another module, then the code +using it must be more conservative, and will therefore receive more +conservative answers to its queries. (For example, a stored property may be +treated as computed.) -Subclass and base both conform to protocol -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - // Library, version 1 - class Elf {} - protocol Summonable {} - -:: - - // Client, version 1 - class ShoemakingElf : Elf, Summonable {} - -:: - - // Library, version 2 - @available(2.0) - extension Elf : Summonable {} - -Now ``ShoemakingElf`` conforms to ``Summonable`` in two different ways, which -may be incompatible (especially if ``Summonable`` had associated types or -requirements involving ``Self``). - -Additionally, the client can't even remove ``ShoemakingElf``'s conformance to -``Summonable``, because it may itself be a library with other code depending on -it. We could fix that with an annotation to explicitly inherent the conformance -of ``Summonable`` from the base class, but even that may not be possible if -there are incompatible associated types involved (because changing a member -typealias is not a safe change). - -One solution is to disallow adding a conformance for an existing protocol to an -``open`` class. - - -Recompiling changes a protocol's implementation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - // Library, version 1 - protocol MagicType {} - protocol Wearable {} - func use(_ item: T) {} - -:: - - // Client, version 1 - struct Amulet : MagicType, Wearable {} - use(Amulet()) - -:: - - // Library, version 2 - protocol MagicType { - @available(2.0) - func equip() { print("Equipped.") } - } - - extension Wearable where Self: MagicType { - @available(2.0) - func equip() { print("You put it on.") } - } - - func use(_ item: T) { item.equip() } - -Before the client is recompiled, the implementation of ``equip()`` used for -``Amulet`` instances can only be the default implementation, i.e. the one that -prints "Equipped". However, recompiling the client will result in the -constrained implementation being considered a "better" match for the protocol -requirement, thus changing the behavior of the program. - -This should never change the *meaning* of a program, since the default -implementation for a newly-added requirement should always be *correct.* -However, it may have significantly different performance characteristics or -side effects that would make the difference in behavior a surprise. - -This is similar to adding a new overload to an existing set of functions, which -can also change the meaning of client code just by recompiling. However, the -difference here is that the before-recompilation behavior was never requested -or acknowledged by the client; it's just the best the library can do. - -A possible solution here is to require the client to acknowledge the added -requirement in some way when it is recompiled. - -(We do not want to perform overload resolution at run time to find the best -possible default implementation for a given type.) +As a special case, inlinable code must be treated as if it is outside the +current module, since once it's inlined it will be. Summary @@ -1630,34 +1086,6 @@ that client code will never accidentally introduce implicit dependencies on specific versions of libraries. -Related Proposals -================= - -The following proposals (some currently in the process, some planned) will -affect the model described in this document, or concern the parts of this -document that affect language semantics: - -- Non-exhaustive enums (`SE-0192 `_) -- Inlineable functions (`SE-0193 `_) -- Frozen structs and enums (`SE-0260 `_) -- (draft) `Overridable methods in extensions`_ -- (planned) Restricting retroactive modeling (protocol conformances for types you don't own) -- (planned) `Generalized existentials (values of protocol type) `_ -- (planned) Removing the "constant" guarantee for 'let' across module boundaries -- (future) Performance annotations for types -- (future) Attributes for stored property accessors -- (future) Stored properties in extensions - -.. _Overridable methods in extensions: https://github.com/jrose-apple/swift-evolution/blob/overridable-members-in-extensions/proposals/nnnn-overridable-members-in-extensions.md -.. _Generics: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials -.. _SE0192: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md -.. _SE0193: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md -.. _SE0260: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md - -This does not mean all of these proposals need to be accepted, only that their -acceptance or rejection will affect this document. - - Glossary ======== From 96f99d2fb29df289e63a829e3b773ae6d6d5dbb6 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 29 Oct 2019 14:46:13 -0700 Subject: [PATCH 025/283] [docs] LibraryEvolution: Bring the per-decl restrictions up to date And replace the term "versioned" with "ABI-public". --- docs/LibraryEvolution.rst | 544 ++++++++++++-------------------------- 1 file changed, 170 insertions(+), 374 deletions(-) diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index 731141160c4b8..c4a4a232a0375 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -125,30 +125,29 @@ Anything *not* listed in this document should be assumed unsafe. Top-Level Functions ~~~~~~~~~~~~~~~~~~~ -A versioned top-level function is fairly restricted in how it can be changed. +An ABI-public top-level function is fairly restricted in how it can be changed. The following changes are permitted: -- Changing the body of the function. +- Changing the body of the function (as long as it is not ``@inlinable``; see + below). - Changing *internal* parameter names (i.e. the names used within the function body, not the labels that are part of the function's full name). - Reordering generic requirements (but not the generic parameters themselves). - Adding a default argument expression to a parameter. - Changing or removing a default argument is a `binary-compatible source-breaking change`. -- The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may - be added to a function without any additional versioning information. +- Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access`` + attributes. No other changes are permitted; the following are particularly of note: -- A versioned function may not change its parameters or return type. -- A versioned function may not change its generic requirements. -- A versioned function may not change its external parameter names (labels). -- A versioned function may not add, remove, or reorder parameters, whether or +- An ABI-public function may not change its parameters or return type. +- An ABI-public function may not change its generic requirements. +- An ABI-public function may not change its external parameter names (labels). +- An ABI-public function may not add, remove, or reorder parameters, whether or not they have default arguments. -- A versioned function that throws may not become non-throwing or vice versa. +- An ABI-public function that throws may not become non-throwing or vice versa. - The ``@escaping`` attribute may not be added to or removed from a parameter. - It is not a `versioned attribute` and so there is no way to guarantee that it - is safe when a client deploys against older versions of the library. Inlinable Functions @@ -173,12 +172,9 @@ are a few common reasons for this: - The function is generic and its performance may be greatly increased by specialization in the client. -A versioned function marked with the ``@inlinable`` attribute makes its body -available to clients as part of the module's public interface. ``@inlinable`` -is a `versioned attribute`; clients may not assume that the body of the -function is suitable when deploying against older versions of the library. - -Clients are not required to inline a function marked ``@inlinable``. +An ABI-public function marked with the ``@inlinable`` attribute makes its body +available to clients as part of the module's public interface. Clients are not +required to inline a function marked ``@inlinable``. .. note:: @@ -190,17 +186,15 @@ Clients are not required to inline a function marked ``@inlinable``. Any local functions or closures within an inlinable function are treated as ``@_alwaysEmitIntoClient`` (see below). A client that inlines the containing function must emit its own copy of the local functions or closures. This is -important in case it is necessary to change the inlinable function later; -existing clients should not be depending on internal details of the previous -implementation. +important in case it is necessary to change the inlinable function later. Removing the ``@inlinable`` attribute completely---say, to reference private -implementation details that should not be `versioned `---is a -safe change. However, existing clients will of course not be affected by this -change, and any future use of the function must take this into account. +implementation details that should not be ABI-public---is a safe change. +However, existing clients will of course not be affected by this change, and +any future use of the function must take this into account. Although they are not a supported feature for arbitrary libraries at this time, -`transparent`_ functions are implicitly marked ``@inlinable``. +public `transparent`_ functions are implicitly marked ``@inlinable``. .. _transparent: https://github.com/apple/swift/blob/master/docs/TransparentAttr.rst @@ -236,19 +230,15 @@ polar representation:: } and the ``x`` and ``y`` properties have now disappeared. To avoid this, the -bodies of inlinable functions have the following restrictions: +bodies of inlinable functions have the following restrictions (enforced by the +compiler): - They may not define any local types. - They must not reference any ``private`` or ``fileprivate`` entities. - They must not reference any ``internal`` entities except for those that have - been ``versioned ` and those declared ``@inlinable``. See - below for a discussion of versioning internal API. - -- They must not reference any entities from the current module introduced - after the function was made inlinable, except under appropriate availability - guards. + been declared ``@usableFromInline`` or ``@inlinable``. Always Emit Into Client @@ -270,91 +260,55 @@ is a binary-compatible source-breaking change. Default Argument Expressions ---------------------------- -Default argument expressions for functions that are public, versioned, or -inlinable are implicitly ``@_alwaysEmitIntoClient``. They are subject to -similar restrictions: - -- They may not define any local types. - -- They must not reference any non-``public`` entities. - -- They must not reference any entities from the current module introduced - after the default argument was added, except under appropriate availability - guards. - -A default argument implicitly has the same availability as the function it is -attached to. Because default argument expressions can be added and removed, a -client that uses one must always emit its own copy of the implementation. +Default argument expressions for functions that are ABI-public are implicitly +``@_alwaysEmitIntoClient``. They are subject to the same restrictions as +inlinable functions except that they also must not reference any non-``public`` +entities, even if they are ``@usableFromInline`` or ``@inlinable``. This is to +make sure a default argument expression can always be written explicitly by a +caller. Top-Level Variables and Constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Given a versioned module-scope variable declared with ``var``, the following +Given an ABI-public module-scope variable declared with ``var``, the following changes are permitted: - Adding (but not removing) a public setter to a computed variable. -- Adding or removing a non-public, non-versioned setter. +- Adding or removing a non-ABI-public setter. - Changing from a stored variable to a computed variable, or vice versa, as - long as a previously versioned setter is not removed. + long as a previously ABI-public setter is not removed. - As a special case of the above, adding or removing ``lazy`` from a stored property. -- Changing the body of an accessor. +- Changing the body of an accessor, if the property is not marked ``@inlinable`` + (see below). - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from an existing variable. This is effectively the same as modifying the body of a setter. - Changing the initial value of a stored variable. -- Adding or removing ``weak`` from a variable with ``Optional`` type. -- Adding or removing ``unowned`` from a variable. +- Adding or removing ``weak`` to/from a variable with ``Optional`` type. +- Adding or removing ``unowned`` to/from a variable. - Adding or removing ``@NSCopying`` to/from a variable. +- If the variable is get-only, or if it has a non-ABI-public setter, it may be + replaced by a ``let`` constant. -If a public setter is added after the property is first exposed (whether the -property is stored or computed), it must be versioned independently of the -property itself. - -.. admonition:: TODO - - This needs syntax. - -Additionally, for a module-scope constant declared with ``let``, the following +For an ABI-public module-scope constant declared with ``let``, the following changes are permitted: - Changing the value of the constant. - -It is *not* safe to change a ``let`` constant into a variable or vice versa. -Top-level constants are assumed not to change for the entire lifetime of the -program once they have been initialized. - -.. admonition:: TODO - - We could make it safe to turn a read-only ``var`` into a ``let``, but do we - want to? We would have to come up with syntax for declaring when it - changed, at least. +- Replacing the constant with a variable. Giving Up Flexibility --------------------- -Both top-level constants and variables can be marked ``@inlinableAccess`` to -allow clients to access them more efficiently. This restricts changes a fair -amount: +Top-level computed variables can be marked ``@inlinable`` just like functions. +This restricts changes a fair amount: -- Adding a versioned setter to a computed variable is still permitted. -- Adding or removing a non-public, non-versioned setter is still permitted. -- Changing from stored to computed or vice versa is forbidden, because it would - break existing clients. -- Similarly, adding or removing ``lazy`` is forbidden. +- Adding an ABI-public setter to a computed variable is still permitted. +- Adding or removing a non-ABI-public setter is still permitted. - Changing the body of an accessor is a `binary-compatible source-breaking change`. -- Adding/removing observing accessors is likewise a `binary-compatible - source-breaking change`. -- Changing the initial value of a stored variable is still permitted. -- Changing the value of a constant is a `binary-compatible source-breaking - change`. -- Adding or removing ``weak`` is forbidden. -- Adding or removing ``unowned`` is forbidden. -- Adding or removing ``@NSCopying`` to/from a variable is `binary-compatible - source-breaking change`. .. admonition:: TODO @@ -365,23 +319,6 @@ amount: Any inlinable accessors must follow the rules for `inlinable functions`_, as described above. -Note that if a constant's initial value expression has any observable side -effects, including the allocation of class instances, it must not be treated -as inlinable. A constant must always behave as if it is initialized exactly -once. - -.. admonition:: TODO - - Is this a condition we can detect at compile-time? Do we have to be - restricted to things that can be lowered to compile-time constants? - -.. admonition:: TODO - - ``@inlinableAccess`` isn't implemented yet, but for computed properties we - already allow putting ``@inlinable`` on the accessors individually. That - doesn't support all the use cases, like promising that a stored property - will remain stored, but it also provides flexibility in only making *one* - accessor inlinable. Is that important? Structs ~~~~~~~ @@ -389,26 +326,39 @@ Structs Swift structs are a little more flexible than their C counterparts. By default, the following changes are permitted: -- Reordering any existing members, including stored properties. +- Reordering any existing members, including stored properties (unless the + struct is marked ``@frozen``; see below). - Adding any new members, including stored properties. -- Changing existing properties from stored to computed or vice versa. +- Changing existing properties from stored to computed or vice versa (unless the + struct is marked ``@frozen``; see below). - As a special case of the above, adding or removing ``lazy`` from a stored property. - Changing the body of any methods, initializers, or accessors. - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from - an existing property. This is effectively the same as modifying the body of a - setter. -- Removing any non-public, non-versioned members, including stored properties. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. + an existing property (unless the struct is marked ``@frozen``; see below). + This is effectively the same as modifying the body of a setter. +- Removing any non-ABI-public members, including stored properties. +- Adding a conformance to an ABI-public protocol *that was introduced in the + same release* (see below). +- Adding or removing a conformance to a non-ABI-public protocol. The important most aspect of a Swift struct is its value semantics, not its layout. It is not safe to add or remove ``mutating`` or ``nonmutating`` from a member -or accessor within a struct. These modifiers are not `versioned attributes -` and as such there is no safety guarantee for a client -deploying against an earlier version of the library. +or accessor within a struct. + +If a conformance is added to a type in version 1.1 of a library, it's important +that it isn't accessed in version 1.0. This means that it is only safe to add +new conformances to ABI-public protocols when the protocol is introduced, and +not after. If the protocol comes from a separate module, there is no safe way +to conform to it. + +.. admonition:: TODO + + Coming up with a way to do this, either with availability annotations for + protocol conformances or a way to emit a fallback copy of the conformance + for clients on older library versions to use, is highly desired. Methods and Initializers @@ -416,28 +366,21 @@ Methods and Initializers For the most part struct methods and initializers are treated exactly like top-level functions. They permit all of the same modifications and can also be -marked ``@inlinable``, with the same restrictions. Inlinable initializers must -always delegate to another initializer or assign an entire value to ``self``, -since new properties may be added between new releases. For the same reason, -initializers declared outside of the struct's module must always delegate to -another initializer or assign to ``self``. +marked ``@inlinable``, with the same restrictions. + +Inlinable initializers must always delegate to another initializer or assign an +entire value to ``self``, since new properties may be added between new +releases. For the same reason, initializers declared outside of the struct's +module must always delegate to another initializer or assign to ``self``. This +is enforced by the compiler. Properties ---------- -Struct properties behave largely the same as top-level bindings. They permit -all of the same modifications, and also allow adding or removing an initial -value entirely. - -Struct properties can also be marked ``@inlinableAccess``, with the same -restrictions as for top-level bindings. An inlinable stored property may not -become computed, but the offset of its storage within the struct is not -necessarily fixed. - -Like top-level constants, it is *not* safe to change a ``let`` property into a -variable or vice versa. Properties declared with ``let`` are assumed not to -change for the entire lifetime of the program once they have been initialized. +Struct properties behave largely the same as top-level variables and constants. +They permit all of the same modifications, and also allow adding or removing an +initial value entirely. Subscripts @@ -447,7 +390,7 @@ Subscripts behave largely the same as properties, except that there are no stored subscripts. This means that the following changes are permitted: - Adding (but not removing) a public setter. -- Adding or removing a non-public, non-versioned setter. +- Adding or removing a non-ABI-public setter. - Changing the body of an accessor. - Changing index parameter internal names (i.e. the names used within the accessor bodies, not the labels that are part of the subscript's full name). @@ -456,64 +399,19 @@ stored subscripts. This means that the following changes are permitted: - Changing or removing a default argument is a `binary-compatible source-breaking change`. -Like properties, subscripts can be marked ``@inlinableAccess``, which makes +Like properties, subscripts can be marked ``@inlinable``, which makes changing the body of an accessor a `binary-compatible source-breaking change`. Any inlinable accessors must follow the rules for `inlinable functions`_, as described above. -New Conformances ----------------- - -If a conformance is added to a type in version 1.1 of a library, it's important -that it isn't accessed in version 1.0. This is implied if the protocol itself -was introduced in version 1.1, but needs special handling if both the protocol -and the type were available earlier. In this case, the conformance *itself* -needs to be labeled as being introduced in version 1.1, so that the compiler -can enforce its safe use. - -.. note:: - - This may feel like a regression from Objective-C, where `duck typing` would - allow a ``Wand`` to be passed as an ``id `` without ill effects. - However, ``Wand`` would still fail a ``-conformsToProtocol:`` check in - version 1.0 of the library, and so whether or not the client code will work - is dependent on what should be implementation details of the library. - -We've considered two possible syntaxes for this:: - - @available(1.1) - extension Wand : MagicType {/*...*/} - -and - -:: - - extension Wand : @available(1.1) MagicType {/*...*/} - -The former requires fewer changes to the language grammar, but the latter could -also be used on the declaration of the type itself (i.e. the ``struct`` -declaration). - -If we went with the former syntax, applying ``@available`` to an extension -would override the default availability of entities declared within the -extension; unlike access control, entities within the extension may freely -declare themselves to be either more or less available than what the extension -provides. - -We could also implement a ``@_alwaysEmitIntoClient`` attribute for conformances. -This introduces its own challenges with runtime uniquing of witness tables now -necessary for conformances. - - Frozen Structs -------------- -To opt out of this flexibility, a struct may be marked ``@frozen``. -This promises that no stored properties will be added to or removed from the -struct, even non-public ones. Additionally, all versioned instance stored -properties in a ``@frozen`` struct are implicitly declared -``@inlinable`` (as described above for top-level variables). In effect: +To opt out of this flexibility, a struct may be marked ``@frozen``. This +promises that no stored properties will be added to or removed from the struct, +even non-ABI-public ones, and allows the compiler to optimize as such. These +stored properties also must not have any observing accessors. In effect: - Reordering stored instance properties (public or non-public) is not permitted. Reordering all other members is still permitted. @@ -523,22 +421,19 @@ properties in a ``@frozen`` struct are implicitly declared vice versa is not permitted. - Similarly, adding or removing ``lazy`` from a stored property is not permitted. -- Changing the body of any *existing* methods, initializers, computed property - accessors, or non-instance stored property accessors is permitted. Changing - the body of a stored instance property observing accessor is permitted if the - property is not `versioned `, and considered a - `binary-compatible source-breaking change` if it is. -- Adding or removing observing accessors from any - `versioned ` stored instance properties (public or - non-public) is not permitted. +- Changing the body of any *existing* methods, initializers, or computed + property accessors is still permitted. +- Adding observing accessors to any stored instance properties (public or + non-public) is not permitted (and is checked by the compiler). - Removing stored instance properties is not permitted. Removing any other - non-public, non-versioned members is still permitted. -- Adding a new protocol conformance is still permitted. -- Removing conformances to non-public protocols is still permitted. + non-ABI-public members is still permitted. +- Adding a new protocol conformance is still permitted, per the usual + restrictions. +- Removing conformances to non-ABI-public protocols is still permitted. Additionally, if the type of any stored instance property includes a struct or -enum, that struct or enum must be `versioned `. This includes -generic parameters and members of tuples. +enum, that struct or enum must be ABI-public. This includes generic parameters +and members of tuples. .. note:: @@ -549,18 +444,16 @@ generic parameters and members of tuples. While adding or removing stored properties is forbidden, existing properties may still be modified in limited ways: -- An existing non-public, non-versioned property may change its access level to - any other non-public access level. -- A non-versioned ``internal`` property may be versioned (see `Versioning - Internal Declarations`_). -- A versioned ``internal`` property may be made ``public`` (without changing - its version). +- An existing non-ABI-public property may change its access level to any other + non-public access level. +- ``@usableFromInline`` may be added to an ``internal`` property (with the + current availability version, if necessary). +- A ``@usableFromInline`` property may be made ``public``. + +Adding or removing ``@frozen`` from an existing struct is forbidden. An initializer of a frozen struct may be declared ``@inlinable`` even -if it does not delegate to another initializer, as long as the ``@inlinable`` -attribute, or the initializer itself, is not introduced earlier than the -``@frozen`` attribute and the struct has no non-versioned stored -properties. +if it does not delegate to another initializer. A ``@frozen`` struct is *not* guaranteed to use the same layout as a C struct with a similar "shape". If such a struct is necessary, it should be @@ -568,33 +461,13 @@ defined in a C header and imported into Swift. .. note:: - We can add a *different* feature to control layout some day, or something + We may add a *different* feature to control layout some day, or something equivalent, but this feature should not restrict Swift from doing useful - things like minimizing member padding. At the very least, Swift structs - don't guarantee the same tail padding that C structs do. - -.. note:: - - Hypothetically, we could use a different model where a ``@frozen`` - struct only guarantees the "shape" of the struct, so to speak, while - leaving all property accesses to go through function calls. This would - allow stored properties to change their accessors, or (with the Behaviors - proposal) to change a behavior's implementation, or change from one - behavior to another. However, the *most common case* here is probably just - a simple C-like struct that groups together simple values, with only public - stored properties and no observing accessors, and having to opt into direct - access to those properties seems unnecessarily burdensome. The struct is - being declared ``@frozen`` for a reason, after all: it's been - discovered that its use is causing performance issues. - - Consequently, as a first pass we may just require all stored properties in - a ``@frozen`` struct, public or non-public, to have trivial - accessors, i.e. no observing accessors and no behaviors. - -``@frozen`` is a `versioned attribute`. This is so that clients can -deploy against older versions of the library, which may have a different layout -for the struct. (In this case the client must manipulate the struct as if the -``@frozen`` attribute were absent.) + things like minimizing member padding. While the layout of ``@frozen`` + structs is part of the stable ABI on Apple platforms now, it's not + something that can't be revised in the future (with appropriate + compatibility considerations). At the very least, Swift structs don't + guarantee the same tail padding that C structs do. Enums @@ -605,16 +478,16 @@ without breaking binary compatibility. As with structs, this results in a fair amount of indirection when dealing with enum values, in order to potentially accommodate new values. More specifically, the following changes are permitted: -- Adding a new case. -- Reordering existing cases is a `binary-compatible source-breaking change`. In - particular, if an enum is RawRepresentable, changing the raw representations - of cases may break existing clients who use them for serialization. +- Adding a new case (unless the enum is marked ``@frozen``; see below). +- Reordering existing cases is a `binary-compatible source-breaking change` + (unless the struct is marked ``@frozen``; see below). In particular, both + CaseIterable and RawRepresentable default implementations may affect client + behavior. - Adding a raw type to an enum that does not have one. -- Removing a non-public, non-versioned case. - Adding any other members. -- Removing any non-public, non-versioned members. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. +- Removing any non-ABI-public members. +- Adding a new protocol conformance, with the same restrictions as for structs. +- Removing conformances to non-ABI-public protocols. .. note:: @@ -623,12 +496,6 @@ accommodate new values. More specifically, the following changes are permitted: representation for the value, just as it may discard fields of structs that are provably never accessed. -.. note:: - - Non-public cases in public enums don't exist at the moment, but they *can* - be useful, and they require essentially the same implementation work as - cases added in future versions of a library. - Adding or removing the ``@objc`` attribute from an enum is not permitted; this affects the enum's memory representation and is not backwards-compatible. @@ -651,19 +518,18 @@ members. Frozen Enums ------------ -A library owner may opt out of this flexibility by marking a versioned enum as -``@frozen``. A "frozen" enum may not have any cases with less access than the -enum itself, and may not add new cases in the future. This guarantees to -clients that the enum cases are exhaustive. In particular: +A library owner may opt out of this flexibility by marking an ABI-public enum +as ``@frozen``. A "frozen" enum may not add new cases in the future, +guaranteeing to clients that the current set of enum cases is exhaustive. In +particular: - Adding new cases is not permitted. - Reordering existing cases is not permitted. -- Removing a non-public case is not applicable. - Adding a raw type is still permitted. - Adding any other members is still permitted. -- Removing any non-public, non-versioned members is still permitted. +- Removing any non-ABI-public members is still permitted. - Adding a new protocol conformance is still permitted. -- Removing conformances to non-public protocols is still permitted. +- Removing conformances to non-ABI-public protocols is still permitted. .. note:: @@ -671,24 +537,12 @@ clients that the enum cases are exhaustive. In particular: the library would still have to treat the enum as opaque and would still have to be able to handle unknown cases in their ``switch`` statements. -``@frozen`` is a `versioned attribute`. This is so that clients can deploy -against older versions of the library, which may have non-public cases in the -enum. (In this case the client must manipulate the enum as if the ``@frozen`` -attribute were absent.) All cases that are not versioned become implicitly -versioned with this number. +Adding or removing ``@frozen`` from an existing enum is forbidden. Even for default "non-frozen" enums, adding new cases should not be done lightly. Any clients attempting to do an exhaustive switch over all enum cases will likely not handle new cases well. -.. note:: - - One possibility would be a way to map new cases to older ones on older - clients. This would only be useful for certain kinds of enums, though, and - adds a lot of additional complexity, all of which would be tied up in - versions. Our generalized switch patterns probably make it hard to nail - down the behavior here. - Protocols ~~~~~~~~~ @@ -703,19 +557,12 @@ There are very few safe changes to make to protocols and their members: - Reordering generic requirements is permitted (but not the generic parameters themselves). - The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may - be added to a function requirement without any additional versioning - information. - -New requirements can be added to a protocol. However, restrictions around -existential types mean that adding new associated types or non-type requirements -involving ``Self`` can break source compatibility. For this reason, the following -are `binary-compatible source-breaking changes `: - -- A new non-type requirement may be added to a protocol, as long as it has an - unconstrained default implementation in a protocol extension of the - protocol itself or some other protocol it refines. -- A new associated type requirement may be added as long as it has a - default. + be added to or removed from a function requirement. +- A new non-type requirement may be added (with the appropriate availability), + as long as it has an unconstrained default implementation. If the requirement + uses ``Self`` and the protocol has no other requirements using ``Self`` and + no associated types, this is a `binary-compatible source-breaking change` due + to restrictions on protocol value types. All other changes to the protocol itself are forbidden, including: @@ -744,19 +591,22 @@ support all of the following changes: - Changing existing properties from stored to computed or vice versa. - As a special case of the above, adding or removing ``lazy`` from a stored property. -- Changing the body of any methods, initializers, or accessors. +- Changing the body of any methods, initializers, accessors, or deinitializers. - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from an existing property. This is effectively the same as modifying the body of a setter. -- Removing any non-public, non-versioned members, including stored properties. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. +- Removing any non-ABI-public members, including stored properties. +- Adding a new protocol conformance (subject to the same restrictions as for + structs). +- Removing conformances to non-ABI-public protocols. Omitted from this list is the free addition of new members. Here classes are a little more restrictive than structs; they only allow the following changes: - Adding a new convenience initializer. -- Adding a new designated initializer, if the class is not ``open``. +- Adding a new designated initializer, if the class is not ``open`` and any + ``open`` subclasses that previously inherited convenience initializers + continue to do so. - Adding a deinitializer. - Adding new, non-overriding method, subscript, or property. - Adding a new overriding member, though if the class is ``open`` the type of @@ -765,8 +615,6 @@ little more restrictive than structs; they only allow the following changes: Finally, classes allow the following changes that do not apply to structs: -- A public class may be made ``open`` if it is not already marked ``final``. -- A non-``open`` public class may be marked ``final``. - Removing an explicit deinitializer. (A class with no declared deinitializer effectively has an implicit deinitializer.) - "Moving" a method, subscript, or property up to its superclass. The @@ -777,55 +625,35 @@ Finally, classes allow the following changes that do not apply to structs: removed as long as the generic parameters, formal parameters, and return type *exactly* match the overridden declaration. Any existing callers should automatically use the superclass implementation. -- Within an ``open`` class, any public method, subscript, or property may be - marked ``open`` if it is not already marked ``final``. -- Any method, subscript, or property may be marked ``final`` if it is not - already marked ``open``. +- ``final`` can be added to or removed from any non-ABI-public class, or any + non-ABI-public member of a class. - ``@IBOutlet``, ``@IBAction``, ``@IBInspectable``, and ``@GKInspectable`` may - be added to a member without providing any extra version information. + be added to a member that is already exposed to Objective-C (either explicitly + with ``@objc`` or implicitly through overriding or protocol requirements). Removing any of these is a `binary-compatible source-breaking change` if the member remains ``@objc``, and disallowed if not. -- Likewise, ``@IBDesignable`` may be added to a class without providing any - extra version information. Removing it is considered a `binary-compatible - source-breaking change`. +- ``@IBDesignable`` may be added to a class; removing it is considered a + `binary-compatible source-breaking change`. - Changing a class's superclass ``A`` to another class ``B``, *if* class ``B`` is a subclass of ``A`` *and* class ``B``, along with any superclasses between it and class ``A``, were introduced in the latest version of the library. -.. admonition:: TODO - - This last is very tricky to get right. We've seen it happen a few times in - Apple's SDKs, but at least one of them, `NSCollectionViewItem`_ becoming a - subclass of NSViewController instead of the root class NSObject, doesn't - strictly follow the rules. While NSViewController was introduced in the - same version of the OS, its superclass, NSResponder, was already present. - If a client app was deploying to an earlier version of the OS, would - NSCollectionViewItem be a subclass of NSResponder or not? How would the - compiler be able to enforce this? - -.. admonition:: TODO - - Both ``final`` and ``open`` may be applied to a declaration after it has - been made public. However, these need to be treated as - `versioned attributes `. It's not clear what syntax - should be used for this. - -.. _NSCollectionViewItem: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSCollectionViewItem_Class/index.html - Other than those detailed above, no other changes to a class or its members are permitted. In particular: -- An ``open`` class or member cannot become non-``open``. -- ``final`` may not be removed from a class or its members. (The presence of - ``final`` enables optimization.) -- ``dynamic`` may not be added to *or* removed from any members. Existing - clients would not know to invoke the member dynamically. +- ``open`` cannot be added to or removed from an ABI-public class or member. +- ``final`` may not be added to or removed from an ABI-public class or its + ABI-public members. (The presence of ``final`` enables optimization.) +- ``dynamic`` may not be added to *or* removed from any ABI-public members. + Existing clients would not know to invoke the member dynamically. - A ``final`` override of a member may *not* be removed, even if the type matches exactly; existing clients may be performing a direct call to the implementation instead of using dynamic dispatch. - ``@objc`` and ``@nonobjc`` may not be added to or removed from the class or - any existing members. -- ``@NSManaged`` may not be added to or removed from any existing members. + any existing members, except if the member already was or was not exposed to + Objective-C. +- ``@NSManaged`` may not be added to or removed from any existing + ABI-public members. .. admonition:: TODO @@ -846,12 +674,6 @@ A new ``required`` initializer may be added to a class only if it is a convenience initializer; that initializer may only call existing ``required`` initializers. An existing initializer may not be marked ``required``. -.. admonition:: TODO - - This implies a different rule for inheriting ``required`` convenience - initializers than non-required convenience initializers, which is not - currently implemented. - All of the modifications permitted for top-level functions are also permitted for class initializers. Convenience initializers may be marked ``@inlinable``, with the same restrictions as top-level functions; designated initializers may @@ -872,20 +694,13 @@ little. They allow the following changes: - Adding a default argument expression to a parameter. - Changing or removing a default argument is a `binary-compatible source-breaking change`. -- The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may - be added to a method without any additional versioning information. +- Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access`` + attributes. Class and instance methods may be marked ``@inlinable``, with the same restrictions as struct methods. Additionally, only non-overriding ``final`` methods may be marked ``@inlinable``. -.. note:: - - A previous draft of this document allowed non-``final`` methods to be - marked ``@inlinable``, permitting inlining based on speculative - devirtualization. This was removed because of the added complexity for - users. - Properties ---------- @@ -895,9 +710,9 @@ struct properties, but the potential for overrides complicates things a little. Variable properties (those declared with ``var``) allow the following changes: - Adding (but not removing) a computed setter to a non-``open`` property. -- Adding or removing a non-public, non-versioned setter. +- Adding or removing a non-ABI-public setter. - Changing from a stored property to a computed property, or vice versa, as - long as a previously versioned setter is not removed. + long as a previously ABI-public setter is not removed. - Changing the body of an accessor. - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from an existing variable. This is effectively the same as modifying the body of a @@ -905,7 +720,7 @@ Variable properties (those declared with ``var``) allow the following changes: - Adding, removing, or changing the initial value of a stored variable. - Adding or removing ``weak`` from a variable with ``Optional`` type. - Adding or removing ``unowned`` from a variable. -- Adding or removing ``@NSCopying`` to/from a variable. +- Adding or removing ``@NSCopying`` from a variable. Adding a public setter to an ``open`` property is a `binary-compatible source-breaking change`; any existing overrides will not @@ -914,9 +729,8 @@ know what to do with the setter and will likely not behave correctly. Constant properties (those declared with ``let``) still permit changing their value, as well as adding or removing an initial value entirely. -Non-overriding ``final`` variable and constant properties (on both instances -and classes) may be marked ``@inlinableAccess``. This behaves as described for -struct properties. +Non-overriding ``final`` computed properties (on both instances and classes) +may be marked ``@inlinable``. This behaves as described for struct properties. Subscripts @@ -927,7 +741,7 @@ counterparts with a few small changes: - Adding (but not removing) a public setter to a non-``open`` subscript is permitted. -- Adding or removing a non-public, non-versioned setter is permitted. +- Adding or removing a non-ABI-public setter is permitted. - Changing the body of an accessor is permitted. - Changing index parameter internal names is permitted. - Reordering generic requirements (but not the generic parameters themselves) @@ -940,23 +754,10 @@ Adding a public setter to an ``open`` subscript is a `binary-compatible source-breaking change`; any existing overrides will not know what to do with the setter and will likely not behave correctly. -Non-overriding ``final`` class subscripts may be marked ``@inlinableAccess``, +Non-overriding ``final`` class subscripts may be marked ``@inlinable``, which behaves as described for struct subscripts. -Possible Restrictions on Classes --------------------------------- - -In addition to ``final``, it may be useful to restrict the stored properties of -a class instance, like `Frozen Structs`_. However, there are open -questions about how this would actually work, and the compiler still wouldn't -be able to make much use of the information, because classes from other -libraries must almost always be allocated on the heap. - -The design of this annotation is not covered by this document. As a purely -additive feature, it can be added to the model at any time. - - Extensions ~~~~~~~~~~ @@ -969,18 +770,19 @@ The following changes are permitted: as both extensions have the exact same constraints. - Adding any new member. - Reordering members. -- Removing any non-public, non-versioned member. +- Removing any non-ABI-public member. - Changing the body of any methods, initializers, or accessors. Additionally, non-protocol extensions allow a few additional changes: - Moving a member from an unconstrained extension to the declaration of the base type, provided that the declaration is in the same module. The reverse - is permitted for all members except stored properties, although note that - moving all initializers out of a type declaration may cause a new one to be - implicitly synthesized. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. + is permitted for all members that would be valid to declare in an extension, + although note that moving all initializers out of a type declaration may + cause a new one to be implicitly synthesized. +- Adding a new protocol conformance (subject to the same restrictions discussed + for structs). +- Removing conformances to non-ABI-public protocols. .. note:: @@ -1001,8 +803,6 @@ existing operators are not changed at all except for the following: Any other change counts as a `binary-compatible source-breaking change`. -Operator and precedence group declarations are not versioned. - Typealiases ~~~~~~~~~~~ @@ -1096,6 +896,11 @@ Glossary including things like symbol names, calling conventions, and type layout information. Stands for "Application Binary Interface". + ABI-public + Describes entities that are part of a library's `ABI`. Marked ``public``, + ``open``, ``@usableFromInline``, or ``@inlinable`` in Swift. See + `SE-0193 `_ for more information. + API An `entity` in a library that a `client` may use, or the collection of all such entities in a library. (If contrasting with `SPI`, only those entities @@ -1146,9 +951,6 @@ Glossary An API that is designed to handle future clients, perhaps allowing certain changes to be made without changing the ABI. - fragility attribute - See `A Unifying Theme`_. - module The primary unit of code sharing in Swift. Code in a module is always built together, though it may be spread across several source files. @@ -1169,9 +971,3 @@ Glossary In this document, a collection of code in a single Swift module that is built together; a "compilation unit". Roughly equivalent to a target in Xcode. - - versioned entity - See `Publishing Versioned API`_. - - versioned attribute - See `Publishing Versioned API`_. From 0b6d0be873c2682beba5297e6b7a1eceda2413b1 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 29 Oct 2019 15:11:12 -0700 Subject: [PATCH 026/283] [docs] LibraryEvolution: Property wrappers, @dynamicCallable... ...and function builders, even though those haven't formally been accepted to the language yet. (This description of property wrappers is also a bit optimistic; at the time of this writing, they haven't been implemented for top-level variables yet.) --- docs/LibraryEvolution.rst | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index c4a4a232a0375..45fae61983d9b 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -148,6 +148,8 @@ No other changes are permitted; the following are particularly of note: not they have default arguments. - An ABI-public function that throws may not become non-throwing or vice versa. - The ``@escaping`` attribute may not be added to or removed from a parameter. +- Adding or removing a function builder from a parameter is a + `binary-compatible source-breaking change`. Inlinable Functions @@ -291,6 +293,11 @@ changes are permitted: - Adding or removing ``@NSCopying`` to/from a variable. - If the variable is get-only, or if it has a non-ABI-public setter, it may be replaced by a ``let`` constant. +- Adding a property wrapper to a variable, or changing from one property + wrapper to another, as long as an ABI-public setter or projected value + (``$foo``) is not removed +- Removing a property wrapper from a variable, as long as the property wrapper + didn't have a projected value (``$foo``). For an ABI-public module-scope constant declared with ``let``, the following changes are permitted: @@ -341,6 +348,7 @@ the following changes are permitted: - Adding a conformance to an ABI-public protocol *that was introduced in the same release* (see below). - Adding or removing a conformance to a non-ABI-public protocol. +- Adding ``@dynamicCallable`` to the struct. The important most aspect of a Swift struct is its value semantics, not its layout. @@ -430,10 +438,11 @@ stored properties also must not have any observing accessors. In effect: - Adding a new protocol conformance is still permitted, per the usual restrictions. - Removing conformances to non-ABI-public protocols is still permitted. +- Adding, changing, or removing property wrappers is not permitted. Additionally, if the type of any stored instance property includes a struct or -enum, that struct or enum must be ABI-public. This includes generic parameters -and members of tuples. +enum, that struct or enum must be ABI-public. This includes generic parameters, +members of tuples, and property wrappers for stored instance properties. .. note:: @@ -488,6 +497,7 @@ accommodate new values. More specifically, the following changes are permitted: - Removing any non-ABI-public members. - Adding a new protocol conformance, with the same restrictions as for structs. - Removing conformances to non-ABI-public protocols. +- Adding ``@dynamicCallable`` to the enum. .. note:: @@ -599,6 +609,7 @@ support all of the following changes: - Adding a new protocol conformance (subject to the same restrictions as for structs). - Removing conformances to non-ABI-public protocols. +- Adding ``@dynamicCallable`` to the class. Omitted from this list is the free addition of new members. Here classes are a little more restrictive than structs; they only allow the following changes: @@ -721,6 +732,14 @@ Variable properties (those declared with ``var``) allow the following changes: - Adding or removing ``weak`` from a variable with ``Optional`` type. - Adding or removing ``unowned`` from a variable. - Adding or removing ``@NSCopying`` from a variable. +- Adding a property wrapper to a non-``open`` variable, or changing from one + property wrapper to another, as long as an ABI-public setter or projected + value (``$foo``) is not removed. +- Adding a property wrapper to an ``open`` variable, or changing from one + property wrapper to another, as long as an ABI-public setter or projected + value (``$foo``) is not added or removed. +- Removing a property wrapper from a variable, as long as the property wrapper + didn't have a projected value (``$foo``). Adding a public setter to an ``open`` property is a `binary-compatible source-breaking change`; any existing overrides will not From e87d243d701752fff80067a70be58e48bdc72299 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 30 Oct 2019 17:31:00 -0700 Subject: [PATCH 027/283] Fix support for subscript default arguments in KeyPath --- include/swift/AST/Expr.h | 10 +++++++--- lib/AST/Expr.cpp | 4 ++-- lib/SILGen/SILGenExpr.cpp | 25 ++++++++++++++++++++++++- test/SILGen/keypaths.swift | 29 +++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 59500a36d3844..76bd95666d0bd 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4916,7 +4916,10 @@ class KeyPathExpr : public Expr { const ProtocolConformanceRef *SubscriptHashableConformancesData; union { - unsigned SubscriptSize; + struct { + unsigned numConformances; + unsigned numLabels; + } SubscriptSize; unsigned TupleIndex; }; @@ -5122,7 +5125,7 @@ class KeyPathExpr : public Expr { switch (getKind()) { case Kind::Subscript: case Kind::UnresolvedSubscript: - return {SubscriptLabelsData, (size_t)SubscriptSize}; + return {SubscriptLabelsData, (size_t)SubscriptSize.numLabels}; case Kind::Invalid: case Kind::OptionalChain: @@ -5143,7 +5146,8 @@ class KeyPathExpr : public Expr { case Kind::Subscript: if (!SubscriptHashableConformancesData) return {}; - return {SubscriptHashableConformancesData, (size_t)SubscriptSize}; + return {SubscriptHashableConformancesData, + (size_t)SubscriptSize.numConformances}; case Kind::UnresolvedSubscript: case Kind::Invalid: diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 584d1c54a8eb8..724dabf969477 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2159,7 +2159,7 @@ KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels, SubscriptLabelsData = subscriptLabels.data(); SubscriptHashableConformancesData = indexHashables.empty() ? nullptr : indexHashables.data(); - SubscriptSize = subscriptLabels.size(); + SubscriptSize.numLabels = subscriptLabels.size(); } KeyPathExpr::Component @@ -2176,7 +2176,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances( ArrayRef hashables) { switch (getKind()) { case Kind::Subscript: - assert(hashables.size() == SubscriptSize); + SubscriptSize.numConformances = hashables.size(); SubscriptHashableConformancesData = getComponentType()->getASTContext() .AllocateCopy(hashables) .data(); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 30ccec9ef7cf4..3bb067e40aeef 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3549,7 +3549,30 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { [this, &operands, E](const KeyPathExpr::Component &component) { if (!component.getIndexExpr()) return; - + + if (auto *parenExpr = dyn_cast(component.getIndexExpr())) { + if (auto *defaultArg = + dyn_cast(parenExpr->getSubExpr())) { + const ParamDecl *defaultParam = getParameterAt( + cast(defaultArg->getDefaultArgsOwner().getDecl()), 0); + parenExpr->setSubExpr(defaultParam->getDefaultValue()); + } + } + + if (auto *tupleExpr = dyn_cast(component.getIndexExpr())) { + size_t count = 0; + for (auto *element : tupleExpr->getElements()) { + if (auto *defaultArg = dyn_cast(element)) { + const ParamDecl *defaultParam = getParameterAt( + cast(defaultArg->getDefaultArgsOwner().getDecl()), + count++); + tupleExpr->setElement(count - 1, defaultParam->getDefaultValue()); + } else { + (void)++count; + } + } + } + // Evaluate the index arguments. SmallVector indexValues; auto indexResult = visit(component.getIndexExpr(), SGFContext()); diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index cfd9b5257bff7..9d956b8b0747e 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -322,6 +322,25 @@ struct Subscripts { } } +struct SubscriptDefaults { + subscript(x: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } + subscript(x: Int, y: Int, z: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } + subscript(x: Bool, bool y: Bool = false) -> Bool { + get { fatalError() } + set { fatalError() } + } + subscript(bool x: Bool, y: Int, z: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } +} + // CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[] @@ -352,6 +371,16 @@ func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[Bass()] _ = \Subscripts.[Treble()] + + _ = \SubscriptDefaults.[] + _ = \SubscriptDefaults.[0] + _ = \SubscriptDefaults.[0, 0] + _ = \SubscriptDefaults.[0, 0, 0] + + _ = \SubscriptDefaults.[false] + _ = \SubscriptDefaults.[false, bool: false] + _ = \SubscriptDefaults.[bool: false, 0] + _ = \SubscriptDefaults.[bool: false, 0, 0] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From 0376dd01abbbead81c4b36b6895a211e5740b239 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 30 Oct 2019 17:58:46 -0700 Subject: [PATCH 028/283] Refactor as suggested by @xedin --- lib/SILGen/SILGenExpr.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 3bb067e40aeef..378b7b9e3cdaf 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3560,15 +3560,12 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { } if (auto *tupleExpr = dyn_cast(component.getIndexExpr())) { - size_t count = 0; - for (auto *element : tupleExpr->getElements()) { - if (auto *defaultArg = dyn_cast(element)) { + for (size_t i = 0, n = tupleExpr->getNumElements(); i < n; ++i) { + if (auto *defaultArg = dyn_cast(tupleExpr->getElement(i))) { const ParamDecl *defaultParam = getParameterAt( cast(defaultArg->getDefaultArgsOwner().getDecl()), - count++); - tupleExpr->setElement(count - 1, defaultParam->getDefaultValue()); - } else { - (void)++count; + i); + tupleExpr->setElement(i, defaultParam->getDefaultValue()); } } } From 4da9c425db168fe6bf2c79d72684972367ea543a Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 31 Oct 2019 11:03:24 -0700 Subject: [PATCH 029/283] Stash progress before changing branches --- lib/AST/ASTWalker.cpp | 4 ++++ lib/SILGen/SILGenExpr.cpp | 20 ------------------ lib/Sema/CSApply.cpp | 44 ++++++++++++++++++++++++++++++++++++++- lib/Sema/CSGen.cpp | 3 ++- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 1bd64abdd41e7..2d72eae6c379e 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -552,6 +552,8 @@ class Traversal : public ASTVisitordump(); + if (Expr *subExpr = doIt(E->getSubExpr())) { E->setSubExpr(subExpr); return E; @@ -1011,6 +1013,8 @@ class Traversal : public ASTVisitordump(); + // For an ObjC key path, the string literal expr serves as the semantic // expression. if (auto objcStringLiteral = E->getObjCStringLiteralExpr()) { diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 378b7b9e3cdaf..61559ddd50715 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3550,26 +3550,6 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { if (!component.getIndexExpr()) return; - if (auto *parenExpr = dyn_cast(component.getIndexExpr())) { - if (auto *defaultArg = - dyn_cast(parenExpr->getSubExpr())) { - const ParamDecl *defaultParam = getParameterAt( - cast(defaultArg->getDefaultArgsOwner().getDecl()), 0); - parenExpr->setSubExpr(defaultParam->getDefaultValue()); - } - } - - if (auto *tupleExpr = dyn_cast(component.getIndexExpr())) { - for (size_t i = 0, n = tupleExpr->getNumElements(); i < n; ++i) { - if (auto *defaultArg = dyn_cast(tupleExpr->getElement(i))) { - const ParamDecl *defaultParam = getParameterAt( - cast(defaultArg->getDefaultArgsOwner().getDecl()), - i); - tupleExpr->setElement(i, defaultParam->getDefaultValue()); - } - } - } - // Evaluate the index arguments. SmallVector indexValues; auto indexResult = visit(component.getIndexExpr(), SGFContext()); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d8330e70bbdee..ff8db0e9b82f0 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4201,7 +4201,7 @@ namespace { return E; } - Expr *visitKeyPathExpr(KeyPathExpr *E) { + Expr *visitKeyPathExpr(KeyPathExpr *E) { if (E->isObjC()) { cs.setType(E, cs.getType(E->getObjCStringLiteralExpr())); return E; @@ -4656,6 +4656,27 @@ namespace { conformances.push_back(*hashableConformance); } + + newIndexExpr->dump(); + if (auto *parenExpr = dyn_cast(newIndexExpr)) { + if (auto *defaultArg = + dyn_cast(parenExpr->getSubExpr())) { + const ParamDecl *defaultParam = getParameterAt( + cast(defaultArg->getDefaultArgsOwner().getDecl()), 0); + parenExpr->setSubExpr(defaultParam->getDefaultValue()); + } + } + + if (auto *tupleExpr = dyn_cast(newIndexExpr)) { + for (size_t i = 0, n = tupleExpr->getNumElements(); i < n; ++i) { + if (auto *defaultArg = dyn_cast(tupleExpr->getElement(i))) { + const ParamDecl *defaultParam = getParameterAt( + cast(defaultArg->getDefaultArgsOwner().getDecl()), + i); + tupleExpr->setElement(i, defaultParam->getDefaultValue()); + } + } + } component.setSubscriptIndexHashableConformances(conformances); return component; @@ -5533,6 +5554,27 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, } } +// arg->dump(); +// if (auto *parenExpr = dyn_cast(arg)) { +// if (auto *defaultArg = +// dyn_cast(parenExpr->getSubExpr())) { +// const ParamDecl *defaultParam = getParameterAt( +// cast(defaultArg->getDefaultArgsOwner().getDecl()), 0); +// parenExpr->setSubExpr(defaultParam->getDefaultValue()); +// } +// } +// +// if (auto *tupleExpr = dyn_cast(arg)) { +// for (size_t i = 0, n = tupleExpr->getNumElements(); i < n; ++i) { +// if (auto *defaultArg = dyn_cast(tupleExpr->getElement(i))) { +// const ParamDecl *defaultParam = getParameterAt( +// cast(defaultArg->getDefaultArgsOwner().getDecl()), +// i); +// tupleExpr->setElement(i, defaultParam->getDefaultValue()); +// } +// } +// } + arg->setType(paramType); return cs.cacheType(arg); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index d3d4cb4f90296..8eb5eaaf390af 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1687,6 +1687,7 @@ namespace { } Type visitIdentityExpr(IdentityExpr *expr) { + expr->dump(); return CS.getType(expr->getSubExpr()); } @@ -1718,7 +1719,7 @@ namespace { return optTy; } - virtual Type visitParenExpr(ParenExpr *expr) { + virtual Type visitParenExpr(ParenExpr *expr) { if (auto favoredTy = CS.getFavoredType(expr->getSubExpr())) { CS.setFavoredType(expr, favoredTy); } From 14a2ad6ab8256a1116ecfa4637cd160e64bf1004 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 1 Nov 2019 09:25:47 -0700 Subject: [PATCH 030/283] Move default argument lowering into coerceCallArguments --- lib/Sema/CSApply.cpp | 55 ++++++++------------------------------------ 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index ff8db0e9b82f0..9de4d7f7628fb 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4656,27 +4656,6 @@ namespace { conformances.push_back(*hashableConformance); } - - newIndexExpr->dump(); - if (auto *parenExpr = dyn_cast(newIndexExpr)) { - if (auto *defaultArg = - dyn_cast(parenExpr->getSubExpr())) { - const ParamDecl *defaultParam = getParameterAt( - cast(defaultArg->getDefaultArgsOwner().getDecl()), 0); - parenExpr->setSubExpr(defaultParam->getDefaultValue()); - } - } - - if (auto *tupleExpr = dyn_cast(newIndexExpr)) { - for (size_t i = 0, n = tupleExpr->getNumElements(); i < n; ++i) { - if (auto *defaultArg = dyn_cast(tupleExpr->getElement(i))) { - const ParamDecl *defaultParam = getParameterAt( - cast(defaultArg->getDefaultArgsOwner().getDecl()), - i); - tupleExpr->setElement(i, defaultParam->getDefaultValue()); - } - } - } component.setSubscriptIndexHashableConformances(conformances); return component; @@ -5421,10 +5400,15 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, // Otherwise, create a call of the default argument generator. } else { - defArg = - new (tc.Context) DefaultArgumentExpr(callee, paramIdx, - arg->getStartLoc(), - param.getParameterType()); + const ParamDecl *defaultParam = getParameterAt(cast(callee.getDecl()), newArgs.size()); + defArg = defaultParam->getDefaultValue(); + + if (defArg == nullptr) { + defArg = + new (tc.Context) DefaultArgumentExpr(callee, paramIdx, + arg->getStartLoc(), + param.getParameterType()); + } } cs.cacheType(defArg); @@ -5554,27 +5538,6 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, } } -// arg->dump(); -// if (auto *parenExpr = dyn_cast(arg)) { -// if (auto *defaultArg = -// dyn_cast(parenExpr->getSubExpr())) { -// const ParamDecl *defaultParam = getParameterAt( -// cast(defaultArg->getDefaultArgsOwner().getDecl()), 0); -// parenExpr->setSubExpr(defaultParam->getDefaultValue()); -// } -// } -// -// if (auto *tupleExpr = dyn_cast(arg)) { -// for (size_t i = 0, n = tupleExpr->getNumElements(); i < n; ++i) { -// if (auto *defaultArg = dyn_cast(tupleExpr->getElement(i))) { -// const ParamDecl *defaultParam = getParameterAt( -// cast(defaultArg->getDefaultArgsOwner().getDecl()), -// i); -// tupleExpr->setElement(i, defaultParam->getDefaultValue()); -// } -// } -// } - arg->setType(paramType); return cs.cacheType(arg); } From ff29b8e9c47b43c1925bfa1f909759c32aa54140 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 1 Nov 2019 09:28:05 -0700 Subject: [PATCH 031/283] Clean up spacing and remove lingering debugging calls --- lib/AST/ASTWalker.cpp | 4 ---- lib/SILGen/SILGenExpr.cpp | 2 +- lib/Sema/CSApply.cpp | 4 ++-- lib/Sema/CSGen.cpp | 3 +-- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 2d72eae6c379e..1bd64abdd41e7 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -552,8 +552,6 @@ class Traversal : public ASTVisitordump(); - if (Expr *subExpr = doIt(E->getSubExpr())) { E->setSubExpr(subExpr); return E; @@ -1013,8 +1011,6 @@ class Traversal : public ASTVisitordump(); - // For an ObjC key path, the string literal expr serves as the semantic // expression. if (auto objcStringLiteral = E->getObjCStringLiteralExpr()) { diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 61559ddd50715..30ccec9ef7cf4 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3549,7 +3549,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { [this, &operands, E](const KeyPathExpr::Component &component) { if (!component.getIndexExpr()) return; - + // Evaluate the index arguments. SmallVector indexValues; auto indexResult = visit(component.getIndexExpr(), SGFContext()); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 9de4d7f7628fb..fe700cb2b6513 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4201,7 +4201,7 @@ namespace { return E; } - Expr *visitKeyPathExpr(KeyPathExpr *E) { + Expr *visitKeyPathExpr(KeyPathExpr *E) { if (E->isObjC()) { cs.setType(E, cs.getType(E->getObjCStringLiteralExpr())); return E; @@ -5402,7 +5402,7 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, } else { const ParamDecl *defaultParam = getParameterAt(cast(callee.getDecl()), newArgs.size()); defArg = defaultParam->getDefaultValue(); - + if (defArg == nullptr) { defArg = new (tc.Context) DefaultArgumentExpr(callee, paramIdx, diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 8eb5eaaf390af..d3d4cb4f90296 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1687,7 +1687,6 @@ namespace { } Type visitIdentityExpr(IdentityExpr *expr) { - expr->dump(); return CS.getType(expr->getSubExpr()); } @@ -1719,7 +1718,7 @@ namespace { return optTy; } - virtual Type visitParenExpr(ParenExpr *expr) { + virtual Type visitParenExpr(ParenExpr *expr) { if (auto favoredTy = CS.getFavoredType(expr->getSubExpr())) { CS.setFavoredType(expr, favoredTy); } From 6c2645a288f4b48d34f36468199651a136096312 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 1 Nov 2019 14:54:12 -0700 Subject: [PATCH 032/283] [docs] LibraryEvolution: make it a little more friendly - Move the original intro into a "Background" section - Link to SE-0193 (@inlinable) and SE-0260 (@frozen) - Note (suggested by @Gankra) on why it's safe to add stored properties to a struct - Remove some bits that reference the content removed in earlier commits - Other minor copyediting --- docs/LibraryEvolution.rst | 115 +++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index 45fae61983d9b..2a44a40db4228 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -3,18 +3,42 @@ .. default-role:: term .. title:: Library Evolution Support in Swift ("Resilience") -:Author: Jordan Rose -:Author: John McCall - .. note:: This document uses some Sphinx-specific features which are not available on - GitHub. For proper rendering, download and build the docs yourself. Jordan - Rose also posts occasional snapshots at - https://jrose-apple.github.io/swift-library-evolution/. + GitHub. For proper rendering, download and build the docs yourself. + +Since Swift 5, ABI-stable platforms have supported `library evolution`_, the +ability to change a library without breaking source or binary compatibility. +This model is intended to serve library designers whose libraries will evolve +over time. Such libraries must be both `backwards-compatible`, meaning that +existing clients should continue to work even when the library is updated, and +`forwards-compatible`, meaning that future clients will be able run using the +current version of the library. In simple terms: + +- Last year's apps should work with this year's library. +- Next year's apps should work with this year's library. -One of Swift's primary design goals is to allow efficient execution of code -without sacrificing load-time abstraction of implementation. +This document is intended to be a specification for *which* changes can be made +without breaking binary compatibility. When a library author wants to make a +change, they can jump to the relevant section of this document to see if it's +allowed. Anything *not* listed in this document should be assumed unsafe, i.e. +changing it will break binary compatibility. + +Library evolution was formally described in `SE-0260 `_, but this +document should be kept up to date as new features are added to the language. + +.. _library evolution: https://swift.org/blog/abi-stability-and-more/ +.. _SE0260: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md + +.. contents:: :local: + + +Background +========== + +One of Swift's primary design goals has always been to allow efficient +execution of code without sacrificing load-time abstraction of implementation. Abstraction of implementation means that code correctly written against a published interface will correctly function when the underlying implementation @@ -49,24 +73,14 @@ This last point is a specific case of a general tenet of Swift: **the default behavior is safe**. Where possible, choices made when an entity is first published should not limit its evolution in the future. -.. contents:: :local: - Introduction ============ -This model is intended to serve library designers whose libraries will evolve -over time. Such libraries must be both `backwards-compatible`, meaning that -existing clients should continue to work even when the library is updated, and -`forwards-compatible`, meaning that future clients will be able run using the -current version of the library. In simple terms: - -- Last year's apps should work with this year's library. -- Next year's apps should work with this year's library. - This document will frequently refer to a *library* which vends public APIs, and a single *client* that uses them. The same principles apply even when multiple -libraries and multiple clients are involved. +libraries and multiple clients are involved. It also uses the term `ABI-public` +introduced in `SE-0193 `_. This document is primarily concerned with `binary compatibility`, i.e. what changes can safely be made to a library between releases that will not break @@ -94,15 +108,15 @@ with a single app target are not forced to think about access control, anyone writing a bundled library should (ideally) not be required to use any of the annotations described below in order to achieve full performance. +.. _SE0193: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md .. _Swift Package Manager: https://swift.org/package-manager/ .. note:: This model may, however, be useful for library authors that want to - preserve *source* compatibility, and it is hoped that the tool for - `Checking Binary Compatibility`_ described below will also be useful for - this purpose. Additionally, we may decide to use some of these annotations - as performance hints for *non-*\ optimized builds. + preserve *source* compatibility, though this document mostly doesn't + discuss that. Additionally, some of these annotations are useful for + performance today, such as ``@inlinable``. The term "resilience" comes from the occasional use of "fragile" to describe certain constructs that have very strict binary compatibility rules. For @@ -119,9 +133,6 @@ This section describes the various changes that are safe to make when releasing a new version of a library, i.e. changes that will not break binary compatibility. They are organized by declaration type. -Anything *not* listed in this document should be assumed unsafe. - - Top-Level Functions ~~~~~~~~~~~~~~~~~~~ @@ -155,10 +166,11 @@ No other changes are permitted; the following are particularly of note: Inlinable Functions ------------------- -Functions are a very common example of resilience: the function's declaration -is published as API, but its body may change between library versions as long -as it upholds the same semantic contracts. This applies to other function-like -constructs as well: initializers, accessors, and deinitializers. +Functions are a very common example of "abstraction of implementation": the +function's declaration is published as API, but its body may change between +library versions as long as it upholds the same semantic contracts. This +applies to other function-like constructs as well: initializers, accessors, and +deinitializers. However, sometimes it is useful to provide the body to clients as well. There are a few common reasons for this: @@ -317,12 +329,6 @@ This restricts changes a fair amount: - Changing the body of an accessor is a `binary-compatible source-breaking change`. -.. admonition:: TODO - - It Would Be Nice(tm) to allow marking the *getter* of a top-level variable - inlinable while still allowing the setter to change. This would need - syntax, though. - Any inlinable accessors must follow the rules for `inlinable functions`_, as described above. @@ -351,7 +357,10 @@ the following changes are permitted: - Adding ``@dynamicCallable`` to the struct. The important most aspect of a Swift struct is its value semantics, not its -layout. +layout. Note that adding a stored property to a struct is *not* a breaking +change even with Swift's synthesis of memberwise and no-argument initializers; +these initializers are always ``internal`` and thus not exposed to clients +outside the module. It is not safe to add or remove ``mutating`` or ``nonmutating`` from a member or accessor within a struct. @@ -866,6 +875,8 @@ the compiler must assume that new overrides may eventually appear from outside the module if the class is marked ``open`` unless the member is marked ``final``. +For more information, see `SE-0193 `_. + Optimization ============ @@ -897,12 +908,12 @@ current module, since once it's inlined it will be. Summary ======= -When possible, Swift gives library authors freedom to evolve their code -without breaking binary compatibility. This has implications for both the -semantics and performance of client code, and so library owners also have tools -to waive the ability to make certain future changes. The language guarantees -that client code will never accidentally introduce implicit dependencies on -specific versions of libraries. +When possible, Swift gives library authors freedom to evolve their code without +breaking binary compatibility. This has implications for both the semantics and +performance of client code, and so library owners also have tools to waive the +ability to make certain future changes. When shipping libraries as part of the +OS, the availability model guarantees that client code will never accidentally +introduce implicit dependencies on specific versions of libraries. Glossary @@ -918,7 +929,7 @@ Glossary ABI-public Describes entities that are part of a library's `ABI`. Marked ``public``, ``open``, ``@usableFromInline``, or ``@inlinable`` in Swift. See - `SE-0193 `_ for more information. + `SE-0193 `_ for more information. API An `entity` in a library that a `client` may use, or the collection of all @@ -926,12 +937,6 @@ Glossary that are available to arbitrary clients.) Marked ``public`` or ``open`` in Swift. Stands for "Application Programming Interface". - availability context - The collection of library and platform versions that can be assumed, at - minimum, to be present in a certain block of code. Availability contexts - are always properly nested, and the global availability context includes - the module's minimum deployment target and minimum dependency versions. - backwards-compatible A modification to an API that does not break existing clients. May also describe the API in question. @@ -974,14 +979,6 @@ Glossary The primary unit of code sharing in Swift. Code in a module is always built together, though it may be spread across several source files. - performance assertion - See `Other Promises About Types`_. - - resilience domain - A grouping for code that will always be recompiled and distributed - together, and can thus take advantage of details about a type - even if it changes in the future. - SPI A subset of `API` that is only available to certain clients. Stands for "System Programming Interface". From 327c08a983c029ff576949a792b4576c3f9640ff Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 6 Nov 2019 09:30:01 -0800 Subject: [PATCH 033/283] Add edgecases to tests --- test/SILGen/keypaths.swift | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 9d956b8b0747e..bea888ba744f8 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -322,7 +322,7 @@ struct Subscripts { } } -struct SubscriptDefaults { +struct SubscriptDefaults1 { subscript(x: Int = 0) -> Int { get { fatalError() } set { fatalError() } @@ -341,6 +341,20 @@ struct SubscriptDefaults { } } +struct SubscriptDefaults2 { + subscript(x: Int? = nil) -> Int { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults3 { + subscript(x: Int = #line) -> Int { + get { fatalError() } + set { fatalError() } + } +} + // CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[] @@ -372,15 +386,18 @@ func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[Bass()] _ = \Subscripts.[Treble()] - _ = \SubscriptDefaults.[] - _ = \SubscriptDefaults.[0] - _ = \SubscriptDefaults.[0, 0] - _ = \SubscriptDefaults.[0, 0, 0] + _ = \SubscriptDefaults1.[] + _ = \SubscriptDefaults1.[0] + _ = \SubscriptDefaults1.[0, 0] + _ = \SubscriptDefaults1.[0, 0, 0] - _ = \SubscriptDefaults.[false] - _ = \SubscriptDefaults.[false, bool: false] - _ = \SubscriptDefaults.[bool: false, 0] - _ = \SubscriptDefaults.[bool: false, 0, 0] + _ = \SubscriptDefaults1.[false] + _ = \SubscriptDefaults1.[false, bool: false] + _ = \SubscriptDefaults1.[bool: false, 0] + _ = \SubscriptDefaults1.[bool: false, 0, 0] + + _ = \SubscriptDefaults2.[] + _ = \SubscriptDefaults3.[] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From 00ebbee3416e6f72f1c6feae293a1437ec8eb07e Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 6 Nov 2019 11:02:57 -0800 Subject: [PATCH 034/283] Fix based on review comments --- lib/Sema/CSApply.cpp | 3 ++- test/SILGen/keypaths.swift | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index c4baa20e0ca6b..ce62c8706a700 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5399,7 +5399,8 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, // Otherwise, create a call of the default argument generator. } else { - const ParamDecl *defaultParam = getParameterAt(cast(callee.getDecl()), newArgs.size()); + const ParamDecl *defaultParam = getParameterAt(callee.getDecl(), + newArgs.size()); defArg = defaultParam->getDefaultValue(); if (defArg == nullptr) { diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index bea888ba744f8..2c4cf966dce2d 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -397,7 +397,10 @@ func subscripts(x: T, y: U, s: String) { _ = \SubscriptDefaults1.[bool: false, 0, 0] _ = \SubscriptDefaults2.[] + _ = \SubscriptDefaults2.[0] + _ = \SubscriptDefaults3.[] + _ = \SubscriptDefaults3.[0] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From a2ed68045703351b231f66a509e0a95f38298e9b Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 8 Nov 2019 14:15:49 -0800 Subject: [PATCH 035/283] [gyb_syntax_support] Add description for ExprListSyntax --- utils/gyb_syntax_support/ExprNodes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index 972786060ad08..208194499cbe8 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -112,7 +112,11 @@ Node('ExprList', kind='SyntaxCollection', element='Expr', - element_name='Expression'), + element_name='Expression', + description=''' + A list of expressions connected by operators. This list is contained + by a `SequenceExprSyntax`. + '''), # A #line expression. Node('PoundLineExpr', kind='Expr', From 3da279933fcaf9cb14acdd83ff15c215912356a5 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 9 Nov 2019 22:24:54 -0800 Subject: [PATCH 036/283] Emit default arguments in silgen --- lib/SILGen/SILGen.cpp | 2 +- lib/SILGen/SILGenExpr.cpp | 3 ++- lib/Sema/CSApply.cpp | 14 ++++---------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index fb8031d865bad..0e414e2532ec6 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1064,7 +1064,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, auto arg = param->getDefaultValue(); emitOrDelayFunction(*this, constant, [this,constant,arg,initDC](SILFunction *f) { - preEmitFunction(constant, arg, f, arg); +// preEmitFunction(constant, arg, f, arg); PrettyStackTraceSILFunction X("silgen emitDefaultArgGenerator ", f); SILGenFunction SGF(*this, *f, initDC); SGF.emitGeneratorFunction(constant, arg); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 65f4e81ffc9a7..2721ddece9c18 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3574,12 +3574,13 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { } }; - for (auto &component : E->getComponents()) { switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Property: case KeyPathExpr::Component::Kind::Subscript: { auto decl = cast(component.getDeclRef().getDecl()); + SGF.SGM.emitDefaultArgGenerators(dyn_cast(decl), + dyn_cast(decl)->getIndices()); unsigned numOperands = operands.size(); loweredComponents.push_back( diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index ce62c8706a700..2f6b10a389c7e 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5399,16 +5399,10 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, // Otherwise, create a call of the default argument generator. } else { - const ParamDecl *defaultParam = getParameterAt(callee.getDecl(), - newArgs.size()); - defArg = defaultParam->getDefaultValue(); - - if (defArg == nullptr) { - defArg = - new (tc.Context) DefaultArgumentExpr(callee, paramIdx, - arg->getStartLoc(), - param.getParameterType()); - } + defArg = + new (tc.Context) DefaultArgumentExpr(callee, paramIdx, + arg->getStartLoc(), + param.getParameterType()); } cs.cacheType(defArg); From 8c3bfc598bf6bea789987afb04dd5aaa1fc675f9 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 10 Nov 2019 15:47:34 -0800 Subject: [PATCH 037/283] Move default arg generation into SILGen --- include/swift/AST/Expr.h | 20 ++++++++ lib/SILGen/SILGen.cpp | 2 +- lib/SILGen/SILGenExpr.cpp | 79 ++++++++++++++++++++++++++++++-- test/SILGen/keypaths.swift | 94 +++++++++++++++++++------------------- 4 files changed, 144 insertions(+), 51 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index a0267c8782554..74c45e5f8c4e9 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5119,6 +5119,26 @@ class KeyPathExpr : public Expr { } llvm_unreachable("unhandled kind"); } + + void setIndexExpr(Expr *newExpr) { + switch (getKind()) { + case Kind::Subscript: + case Kind::UnresolvedSubscript: + SubscriptIndexExpr = newExpr; + return; + + case Kind::Invalid: + case Kind::OptionalChain: + case Kind::OptionalWrap: + case Kind::OptionalForce: + case Kind::UnresolvedProperty: + case Kind::Property: + case Kind::Identity: + case Kind::TupleElement: + return; + } + llvm_unreachable("unhandled kind"); + } ArrayRef getSubscriptLabels() const { switch (getKind()) { diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index fe2b7b19b06df..b1e5e9c22937f 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1063,7 +1063,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, auto arg = param->getDefaultValue(); emitOrDelayFunction(*this, constant, [this,constant,arg,initDC](SILFunction *f) { -// preEmitFunction(constant, arg, f, arg); + preEmitFunction(constant, arg, f, arg); PrettyStackTraceSILFunction X("silgen emitDefaultArgGenerator ", f); SILGenFunction SGF(*this, *f, initDC); SGF.emitGeneratorFunction(constant, arg); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 2721ddece9c18..cf16a64667f29 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -50,6 +50,8 @@ #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" +#include + #include "swift/AST/DiagnosticsSIL.h" using namespace swift; @@ -460,6 +462,8 @@ namespace { RValue visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C); RValue visitObjCSelectorExpr(ObjCSelectorExpr *E, SGFContext C); RValue visitKeyPathExpr(KeyPathExpr *E, SGFContext C); + void generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, + KeyPathExpr::Component &component); RValue visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C); RValue visitCollectionExpr(CollectionExpr *E, SGFContext C); @@ -3532,6 +3536,74 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, llvm_unreachable("unknown kind of storage"); } +// There is some uglyness here because this function has to not only handle +// the generation of default arguments but it also has to create a new TupleExpr +// or ParenExpr depending on how many default args are found. +void RValueEmitter::generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, + KeyPathExpr::Component &component) { + // Collect both all the args (default and not) into one place and + // keep track of their types. + llvm::SmallVector newTypeEls; + newTypeEls.reserve(S->getIndices()->size()); + llvm::SmallVector newArgs; + newArgs.reserve(S->getIndices()->size()); + size_t index = 0; + for (auto param : *S->getIndices()) { + newTypeEls.push_back(TupleTypeElt(param->getType())); + + if (param->isDefaultArgument()) { + newArgs.push_back(param->getDefaultValue()); + } else { + if (auto paren = dyn_cast(component.getIndexExpr())) { + newArgs.push_back(paren->getSubExpr()); + } else if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { + newArgs.push_back(tupleExpr->getElement(index)); + } + } + + (void)++index; + } + + // This looks it's going to give us a TypleType but it will give us a + // ParenType when newTypeEls.size() == 1. + auto newIndexType = TupleType::get(newTypeEls, SGF.getASTContext()); + + // Unconditionally recreate the index expr based on the new type. + if (isa(newIndexType.getPointer())) { + assert(newArgs.size() == 1 && + "If this was converted to a paren expr it should have exact one element"); + auto newIndexExpr = new (SGF.getASTContext()) + ParenExpr(component.getIndexExpr()->getStartLoc(), + newArgs[0], + component.getIndexExpr()->getEndLoc(), + /*hasTrailingClosure=*/false); + + component.setIndexExpr(newIndexExpr); + } else if (isa(newIndexType.getPointer())) { + SmallVector elementNames; + elementNames.reserve(newArgs.size()); + if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { + std::copy(tupleExpr->getElementNames().begin(), + tupleExpr->getElementNames().begin(), + elementNames.begin()); + } else { + std::transform(newArgs.begin(), newArgs.end(), + elementNames.begin(), + [](auto&&) { + return Identifier(); + }); + } + + auto newIndexExpr = TupleExpr::createImplicit(SGF.getASTContext(), + newArgs, elementNames); + component.setIndexExpr(newIndexExpr); + } else { + llvm_unreachable("Created invalid type"); + } + + component.getIndexExpr()->setType(newIndexType); +} + RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { if (E->isObjC()) { return visit(E->getObjCStringLiteralExpr(), C); @@ -3574,13 +3646,14 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { } }; - for (auto &component : E->getComponents()) { + for (auto &component : E->getMutableComponents()) { switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Property: case KeyPathExpr::Component::Kind::Subscript: { auto decl = cast(component.getDeclRef().getDecl()); - SGF.SGM.emitDefaultArgGenerators(dyn_cast(decl), - dyn_cast(decl)->getIndices()); + if (auto subscriptDecl = dyn_cast(decl)) { + generateDefaultArgsForKeyPathSubscript(subscriptDecl, component); + } unsigned numOperands = operands.size(); loweredComponents.push_back( diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 2c4cf966dce2d..eabb2c9562798 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -322,38 +322,38 @@ struct Subscripts { } } -struct SubscriptDefaults1 { - subscript(x: Int = 0) -> Int { - get { fatalError() } - set { fatalError() } - } - subscript(x: Int, y: Int, z: Int = 0) -> Int { - get { fatalError() } - set { fatalError() } - } - subscript(x: Bool, bool y: Bool = false) -> Bool { - get { fatalError() } - set { fatalError() } - } - subscript(bool x: Bool, y: Int, z: Int = 0) -> Int { - get { fatalError() } - set { fatalError() } - } -} - -struct SubscriptDefaults2 { - subscript(x: Int? = nil) -> Int { - get { fatalError() } - set { fatalError() } - } -} - -struct SubscriptDefaults3 { - subscript(x: Int = #line) -> Int { - get { fatalError() } - set { fatalError() } - } -} +//struct SubscriptDefaults1 { +// subscript(x: Int = 0) -> Int { +// get { fatalError() } +// set { fatalError() } +// } +// subscript(x: Int, y: Int, z: Int = 0) -> Int { +// get { fatalError() } +// set { fatalError() } +// } +// subscript(x: Bool, bool y: Bool = false) -> Bool { +// get { fatalError() } +// set { fatalError() } +// } +// subscript(bool x: Bool, y: Int, z: Int = 0) -> Int { +// get { fatalError() } +// set { fatalError() } +// } +//} +// +//struct SubscriptDefaults2 { +// subscript(x: Int? = nil) -> Int { +// get { fatalError() } +// set { fatalError() } +// } +//} +// +//struct SubscriptDefaults3 { +// subscript(x: Int = #line) -> Int { +// get { fatalError() } +// set { fatalError() } +// } +//} // CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts func subscripts(x: T, y: U, s: String) { @@ -386,21 +386,21 @@ func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[Bass()] _ = \Subscripts.[Treble()] - _ = \SubscriptDefaults1.[] - _ = \SubscriptDefaults1.[0] - _ = \SubscriptDefaults1.[0, 0] - _ = \SubscriptDefaults1.[0, 0, 0] - - _ = \SubscriptDefaults1.[false] - _ = \SubscriptDefaults1.[false, bool: false] - _ = \SubscriptDefaults1.[bool: false, 0] - _ = \SubscriptDefaults1.[bool: false, 0, 0] - - _ = \SubscriptDefaults2.[] - _ = \SubscriptDefaults2.[0] - - _ = \SubscriptDefaults3.[] - _ = \SubscriptDefaults3.[0] +// _ = \SubscriptDefaults1.[] +// _ = \SubscriptDefaults1.[0] +// _ = \SubscriptDefaults1.[0, 0] +// _ = \SubscriptDefaults1.[0, 0, 0] +// +// _ = \SubscriptDefaults1.[false] +// _ = \SubscriptDefaults1.[false, bool: false] +// _ = \SubscriptDefaults1.[bool: false, 0] +// _ = \SubscriptDefaults1.[bool: false, 0, 0] +// +// _ = \SubscriptDefaults2.[] +// _ = \SubscriptDefaults2.[0] +// +// _ = \SubscriptDefaults3.[] +// _ = \SubscriptDefaults3.[0] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From f7dd8138c2b8dc45026c7cc0cd99816773aa7d5f Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 10 Nov 2019 15:49:27 -0800 Subject: [PATCH 038/283] Run code through clang-format --- include/swift/AST/Expr.h | 2 +- lib/SILGen/SILGenExpr.cpp | 53 ++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 74c45e5f8c4e9..eab6082c1b05d 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5119,7 +5119,7 @@ class KeyPathExpr : public Expr { } llvm_unreachable("unhandled kind"); } - + void setIndexExpr(Expr *newExpr) { switch (getKind()) { case Kind::Subscript: diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index cf16a64667f29..e01126cb94e55 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -462,8 +462,9 @@ namespace { RValue visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C); RValue visitObjCSelectorExpr(ObjCSelectorExpr *E, SGFContext C); RValue visitKeyPathExpr(KeyPathExpr *E, SGFContext C); - void generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, - KeyPathExpr::Component &component); + void + generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, + KeyPathExpr::Component &component); RValue visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C); RValue visitCollectionExpr(CollectionExpr *E, SGFContext C); @@ -3539,44 +3540,44 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, // There is some uglyness here because this function has to not only handle // the generation of default arguments but it also has to create a new TupleExpr // or ParenExpr depending on how many default args are found. -void RValueEmitter::generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, - KeyPathExpr::Component &component) { +void RValueEmitter::generateDefaultArgsForKeyPathSubscript( + SubscriptDecl *S, KeyPathExpr::Component &component) { // Collect both all the args (default and not) into one place and // keep track of their types. llvm::SmallVector newTypeEls; newTypeEls.reserve(S->getIndices()->size()); - llvm::SmallVector newArgs; + llvm::SmallVector newArgs; newArgs.reserve(S->getIndices()->size()); size_t index = 0; for (auto param : *S->getIndices()) { newTypeEls.push_back(TupleTypeElt(param->getType())); - + if (param->isDefaultArgument()) { newArgs.push_back(param->getDefaultValue()); } else { if (auto paren = dyn_cast(component.getIndexExpr())) { newArgs.push_back(paren->getSubExpr()); - } else if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { + } else if (auto tupleExpr = + dyn_cast(component.getIndexExpr())) { newArgs.push_back(tupleExpr->getElement(index)); } } (void)++index; } - + // This looks it's going to give us a TypleType but it will give us a // ParenType when newTypeEls.size() == 1. auto newIndexType = TupleType::get(newTypeEls, SGF.getASTContext()); - + // Unconditionally recreate the index expr based on the new type. if (isa(newIndexType.getPointer())) { - assert(newArgs.size() == 1 && - "If this was converted to a paren expr it should have exact one element"); + assert(newArgs.size() == 1 && "If this was converted to a paren expr it " + "should have exact one element"); auto newIndexExpr = new (SGF.getASTContext()) - ParenExpr(component.getIndexExpr()->getStartLoc(), - newArgs[0], - component.getIndexExpr()->getEndLoc(), - /*hasTrailingClosure=*/false); + ParenExpr(component.getIndexExpr()->getStartLoc(), newArgs[0], + component.getIndexExpr()->getEndLoc(), + /*hasTrailingClosure=*/false); component.setIndexExpr(newIndexExpr); } else if (isa(newIndexType.getPointer())) { @@ -3584,23 +3585,19 @@ void RValueEmitter::generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, elementNames.reserve(newArgs.size()); if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { std::copy(tupleExpr->getElementNames().begin(), - tupleExpr->getElementNames().begin(), - elementNames.begin()); + tupleExpr->getElementNames().begin(), elementNames.begin()); } else { - std::transform(newArgs.begin(), newArgs.end(), - elementNames.begin(), - [](auto&&) { - return Identifier(); - }); + std::transform(newArgs.begin(), newArgs.end(), elementNames.begin(), + [](auto &&) { return Identifier(); }); } - - auto newIndexExpr = TupleExpr::createImplicit(SGF.getASTContext(), - newArgs, elementNames); + + auto newIndexExpr = + TupleExpr::createImplicit(SGF.getASTContext(), newArgs, elementNames); component.setIndexExpr(newIndexExpr); } else { llvm_unreachable("Created invalid type"); } - + component.getIndexExpr()->setType(newIndexType); } @@ -3645,7 +3642,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { std::move(rv).forwardAsSingleValue(SGF, E)); } }; - + for (auto &component : E->getMutableComponents()) { switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Property: @@ -3726,7 +3723,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { llvm_unreachable("not resolved"); } } - + StringRef objcString; if (auto objcExpr = dyn_cast_or_null (E->getObjCStringLiteralExpr())) From b6bca529ec795ac093cae7363c77bfd774ab6f9b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 10 Nov 2019 15:58:55 -0800 Subject: [PATCH 039/283] Remove lingering newline --- lib/SILGen/SILGenExpr.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index e01126cb94e55..2d3f5eba34fbf 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3723,7 +3723,6 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { llvm_unreachable("not resolved"); } } - StringRef objcString; if (auto objcExpr = dyn_cast_or_null (E->getObjCStringLiteralExpr())) From b0877fb4d1ee233d0645be9aa97ea2f73bd34ee4 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 10 Nov 2019 15:59:38 -0800 Subject: [PATCH 040/283] Re-enable tests (oops) --- test/SILGen/keypaths.swift | 94 +++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index eabb2c9562798..2c4cf966dce2d 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -322,38 +322,38 @@ struct Subscripts { } } -//struct SubscriptDefaults1 { -// subscript(x: Int = 0) -> Int { -// get { fatalError() } -// set { fatalError() } -// } -// subscript(x: Int, y: Int, z: Int = 0) -> Int { -// get { fatalError() } -// set { fatalError() } -// } -// subscript(x: Bool, bool y: Bool = false) -> Bool { -// get { fatalError() } -// set { fatalError() } -// } -// subscript(bool x: Bool, y: Int, z: Int = 0) -> Int { -// get { fatalError() } -// set { fatalError() } -// } -//} -// -//struct SubscriptDefaults2 { -// subscript(x: Int? = nil) -> Int { -// get { fatalError() } -// set { fatalError() } -// } -//} -// -//struct SubscriptDefaults3 { -// subscript(x: Int = #line) -> Int { -// get { fatalError() } -// set { fatalError() } -// } -//} +struct SubscriptDefaults1 { + subscript(x: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } + subscript(x: Int, y: Int, z: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } + subscript(x: Bool, bool y: Bool = false) -> Bool { + get { fatalError() } + set { fatalError() } + } + subscript(bool x: Bool, y: Int, z: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults2 { + subscript(x: Int? = nil) -> Int { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults3 { + subscript(x: Int = #line) -> Int { + get { fatalError() } + set { fatalError() } + } +} // CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts func subscripts(x: T, y: U, s: String) { @@ -386,21 +386,21 @@ func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[Bass()] _ = \Subscripts.[Treble()] -// _ = \SubscriptDefaults1.[] -// _ = \SubscriptDefaults1.[0] -// _ = \SubscriptDefaults1.[0, 0] -// _ = \SubscriptDefaults1.[0, 0, 0] -// -// _ = \SubscriptDefaults1.[false] -// _ = \SubscriptDefaults1.[false, bool: false] -// _ = \SubscriptDefaults1.[bool: false, 0] -// _ = \SubscriptDefaults1.[bool: false, 0, 0] -// -// _ = \SubscriptDefaults2.[] -// _ = \SubscriptDefaults2.[0] -// -// _ = \SubscriptDefaults3.[] -// _ = \SubscriptDefaults3.[0] + _ = \SubscriptDefaults1.[] + _ = \SubscriptDefaults1.[0] + _ = \SubscriptDefaults1.[0, 0] + _ = \SubscriptDefaults1.[0, 0, 0] + + _ = \SubscriptDefaults1.[false] + _ = \SubscriptDefaults1.[false, bool: false] + _ = \SubscriptDefaults1.[bool: false, 0] + _ = \SubscriptDefaults1.[bool: false, 0, 0] + + _ = \SubscriptDefaults2.[] + _ = \SubscriptDefaults2.[0] + + _ = \SubscriptDefaults3.[] + _ = \SubscriptDefaults3.[0] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From 921c1ebfe91e04a113e0ae814f5335a204a74ec2 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 10 Nov 2019 17:54:02 -0800 Subject: [PATCH 041/283] Move default arg generation into lambda instead of modifing AST --- include/swift/AST/Expr.h | 20 ----- lib/SILGen/SILGenExpr.cpp | 150 +++++++++++++++----------------------- 2 files changed, 58 insertions(+), 112 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index eab6082c1b05d..a0267c8782554 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5120,26 +5120,6 @@ class KeyPathExpr : public Expr { llvm_unreachable("unhandled kind"); } - void setIndexExpr(Expr *newExpr) { - switch (getKind()) { - case Kind::Subscript: - case Kind::UnresolvedSubscript: - SubscriptIndexExpr = newExpr; - return; - - case Kind::Invalid: - case Kind::OptionalChain: - case Kind::OptionalWrap: - case Kind::OptionalForce: - case Kind::UnresolvedProperty: - case Kind::Property: - case Kind::Identity: - case Kind::TupleElement: - return; - } - llvm_unreachable("unhandled kind"); - } - ArrayRef getSubscriptLabels() const { switch (getKind()) { case Kind::Subscript: diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 2d3f5eba34fbf..322d17c2bf9c8 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -50,8 +50,6 @@ #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" -#include - #include "swift/AST/DiagnosticsSIL.h" using namespace swift; @@ -462,9 +460,6 @@ namespace { RValue visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C); RValue visitObjCSelectorExpr(ObjCSelectorExpr *E, SGFContext C); RValue visitKeyPathExpr(KeyPathExpr *E, SGFContext C); - void - generateDefaultArgsForKeyPathSubscript(SubscriptDecl *S, - KeyPathExpr::Component &component); RValue visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C); RValue visitCollectionExpr(CollectionExpr *E, SGFContext C); @@ -3537,70 +3532,6 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, llvm_unreachable("unknown kind of storage"); } -// There is some uglyness here because this function has to not only handle -// the generation of default arguments but it also has to create a new TupleExpr -// or ParenExpr depending on how many default args are found. -void RValueEmitter::generateDefaultArgsForKeyPathSubscript( - SubscriptDecl *S, KeyPathExpr::Component &component) { - // Collect both all the args (default and not) into one place and - // keep track of their types. - llvm::SmallVector newTypeEls; - newTypeEls.reserve(S->getIndices()->size()); - llvm::SmallVector newArgs; - newArgs.reserve(S->getIndices()->size()); - size_t index = 0; - for (auto param : *S->getIndices()) { - newTypeEls.push_back(TupleTypeElt(param->getType())); - - if (param->isDefaultArgument()) { - newArgs.push_back(param->getDefaultValue()); - } else { - if (auto paren = dyn_cast(component.getIndexExpr())) { - newArgs.push_back(paren->getSubExpr()); - } else if (auto tupleExpr = - dyn_cast(component.getIndexExpr())) { - newArgs.push_back(tupleExpr->getElement(index)); - } - } - - (void)++index; - } - - // This looks it's going to give us a TypleType but it will give us a - // ParenType when newTypeEls.size() == 1. - auto newIndexType = TupleType::get(newTypeEls, SGF.getASTContext()); - - // Unconditionally recreate the index expr based on the new type. - if (isa(newIndexType.getPointer())) { - assert(newArgs.size() == 1 && "If this was converted to a paren expr it " - "should have exact one element"); - auto newIndexExpr = new (SGF.getASTContext()) - ParenExpr(component.getIndexExpr()->getStartLoc(), newArgs[0], - component.getIndexExpr()->getEndLoc(), - /*hasTrailingClosure=*/false); - - component.setIndexExpr(newIndexExpr); - } else if (isa(newIndexType.getPointer())) { - SmallVector elementNames; - elementNames.reserve(newArgs.size()); - if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { - std::copy(tupleExpr->getElementNames().begin(), - tupleExpr->getElementNames().begin(), elementNames.begin()); - } else { - std::transform(newArgs.begin(), newArgs.end(), elementNames.begin(), - [](auto &&) { return Identifier(); }); - } - - auto newIndexExpr = - TupleExpr::createImplicit(SGF.getASTContext(), newArgs, elementNames); - component.setIndexExpr(newIndexExpr); - } else { - llvm_unreachable("Created invalid type"); - } - - component.getIndexExpr()->setType(newIndexType); -} - RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { if (E->isObjC()) { return visit(E->getObjCStringLiteralExpr(), C); @@ -3622,35 +3553,66 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto baseTy = rootTy; SmallVector operands; - - auto lowerSubscriptOperands = - [this, &operands, E](const KeyPathExpr::Component &component) { - if (!component.getIndexExpr()) - return; - - // Evaluate the index arguments. - SmallVector indexValues; - auto indexResult = visit(component.getIndexExpr(), SGFContext()); - if (isa(indexResult.getType())) { - std::move(indexResult).extractElements(indexValues); - } else { + + auto lowerSubscriptOperands = [this, &operands, + E](const KeyPathExpr::Component &component) { + if (!component.getIndexExpr()) + return; + + auto decl = dyn_cast(component.getDeclRef().getDecl()); + assert(decl && + "lowerSubscriptOperands must be called with a subscript decl"); + + // Evaluate the index arguments. + SmallVector indexValues; + RValue indexResult; + if (auto paren = dyn_cast(component.getIndexExpr())) { + auto param = decl->getIndices()->get(0); + if (param->isDefaultArgument()) + indexResult = visit(param->getDefaultValue(), SGFContext()); + else + indexResult = visit(paren, SGFContext()); + indexValues.push_back(std::move(indexResult)); + } else if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { + for (size_t index = 0; index < tupleExpr->getNumElements(); ++index) { + auto param = decl->getIndices()->get(index); + if (param->isDefaultArgument()) + indexResult = visit(param->getDefaultValue(), SGFContext()); + else + indexResult = visit(tupleExpr->getElement(index), SGFContext()); indexValues.push_back(std::move(indexResult)); } + } - for (auto &rv : indexValues) { - operands.push_back( - std::move(rv).forwardAsSingleValue(SGF, E)); - } - }; + for (auto &rv : indexValues) { + operands.push_back(std::move(rv).forwardAsSingleValue(SGF, E)); + } + }; + + auto lowerOperands = [this, &operands, + E](const KeyPathExpr::Component &component) { + if (!component.getIndexExpr()) + return; - for (auto &component : E->getMutableComponents()) { + // Evaluate the index arguments. + SmallVector indexValues; + auto indexResult = visit(component.getIndexExpr(), SGFContext()); + if (isa(indexResult.getType())) { + std::move(indexResult).extractElements(indexValues); + } else { + indexValues.push_back(std::move(indexResult)); + } + + for (auto &rv : indexValues) { + operands.push_back(std::move(rv).forwardAsSingleValue(SGF, E)); + } + }; + + for (auto &component : E->getComponents()) { switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Property: case KeyPathExpr::Component::Kind::Subscript: { auto decl = cast(component.getDeclRef().getDecl()); - if (auto subscriptDecl = dyn_cast(decl)) { - generateDefaultArgsForKeyPathSubscript(subscriptDecl, component); - } unsigned numOperands = operands.size(); loweredComponents.push_back( @@ -3664,8 +3626,12 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { component.getSubscriptIndexHashableConformances(), baseTy, /*for descriptor*/ false)); - lowerSubscriptOperands(component); - + if (kind == KeyPathExpr::Component::Kind::Subscript) { + lowerSubscriptOperands(component); + } else { + lowerOperands(component); + } + assert(numOperands == operands.size() && "operand count out of sync"); baseTy = loweredComponents.back().getComponentType(); From 18f918b5dd630eee43c49dee8e2af13c24f99353 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 10 Nov 2019 20:19:34 -0800 Subject: [PATCH 042/283] Regenerate labels in CSApply instead of keeping two sizes --- include/swift/AST/Expr.h | 10 +++------- lib/AST/Expr.cpp | 4 ++-- lib/Sema/CSApply.cpp | 14 ++++++++++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index a0267c8782554..7604f05d1592a 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4915,10 +4915,7 @@ class KeyPathExpr : public Expr { const ProtocolConformanceRef *SubscriptHashableConformancesData; union { - struct { - unsigned numConformances; - unsigned numLabels; - } SubscriptSize; + unsigned SubscriptSize; unsigned TupleIndex; }; @@ -5124,7 +5121,7 @@ class KeyPathExpr : public Expr { switch (getKind()) { case Kind::Subscript: case Kind::UnresolvedSubscript: - return {SubscriptLabelsData, (size_t)SubscriptSize.numLabels}; + return {SubscriptLabelsData, (size_t)SubscriptSize}; case Kind::Invalid: case Kind::OptionalChain: @@ -5145,8 +5142,7 @@ class KeyPathExpr : public Expr { case Kind::Subscript: if (!SubscriptHashableConformancesData) return {}; - return {SubscriptHashableConformancesData, - (size_t)SubscriptSize.numConformances}; + return {SubscriptHashableConformancesData, (size_t)SubscriptSize}; case Kind::UnresolvedSubscript: case Kind::Invalid: diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index a72dbdd0fe8ef..edcdab2c38ffb 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2159,7 +2159,7 @@ KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels, SubscriptLabelsData = subscriptLabels.data(); SubscriptHashableConformancesData = indexHashables.empty() ? nullptr : indexHashables.data(); - SubscriptSize.numLabels = subscriptLabels.size(); + SubscriptSize = subscriptLabels.size(); } KeyPathExpr::Component @@ -2176,7 +2176,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances( ArrayRef hashables) { switch (getKind()) { case Kind::Subscript: - SubscriptSize.numConformances = hashables.size(); + assert(hashables.size() == SubscriptSize); SubscriptHashableConformancesData = getComponentType()->getASTContext() .AllocateCopy(hashables) .data(); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 187411d348b6e..52f4f73b43e79 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4590,9 +4590,6 @@ namespace { /*applyExpr*/ nullptr, labels, /*hasTrailingClosure*/ false, locator); - auto component = KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr( - ref, newIndexExpr, labels, resolvedTy, componentLoc, {}); - // We need to be able to hash the captured index values in order for // KeyPath itself to be hashable, so check that all of the subscript // index components are hashable and collect their conformances here. @@ -4602,7 +4599,12 @@ namespace { cs.getASTContext().getProtocol(KnownProtocolKind::Hashable); auto fnType = overload.openedType->castTo(); - for (const auto ¶m : fnType->getParams()) { + auto params = fnType->getParams(); + SmallVector newLabels; + for (size_t index = 0; index < fnType->getNumParams(); ++index) { + auto param = params[index]; + newLabels.push_back(param.getLabel()); + auto indexType = simplifyType(param.getPlainType()); // Index type conformance to Hashable protocol has been // verified by the solver, we just need to get it again @@ -4615,6 +4617,10 @@ namespace { conformances.push_back(hashableConformance); } + auto component = + KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr( + ref, newIndexExpr, newLabels, resolvedTy, componentLoc, {}); + component.setSubscriptIndexHashableConformances(conformances); return component; } From 9e77d31d6e269cd0fccf3e853811fa7da214874c Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 8 Nov 2019 17:21:08 -0800 Subject: [PATCH 043/283] NFC: Fix ClassDecl inline bitfield count --- include/swift/AST/Decl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index a1245b55a7ff5..6b27796fdc15a 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -531,7 +531,7 @@ class alignas(1 << DeclAlignInBits) Decl { NumRequirementsInSignature : 16 ); - SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 2+1+1+2+1+7+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 2+1+1+2+1+1+1+1+1+1, /// The stage of the inheritance circularity check for this class. Circularity : 2, From eacca4ed0c1ce0d614004d04a41155ae758c3165 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 8 Nov 2019 17:21:08 -0800 Subject: [PATCH 044/283] Requestify circular inheritance checking Add requests for checking whether a class, protocol, or enum have circular references in their inheritance lists. --- include/swift/AST/Decl.h | 61 ++----- include/swift/AST/TypeCheckRequests.h | 68 +++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 6 + lib/AST/Decl.cpp | 26 ++- lib/AST/TypeCheckRequests.cpp | 49 +++++ lib/ClangImporter/ImportDecl.cpp | 5 - lib/Sema/TypeCheckDecl.cpp | 168 +++++++----------- lib/Serialization/Deserialization.cpp | 4 - test/decl/class/circular_inheritance.swift | 11 +- .../conforms/circular_validation.swift | 4 +- 10 files changed, 229 insertions(+), 173 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 6b27796fdc15a..3bba7ff3d6106 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -176,16 +176,6 @@ enum class DescriptiveDeclKind : uint8_t { OpaqueVarType }; -/// Keeps track of stage of circularity checking for the given protocol. -enum class CircularityCheck { - /// Circularity has not yet been checked. - Unchecked, - /// We're currently checking circularity. - Checking, - /// Circularity has already been checked. - Checked -}; - /// Describes which spelling was used in the source for the 'static' or 'class' /// keyword. enum class StaticSpellingKind : uint8_t { @@ -489,7 +479,7 @@ class alignas(1 << DeclAlignInBits) Decl { IsComputingSemanticMembers : 1 ); - SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+2+1+1+8+16, + SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+8+16, /// Whether the \c RequiresClass bit is valid. RequiresClassValid : 1, @@ -512,9 +502,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// because they could not be imported from Objective-C). HasMissingRequirements : 1, - /// The stage of the circularity check for this protocol. - Circularity : 2, - /// Whether we've computed the inherited protocols list yet. InheritedProtocolsValid : 1, @@ -531,10 +518,7 @@ class alignas(1 << DeclAlignInBits) Decl { NumRequirementsInSignature : 16 ); - SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 2+1+1+2+1+1+1+1+1+1, - /// The stage of the inheritance circularity check for this class. - Circularity : 2, - + SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 1+1+2+1+1+1+1+1+1, /// Whether this class inherits its superclass's convenience initializers. InheritsSuperclassInits : 1, ComputedInheritsSuperclassInits : 1, @@ -562,10 +546,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasUnreferenceableStorage : 1 ); - SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+2+1, - /// The stage of the raw type circularity check for this class. - Circularity : 2, - + SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+1, /// True if the enum has cases and at least one case has associated values. HasAssociatedValues : 2, /// True if the enum has at least one case that has some availability @@ -3601,16 +3582,9 @@ class EnumDecl final : public NominalTypeDecl { for (auto elt : getAllElements()) elements.insert(elt); } - - /// Retrieve the status of circularity checking for class inheritance. - CircularityCheck getCircularityCheck() const { - return static_cast(Bits.EnumDecl.Circularity); - } - - /// Record the current stage of circularity checking. - void setCircularityCheck(CircularityCheck circularity) { - Bits.EnumDecl.Circularity = static_cast(circularity); - } + + /// Whether this enum has a raw value type that recursively references itself. + bool hasCircularRawValue() const; /// Record that this enum has had all of its raw values computed. void setHasFixedRawValues(); @@ -3869,15 +3843,8 @@ class ClassDecl final : public NominalTypeDecl { /// Set the superclass of this class. void setSuperclass(Type superclass); - /// Retrieve the status of circularity checking for class inheritance. - CircularityCheck getCircularityCheck() const { - return static_cast(Bits.ClassDecl.Circularity); - } - - /// Record the current stage of circularity checking. - void setCircularityCheck(CircularityCheck circularity) { - Bits.ClassDecl.Circularity = static_cast(circularity); - } + /// Whether this class has a circular reference in its inheritance hierarchy. + bool hasCircularInheritance() const; /// Walk this class and all of the superclasses of this class, transitively, /// invoking the callback function for each class. @@ -4348,15 +4315,9 @@ class ProtocolDecl final : public NominalTypeDecl { return false; } - /// Retrieve the status of circularity checking for protocol inheritance. - CircularityCheck getCircularityCheck() const { - return static_cast(Bits.ProtocolDecl.Circularity); - } - - /// Record the current stage of circularity checking. - void setCircularityCheck(CircularityCheck circularity) { - Bits.ProtocolDecl.Circularity = static_cast(circularity); - } + /// Whether this protocol has a circular reference in its list of inherited + /// protocols. + bool hasCircularInheritedProtocols() const; /// Returns true if the protocol has requirements that are not listed in its /// members. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 635a2ccc2434e..427c026cd03f7 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1775,6 +1775,74 @@ class PreCheckFunctionBuilderRequest bool isCached() const { return true; } }; +/// Computes whether a class has a circular reference in its inheritance +/// hierarchy. +class HasCircularInheritanceRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ClassDecl *decl) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Cached. + bool isCached() const { return true; } +}; + +/// Computes whether a protocol has a circular reference in its list of +/// inherited protocols. +class HasCircularInheritedProtocolsRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ProtocolDecl *decl) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Cached. + bool isCached() const { return true; } +}; + +/// Computes whether an enum's raw value has a circular reference. +class HasCircularRawValueRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, EnumDecl *decl) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Cached. + bool isCached() const { return true; } +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index ff7c911067b53..4fd4cb0434836 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -58,6 +58,12 @@ SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *), SWIFT_REQUEST(NameLookup, GenericSignatureRequest, GenericSignature (GenericContext *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasCircularInheritanceRequest, + bool(ClassDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasCircularInheritedProtocolsRequest, + bool(ProtocolDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasCircularRawValueRequest, + bool(EnumDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequest, GenericSignature (ModuleDecl *, GenericSignatureImpl *, GenericParamList *, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 272e7ae64fab9..a59f780d6945e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3888,8 +3888,6 @@ EnumDecl::EnumDecl(SourceLoc EnumLoc, GenericParams), EnumLoc(EnumLoc) { - Bits.EnumDecl.Circularity - = static_cast(CircularityCheck::Unchecked); Bits.EnumDecl.HasAssociatedValues = static_cast(AssociatedValueCheck::Unchecked); Bits.EnumDecl.HasAnyUnavailableValues @@ -3997,14 +3995,19 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { } } +bool ClassDecl::hasCircularInheritance() const { + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + HasCircularInheritanceRequest{mutableThis}, true); +} + ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, MutableArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Class, Parent, Name, NameLoc, Inherited, GenericParams), ClassLoc(ClassLoc) { - Bits.ClassDecl.Circularity - = static_cast(CircularityCheck::Unchecked); Bits.ClassDecl.InheritsSuperclassInits = 0; Bits.ClassDecl.ComputedInheritsSuperclassInits = 0; Bits.ClassDecl.RawForeignKind = 0; @@ -4477,6 +4480,13 @@ void EnumDecl::setHasFixedRawValues() { LazySemanticInfo.RawTypeAndFlags.setInt(flags); } +bool EnumDecl::hasCircularRawValue() const { + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + HasCircularRawValueRequest{mutableThis}, true); +} + ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, Identifier Name, MutableArrayRef Inherited, @@ -4488,8 +4498,6 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, Bits.ProtocolDecl.RequiresClass = false; Bits.ProtocolDecl.ExistentialConformsToSelfValid = false; Bits.ProtocolDecl.ExistentialConformsToSelf = false; - Bits.ProtocolDecl.Circularity - = static_cast(CircularityCheck::Unchecked); Bits.ProtocolDecl.InheritedProtocolsValid = 0; Bits.ProtocolDecl.NumRequirementsInSignature = 0; Bits.ProtocolDecl.HasMissingRequirements = false; @@ -4916,6 +4924,12 @@ void ProtocolDecl::computeKnownProtocolKind() const { const_cast(this)->Bits.ProtocolDecl.KnownProtocol = value; } +bool ProtocolDecl::hasCircularInheritedProtocols() const { + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, HasCircularInheritedProtocolsRequest{mutableThis}, true); +} StorageImplInfo AbstractStorageDecl::getImplInfo() const { ASTContext &ctx = getASTContext(); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index cc352c8c2f0f4..5a3b6ed684863 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1132,3 +1132,52 @@ void swift::simple_display(llvm::raw_ostream &out, break; } } + +//----------------------------------------------------------------------------// +// HasCircularInheritanceRequest computation. +//----------------------------------------------------------------------------// + +void HasCircularInheritanceRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::circular_class_inheritance, decl->getName()); +} + +void HasCircularInheritanceRequest::noteCycleStep( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + +//----------------------------------------------------------------------------// +// HasCircularInheritedProtocolsRequest computation. +//----------------------------------------------------------------------------// + +void HasCircularInheritedProtocolsRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::circular_protocol_def, decl->getName()); +} + +void HasCircularInheritedProtocolsRequest::noteCycleStep( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + +//----------------------------------------------------------------------------// +// HasCircularRawValueRequest computation. +//----------------------------------------------------------------------------// + +void HasCircularRawValueRequest::diagnoseCycle(DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::circular_enum_inheritance, decl->getName()); +} + +void HasCircularRawValueRequest::noteCycleStep(DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index cc6be0ca8bb02..4845cd3e05124 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4699,8 +4699,6 @@ namespace { Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - result->setCircularityCheck(CircularityCheck::Checked); - // Import protocols this protocol conforms to. SmallVector inheritedTypes; importObjCProtocols(result, decl->getReferencedProtocols(), @@ -4736,7 +4734,6 @@ namespace { SourceLoc(), None, nullptr, dc); Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - result->setCircularityCheck(CircularityCheck::Checked); result->setSuperclass(Type()); result->setAddedImplicitInitializers(); // suppress all initializers result->setHasMissingVTableEntries(false); @@ -4842,7 +4839,6 @@ namespace { } Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - result->setCircularityCheck(CircularityCheck::Checked); addObjCAttribute(result, Impl.importIdentifier(decl->getIdentifier())); if (declaredNative) @@ -5319,7 +5315,6 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl, auto theClass = Impl.createDeclWithClangNode( decl, AccessLevel::Public, SourceLoc(), className, SourceLoc(), None, nullptr, dc); - theClass->setCircularityCheck(CircularityCheck::Checked); theClass->setSuperclass(superclass); theClass->setAddedImplicitInitializers(); // suppress all initializers theClass->setHasMissingVTableEntries(false); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 67fe513b5aec1..95c80b1acbd73 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -563,93 +563,75 @@ static void checkGenericParams(GenericContext *ownerCtx) { [](Requirement, RequirementRepr *) { return false; }); } -/// Retrieve the set of protocols the given protocol inherits. -static llvm::TinyPtrVector -getInheritedForCycleCheck(ProtocolDecl *proto, ProtocolDecl **scratch) { - TinyPtrVector result; - - bool anyObject = false; - for (const auto &found : - getDirectlyInheritedNominalTypeDecls(proto, anyObject)) { - if (auto protoDecl = dyn_cast(found.second)) - result.push_back(protoDecl); - } - - return result; +static bool canSkipCircularityCheck(NominalTypeDecl *decl) { + // Don't bother checking imported or deserialized decls. + return decl->hasClangNode() || decl->wasDeserialized(); } -/// Retrieve the superclass of the given class. -static ArrayRef getInheritedForCycleCheck(ClassDecl *classDecl, - ClassDecl **scratch) { - if (classDecl->hasSuperclass()) { - *scratch = classDecl->getSuperclassDecl(); - return *scratch; - } - return { }; -} +llvm::Expected +HasCircularInheritanceRequest::evaluate(Evaluator &evaluator, + ClassDecl *decl) const { + if (canSkipCircularityCheck(decl) || !decl->hasSuperclass()) + return false; -/// Retrieve the raw type of the given enum. -static ArrayRef getInheritedForCycleCheck(EnumDecl *enumDecl, - EnumDecl **scratch) { - if (enumDecl->hasRawType()) { - *scratch = enumDecl->getRawType()->getEnumOrBoundGenericEnum(); - return *scratch ? ArrayRef(*scratch) : ArrayRef{}; + auto *superclass = decl->getSuperclassDecl(); + auto result = evaluator(HasCircularInheritanceRequest{superclass}); + + // If we have a cycle, handle it and return true. + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; } - return { }; + return result; } -/// Check for circular inheritance. -template -static void checkCircularity(T *decl, Diag circularDiag, - DescriptiveDeclKind declKind, - SmallVectorImpl &path) { - switch (decl->getCircularityCheck()) { - case CircularityCheck::Checked: - return; - - case CircularityCheck::Checking: { - // We're already checking this type, which means we have a cycle. +llvm::Expected +HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, + ProtocolDecl *decl) const { + if (canSkipCircularityCheck(decl)) + return false; - // The beginning of the path might not be part of the cycle, so find - // where the cycle starts. - assert(!path.empty()); + bool anyObject = false; + auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); + for (auto &found : inherited) { + auto *protoDecl = dyn_cast(found.second); + if (!protoDecl) + continue; - auto cycleStart = path.end() - 1; - while (*cycleStart != decl) { - assert(cycleStart != path.begin() && "Missing cycle start?"); - --cycleStart; + // If we have a cycle, handle it and return true. + auto result = evaluator(HasCircularInheritedProtocolsRequest{protoDecl}); + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; } - // If the path length is 1 the type directly references itself. - if (path.end() - cycleStart == 1) { - path.back()->diagnose(circularDiag, path.back()->getName()); - - break; - } + // If the underlying request handled a cycle and returned true, bail. + if (*result) + return true; + } + return false; +} - // Diagnose the cycle. - decl->diagnose(circularDiag, (*cycleStart)->getName()); - for (auto i = cycleStart + 1, iEnd = path.end(); i != iEnd; ++i) { - (*i)->diagnose(diag::kind_declname_declared_here, declKind, - (*i)->getName()); - } +llvm::Expected +HasCircularRawValueRequest::evaluate(Evaluator &evaluator, + EnumDecl *decl) const { + if (canSkipCircularityCheck(decl) || !decl->hasRawType()) + return false; - break; - } + auto *inherited = decl->getRawType()->getEnumOrBoundGenericEnum(); + if (!inherited) + return false; - case CircularityCheck::Unchecked: { - // Walk to the inherited class or protocols. - path.push_back(decl); - decl->setCircularityCheck(CircularityCheck::Checking); - T *scratch = nullptr; - for (auto inherited : getInheritedForCycleCheck(decl, &scratch)) { - checkCircularity(inherited, circularDiag, declKind, path); - } - decl->setCircularityCheck(CircularityCheck::Checked); - path.pop_back(); - break; - } + // If we have a cycle, handle it and return true. + auto result = evaluator(HasCircularRawValueRequest{inherited}); + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; } + return result; } /// Expose TypeChecker's handling of GenericParamList to SIL parsing. @@ -2780,13 +2762,8 @@ class DeclChecker : public DeclVisitor { checkGenericParams(ED); - { - // Check for circular inheritance of the raw type. - SmallVector path; - path.push_back(ED); - checkCircularity(ED, diag::circular_enum_inheritance, - DescriptiveDeclKind::Enum, path); - } + // Check for circular inheritance of the raw type. + (void)ED->hasCircularRawValue(); for (Decl *member : ED->getMembers()) visit(member); @@ -2952,13 +2929,8 @@ class DeclChecker : public DeclVisitor { checkGenericParams(CD); - { - // Check for circular inheritance. - SmallVector path; - path.push_back(CD); - checkCircularity(CD, diag::circular_class_inheritance, - DescriptiveDeclKind::Class, path); - } + // Check for circular inheritance. + (void)CD->hasCircularInheritance(); // Force lowering of stored properties. (void) CD->getStoredProperties(); @@ -3102,21 +3074,15 @@ class DeclChecker : public DeclVisitor { void visitProtocolDecl(ProtocolDecl *PD) { checkUnsupportedNestedType(PD); - auto *SF = PD->getParentSourceFile(); - { - // Check for circular inheritance within the protocol. - SmallVector path; - path.push_back(PD); - checkCircularity(PD, diag::circular_protocol_def, - DescriptiveDeclKind::Protocol, path); + // Check for circular inheritance within the protocol. + (void)PD->hasCircularInheritedProtocols(); - if (SF) { - if (auto *tracker = SF->getReferencedNameTracker()) { - bool isNonPrivate = - (PD->getFormalAccess() > AccessLevel::FilePrivate); - for (auto *parentProto : PD->getInheritedProtocols()) - tracker->addUsedMember({parentProto, Identifier()}, isNonPrivate); - } + auto *SF = PD->getParentSourceFile(); + if (SF) { + if (auto *tracker = SF->getReferencedNameTracker()) { + bool isNonPrivate = (PD->getFormalAccess() > AccessLevel::FilePrivate); + for (auto *parentProto : PD->getInheritedProtocols()) + tracker->addUsedMember({parentProto, Identifier()}, isNonPrivate); } } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 12e2afa585232..8e71fccdd34c3 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3218,8 +3218,6 @@ class swift::DeclDeserializer { proto->setImplicit(); proto->setIsObjC(isObjC); - proto->setCircularityCheck(CircularityCheck::Checked); - proto->setLazyRequirementSignature(&MF, MF.DeclTypeCursor.GetCurrentBitNo()); skipGenericRequirements(MF.DeclTypeCursor); @@ -3429,8 +3427,6 @@ class swift::DeclDeserializer { &MF, encodeLazyConformanceContextData(numConformances, MF.DeclTypeCursor.GetCurrentBitNo())); - - theClass->setCircularityCheck(CircularityCheck::Checked); return theClass; } diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index f1ae0fd4a3f60..aa48b67490cc1 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -47,11 +47,12 @@ class Outer3 // expected-error {{circular reference}} } // CHECK: ===CYCLE DETECTED=== -// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}} -// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@ -// CHECK: `--{{.*}}SuperclassDeclRequest -// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Right@ -// CHECK: `--{{.*}}SuperclassDeclRequest{{.*(cyclic dependency)}} +// CHECK-NEXT: `--{{.*}}HasCircularInheritanceRequest(circular_inheritance.(file).Left@ +// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}} +// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@ +// CHECK: `--{{.*}}SuperclassDeclRequest +// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Right@ +// CHECK: `--{{.*}}SuperclassDeclRequest{{.*(cyclic dependency)}} // CHECK-DOT: digraph Dependencies // CHECK-DOT: label="InheritedTypeRequest diff --git a/test/decl/protocol/conforms/circular_validation.swift b/test/decl/protocol/conforms/circular_validation.swift index 5d22466b7fa3b..b52a8b516f063 100644 --- a/test/decl/protocol/conforms/circular_validation.swift +++ b/test/decl/protocol/conforms/circular_validation.swift @@ -13,6 +13,6 @@ struct S : P { // expected-error {{type 'S' does not conform to protocol 'P'}} } // FIXME: Lousy diagnostics on this case. -protocol SR9224_Foo: SR9224_Foobar {} // expected-error 3 {{protocol 'SR9224_Foo' refines itself}} -protocol SR9224_Bar: SR9224_Foobar {} // expected-error {{protocol 'SR9224_Bar' refines itself}} expected-note 2 {{protocol 'SR9224_Bar' declared here}} +protocol SR9224_Foo: SR9224_Foobar {} // expected-error 2 {{protocol 'SR9224_Foo' refines itself}} +protocol SR9224_Bar: SR9224_Foobar {} // expected-note {{protocol 'SR9224_Bar' declared here}} typealias SR9224_Foobar = SR9224_Foo & SR9224_Bar From 7acabfe1cfd3c1b9c70b8dd29fba6fabf84dbf9a Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 11 Nov 2019 08:54:07 -0800 Subject: [PATCH 045/283] IRGen: We need to emit metadata for types that are emitted with shared linkage Otherwise one TU could only require the type descriptor without metadata and another TU could require metadata and type descriptor. Whether the metadata access function is available would then depend on the linking order of the two TUs. rdar://56929811 --- lib/IRGen/GenDecl.cpp | 11 ++++++++++ test/IRGen/foreign_type_metadata.swift | 22 +++++++++++++++++++ test/Interpreter/ForeignMetadata.swift | 11 ++++++++++ .../Inputs/ForeignTypeMetadata1.swift | 12 ++++++++++ .../Inputs/ForeignTypeMetadata2.swift | 7 ++++++ 5 files changed, 63 insertions(+) create mode 100644 test/IRGen/foreign_type_metadata.swift create mode 100644 test/Interpreter/ForeignMetadata.swift create mode 100644 test/Interpreter/Inputs/ForeignTypeMetadata1.swift create mode 100644 test/Interpreter/Inputs/ForeignTypeMetadata2.swift diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 8f36545b5027d..2c15da60dc75b 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1262,6 +1262,17 @@ void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type, if (!hasLazyMetadata(type)) return; + // If the type can be generated in several TU with weak linkage we don't know + // which one will be picked up so we have to require the metadata. Otherwise, + // the situation can arise where one TU contains a type descriptor with a null + // metadata access function and the other TU which requires metadata has a + // type descriptor with a valid metadata access function but the linker picks + // the first one. + if (isAccessorLazilyGenerated(getTypeMetadataAccessStrategy( + type->getDeclaredType()->getCanonicalType()))) { + requireMetadata = RequireMetadata; + } + // Try to create a new record of the fact that we used this type. auto insertResult = LazyTypeGlobals.try_emplace(type); auto &entry = insertResult.first->second; diff --git a/test/IRGen/foreign_type_metadata.swift b/test/IRGen/foreign_type_metadata.swift new file mode 100644 index 0000000000000..19761bc2d107c --- /dev/null +++ b/test/IRGen/foreign_type_metadata.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend %s -emit-ir | %FileCheck %s + +// REQUIRES: objc_interop + +import Foundation + +// Make sure we emit a metadata accessor for foreign types even if the type +// metadata is not required by this TU. Another TU could require it and the +// linker could choose the less defined one of the two. + +// CHECK: @"$sSo8_NSRangeVMn" = linkonce_odr hidden constant <{ {{.*}}sSo8_NSRangeVMa{{.*}} }>, section "__TEXT,__const" + +func use(_ closure: @escaping (Int) -> ()) {} + +public func captureRange(_ r: NSRange?) { + var l = r + use { + if $0 == 0 { + l = NSRange() + } + } +} diff --git a/test/Interpreter/ForeignMetadata.swift b/test/Interpreter/ForeignMetadata.swift new file mode 100644 index 0000000000000..e42eb59e6ed08 --- /dev/null +++ b/test/Interpreter/ForeignMetadata.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: cp %s %t/main.swift +// RUN: %target-build-swift -o %t/main %S/Inputs/ForeignTypeMetadata1.swift %S/Inputs/ForeignTypeMetadata2.swift %t/main.swift -swift-version 5 +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main + + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +useType() diff --git a/test/Interpreter/Inputs/ForeignTypeMetadata1.swift b/test/Interpreter/Inputs/ForeignTypeMetadata1.swift new file mode 100644 index 0000000000000..5bdd0f08b3dc5 --- /dev/null +++ b/test/Interpreter/Inputs/ForeignTypeMetadata1.swift @@ -0,0 +1,12 @@ +import Foundation + +func use(_ closure: @escaping (Int) -> ()) {} + +public func captureRange(_ r: NSRange?) { + var l = r + use { + if $0 == 0 { + l = NSRange() + } + } +} diff --git a/test/Interpreter/Inputs/ForeignTypeMetadata2.swift b/test/Interpreter/Inputs/ForeignTypeMetadata2.swift new file mode 100644 index 0000000000000..386639931ac45 --- /dev/null +++ b/test/Interpreter/Inputs/ForeignTypeMetadata2.swift @@ -0,0 +1,7 @@ +import Foundation + +public func useType() { + var x = [NSRange]() + x.append(NSRange()) + print(x) +} From 7f2d4c0a999eb9272af28d31d4976da5800cedce Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 8 Nov 2019 13:49:04 -0800 Subject: [PATCH 046/283] [CSApply] When applying constraint fixes for a solution, only coalesce fixes of the same kind. --- lib/Sema/CSApply.cpp | 29 ++++++++++++++------------ lib/Sema/CSFix.h | 23 +++++++++++++++++++- lib/Sema/CSSimplify.cpp | 5 +++-- test/Constraints/diagnostics.swift | 2 ++ test/Constraints/protocols.swift | 1 + test/decl/subscript/subscripting.swift | 3 +++ test/expr/expressions.swift | 5 +++-- test/expr/unary/keypath/keypath.swift | 6 +++--- test/type/opaque.swift | 3 ++- 9 files changed, 55 insertions(+), 22 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index f0f43ed50237e..83352fd9ace0b 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7367,23 +7367,26 @@ bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) { return; // Coalesce fixes with the same locator to avoid duplicating notes. + using ConstraintFixVector = llvm::SmallVector; llvm::SmallMapVector, 4> aggregatedFixes; + llvm::SmallMapVector, 4> aggregatedFixes; for (auto *fix : fixes->second) - aggregatedFixes[fix->getLocator()].push_back(fix); + aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix); for (auto fixesPerLocator : aggregatedFixes) { - auto fixes = fixesPerLocator.second; - auto *primaryFix = fixes[0]; - ArrayRef secondaryFixes{fixes.begin() + 1, fixes.end()}; - - auto *coalescedFix = primaryFix->coalescedWith(secondaryFixes); - auto diagnosed = coalescedFix->diagnose(); - if (coalescedFix->isWarning()) { - assert(diagnosed && "warnings should always be diagnosed"); - (void)diagnosed; - } else { - DiagnosedAnyErrors |= diagnosed; + for (auto fixesPerKind : fixesPerLocator.second) { + auto fixes = fixesPerKind.second; + auto *primaryFix = fixes[0]; + ArrayRef secondaryFixes{fixes.begin() + 1, fixes.end()}; + + auto *coalescedFix = primaryFix->coalescedWith(secondaryFixes); + auto diagnosed = coalescedFix->diagnose(); + if (coalescedFix->isWarning()) { + assert(diagnosed && "warnings should always be diagnosed"); + (void)diagnosed; + } else { + DiagnosedAnyErrors |= diagnosed; + } } } } diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 2f5b11a99c604..36ff2964b5b27 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -47,9 +47,11 @@ class Solution; /// Describes the kind of fix to apply to the given constraint before /// visiting it. +/// +/// Note: values 0 and 1 are reserved for empty and tombstone kinds. enum class FixKind : uint8_t { /// Introduce a '!' to force an optional unwrap. - ForceOptional, + ForceOptional = 2, /// Unwrap an optional base when we have a member access. UnwrapOptionalBase, @@ -1553,4 +1555,23 @@ class TreatEphemeralAsNonEphemeral final : public AllowArgumentMismatch { } // end namespace constraints } // end namespace swift +namespace llvm { + template <> + struct DenseMapInfo { + using FixKind = swift::constraints::FixKind; + static inline FixKind getEmptyKey() { + return static_cast(0); + } + static inline FixKind getTombstoneKey() { + return static_cast(1); + } + static unsigned getHashValue(FixKind kind) { + return static_cast(kind); + } + static bool isEqual(FixKind lhs, FixKind rhs) { + return lhs == rhs; + } + }; +} + #endif // SWIFT_SEMA_CSFIX_H diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 7b98c57825225..f286b0ab556be 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6780,8 +6780,9 @@ ConstraintSystem::simplifyKeyPathConstraint( if (auto *fix = AllowInvalidRefInKeyPath::forRef( *this, choices[i].getDecl(), componentLoc)) { - if (!shouldAttemptFixes() || recordFix(fix)) - return SolutionKind::Error; + if (!hasFixFor(componentLoc, FixKind::AllowTypeOrInstanceMember)) + if (!shouldAttemptFixes() || recordFix(fix)) + return SolutionKind::Error; // If this was a method reference let's mark it as read-only. if (!storage) { diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 3f2d8da2d752f..c03c1ab8c347a 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -454,6 +454,7 @@ CurriedClass.method3(c)(1.0, b: 1) // expected-error {{cannot convert valu CurriedClass.method3(c)(1) // expected-error {{missing argument for parameter 'b' in call}} CurriedClass.method3(c)(c: 1.0) // expected-error {{incorrect argument labels in call (have 'c:', expected '_:b:')}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} +// expected-error@-2 {{missing argument for parameter #1 in call}} extension CurriedClass { @@ -1295,6 +1296,7 @@ rdar43525641(1, c: 2, 3) // Ok struct Array {} let foo: Swift.Array = Array() // expected-error {{cannot convert value of type 'Array' to specified type 'Array'}} +// expected-error@-1 {{generic parameter 'Element' could not be inferred}} struct Error {} let bar: Swift.Error = Error() //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} diff --git a/test/Constraints/protocols.swift b/test/Constraints/protocols.swift index 477c43aafbc7f..eef0fe619afe3 100644 --- a/test/Constraints/protocols.swift +++ b/test/Constraints/protocols.swift @@ -212,6 +212,7 @@ func staticExistential(_ p: P.Type, pp: P.Protocol) { // Instance member of existential metatype -- not allowed _ = p.bar // expected-error{{instance member 'bar' cannot be used on type 'P'}} _ = p.mut // expected-error{{instance member 'mut' cannot be used on type 'P'}} + // expected-error@-1 {{partial application of 'mutating' method is not allowed}} // Static member of metatype -- not allowed _ = pp.tum // expected-error{{static member 'tum' cannot be used on protocol metatype 'P.Protocol'}} diff --git a/test/decl/subscript/subscripting.swift b/test/decl/subscript/subscripting.swift index 1cd4fe9c6ff49..1cd03a75154cd 100644 --- a/test/decl/subscript/subscripting.swift +++ b/test/decl/subscript/subscripting.swift @@ -327,6 +327,7 @@ class ClassConformingToRefinedProtocol: RefinedProtocol {} struct GenSubscriptFixitTest { subscript(_ arg: T) -> Bool { return true } // expected-note 3 {{declared here}} + // expected-note@-1 2 {{in call to 'subscript(_:)'}} } func testGenSubscriptFixit(_ s0: GenSubscriptFixitTest) { @@ -339,9 +340,11 @@ func testUnresolvedMemberSubscriptFixit(_ s0: GenSubscriptFixitTest) { _ = s0.subscript // expected-error@-1 {{value of type 'GenSubscriptFixitTest' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{9-19=[<#index#>]}} + // expected-error@-2 {{generic parameter 'T' could not be inferred}} s0.subscript = true // expected-error@-1 {{value of type 'GenSubscriptFixitTest' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{5-15=[<#index#>]}} + // expected-error@-2 {{generic parameter 'T' could not be inferred}} } struct SubscriptTest1 { diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 1f8cc1490751e..58c7d69669147 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -593,7 +593,8 @@ func conversionTest(_ a: inout Double, b: inout Int) { var e3 = Empty(Float(d)) // expected-warning {{variable 'e3' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{9-9=: Empty}} } -struct Rule { +// FIXME(diagnostics): This note is pointing to a synthesized init +struct Rule { // expected-note {{'init(target:dependencies:)' declared here}} var target: String var dependencies: String } @@ -603,7 +604,7 @@ var ruleVar: Rule // does argument belong to in this case. If the `target` was of a different type, we currently suggest to add an argument for `dependencies:` // which is incorrect. ruleVar = Rule("a") // expected-error {{missing argument label 'target:' in call}} - +// expected-error@-1 {{missing argument for parameter 'dependencies' in call}} class C { var x: C? diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index f49b4d9eb71a7..f6f94f1274d4e 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -36,7 +36,7 @@ struct A: Hashable { func hash(into hasher: inout Hasher) { fatalError() } } struct B {} -struct C { // expected-note 2 {{'T' declared as parameter to type 'C'}} +struct C { // expected-note 3 {{'T' declared as parameter to type 'C'}} var value: T subscript() -> T { get { return value } } subscript(sub: Sub) -> T { get { return value } set { } } @@ -230,9 +230,9 @@ func testDisembodiedStringInterpolation(x: Int) { func testNoComponents() { let _: KeyPath = \A // expected-error{{must have at least one component}} - // FIXME(diagnostics): This should be diagnosed as `missing generic parameter 'T'` instead of a contextual failure. let _: KeyPath = \C // expected-error{{must have at least one component}} expected-error{{}} - // expected-error@-1 {{cannot convert value of type 'KeyPath' to specified type 'KeyPath, A>'}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} + // expected-error@-2 {{cannot convert value of type 'KeyPath' to specified type 'KeyPath, A>'}} } struct TupleStruct { diff --git a/test/type/opaque.swift b/test/type/opaque.swift index 9d65b73b214a1..f17474e7d2e4a 100644 --- a/test/type/opaque.swift +++ b/test/type/opaque.swift @@ -122,7 +122,8 @@ func typeIdentity() { var af = alice af = alice af = bob // expected-error{{}} - af = grace // expected-error{{}} + af = grace // expected-error{{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{cannot assign value of type '(T) -> some P' to type '() -> some P'}} } do { From 212f20245a2bee58babb368ae538dfccf8ab9ff8 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 8 Nov 2019 14:23:57 -0800 Subject: [PATCH 047/283] [ConstraintSystem] Add TypeBase::hasHole to be used by the constraint system. Eventually, this will replace TypeBase::hasUnresolvedType. --- include/swift/AST/Types.h | 9 +++++++-- lib/Sema/CSSimplify.cpp | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 89bb823f7633c..a37598ce3fe23 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -175,7 +175,7 @@ class RecursiveTypeProperties { /// Does a type with these properties have an unresolved type somewhere in it? bool hasUnresolvedType() const { return Bits & HasUnresolvedType; } - + /// Is a type with these properties an lvalue? bool isLValue() const { return Bits & IsLValue; } @@ -563,7 +563,12 @@ class alignas(1 << TypeAlignInBits) TypeBase { bool hasUnresolvedType() const { return getRecursiveProperties().hasUnresolvedType(); } - + + /// Determine whether this type involves a hole. + bool hasHole() const { + return getRecursiveProperties().hasUnresolvedType(); + } + /// Determine whether the type involves a context-dependent archetype. bool hasArchetype() const { return getRecursiveProperties().hasArchetype(); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f286b0ab556be..53447cb8c53d6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3089,7 +3089,7 @@ bool ConstraintSystem::repairFailures( } // If either type has a hole, consider this fixed. - if (lhs->hasUnresolvedType() || rhs->hasUnresolvedType()) + if (lhs->hasHole() || rhs->hasHole()) return true; conversionsOrFixes.push_back( @@ -3155,7 +3155,7 @@ bool ConstraintSystem::repairFailures( case ConstraintLocator::TypeParameterRequirement: case ConstraintLocator::ConditionalRequirement: { // If either type has a hole, consider this fixed. - if (lhs->hasUnresolvedType() || rhs->hasUnresolvedType()) + if (lhs->hasHole() || rhs->hasHole()) return true; // If dependent members are present here it's because @@ -3415,7 +3415,7 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::InstanceType: { - if (lhs->hasUnresolvedType() || rhs->hasUnresolvedType()) + if (lhs->hasHole() || rhs->hasHole()) return true; break; From 3f36ecf5baaa74a702b295106555760a59057072 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 11 Nov 2019 10:27:26 -0800 Subject: [PATCH 048/283] Add a regression test for a GSB crasher From rdar://56673657 --- .../compiler_crashers_2/rdar56398071.swift | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 validation-test/compiler_crashers_2/rdar56398071.swift diff --git a/validation-test/compiler_crashers_2/rdar56398071.swift b/validation-test/compiler_crashers_2/rdar56398071.swift new file mode 100644 index 0000000000000..6f8bda87f6d8e --- /dev/null +++ b/validation-test/compiler_crashers_2/rdar56398071.swift @@ -0,0 +1,151 @@ +// RUN: not --crash %target-swift-frontend -primary-file %s -emit-silgen + +// REQUIRES: asserts + +public protocol WrappedSignedInteger: SignedInteger where Stride == Int { + typealias WrappedInteger = Int + + var wrappedNumber: WrappedInteger { get set } + init(wrappedNumber: WrappedInteger) +} + +public extension WrappedSignedInteger { + typealias IntegerLiteralType = WrappedInteger.IntegerLiteralType + typealias Magnitude = WrappedInteger.Magnitude + typealias Words = WrappedInteger.Words + + // MARK: - Initializers - + + init(_ source: T) where T : BinaryInteger { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init(truncatingIfNeeded source: T) where T : BinaryInteger { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init?(exactly source: T) where T : BinaryFloatingPoint { + if let wrappedNumber = WrappedInteger(exactly: source) { + self.init(wrappedNumber: wrappedNumber) + } else { + return nil + } + } + + init(_ source: T) where T : BinaryFloatingPoint { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init(clamping source: T) where T : BinaryInteger { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init?(exactly source: T) where T : BinaryInteger { + if let wrappedNumber = WrappedInteger(exactly: source) { + self.init(wrappedNumber: wrappedNumber) + } else { + return nil + } + } + + init(integerLiteral wrappedNumber: WrappedInteger.IntegerLiteralType) { + let wrapped = WrappedInteger(integerLiteral: wrappedNumber) + self.init(wrappedNumber: wrapped) + } + + // MARK: - Stride - + + func advanced(by n: Int) -> Self { + .init(wrappedNumber: wrappedNumber + n) + } + + func distance(to other: Self) -> Self.Stride { + other.wrappedNumber - wrappedNumber + } + + // MARK: - Properties - + + var magnitude: WrappedInteger.Magnitude { + wrappedNumber.magnitude + } + + var words: WrappedInteger.Words { + wrappedNumber.words + } + + var bitWidth: Int { + wrappedNumber.bitWidth + } + + var trailingZeroBitCount: Int { + wrappedNumber.trailingZeroBitCount + } + + // MARK: - Operators - + + static func <<= (lhs: inout Self, rhs: RHS) where RHS : BinaryInteger { + lhs.wrappedNumber <<= rhs + } + + static func >>= (lhs: inout Self, rhs: RHS) where RHS : BinaryInteger { + lhs.wrappedNumber >>= rhs + } + + static prefix func ~ (x: Self) -> Self { + .init(wrappedNumber: ~x.wrappedNumber) + } + + static func / (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber / rhs.wrappedNumber) + } + + static func /= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber /= rhs.wrappedNumber + } + + static func % (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber % rhs.wrappedNumber) + } + + static func %= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber %= rhs.wrappedNumber + } + + static func &= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber &= rhs.wrappedNumber + } + + static func |= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber |= rhs.wrappedNumber + } + + static func ^= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber ^= rhs.wrappedNumber + } + + static func + (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber + rhs.wrappedNumber) + } + + static func += (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber += rhs.wrappedNumber + } + + static func - (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber - rhs.wrappedNumber) + } + + static func -= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber -= rhs.wrappedNumber + } + + static func * (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber * rhs.wrappedNumber) + } + + static func *= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber *= rhs.wrappedNumber + } + +} + From 76e243ce55a1f7be68f280d0dd1e5f9008b9d3fc Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 10 Nov 2019 16:22:24 -0800 Subject: [PATCH 049/283] [pmo] Allow for the AvailableValueAggregator to know if it is being used to Borrow, Copy, Take. Previously, we only specified via a boolean value whether or not we were a take or not. Now we have an enum which is more specific and descriptive. It will also let me fix an issue that occurs with Borrow/Copy incrementally as separate patches. --- .../Mandatory/PredictableMemOpt.cpp | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 586938773f638..2a1934ed35193 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -387,6 +387,12 @@ static bool anyMissing(unsigned StartSubElt, unsigned NumSubElts, namespace { +enum class AvailableValueExpectedOwnership { + Take, + Borrow, + Copy, +}; + /// A class that aggregates available values, loading them if they are not /// available. class AvailableValueAggregator { @@ -396,7 +402,7 @@ class AvailableValueAggregator { MutableArrayRef AvailableValueList; SmallVectorImpl &Uses; DeadEndBlocks &deadEndBlocks; - bool isTake; + AvailableValueExpectedOwnership expectedOwnership; /// Keep track of all instructions that we have added. Once we are done /// promoting a value, we need to make sure that if we need to balance any @@ -408,10 +414,11 @@ class AvailableValueAggregator { AvailableValueAggregator(SILInstruction *Inst, MutableArrayRef AvailableValueList, SmallVectorImpl &Uses, - DeadEndBlocks &deadEndBlocks, bool isTake) + DeadEndBlocks &deadEndBlocks, + AvailableValueExpectedOwnership expectedOwnership) : M(Inst->getModule()), B(Inst), Loc(Inst->getLoc()), AvailableValueList(AvailableValueList), Uses(Uses), - deadEndBlocks(deadEndBlocks), isTake(isTake) {} + deadEndBlocks(deadEndBlocks), expectedOwnership(expectedOwnership) {} // This is intended to be passed by reference only once constructed. AvailableValueAggregator(const AvailableValueAggregator &) = delete; @@ -435,6 +442,19 @@ class AvailableValueAggregator { void print(llvm::raw_ostream &os) const; void dump() const LLVM_ATTRIBUTE_USED; + + bool isTake() const { + return expectedOwnership == AvailableValueExpectedOwnership::Take; + } + + bool isBorrow() const { + return expectedOwnership == AvailableValueExpectedOwnership::Borrow; + } + + bool isCopy() const { + return expectedOwnership == AvailableValueExpectedOwnership::Copy; + } + private: SILValue aggregateFullyAvailableValue(SILType loadTy, unsigned firstElt); SILValue aggregateTupleSubElts(TupleType *tt, SILType loadTy, @@ -526,7 +546,7 @@ SILValue AvailableValueAggregator::aggregateValues(SILType LoadTy, bool isTopLevel) { // If we are performing a take, make sure that we have available values for // /all/ of our values. Otherwise, bail. - if (isTopLevel && isTake && !canTake(LoadTy, FirstElt)) { + if (isTopLevel && isTake() && !canTake(LoadTy, FirstElt)) { return SILValue(); } @@ -576,7 +596,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, SILBuilderWithScope builder(insertPts[0], &insertedInsts); SILLocation loc = insertPts[0]->getLoc(); // If we have a take, just return the value. - if (isTake) + if (isTake()) return firstVal.getValue(); // Otherwise, return a copy of the value. return builder.emitCopyValueOperation(loc, firstVal.getValue()); @@ -597,7 +617,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, SILValue eltVal = firstVal.getValue(); // If we are not taking, copy the element value. - if (!isTake) { + if (!isTake()) { eltVal = builder.emitCopyValueOperation(loc, eltVal); } @@ -636,7 +656,7 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, // compute an address to load from. SILValue EltAddr; if (anyMissing(FirstElt, NumSubElt, AvailableValueList)) { - assert(!isTake && "When taking, values should never be missing?!"); + assert(!isTake() && "When taking, values should never be missing?!"); EltAddr = B.createTupleElementAddr(Loc, Address, EltNo, EltTy.getAddressType()); } @@ -663,7 +683,7 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, // compute an address to load from. SILValue eltAddr; if (anyMissing(firstElt, numSubElt, AvailableValueList)) { - assert(!isTake && "When taking, values should never be missing?!"); + assert(!isTake() && "When taking, values should never be missing?!"); eltAddr = B.createStructElementAddr(Loc, address, decl, eltTy.getAddressType()); } @@ -686,7 +706,7 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, // If the value is not available, load the value and update our use list. if (!val) { - assert(!isTake && "Should only take fully available values?!"); + assert(!isTake() && "Should only take fully available values?!"); LoadInst *load = ([&]() { if (B.hasOwnership()) { return B.createTrivialLoadOr(Loc, address, @@ -1650,7 +1670,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { // not available. We are "propagating" a +1 available value from the store // points. AvailableValueAggregator agg(li, availableValues, Uses, deadEndBlocks, - false /*isTake*/); + AvailableValueExpectedOwnership::Copy); SILValue newVal = agg.aggregateValues(loadTy, li->getOperand(), firstElt); LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *li << "\n"); @@ -1750,7 +1770,7 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { // not available. We are "propagating" a +1 available value from the store // points. AvailableValueAggregator agg(lbi, availableValues, Uses, deadEndBlocks, - false /*isTake*/); + AvailableValueExpectedOwnership::Borrow); SILValue newVal = agg.aggregateValues(loadTy, lbi->getOperand(), firstElt); LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *lbi << "\n"); @@ -1829,7 +1849,7 @@ bool AllocOptimize::canPromoteTake( // available, we would need to split stores to promote this destroy_addr. We // do not support that yet. AvailableValueAggregator agg(inst, tmpList, Uses, deadEndBlocks, - true /*isTake*/); + AvailableValueExpectedOwnership::Take); if (!agg.canTake(loadTy, firstElt)) return false; @@ -1861,7 +1881,7 @@ void AllocOptimize::promoteDestroyAddr( // type as the load did, and emit smaller) loads for any subelements that were // not available. AvailableValueAggregator agg(dai, availableValues, Uses, deadEndBlocks, - true /*isTake*/); + AvailableValueExpectedOwnership::Take); SILValue newVal = agg.aggregateValues(loadTy, address, firstElt); ++NumDestroyAddrPromoted; @@ -1889,7 +1909,7 @@ void AllocOptimize::promoteLoadTake( // type as the load did, and emit smaller) loads for any subelements that were // not available. AvailableValueAggregator agg(li, availableValues, Uses, deadEndBlocks, - true /*isTake*/); + AvailableValueExpectedOwnership::Take); SILValue newVal = agg.aggregateValues(loadTy, address, firstElt); ++NumLoadTakePromoted; From 2f2b8afad828c3d51e239735653b51324246fc4b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 11 Nov 2019 11:33:59 -0800 Subject: [PATCH 050/283] Remove the list of synthesized decls" This used to be a lot more relevant a long time ago when typeCheckFunctionsAndExternalDecls actually did type check external functions defined in C. Now, it serves no purpose. The validation order change from just type checking these things eagerly doesn't seem to affect anything. --- include/swift/AST/ASTContext.h | 4 ---- lib/AST/ASTContext.cpp | 6 ------ lib/Sema/DerivedConformanceCodable.cpp | 6 ++---- lib/Sema/DerivedConformanceCodingKey.cpp | 7 ++----- lib/Sema/DerivedConformanceEquatableHashable.cpp | 7 +------ lib/Sema/DerivedConformanceRawRepresentable.cpp | 2 -- lib/Sema/DerivedConformances.cpp | 1 - lib/Sema/TypeChecker.cpp | 2 -- 8 files changed, 5 insertions(+), 30 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 2ae67ba07d6d2..ac9ea106e580f 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -553,10 +553,6 @@ class ASTContext final { ForeignLanguage language, const DeclContext *dc); - /// Add a declaration that was synthesized to a per-source file list if - /// if is part of a source file. - void addSynthesizedDecl(Decl *decl); - /// Add a cleanup function to be called when the ASTContext is deallocated. void addCleanup(std::function cleanup); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a7a6e8f414b58..cc890454af87d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1366,12 +1366,6 @@ bool ASTContext::hasArrayLiteralIntrinsics() const { && getDeallocateUninitializedArray(); } -void ASTContext::addSynthesizedDecl(Decl *decl) { - auto *fileUnit = decl->getDeclContext()->getModuleScopeContext(); - if (auto *sf = dyn_cast(fileUnit)) - sf->SynthesizedDecls.push_back(decl); -} - void ASTContext::addCleanup(std::function cleanup) { getImpl().Cleanups.push_back(std::move(cleanup)); } diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 0eddc4877b800..cc9e70612bf67 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -741,9 +741,8 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { encodeDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - C.addSynthesizedDecl(encodeDecl); - derived.addMembersToConformanceContext({encodeDecl}); + return encodeDecl; } @@ -1019,9 +1018,8 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { initDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - C.addSynthesizedDecl(initDecl); - derived.addMembersToConformanceContext({initDecl}); + return initDecl; } diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index b794bb6e06e16..293670d347af1 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -145,9 +145,8 @@ static ValueDecl *deriveInitDecl(DerivedConformance &derived, Type paramType, initDecl->setAccess(derived.Nominal->getFormalAccess()); - C.addSynthesizedDecl(initDecl); - derived.addMembersToConformanceContext({initDecl}); + return initDecl; } @@ -176,9 +175,7 @@ static ValueDecl *deriveProperty(DerivedConformance &derived, Type type, // Synthesize the body. synthesizer(getterDecl); - auto *dc = cast(derived.ConformanceDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); + derived.addMembersToConformanceContext({propDecl, pbDecl}); return propDecl; } diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 0e2efebaa115b..f2bb80f778afc 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -769,8 +769,6 @@ deriveEquatable_eq( eqDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - C.addSynthesizedDecl(eqDecl); - // Add the operator to the parent scope. derived.addMembersToConformanceContext({eqDecl}); @@ -890,9 +888,8 @@ deriveHashable_hashInto( hashDecl->copyFormalAccessFrom(derived.Nominal); - C.addSynthesizedDecl(hashDecl); - derived.addMembersToConformanceContext({hashDecl}); + return hashDecl; } @@ -1253,8 +1250,6 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { auto *patDecl = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, hashValuePat, /*InitExpr*/ nullptr, parentDC); - C.addSynthesizedDecl(hashValueDecl); - C.addSynthesizedDecl(getterDecl); derived.addMembersToConformanceContext({hashValueDecl, patDecl}); return hashValueDecl; diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index adbdb86e42b95..91f3850508632 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -434,8 +434,6 @@ deriveRawRepresentable_init(DerivedConformance &derived) { // an instance without function call overhead. maybeMarkAsInlinable(derived, initDecl); - C.addSynthesizedDecl(initDecl); - derived.addMembersToConformanceContext({initDecl}); return initDecl; } diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 42d4f7e2a64b4..ec1a3945ea237 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -311,7 +311,6 @@ DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, getterDecl->copyFormalAccessFrom(property); - C.addSynthesizedDecl(getterDecl); return getterDecl; } diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index d06c5c7ea2de4..2bbc2493baf85 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -318,8 +318,6 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) } while (currentFunctionIdx < TC.definedFunctions.size() || currentSynthesizedDecl < SF.SynthesizedDecls.size()); - // FIXME: Horrible hack. Store this somewhere more appropriate. - SF.LastCheckedSynthesizedDecl = currentSynthesizedDecl; // Compute captures for functions and closures we visited. for (auto *closure : TC.ClosuresWithUncomputedCaptures) { From cab4e9f011590d900d70f879202aa121606295a8 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 11 Nov 2019 11:34:52 -0800 Subject: [PATCH 051/283] Eagerly TypeCheck Synthesized Decls --- include/swift/AST/SourceFile.h | 5 ----- lib/Sema/DerivedConformances.cpp | 27 +++++++++------------------ lib/Sema/DerivedConformances.h | 7 +------ lib/Sema/TypeCheckDecl.cpp | 1 + lib/Sema/TypeChecker.cpp | 14 +------------- 5 files changed, 12 insertions(+), 42 deletions(-) diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index a829a478c1936..7f6e31428d1cd 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -147,11 +147,6 @@ class SourceFile final : public FileUnit { /// A set of synthesized declarations that need to be type checked. llvm::SmallVector SynthesizedDecls; - /// We might perform type checking on the same source file more than once, - /// if its the main file or a REPL instance, so keep track of the last - /// checked synthesized declaration to avoid duplicating work. - unsigned LastCheckedSynthesizedDecl = 0; - /// A mapping from Objective-C selectors to the methods that have /// those selectors. llvm::DenseMap> diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index ec1a3945ea237..0e9e33a67b6d1 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -40,6 +40,7 @@ void DerivedConformance::addMembersToConformanceContext( auto IDC = cast(ConformanceDecl); for (auto child : children) { IDC->addMember(child); + TypeChecker::typeCheckDecl(child); } } @@ -281,38 +282,28 @@ DerivedConformance::createSelfDeclRef(AbstractFunctionDecl *fn) { AccessorDecl *DerivedConformance:: addGetterToReadOnlyDerivedProperty(VarDecl *property, Type propertyContextType) { - auto getter = - declareDerivedPropertyGetter(property, propertyContextType); - - property->setImplInfo(StorageImplInfo::getImmutableComputed()); - property->setAccessors(SourceLoc(), {getter}, SourceLoc()); - - return getter; -} - -AccessorDecl * -DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, - Type propertyContextType) { auto &C = property->getASTContext(); auto parentDC = property->getDeclContext(); ParameterList *params = ParameterList::createEmpty(C); Type propertyInterfaceType = property->getInterfaceType(); - - auto getterDecl = AccessorDecl::create(C, + + auto getter = AccessorDecl::create(C, /*FuncLoc=*/SourceLoc(), /*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, property, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, params, TypeLoc::withoutLoc(propertyInterfaceType), parentDC); - getterDecl->setImplicit(); - getterDecl->setIsTransparent(false); + getter->setImplicit(); + getter->setIsTransparent(false); - getterDecl->copyFormalAccessFrom(property); + getter->copyFormalAccessFrom(property); + property->setImplInfo(StorageImplInfo::getImmutableComputed()); + property->setAccessors(SourceLoc(), {getter}, SourceLoc()); - return getterDecl; + return getter; } std::pair diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index f390a6c144d29..825abffdff81f 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -197,12 +197,7 @@ class DerivedConformance { /// Add a getter to a derived property. The property becomes read-only. static AccessorDecl * addGetterToReadOnlyDerivedProperty(VarDecl *property, - Type propertyContextType); - - /// Declare a getter for a derived property. - /// The getter will not be added to the property yet. - static AccessorDecl *declareDerivedPropertyGetter(VarDecl *property, - Type propertyContextType); + Type propertyContextType); /// Build a reference to the 'self' decl of a derived function. static DeclRefExpr *createSelfDeclRef(AbstractFunctionDecl *fn); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 67fe513b5aec1..dae9f7493ab1d 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -3675,6 +3675,7 @@ void TypeChecker::typeCheckDecl(Decl *D) { // Returns 'nullptr' if this is the setter's 'newValue' parameter; // otherwise, returns the corresponding parameter of the subscript // declaration. + static ParamDecl *getOriginalParamFromAccessor(AbstractStorageDecl *storage, AccessorDecl *accessor, ParamDecl *param) { diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 2bbc2493baf85..61ca40efc3abf 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -294,7 +294,6 @@ static void bindExtensions(SourceFile &SF) { static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) { unsigned currentFunctionIdx = 0; - unsigned currentSynthesizedDecl = SF.LastCheckedSynthesizedDecl; do { // Type check the body of each of the function in turn. Note that outside // functions must be visited before nested functions for type-checking to @@ -306,18 +305,7 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) TypeChecker::typeCheckAbstractFunctionBody(AFD); } - - // Type check synthesized functions and their bodies. - for (unsigned n = SF.SynthesizedDecls.size(); - currentSynthesizedDecl != n; - ++currentSynthesizedDecl) { - auto decl = SF.SynthesizedDecls[currentSynthesizedDecl]; - TypeChecker::typeCheckDecl(decl); - } - - } while (currentFunctionIdx < TC.definedFunctions.size() || - currentSynthesizedDecl < SF.SynthesizedDecls.size()); - + } while (currentFunctionIdx < TC.definedFunctions.size()); // Compute captures for functions and closures we visited. for (auto *closure : TC.ClosuresWithUncomputedCaptures) { From 0775808db184988ee09c07504c3340eee070998a Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 11 Nov 2019 11:58:33 -0800 Subject: [PATCH 052/283] Reject invalid derived Hashable members explicitly Hashable doesn't quite have the know-how to reject invalid derivation contexts before hand. Give it a little help by adding a way to retrieve if a decl added to the conformance context was invalid after type checking completes. Otherwise we'll emit "Hashable is broken". --- lib/Sema/DerivedConformanceEquatableHashable.cpp | 6 +++++- lib/Sema/DerivedConformances.cpp | 5 ++++- lib/Sema/DerivedConformances.h | 5 ++++- test/Sema/enum_conformance_synthesis.swift | 4 ++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index f2bb80f778afc..9459e241c4a4b 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -1251,7 +1251,11 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { C, StaticSpellingKind::None, hashValuePat, /*InitExpr*/ nullptr, parentDC); - derived.addMembersToConformanceContext({hashValueDecl, patDecl}); + // If any of the members we synthesized didn't typecheck, bail out. + if (derived.addMembersToConformanceContext({hashValueDecl, patDecl})) { + return nullptr; + } + return hashValueDecl; } diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 0e9e33a67b6d1..67d8291fcd03c 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -35,13 +35,16 @@ DeclContext *DerivedConformance::getConformanceContext() const { return cast(ConformanceDecl); } -void DerivedConformance::addMembersToConformanceContext( +bool DerivedConformance::addMembersToConformanceContext( ArrayRef children) { auto IDC = cast(ConformanceDecl); + bool anyInvalid = false; for (auto child : children) { IDC->addMember(child); TypeChecker::typeCheckDecl(child); + anyInvalid |= child->isInvalid(); } + return anyInvalid; } Type DerivedConformance::getProtocolType() const { diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 825abffdff81f..0d74211abcf5e 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -46,7 +46,10 @@ class DerivedConformance { DeclContext *getConformanceContext() const; /// Add \c children as members of the context that declares the conformance. - void addMembersToConformanceContext(ArrayRef children); + /// + /// \returns True if any of the added members were found to be invalid after type + /// checking. + bool addMembersToConformanceContext(ArrayRef children); /// Get the declared type of the protocol that this is conformance is for. Type getProtocolType() const; diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index 94dd3577dcba2..8ae6a440aff5f 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -61,7 +61,7 @@ func customHashable() { enum InvalidCustomHashable { case A, B - var hashValue: String { return "" } // expected-note{{previously declared here}} + var hashValue: String { return "" } // expected-note 2 {{previously declared here}} } func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String { return "" @@ -72,7 +72,7 @@ func invalidCustomHashable() { s = InvalidCustomHashable.A.hashValue _ = s var _: Int = InvalidCustomHashable.A.hashValue - InvalidCustomHashable.A.hash(into: &hasher) + InvalidCustomHashable.A.hash(into: &hasher) // expected-error {{value of type 'InvalidCustomHashable' has no member 'hash'}} } // Check use of an enum's synthesized members before the enum is actually declared. From 7a3d2307f7711450cde0cac12c18c968670c75f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Mon, 11 Nov 2019 12:06:00 -0800 Subject: [PATCH 053/283] [test] type_checker_perf test rdar33688063 still fails, let's disable it --- validation-test/Sema/type_checker_perf/slow/rdar33688063.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar33688063.swift b/validation-test/Sema/type_checker_perf/slow/rdar33688063.swift index f33d299f9a109..70f403f52057c 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar33688063.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar33688063.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan +// REQUIRES: rdar57050532 let _ = 1 | UInt32(0) << 0 | UInt32(1) << 1 | UInt32(2) << 2 | UInt32(3) << 3 | UInt32(4) << 4 From 08f8f4191da32b3674cd3872bb3439712306d016 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 11 Nov 2019 12:59:28 -0800 Subject: [PATCH 054/283] [Diagnostics] Produce a tailored diagnostic for property wrapper argument mismatch Diagnose an attempt to initialize a property, which has a property wrapper, with a value of an incorrect type. --- lib/Sema/CSDiagnostics.cpp | 29 +++++++++++++++++++++++++++ lib/Sema/CSDiagnostics.h | 5 +++++ lib/Sema/CSSimplify.cpp | 10 --------- test/decl/var/property_wrappers.swift | 2 +- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index c2b275a7ad33d..d3a61c47d069c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5303,6 +5303,9 @@ bool ArgumentMismatchFailure::diagnoseAsError() { if (diagnoseUseOfReferenceEqualityOperator()) return true; + if (diagnosePropertyWrapperMismatch()) + return true; + auto argType = getFromType(); auto paramType = getToType(); @@ -5542,6 +5545,32 @@ bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const { return failure.diagnoseSingleMissingArgument(); } +bool ArgumentMismatchFailure::diagnosePropertyWrapperMismatch() const { + auto argType = getFromType(); + auto paramType = getToType(); + + // Verify that this is an implicit call to a property wrapper initializer + // in a form of `init(wrappedValue:)` or deprecated `init(initialValue:)`. + auto *call = dyn_cast(getRawAnchor()); + if (!(call && call->isImplicit() && isa(call->getFn()) && + call->getNumArguments() == 1 && + (call->getArgumentLabels().front() == getASTContext().Id_wrappedValue || + call->getArgumentLabels().front() == getASTContext().Id_initialValue))) + return false; + + auto argExpr = cast(call->getArg())->getElement(0); + // If this is an attempt to initialize property wrapper with opaque value + // of error type, let's just ignore that problem since original mismatch + // has been diagnosed already. + if (argExpr->isImplicit() && isa(argExpr) && + argType->is()) + return true; + + emitDiagnostic(getLoc(), diag::cannot_convert_initializer_value, argType, + paramType); + return true; +} + void ExpandArrayIntoVarargsFailure::tryDropArrayBracketsFixIt( Expr *anchor) const { // If this is an array literal, offer to remove the brackets and pass the diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index b7421a57dcc58..a537d17f61176 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1845,6 +1845,11 @@ class ArgumentMismatchFailure : public ContextualFailure { /// reference equality operators `===` and `!==`. bool diagnoseUseOfReferenceEqualityOperator() const; + /// Tailored diagnostics for type mismatches associated with + /// property wrapper initialization via implicit `init(wrappedValue:)` + /// or now deprecated `init(initialValue:)`. + bool diagnosePropertyWrapperMismatch() const; + protected: /// \returns The position of the argument being diagnosed, starting at 1. unsigned getArgPosition() const { return Info->getArgPosition(); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index c89ae87905d06..adb6f2961e17e 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3078,16 +3078,6 @@ bool ConstraintSystem::repairFailures( locator); } - // Let's not complain about argument type mismatch if we have a synthesized - // wrappedValue initializer, because we already emit a diagnostic for a - // type mismatch between the property's type and the wrappedValue type. - if (auto CE = dyn_cast(loc->getAnchor())) { - if (CE->isImplicit() && !CE->getArgumentLabels().empty() && - CE->getArgumentLabels().front() == getASTContext().Id_wrappedValue) { - break; - } - } - // If either type has a hole, consider this fixed. if (lhs->hasUnresolvedType() || rhs->hasUnresolvedType()) return true; diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 85438807c0422..d9c3b24c9d0b7 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -245,7 +245,7 @@ struct Initialization { var y = true @WrapperWithInitialValue - var y2 = true // expected-error{{Bool' is not convertible to 'Int}} + var y2 = true // expected-error{{cannot convert value of type 'Bool' to specified type 'Int'}} mutating func checkTypes(s: String) { x2 = s // expected-error{{cannot assign value of type 'String' to type 'Double'}} From 55b6ff1f5e5f401bd6e99c09e6f1472e3e7f846d Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 11 Nov 2019 12:05:59 -0800 Subject: [PATCH 055/283] [pmo] Expand an assert that validates that we never handlePrimitiveValues when promoting takes. The key thing to note here is that when we are processing a take, we validate that at all destroy points we have a fully available value. So we should never hit this code path which is correct. This assert just makes the assumption clearer in the small when reading code locally in handlePrimitiveValue. --- lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 2a1934ed35193..1160d4c9bdec6 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -567,6 +567,10 @@ SILValue AvailableValueAggregator::aggregateValues(SILType LoadTy, return aggregateStructSubElts(SD, LoadTy, Address, FirstElt); // Otherwise, we have a non-aggregate primitive. Load or extract the value. + // + // NOTE: We should never call this when taking since when taking we know that + // our underlying value is always fully available. + assert(!isTake()); return handlePrimitiveValue(LoadTy, Address, FirstElt); } @@ -696,17 +700,18 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, return B.createStruct(Loc, loadTy, resultElts); } -// We have looked through all of the aggregate values and finally found a -// "primitive value". If the value is available, use it (extracting if we need -// to), otherwise emit a load of the value with the appropriate qualifier. +// We have looked through all of the aggregate values and finally found a value +// that is not available without transforming, i.e. a "primitive value". If the +// value is available, use it (extracting if we need to), otherwise emit a load +// of the value with the appropriate qualifier. SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, SILValue address, unsigned firstElt) { - auto &val = AvailableValueList[firstElt]; + assert(!isTake() && "Should only take fully available values?!"); // If the value is not available, load the value and update our use list. + auto &val = AvailableValueList[firstElt]; if (!val) { - assert(!isTake() && "Should only take fully available values?!"); LoadInst *load = ([&]() { if (B.hasOwnership()) { return B.createTrivialLoadOr(Loc, address, From 64eeed835a0845d2c6c8f2dc7e4ef72c3c3da794 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 11 Nov 2019 12:12:04 -0800 Subject: [PATCH 056/283] [pmo] Add an assert that when using SILSSAUpdater in ossa, we only have singular values if the underlying type is either trivial or we have a take. The reason why this is true is that we will be inserting new copy_values for each available value implying that we can never have such a singular value. I also added two test cases that show that we have a singular value with the trivial type and that works. --- .../Mandatory/PredictableMemOpt.cpp | 38 ++++- .../predictable_memaccess_opts.sil | 135 ++++++++++++++++++ 2 files changed, 167 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 1160d4c9bdec6..50700bd41e8a2 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -637,8 +637,19 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // If we only are tracking a singular value, we do not need to construct // SSA. Just return that value. - if (auto val = singularValue.getValueOr(SILValue())) + if (auto val = singularValue.getValueOr(SILValue())) { + // This assert documents that we are expecting that if we are in ossa, have + // a non-trivial value, and are not taking, we should never go down this + // code path. If we did, we would need to insert a copy here. The reason why + // we know we will never go down this code path is since we have been + // inserting copy_values implying that our potential singular value would be + // of the copy_values which are guaranteed to all be different. + assert((!B.hasOwnership() || isTake() || + val->getType().isTrivial(*B.getInsertionBB()->getParent())) && + "Should never reach this code path if we are in ossa and have a " + "non-trivial value"); return val; + } // Finally, grab the value from the SSA updater. SILValue result = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); @@ -741,7 +752,10 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, } // If we have an available value, then we want to extract the subelement from - // the borrowed aggregate before each insertion point. + // the borrowed aggregate before each insertion point. Note that since we have + // inserted copies at each of these insertion points, we know that we will + // never have the same value along all paths unless we have a trivial value + // meaning the SSA updater given a non-trivial value must /always/ be used. SILSSAUpdater updater; updater.Initialize(loadTy); @@ -764,13 +778,25 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, updater.AddAvailableValue(i->getParent(), eltVal); } - // If we only are tracking a singular value, we do not need to construct - // SSA. Just return that value. - if (auto val = singularValue.getValueOr(SILValue())) + SILBasicBlock *insertBlock = B.getInsertionBB(); + + // If we are not in ossa and have a singular value or if we are in ossa and + // have a trivial singular value, just return that value. + // + // This can never happen for non-trivial values in ossa since we never should + // visit this code path if we have a take implying that non-trivial values + // /will/ have a copy and thus are guaranteed (since each copy yields a + // different value) to not be singular values. + if (auto val = singularValue.getValueOr(SILValue())) { + assert((!B.hasOwnership() || + val->getType().isTrivial(*insertBlock->getParent())) && + "Should have inserted copies for each insertion point, so shouldn't " + "have a singular value if non-trivial?!"); return val; + } // Finally, grab the value from the SSA updater. - SILValue eltVal = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); + SILValue eltVal = updater.GetValueInMiddleOfBlock(insertBlock); assert(!B.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index f261e4a12b4ad..cf92559a25b39 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -16,7 +16,14 @@ struct NativeObjectPair { var y: Builtin.NativeObject } +struct IntPair { + var x: Builtin.Int32 + var y: Builtin.Int32 +} + sil @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +sil @intpair_user : $@convention(thin) (IntPair) -> () +sil @inout_int32_user : $@convention(thin) (@inout Builtin.Int32) -> () /// Needed to avoid tuple scalarization code in the use gatherer. struct NativeObjectAndTuple { @@ -686,3 +693,131 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %9999 = tuple() return %9999 : $() } + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +// CHECK: bb0( +// CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair +// CHECK-NOT: bb{{[0-9][0-9]*}}( +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_trivial' +sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32): + %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () + %1 = alloc_stack $IntPair + %1a = struct_element_addr %1 : $*IntPair, #IntPair.x + %1b = struct_element_addr %1 : $*IntPair, #IntPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb2: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [trivial] %1 : $*IntPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (IntPair) -> () + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (IntPair) -> () + dealloc_stack %1 : $*IntPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK: bb0( +// CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair +// CHECK-NOT: bb{{[0-9][0-9]*}}( +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_trivial_reload' +sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): + %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () + %1 = alloc_stack $IntPair + %1a = struct_element_addr %1 : $*IntPair, #IntPair.x + %1b = struct_element_addr %1 : $*IntPair, #IntPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0c to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb2: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [trivial] %1 : $*IntPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (IntPair) -> () + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (IntPair) -> () + dealloc_stack %1 : $*IntPair + %9999 = tuple() + return %9999 : $() +} + +sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): + %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () + %1 = alloc_stack $IntPair + %1a = struct_element_addr %1 : $*IntPair, #IntPair.x + %1b = struct_element_addr %1 : $*IntPair, #IntPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb2: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [trivial] %1 : $*IntPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (IntPair) -> () + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (IntPair) -> () + dealloc_stack %1 : $*IntPair + %9999 = tuple() + return %9999 : $() +} \ No newline at end of file From 4d216c7052ee84248f3ce1d83fdad13c8ccc3e1f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 7 Oct 2019 17:33:38 -0700 Subject: [PATCH 057/283] [AST] Add swift::getParameterList --- include/swift/AST/Decl.h | 3 +++ lib/AST/Decl.cpp | 14 ++++++++------ lib/Sema/TypeCheckProtocol.cpp | 8 -------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 3bba7ff3d6106..4cb20bd66b405 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -7286,6 +7286,9 @@ inline EnumElementDecl *EnumDecl::getUniqueElement(bool hasValue) const { return result; } +/// Retrieve the parameter list for a given declaration. +ParameterList *getParameterList(ValueDecl *source); + /// Retrieve parameter declaration from the given source at given index. const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a59f780d6945e..6acad04ad7f15 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6465,17 +6465,19 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { return DeclName(); } -const ParamDecl *swift::getParameterAt(const ValueDecl *source, unsigned index) { - const ParameterList *paramList; +ParameterList *swift::getParameterList(ValueDecl *source) { if (auto *AFD = dyn_cast(source)) { - paramList = AFD->getParameters(); + return AFD->getParameters(); } else if (auto *EED = dyn_cast(source)) { - paramList = EED->getParameterList(); + return EED->getParameterList(); } else { - paramList = cast(source)->getIndices(); + return cast(source)->getIndices(); } +} - return paramList->get(index); +const ParamDecl *swift::getParameterAt(const ValueDecl *source, + unsigned index) { + return getParameterList(const_cast(source))->get(index); } Type AbstractFunctionDecl::getMethodInterfaceType() const { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 4fef9724ed8fb..78d94c4a0257c 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -253,14 +253,6 @@ static bool checkObjCWitnessSelector(ValueDecl *req, ValueDecl *witness) { return false; } -static ParameterList *getParameterList(ValueDecl *value) { - if (auto func = dyn_cast(value)) - return func->getParameters(); - - auto subscript = cast(value); - return subscript->getIndices(); -} - // Find a standin declaration to place the diagnostic at for the // given accessor kind. static ValueDecl *getStandinForAccessor(AbstractStorageDecl *witness, From 7ae3d1f8d3f66bfc4da7f62d1cd3a97d1d60876c Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 7 Oct 2019 17:33:38 -0700 Subject: [PATCH 058/283] Requestify default initializer context creation This commit adds a request that computes the initializer context for a parameter with a default expr or stored property default. This avoids having to compute them for synthesized decls and is a step towards requestifying default argument parsing. --- include/swift/AST/ASTTypeIDZone.def | 1 + include/swift/AST/Decl.h | 13 +++++--- include/swift/AST/TypeCheckRequests.h | 22 +++++++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 ++ lib/AST/Decl.cpp | 27 ++++++++++++++-- lib/AST/TypeCheckRequests.cpp | 18 +++++++++++ lib/Sema/CodeSynthesis.cpp | 15 +-------- lib/Sema/TypeCheckDecl.cpp | 35 +++++++++++++++++++++ 8 files changed, 112 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 75021ab10d678..ea3bdab6c8820 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -45,6 +45,7 @@ SWIFT_TYPEID_NAMED(OpaqueTypeDecl *, OpaqueTypeDecl) SWIFT_TYPEID_NAMED(OperatorDecl *, OperatorDecl) SWIFT_TYPEID_NAMED(Optional, PropertyWrapperMutability) +SWIFT_TYPEID_NAMED(ParamDecl *, ParamDecl) SWIFT_TYPEID_NAMED(PatternBindingEntry *, PatternBindingEntry) SWIFT_TYPEID_NAMED(PrecedenceGroupDecl *, PrecedenceGroupDecl) SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 4cb20bd66b405..e4e2961ed9d72 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5175,6 +5175,8 @@ enum class ParamSpecifier : uint8_t { /// A function parameter declaration. class ParamDecl : public VarDecl { + friend class DefaultArgumentInitContextRequest; + llvm::PointerIntPair ArgumentNameAndDestructured; SourceLoc ParameterNameLoc; SourceLoc ArgumentNameLoc; @@ -5189,6 +5191,10 @@ class ParamDecl : public VarDecl { CaptureInfo Captures; }; + /// Retrieve the cached initializer context for the parameter's default + /// argument without triggering a request. + Initializer *getDefaultArgumentInitContextCached() const; + enum class Flags : uint8_t { /// Whether or not this parameter is vargs. IsVariadic = 1 << 0, @@ -5262,11 +5268,8 @@ class ParamDecl : public VarDecl { void setStoredProperty(VarDecl *var); - Initializer *getDefaultArgumentInitContext() const { - if (auto stored = DefaultValueAndFlags.getPointer()) - return stored->InitContext; - return nullptr; - } + /// Retrieve the initializer context for the parameter's default argument. + Initializer *getDefaultArgumentInitContext() const; void setDefaultArgumentInitContext(Initializer *initContext); diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 427c026cd03f7..f4248b091ca7e 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1843,6 +1843,28 @@ class HasCircularRawValueRequest bool isCached() const { return true; } }; +/// Computes an initializer context for a parameter with a default argument. +class DefaultArgumentInitContextRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + ParamDecl *param) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Initializer *init) const; +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 4fd4cb0434836..b3d4c0dfe7f1c 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -32,6 +32,8 @@ SWIFT_REQUEST(TypeChecker, ClassAncestryFlagsRequest, SWIFT_REQUEST(TypeChecker, CompareDeclSpecializationRequest, bool (DeclContext *, ValueDecl *, ValueDecl *, bool), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest, + Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest, Type(AssociatedTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultTypeRequest, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 6acad04ad7f15..e62810b8aed9e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6046,6 +6046,24 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { return AnyFunctionType::Param(type, label, flags); } +Initializer *ParamDecl::getDefaultArgumentInitContextCached() const { + if (auto *defaultInfo = DefaultValueAndFlags.getPointer()) + return defaultInfo->InitContext; + + return nullptr; +} + +Initializer *ParamDecl::getDefaultArgumentInitContext() const { + // If this param doesn't need a context, don't bother kicking off a request. + if (!getDefaultValue() && !getStoredProperty()) + return nullptr; + + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, DefaultArgumentInitContextRequest{mutableThis}, nullptr); +} + void ParamDecl::setDefaultValue(Expr *E) { if (!DefaultValueAndFlags.getPointer()) { if (!E) return; @@ -6093,8 +6111,13 @@ CustomAttr *ValueDecl::getAttachedFunctionBuilder() const { } void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) { - assert(DefaultValueAndFlags.getPointer()); - DefaultValueAndFlags.getPointer()->InitContext = initContext; + auto *oldContext = getDefaultArgumentInitContextCached(); + assert((!oldContext || oldContext == initContext) && + "Cannot change init context after setting"); + + auto *defaultInfo = DefaultValueAndFlags.getPointer(); + assert(defaultInfo); + defaultInfo->InitContext = initContext; } void ParamDecl::setDefaultArgumentCaptureInfo(CaptureInfo captures) { diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5a3b6ed684863..7655800eae6cd 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1181,3 +1181,21 @@ void HasCircularRawValueRequest::noteCycleStep(DiagnosticEngine &diags) const { diags.diagnose(decl, diag::kind_declname_declared_here, decl->getDescriptiveKind(), decl->getName()); } + +//----------------------------------------------------------------------------// +// DefaultArgumentInitContextRequest computation. +//----------------------------------------------------------------------------// + +Optional +DefaultArgumentInitContextRequest::getCachedResult() const { + auto *param = std::get<0>(getStorage()); + if (auto *init = param->getDefaultArgumentInitContextCached()) + return init; + + return None; +} + +void DefaultArgumentInitContextRequest::cacheResult(Initializer *init) const { + auto *param = std::get<0>(getStorage()); + param->setDefaultArgumentInitContext(init); +} diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 1d3da7ccf363f..9cecc8c1b10a1 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -107,7 +107,6 @@ Expr *swift::buildArgumentForwardingExpr(ArrayRef params, } static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, - SmallVectorImpl &defaultInits, unsigned paramSize, ASTContext &ctx) { // First and foremost, if this is a constant don't bother. if (var->isLet()) @@ -138,13 +137,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, // We can add a default value now. - // Give this some bogus context right now, we'll fix it after making - // the constructor. - auto *initDC = new (ctx) DefaultArgumentInitializer( - arg->getDeclContext(), paramSize); - - defaultInits.push_back(initDC); - // If the variable has a type T? and no initial value, return a nil literal // default arg. All lazy variables return a nil literal as well. *Note* that // the type will always be a sugared T? because we don't default init an @@ -244,7 +236,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, // Don't allow the parameter to accept temporary pointer conversions. arg->setNonEphemeralIfPossible(); - maybeAddMemberwiseDefaultArg(arg, var, defaultInits, params.size(), ctx); + maybeAddMemberwiseDefaultArg(arg, var, params.size(), ctx); params.push_back(arg); } @@ -266,11 +258,6 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (ICK == ImplicitConstructorKind::Memberwise) { ctor->setIsMemberwiseInitializer(); - - // Fix default argument init contexts now that we have a constructor. - for (auto initDC : defaultInits) { - initDC->changeFunction(ctor, paramList); - } } // If we are defining a default initializer for a class that has a superclass, diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 95c80b1acbd73..de5d2e4a381c8 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2035,6 +2035,41 @@ static void checkDefaultArguments(ParameterList *params) { } } +llvm::Expected +DefaultArgumentInitContextRequest::evaluate(Evaluator &eval, + ParamDecl *param) const { + auto &ctx = param->getASTContext(); + auto *parentDC = param->getDeclContext(); + auto *paramList = getParameterList(cast(parentDC->getAsDecl())); + + // In order to compute the initializer context for this parameter, we need to + // know its index in the parameter list. Therefore iterate over the parameters + // looking for it and fill in the other parameter's contexts while we're here. + Initializer *result = nullptr; + for (auto idx : indices(*paramList)) { + auto *otherParam = paramList->get(idx); + + // If this param doesn't need a context, we're done. + if (!otherParam->getDefaultValue() && !otherParam->getStoredProperty()) + continue; + + // If this param already has a context, continue using it. + if (otherParam->getDefaultArgumentInitContextCached()) + continue; + + // Create a new initializer context. If this is for the parameter that + // kicked off the request, make a note of it for when we return. Otherwise + // cache the result ourselves. + auto *initDC = new (ctx) DefaultArgumentInitializer(parentDC, idx); + if (param == otherParam) + result = initDC; + else + eval.cacheOutput(DefaultArgumentInitContextRequest{otherParam}, initDC); + } + assert(result && "Didn't create init context?"); + return result; +} + PrecedenceGroupDecl *TypeChecker::lookupPrecedenceGroup(DeclContext *dc, Identifier name, SourceLoc nameLoc) { From 01d5c00f9b5e38a2099a22299b175a0bd5524bac Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 10 Nov 2019 14:46:13 -0800 Subject: [PATCH 059/283] [Sema] Requestify default arg type checking This commit introduces a request to type-check a default argument expression and splits `getDefaultValue` into 2 accessors: - `getStructuralDefaultExpr` which retrieves the potentially un-type-checked default argument expression. - `getTypeCheckedDefaultExpr` which retrieves a fully type-checked default argument expression. In addition, this commit adds `hasDefaultExpr`, which allows checking for a default expr without kicking off a request. --- include/swift/AST/Decl.h | 38 +++++++++-- include/swift/AST/TypeCheckRequests.h | 21 ++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 + lib/AST/ASTDumper.cpp | 6 +- lib/AST/ASTPrinter.cpp | 2 +- lib/AST/ASTScopeCreation.cpp | 4 +- lib/AST/ASTScopeSourceRange.cpp | 2 +- lib/AST/ASTWalker.cpp | 4 +- lib/AST/Decl.cpp | 71 +++++++++++++++++---- lib/AST/TypeCheckRequests.cpp | 22 +++++++ lib/Parse/ParsePattern.cpp | 2 +- lib/SIL/TypeLowering.cpp | 6 +- lib/SILGen/SILGen.cpp | 2 +- lib/Sema/TypeCheckCaptures.cpp | 2 +- lib/Sema/TypeCheckDecl.cpp | 56 +++++++++------- 15 files changed, 188 insertions(+), 52 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index e4e2961ed9d72..975b876dc53cc 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5176,6 +5176,7 @@ enum class ParamSpecifier : uint8_t { /// A function parameter declaration. class ParamDecl : public VarDecl { friend class DefaultArgumentInitContextRequest; + friend class DefaultArgumentExprRequest; llvm::PointerIntPair ArgumentNameAndDestructured; SourceLoc ParameterNameLoc; @@ -5186,7 +5187,11 @@ class ParamDecl : public VarDecl { struct StoredDefaultArgument { PointerUnion DefaultArg; - Initializer *InitContext = nullptr; + + /// Stores the context for the default argument as well as a bit to + /// indicate whether the default expression has been type-checked. + llvm::PointerIntPair InitContextAndIsTypeChecked; + StringRef StringRepresentation; CaptureInfo Captures; }; @@ -5251,8 +5256,27 @@ class ParamDecl : public VarDecl { void setDefaultArgumentKind(DefaultArgumentKind K) { Bits.ParamDecl.defaultArgumentKind = static_cast(K); } - - Expr *getDefaultValue() const { + + /// Whether this parameter has a default argument expression available. + /// + /// Note that this will return false for deserialized declarations, which only + /// have a textual representation of their default expression. + bool hasDefaultExpr() const; + + /// Retrieve the fully type-checked default argument expression for this + /// parameter, or \c nullptr if there is no default expression. + /// + /// Note that while this will produce a type-checked expression for + /// caller-side default arguments such as \c #function, this is done purely to + /// check whether the code is valid. Such default arguments get re-created + /// at the call site in order to have the correct context information. + Expr *getTypeCheckedDefaultExpr() const; + + /// Retrieve the potentially un-type-checked default argument expression for + /// this parameter, which can be queried for information such as its source + /// range and textual representation. Returns \c nullptr if there is no + /// default expression. + Expr *getStructuralDefaultExpr() const { if (auto stored = DefaultValueAndFlags.getPointer()) return stored->DefaultArg.dyn_cast(); return nullptr; @@ -5264,7 +5288,13 @@ class ParamDecl : public VarDecl { return nullptr; } - void setDefaultValue(Expr *E); + /// Sets a new default argument expression for this parameter. This should + /// only be called internally by ParamDecl and AST walkers. + /// + /// \param E The new default argument. + /// \param isTypeChecked Whether this argument should be used as the + /// parameter's fully type-checked default argument. + void setDefaultExpr(Expr *E, bool isTypeChecked); void setStoredProperty(VarDecl *var); diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index f4248b091ca7e..f62653436e8ea 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1865,6 +1865,27 @@ class DefaultArgumentInitContextRequest void cacheResult(Initializer *init) const; }; +/// Computes the fully type-checked default argument expression for a given +/// parameter. +class DefaultArgumentExprRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ParamDecl *param) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Expr *expr) const; +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index b3d4c0dfe7f1c..95ec95ce10449 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -32,6 +32,8 @@ SWIFT_REQUEST(TypeChecker, ClassAncestryFlagsRequest, SWIFT_REQUEST(TypeChecker, CompareDeclSpecializationRequest, bool (DeclContext *, ValueDecl *, ValueDecl *, bool), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest, + Expr *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest, Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest, diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 6bb9bd5e02add..5d0abee08f7bd 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -996,14 +996,14 @@ namespace { getDefaultArgumentKindString(P->getDefaultArgumentKind())); } - if (P->getDefaultValue() && - !P->getDefaultArgumentCaptureInfo().isTrivial()) { + if (P->hasDefaultExpr() && + !P->getDefaultArgumentCaptureInfo().isTrivial()) { OS << " "; P->getDefaultArgumentCaptureInfo().print( PrintWithColorRAII(OS, CapturesColor).getOS()); } - if (auto init = P->getDefaultValue()) { + if (auto init = P->getStructuralDefaultExpr()) { OS << " expression=\n"; printRec(init); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 76ab23270673d..adaabbf74c058 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2562,7 +2562,7 @@ void PrintAST::printOneParameter(const ParamDecl *param, auto BodyName = param->getName(); switch (Options.ArgAndParamPrinting) { case PrintOptions::ArgAndParamPrintingMode::EnumElement: - if (ArgName.empty() && BodyName.empty() && !param->getDefaultValue()) { + if (ArgName.empty() && BodyName.empty() && !param->hasDefaultExpr()) { // Don't print anything, in the style of a tuple element. return; } diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 7be22a9a66703..4c1966841c53e 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -1212,7 +1212,7 @@ ParameterListScope::expandAScopeThatCreatesANewInsertionPoint( // Unlike generic parameters or pattern initializers, it cannot refer to a // previous parameter. for (ParamDecl *pd : params->getArray()) { - if (pd->getDefaultValue()) + if (pd->hasDefaultExpr()) scopeCreator .constructExpandAndInsertUncheckable( this, pd); @@ -1535,7 +1535,7 @@ void ClosureBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint( void DefaultArgumentInitializerScope:: expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { - auto *initExpr = decl->getDefaultValue(); + auto *initExpr = decl->getStructuralDefaultExpr(); ASTScopeAssert(initExpr, "Default argument initializer must have an initializer."); scopeCreator.addToScopeTree(initExpr, this); diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index e5a1a69a73149..c5689881e176f 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -225,7 +225,7 @@ SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode( SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - if (auto *dv = decl->getDefaultValue()) + if (auto *dv = decl->getStructuralDefaultExpr()) return dv->getSourceRange(); return SourceRange(); } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 1bd64abdd41e7..cff2b316e727b 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1164,10 +1164,10 @@ class Traversal : public ASTVisitorgetDefaultValue()) { + if (auto *E = P->getStructuralDefaultExpr()) { auto res = doIt(E); if (!res) return true; - P->setDefaultValue(res); + P->setDefaultExpr(res, /*isTypeChecked*/ (bool)res->getType()); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e62810b8aed9e..67b9b7f2be5c7 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5999,7 +5999,7 @@ SourceRange ParamDecl::getSourceRange() const { // It would be nice to extend the front of the range to show where inout is, // but we don't have that location info. Extend the back of the range to the // location of the default argument, or the typeloc if they are valid. - if (auto expr = getDefaultValue()) { + if (auto expr = getStructuralDefaultExpr()) { auto endLoc = expr->getEndLoc(); if (endLoc.isValid()) return SourceRange(startLoc, endLoc); @@ -6048,14 +6048,14 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { Initializer *ParamDecl::getDefaultArgumentInitContextCached() const { if (auto *defaultInfo = DefaultValueAndFlags.getPointer()) - return defaultInfo->InitContext; + return defaultInfo->InitContextAndIsTypeChecked.getPointer(); return nullptr; } Initializer *ParamDecl::getDefaultArgumentInitContext() const { // If this param doesn't need a context, don't bother kicking off a request. - if (!getDefaultValue() && !getStoredProperty()) + if (!hasDefaultExpr() && !getStoredProperty()) return nullptr; auto &ctx = getASTContext(); @@ -6064,7 +6064,44 @@ Initializer *ParamDecl::getDefaultArgumentInitContext() const { ctx.evaluator, DefaultArgumentInitContextRequest{mutableThis}, nullptr); } -void ParamDecl::setDefaultValue(Expr *E) { +bool ParamDecl::hasDefaultExpr() const { + switch (getDefaultArgumentKind()) { + case DefaultArgumentKind::None: + case DefaultArgumentKind::Inherited: + case DefaultArgumentKind::StoredProperty: + return false; + case DefaultArgumentKind::Normal: + case DefaultArgumentKind::File: + case DefaultArgumentKind::Line: + case DefaultArgumentKind::Column: + case DefaultArgumentKind::Function: + case DefaultArgumentKind::DSOHandle: + case DefaultArgumentKind::NilLiteral: + case DefaultArgumentKind::EmptyArray: + case DefaultArgumentKind::EmptyDictionary: + // Check if we have a structural default expr. This ensures we return false + // for deserialized decls. + return getStructuralDefaultExpr(); + } + llvm_unreachable("Unhandled case in switch"); +} + +Expr *ParamDecl::getTypeCheckedDefaultExpr() const { + // Don't kick off a request if we know there's no default expr. The only + // exception is for inherited default args which we need to perform a couple + // of semantic checks for. + if (!hasDefaultExpr() && + getDefaultArgumentKind() != DefaultArgumentKind::Inherited) { + return nullptr; + } + + auto &ctx = getASTContext(); + return evaluateOrDefault( + ctx.evaluator, DefaultArgumentExprRequest{const_cast(this)}, + new (ctx) ErrorExpr(getSourceRange(), ErrorType::get(ctx))); +} + +void ParamDecl::setDefaultExpr(Expr *E, bool isTypeChecked) { if (!DefaultValueAndFlags.getPointer()) { if (!E) return; @@ -6072,7 +6109,16 @@ void ParamDecl::setDefaultValue(Expr *E) { getASTContext().Allocate()); } - DefaultValueAndFlags.getPointer()->DefaultArg = E; + auto *defaultInfo = DefaultValueAndFlags.getPointer(); + assert(defaultInfo->DefaultArg.isNull() || + defaultInfo->DefaultArg.is()); + + if (!isTypeChecked) { + assert(!defaultInfo->InitContextAndIsTypeChecked.getInt() && + "Can't overwrite type-checked default with un-type-checked default"); + } + defaultInfo->DefaultArg = E; + defaultInfo->InitContextAndIsTypeChecked.setInt(isTypeChecked); } void ParamDecl::setStoredProperty(VarDecl *var) { @@ -6083,7 +6129,10 @@ void ParamDecl::setStoredProperty(VarDecl *var) { getASTContext().Allocate()); } - DefaultValueAndFlags.getPointer()->DefaultArg = var; + auto *defaultInfo = DefaultValueAndFlags.getPointer(); + assert(defaultInfo->DefaultArg.isNull() || + defaultInfo->DefaultArg.is()); + defaultInfo->DefaultArg = var; } Type ValueDecl::getFunctionBuilderType() const { @@ -6117,7 +6166,7 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) { auto *defaultInfo = DefaultValueAndFlags.getPointer(); assert(defaultInfo); - defaultInfo->InitContext = initContext; + defaultInfo->InitContextAndIsTypeChecked.setPointer(initContext); } void ParamDecl::setDefaultArgumentCaptureInfo(CaptureInfo captures) { @@ -6265,10 +6314,10 @@ ParamDecl::getDefaultValueStringRepresentation( if (!existing.empty()) return existing; - assert(getDefaultValue() + assert(hasDefaultExpr() && "Normal default argument with no default expression?!"); - return extractInlinableText(getASTContext().SourceMgr, getDefaultValue(), - scratch); + return extractInlinableText(getASTContext().SourceMgr, + getStructuralDefaultExpr(), scratch); } case DefaultArgumentKind::StoredProperty: { assert(DefaultValueAndFlags.getPointer() && @@ -6368,7 +6417,7 @@ void DefaultArgumentInitializer::changeFunction( } auto param = paramList->get(getIndex()); - if (param->getDefaultValue() || param->getStoredProperty()) + if (param->hasDefaultExpr() || param->getStoredProperty()) param->setDefaultArgumentInitContext(this); } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 7655800eae6cd..c9b265958d340 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -13,6 +13,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/Initializer.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PropertyWrappers.h" @@ -1199,3 +1200,24 @@ void DefaultArgumentInitContextRequest::cacheResult(Initializer *init) const { auto *param = std::get<0>(getStorage()); param->setDefaultArgumentInitContext(init); } + +//----------------------------------------------------------------------------// +// DefaultArgumentExprRequest computation. +//----------------------------------------------------------------------------// + +Optional DefaultArgumentExprRequest::getCachedResult() const { + auto *param = std::get<0>(getStorage()); + auto *defaultInfo = param->DefaultValueAndFlags.getPointer(); + if (!defaultInfo) + return None; + + if (!defaultInfo->InitContextAndIsTypeChecked.getInt()) + return None; + + return defaultInfo->DefaultArg.get(); +} + +void DefaultArgumentExprRequest::cacheResult(Expr *expr) const { + auto *param = std::get<0>(getStorage()); + param->setDefaultExpr(expr, /*isTypeChecked*/ true); +} diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 1c94c419cdda0..d0cd5536ee1ce 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -654,7 +654,7 @@ mapParsedParameters(Parser &parser, if (param.DefaultArg) { DefaultArgumentKind kind = getDefaultArgKind(param.DefaultArg); result->setDefaultArgumentKind(kind); - result->setDefaultValue(param.DefaultArg); + result->setDefaultExpr(param.DefaultArg, /*isTypeChecked*/ false); } else if (param.hasInheritedDefaultArg) { result->setDefaultArgumentKind(DefaultArgumentKind::Inherited); } diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 16b373c9e6447..015584a573826 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -1778,7 +1778,7 @@ static CanAnyFunctionType getDefaultArgGeneratorInterfaceType( auto sig = vd->getInnermostDeclContext()->getGenericSignatureOfContext(); if (auto *afd = dyn_cast(vd)) { auto *param = getParameterAt(afd, c.defaultArgIndex); - if (param->getDefaultValue()) { + if (param->hasDefaultExpr()) { auto captureInfo = param->getDefaultArgumentCaptureInfo(); sig = getEffectiveGenericSignature(afd, captureInfo); } @@ -2285,7 +2285,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { // captures for default arguments that are actually referenced. if (auto *AFD = curFn.getAbstractFunctionDecl()) { for (auto *P : *AFD->getParameters()) { - if (P->getDefaultValue()) + if (P->hasDefaultExpr()) collectCaptures(P->getDefaultArgumentCaptureInfo(), dc); } } @@ -2298,7 +2298,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { if (auto *afd = dyn_cast(curFn.getDecl())) { auto *param = getParameterAt(afd, curFn.defaultArgIndex); - if (param->getDefaultValue()) { + if (param->hasDefaultExpr()) { auto dc = afd->getInnermostDeclContext(); collectCaptures(param->getDefaultArgumentCaptureInfo(), dc); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index b1e5e9c22937f..1893dc595e654 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1060,7 +1060,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, llvm_unreachable("No default argument here?"); case DefaultArgumentKind::Normal: { - auto arg = param->getDefaultValue(); + auto arg = param->getTypeCheckedDefaultExpr(); emitOrDelayFunction(*this, constant, [this,constant,arg,initDC](SILFunction *f) { preEmitFunction(constant, arg, f, arg); diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 53efd0214509d..98c756e966296 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -612,7 +612,7 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { // Compute captures for default argument expressions. if (auto *AFD = AFR.getAbstractFunctionDecl()) { for (auto *P : *AFD->getParameters()) { - if (auto E = P->getDefaultValue()) { + if (auto E = P->getTypeCheckedDefaultExpr()) { FindCapturedVars finder(Context, E->getLoc(), AFD, diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index de5d2e4a381c8..160d861143046 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1972,8 +1972,7 @@ getParamIndex(const ParameterList *paramList, const ParamDecl *decl) { } static void checkInheritedDefaultValueRestrictions(ParamDecl *PD) { - if (PD->getDefaultArgumentKind() != DefaultArgumentKind::Inherited) - return; + assert(PD->getDefaultArgumentKind() == DefaultArgumentKind::Inherited); auto *DC = PD->getInnermostDeclContext(); const SourceFile *SF = DC->getParentSourceFile(); @@ -2009,30 +2008,43 @@ static void checkInheritedDefaultValueRestrictions(ParamDecl *PD) { /// Check the default arguments that occur within this pattern. static void checkDefaultArguments(ParameterList *params) { - for (auto *param : *params) { - checkInheritedDefaultValueRestrictions(param); - if (!param->getDefaultValue() || - !param->hasInterfaceType() || - param->getInterfaceType()->hasError()) - continue; + // Force the default values in case they produce diagnostics. + for (auto *param : *params) + (void)param->getTypeCheckedDefaultExpr(); +} - Expr *e = param->getDefaultValue(); - auto *initContext = param->getDefaultArgumentInitContext(); +llvm::Expected +DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, + ParamDecl *param) const { + if (param->getDefaultArgumentKind() == DefaultArgumentKind::Inherited) { + // Inherited default arguments don't have expressions, but we need to + // perform a couple of semantic checks to make sure they're valid. + checkInheritedDefaultValueRestrictions(param); + return nullptr; + } - auto resultTy = - TypeChecker::typeCheckParameterDefault(e, initContext, param->getType(), - param->isAutoClosure()); + auto &ctx = param->getASTContext(); + auto paramTy = param->getType(); + auto *initExpr = param->getStructuralDefaultExpr(); + assert(initExpr); - if (resultTy) { - param->setDefaultValue(e); - } + if (paramTy->hasError()) + return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx)); - TypeChecker::checkInitializerErrorHandling(initContext, e); + auto *dc = param->getDefaultArgumentInitContext(); + assert(dc); - // Walk the checked initializer and contextualize any closures - // we saw there. - (void)TypeChecker::contextualizeInitializer(initContext, e); + if (!TypeChecker::typeCheckParameterDefault(initExpr, dc, paramTy, + param->isAutoClosure())) { + return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx)); } + + TypeChecker::checkInitializerErrorHandling(dc, initExpr); + + // Walk the checked initializer and contextualize any closures + // we saw there. + (void)TypeChecker::contextualizeInitializer(dc, initExpr); + return initExpr; } llvm::Expected @@ -2050,7 +2062,7 @@ DefaultArgumentInitContextRequest::evaluate(Evaluator &eval, auto *otherParam = paramList->get(idx); // If this param doesn't need a context, we're done. - if (!otherParam->getDefaultValue() && !otherParam->getStoredProperty()) + if (!otherParam->hasDefaultExpr() && !otherParam->getStoredProperty()) continue; // If this param already has a context, continue using it. @@ -4121,7 +4133,7 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator, if (isa(nestedRepr) && param->isDefaultArgument()) { auto &ctx = param->getASTContext(); - ctx.Diags.diagnose(param->getDefaultValue()->getLoc(), + ctx.Diags.diagnose(param->getStructuralDefaultExpr()->getLoc(), swift::diag::cannot_provide_default_value_inout, param->getName()); return ParamSpecifier::Default; From 5accda22b2113d13cc9829f70e5768cd168ae949 Mon Sep 17 00:00:00 2001 From: Gogul Balakrishnan Date: Mon, 11 Nov 2019 13:58:34 -0800 Subject: [PATCH 060/283] [AutoDiff upstream] Introduce @differentiable attribute to mark functions as differentiable. (#27506) This PR introduces `@differentiable` attribute to mark functions as differentiable. This PR only contains changes related to parsing the attribute. Type checking and other changes will be added in subsequent patches. See https://github.com/apple/swift/pull/27506/files#diff-f3216f4188fd5ed34e1007e5a9c2490f for examples and tests for the new attribute. --- include/swift/AST/Attr.def | 6 + include/swift/AST/Attr.h | 143 ++++++++++ include/swift/AST/AutoDiff.h | 91 ++++++ include/swift/AST/DiagnosticsParse.def | 18 ++ include/swift/Parse/Parser.h | 34 +++ lib/AST/Attr.cpp | 260 +++++++++++++++++ lib/Parse/ParseDecl.cpp | 268 ++++++++++++++++++ lib/Parse/Parser.cpp | 12 + lib/Sema/TypeCheckAttr.cpp | 3 + lib/Sema/TypeCheckDeclOverride.cpp | 3 + lib/Serialization/ModuleFormat.h | 14 +- lib/Serialization/Serialization.cpp | 32 +++ test/AutoDiff/differentiable_attr_parse.swift | 180 ++++++++++++ test/IDE/complete_decl_attribute.swift | 6 + utils/gyb_syntax_support/AttributeNodes.py | 121 +++++++- .../NodeSerializationCodes.py | 7 + 16 files changed, 1196 insertions(+), 2 deletions(-) create mode 100644 include/swift/AST/AutoDiff.h create mode 100644 test/AutoDiff/differentiable_attr_parse.swift diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index bcef28ba9fbf4..301028b5bdc01 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -502,6 +502,12 @@ SIMPLE_DECL_ATTR(_nonEphemeral, NonEphemeral, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, 90) +DECL_ATTR(differentiable, Differentiable, + OnAccessor | OnConstructor | OnFunc | OnVar | OnSubscript | LongAttribute | + AllowMultipleAttributes | + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + 91) + SIMPLE_DECL_ATTR(IBSegueAction, IBSegueAction, OnFunc | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 45cec6656a043..b6c7913426cce 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -27,6 +27,7 @@ #include "swift/Basic/Version.h" #include "swift/AST/Identifier.h" #include "swift/AST/AttrKind.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/KnownProtocols.h" @@ -1724,6 +1725,148 @@ class DeclAttributes { SourceLoc getStartLoc(bool forModifiers = false) const; }; +/// A declaration name with location. +struct DeclNameWithLoc { + DeclName Name; + DeclNameLoc Loc; +}; + +/// Attribute that marks a function as differentiable and optionally specifies +/// custom associated derivative functions: 'jvp' and 'vjp'. +/// +/// Examples: +/// @differentiable(jvp: jvpFoo where T : FloatingPoint) +/// @differentiable(wrt: (self, x, y), jvp: jvpFoo) +class DifferentiableAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// Whether this function is linear (optional). + bool linear; + /// The number of parsed parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The JVP function. + Optional JVP; + /// The VJP function. + Optional VJP; + /// The JVP function (optional), resolved by the type checker if JVP name is + /// specified. + FuncDecl *JVPFunction = nullptr; + /// The VJP function (optional), resolved by the type checker if VJP name is + /// specified. + FuncDecl *VJPFunction = nullptr; + /// The differentiation parameters' indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + /// The trailing where clause (optional). + TrailingWhereClause *WhereClause = nullptr; + /// The generic signature for autodiff associated functions. Resolved by the + /// type checker based on the original function's generic signature and the + /// attribute's where clause requirements. This is set only if the attribute + /// has a where clause. + GenericSignature DerivativeGenericSignature; + + explicit DifferentiableAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + ArrayRef parameters, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause); + + explicit DifferentiableAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, IndexSubset *indices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenericSignature); + +public: + static DifferentiableAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + ArrayRef params, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause); + + static DifferentiableAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, IndexSubset *indices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenSig); + + /// Get the optional 'jvp:' function name and location. + /// Use this instead of `getJVPFunction` to check whether the attribute has a + /// registered JVP. + Optional getJVP() const { return JVP; } + + /// Get the optional 'vjp:' function name and location. + /// Use this instead of `getVJPFunction` to check whether the attribute has a + /// registered VJP. + Optional getVJP() const { return VJP; } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *pi) { + ParameterIndices = pi; + } + + /// The parsed differentiation parameters, i.e. the list of parameters + /// specified in 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + bool isLinear() const { return linear; } + + TrailingWhereClause *getWhereClause() const { return WhereClause; } + + GenericSignature getDerivativeGenericSignature() const { + return DerivativeGenericSignature; + } + void setDerivativeGenericSignature(ASTContext &context, + GenericSignature derivativeGenSig) { + DerivativeGenericSignature = derivativeGenSig; + } + + FuncDecl *getJVPFunction() const { return JVPFunction; } + void setJVPFunction(FuncDecl *decl); + FuncDecl *getVJPFunction() const { return VJPFunction; } + void setVJPFunction(FuncDecl *decl); + + bool parametersMatch(const DifferentiableAttr &other) const { + assert(ParameterIndices && other.ParameterIndices); + return ParameterIndices == other.ParameterIndices; + } + + /// Get the derivative generic environment for the given `@differentiable` + /// attribute and original function. + GenericEnvironment * + getDerivativeGenericEnvironment(AbstractFunctionDecl *original) const; + + // Print the attribute to the given stream. + // If `omitWrtClause` is true, omit printing the `wrt:` clause. + // If `omitAssociatedFunctions` is true, omit printing associated functions. + void print(llvm::raw_ostream &OS, const Decl *D, + bool omitWrtClause = false, + bool omitAssociatedFunctions = false) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Differentiable; + } +}; + + void simple_display(llvm::raw_ostream &out, const DeclAttribute *attr); inline SourceLoc extractNearestSourceLoc(const DeclAttribute *attr) { diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h new file mode 100644 index 0000000000000..6c2fee70cb12a --- /dev/null +++ b/include/swift/AST/AutoDiff.h @@ -0,0 +1,91 @@ +//===--- AutoDiff.h - Swift Automatic Differentiation ---------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// SWIFT_ENABLE_TENSORFLOW +// This file defines AST support for automatic differentiation. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_AUTODIFF_H +#define SWIFT_AST_AUTODIFF_H + +#include "swift/AST/IndexSubset.h" +#include "swift/Basic/Range.h" + +namespace swift { + +class ParsedAutoDiffParameter { +public: + enum class Kind { Named, Ordered, Self }; + +private: + SourceLoc loc; + Kind kind; + union Value { + struct { Identifier name; } Named; + struct { unsigned index; } Ordered; + struct {} self; + Value(Identifier name) : Named({name}) {} + Value(unsigned index) : Ordered({index}) {} + Value() {} + } value; + +public: + ParsedAutoDiffParameter(SourceLoc loc, Kind kind, Value value) + : loc(loc), kind(kind), value(value) {} + + ParsedAutoDiffParameter(SourceLoc loc, Kind kind, unsigned index) + : loc(loc), kind(kind), value(index) {} + + static ParsedAutoDiffParameter getNamedParameter(SourceLoc loc, + Identifier name) { + return { loc, Kind::Named, name }; + } + + static ParsedAutoDiffParameter getOrderedParameter(SourceLoc loc, + unsigned index) { + return { loc, Kind::Ordered, index }; + } + + static ParsedAutoDiffParameter getSelfParameter(SourceLoc loc) { + return { loc, Kind::Self, {} }; + } + + Identifier getName() const { + assert(kind == Kind::Named); + return value.Named.name; + } + + unsigned getIndex() const { + return value.Ordered.index; + } + + Kind getKind() const { + return kind; + } + + SourceLoc getLoc() const { + return loc; + } + + bool isEqual(const ParsedAutoDiffParameter &other) const { + if (getKind() != other.getKind()) + return false; + if (getKind() == Kind::Named) + return getName() == other.getName(); + return getKind() == Kind::Self; + } +}; + +} // end namespace swift + +#endif // SWIFT_AST_AUTODIFF_H diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 1631b418a8597..ce61e1771fb96 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1511,11 +1511,29 @@ ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, "expected a member name as second parameter in '_implements' attribute", ()) // differentiable +ERROR(attr_differentiable_expected_function_name,PointsToFirstBadToken, + "expected a %0 function name", (StringRef)) +ERROR(attr_differentiable_expected_parameter_list,PointsToFirstBadToken, + "expected a list of parameters to differentiate with respect to", ()) +ERROR(attr_differentiable_use_wrt_not_withrespectto,none, + "use 'wrt:' to specify parameters to differentiate with respect to", ()) +ERROR(attr_differentiable_missing_label,PointsToFirstBadToken, + "missing label '%0:' in '@differentiable' attribute", (StringRef)) +ERROR(attr_differentiable_expected_label,none, + "expected either 'wrt:' or a function specifier label, e.g. 'jvp:', " + "or 'vjp:'", ()) ERROR(differentiable_attribute_expected_rparen,none, "expected ')' in '@differentiable' attribute", ()) ERROR(unexpected_argument_differentiable,none, "unexpected argument '%0' in '@differentiable' attribute", (StringRef)) +// differentiation `wrt` parameters clause +ERROR(expected_colon_after_label,PointsToFirstBadToken, + "expected a colon ':' after '%0'", (StringRef)) +ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, + "expected a parameter, which can be a function parameter name, " + "parameter index, or 'self'", ()) + //------------------------------------------------------------------------------ // MARK: Generics parsing diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index e8d16b476fe1c..0723b9184b485 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -711,6 +711,20 @@ class Parser { /// Check whether the current token starts with '>'. bool startsWithGreater(Token Tok) { return startsWithSymbol(Tok, '>'); } + /// Returns true if token is an identifier with the given value. + bool isIdentifier(Token Tok, StringRef value) { + return Tok.is(tok::identifier) && Tok.getText() == value; + } + + /// Returns true if token is the identifier "wrt". + bool isWRTIdentifier(Token tok) { return isIdentifier(Tok, "wrt"); } + + /// Returns true if token is the identifier "jvp". + bool isJVPIdentifier(Token Tok) { return isIdentifier(Tok, "jvp"); } + + /// Returns true if token is the identifier "vjp". + bool isVJPIdentifier(Token Tok) { return isIdentifier(Tok, "vjp"); } + /// Consume the starting '<' of the current token, which may either /// be a complete '<' token or some kind of operator token starting with '<', /// e.g., '<>'. @@ -796,6 +810,12 @@ class Parser { return parseAnyIdentifier(Result, L, Diagnostic(ID, Args...)); } + /// \brief Parse an unsigned integer and returns it in \p Result. On failure + /// emit the specified error diagnostic, and a note at the specified note + /// location. + bool parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, + const Diagnostic &D); + /// The parser expects that \p K is next token in the input. If so, /// it is consumed and false is returned. /// @@ -973,6 +993,20 @@ class Parser { ParserResult parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc); + /// Parse the @differentiable attribute. + ParserResult parseDifferentiableAttribute(SourceLoc AtLoc, + SourceLoc Loc); + + /// Parse the arguments inside the @differentiable attribute. + bool parseDifferentiableAttributeArguments( + bool &linear, SmallVectorImpl ¶ms, + Optional &jvpSpec, Optional &vjpSpec, + TrailingWhereClause *&whereClause); + + /// Parse a differentiation parameters clause. + bool parseDifferentiationParametersClause( + SmallVectorImpl ¶ms, StringRef attrName); + /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc); diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 415774c3c822c..0600d3591d06b 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -20,9 +20,11 @@ #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IndexSubset.h" #include "swift/AST/Module.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/Types.h" +#include "swift/AST/ParameterList.h" #include "swift/Basic/Defer.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" @@ -351,6 +353,177 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer.printNewline(); } +static std::string getDifferentiationParametersClauseString( + const AbstractFunctionDecl *function, IndexSubset *indices, + ArrayRef parsedParams) { + bool isInstanceMethod = function && function->isInstanceMember(); + std::string result; + llvm::raw_string_ostream printer(result); + + // Use parameter indices from `IndexSubset`, if specified. + if (indices) { + auto parameters = indices->getBitVector(); + auto parameterCount = parameters.count(); + printer << "wrt: "; + if (parameterCount > 1) + printer << '('; + // Check if differentiating wrt `self`. If so, manually print it first. + if (isInstanceMethod && parameters.test(parameters.size() - 1)) { + parameters.reset(parameters.size() - 1); + printer << "self"; + if (parameters.any()) + printer << ", "; + } + // Print remaining differentiation parameters. + interleave(parameters.set_bits(), [&](unsigned index) { + printer << function->getParameters()->get(index)->getName().str(); + }, [&] { printer << ", "; }); + if (parameterCount > 1) + printer << ')'; + } + // Otherwise, use the parsed parameters. + else if (!parsedParams.empty()) { + printer << "wrt: "; + if (parsedParams.size() > 1) + printer << '('; + interleave(parsedParams, [&](const ParsedAutoDiffParameter ¶m) { + switch (param.getKind()) { + case ParsedAutoDiffParameter::Kind::Named: + printer << param.getName(); + break; + case ParsedAutoDiffParameter::Kind::Self: + printer << "self"; + break; + case ParsedAutoDiffParameter::Kind::Ordered: + auto *paramList = function->getParameters(); + assert(param.getIndex() <= paramList->size() && + "wrt parameter is out of range"); + auto *funcParam = paramList->get(param.getIndex()); + printer << funcParam->getNameStr(); + break; + } + }, [&] { printer << ", "; }); + if (parsedParams.size() > 1) + printer << ')'; + } + return printer.str(); +} + +// Print the arguments of the given `@differentiable` attribute. +static void printDifferentiableAttrArguments( + const DifferentiableAttr *attr, ASTPrinter &printer, PrintOptions Options, + const Decl *D, bool omitWrtClause = false, + bool omitAssociatedFunctions = false) { + // Create a temporary string for the attribute argument text. + std::string attrArgText; + llvm::raw_string_ostream stream(attrArgText); + + // Get original function. + auto *original = dyn_cast_or_null(D); + // Handle stored/computed properties and subscript methods. + if (auto *asd = dyn_cast_or_null(D)) + original = asd->getAccessor(AccessorKind::Get); + + // Print comma if not leading clause. + bool isLeadingClause = true; + auto printCommaIfNecessary = [&] { + if (isLeadingClause) { + isLeadingClause = false; + return; + } + stream << ", "; + }; + + // Print if the function is marked as linear. + if (attr->isLinear()) { + isLeadingClause = false; + stream << "linear"; + } + + // Print differentiation parameters, unless they are to be omitted. + if (!omitWrtClause) { + auto diffParamsString = getDifferentiationParametersClauseString( + original, attr->getParameterIndices(), attr->getParsedParameters()); + // Check whether differentiation parameter clause is empty. + // Handles edge case where resolved parameter indices are unset and + // parsed parameters are empty. This case should never trigger for + // user-visible printing. + if (!diffParamsString.empty()) { + printCommaIfNecessary(); + stream << diffParamsString; + } + } + // Print associated function names, unless they are to be omitted. + if (!omitAssociatedFunctions) { + // Print jvp function name, if specified. + if (auto jvp = attr->getJVP()) { + printCommaIfNecessary(); + stream << "jvp: " << jvp->Name; + } + // Print vjp function name, if specified. + if (auto vjp = attr->getVJP()) { + printCommaIfNecessary(); + stream << "vjp: " << vjp->Name; + } + } + // Print 'where' clause, if any. + // First, filter out requirements satisfied by the original function's + // generic signature. They should not be printed. + ArrayRef derivativeRequirements; + if (auto derivativeGenSig = attr->getDerivativeGenericSignature()) + derivativeRequirements = derivativeGenSig->getRequirements(); + auto requirementsToPrint = + llvm::make_filter_range(derivativeRequirements, [&](Requirement req) { + if (const auto &originalGenSig = original->getGenericSignature()) + if (originalGenSig->isRequirementSatisfied(req)) + return false; + return true; + }); + if (!llvm::empty(requirementsToPrint)) { + if (!isLeadingClause) + stream << ' '; + stream << "where "; + std::function getInterfaceType; + if (!original || !original->getGenericEnvironment()) { + getInterfaceType = [](Type Ty) -> Type { return Ty; }; + } else { + // Use GenericEnvironment to produce user-friendly + // names instead of something like 't_0_0'. + auto *genericEnv = original->getGenericEnvironment(); + assert(genericEnv); + getInterfaceType = [=](Type Ty) -> Type { + return genericEnv->getSugaredType(Ty); + }; + } + interleave(requirementsToPrint, [&](Requirement req) { + if (const auto &originalGenSig = original->getGenericSignature()) + if (originalGenSig->isRequirementSatisfied(req)) + return; + auto FirstTy = getInterfaceType(req.getFirstType()); + if (req.getKind() != RequirementKind::Layout) { + auto SecondTy = getInterfaceType(req.getSecondType()); + Requirement ReqWithDecls(req.getKind(), FirstTy, SecondTy); + ReqWithDecls.print(stream, Options); + } else { + Requirement ReqWithDecls(req.getKind(), FirstTy, + req.getLayoutConstraint()); + ReqWithDecls.print(stream, Options); + } + }, [&] { + stream << ", "; + }); + } + + // If the attribute argument text is empty, return. Do not print parentheses. + if (stream.str().empty()) + return; + + // Otherwise, print the attribute argument text enclosed in parentheses. + printer << '('; + printer << stream.str(); + printer << ')'; +} + void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options, const Decl *D) const { if (!DeclAttrs) @@ -814,6 +987,8 @@ StringRef DeclAttribute::getAttrName() const { return "<>"; case DAK_ProjectedValueProperty: return "_projectedValueProperty"; + case DAK_Differentiable: + return "differentiable"; } llvm_unreachable("bad DeclAttrKind"); } @@ -1149,6 +1324,91 @@ SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, specializedSignature); } +DifferentiableAttr::DifferentiableAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + ArrayRef params, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause) + : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), + linear(linear), NumParsedParameters(params.size()), JVP(std::move(jvp)), + VJP(std::move(vjp)), WhereClause(clause) { + std::copy(params.begin(), params.end(), + getTrailingObjects()); +} + +DifferentiableAttr::DifferentiableAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + IndexSubset *indices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenSig) + : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), + linear(linear), JVP(std::move(jvp)), VJP(std::move(vjp)), + ParameterIndices(indices) { + setDerivativeGenericSignature(context, derivativeGenSig); +} + +DifferentiableAttr * +DifferentiableAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + ArrayRef parameters, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause) { + unsigned size = totalSizeToAlloc(parameters.size()); + void *mem = context.Allocate(size, alignof(DifferentiableAttr)); + return new (mem) DifferentiableAttr(context, implicit, atLoc, baseRange, + linear, parameters, std::move(jvp), + std::move(vjp), clause); +} + +DifferentiableAttr * +DifferentiableAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, IndexSubset *indices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenSig) { + void *mem = context.Allocate(sizeof(DifferentiableAttr), + alignof(DifferentiableAttr)); + return new (mem) DifferentiableAttr(context, implicit, atLoc, baseRange, + linear, indices, std::move(jvp), + std::move(vjp), derivativeGenSig); +} + +void DifferentiableAttr::setJVPFunction(FuncDecl *decl) { + JVPFunction = decl; + if (decl && !JVP) + JVP = {decl->getFullName(), DeclNameLoc(decl->getNameLoc())}; +} + +void DifferentiableAttr::setVJPFunction(FuncDecl *decl) { + VJPFunction = decl; + if (decl && !VJP) + VJP = {decl->getFullName(), DeclNameLoc(decl->getNameLoc())}; +} + +GenericEnvironment *DifferentiableAttr::getDerivativeGenericEnvironment( + AbstractFunctionDecl *original) const { + GenericEnvironment *derivativeGenEnv = original->getGenericEnvironment(); + if (auto derivativeGenSig = getDerivativeGenericSignature()) + return derivativeGenEnv = derivativeGenSig->getGenericEnvironment(); + return original->getGenericEnvironment(); +} + +void DifferentiableAttr::print(llvm::raw_ostream &OS, const Decl *D, + bool omitWrtClause, + bool omitAssociatedFunctions) const { + StreamPrinter P(OS); + P << "@" << getAttrName(); + printDifferentiableAttrArguments(this, P, PrintOptions(), D, omitWrtClause, + omitAssociatedFunctions); +} + ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, TypeLoc ProtocolType, DeclName MemberName, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 5f6ca9da1b293..b379b60ace174 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -799,6 +799,267 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { ProtocolType.get(), MemberName, MemberNameLoc)); } +ParserResult +Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { + StringRef AttrName = "differentiable"; + SourceLoc lParenLoc = loc, rParenLoc = loc; + bool linear = false; + SmallVector params; + Optional jvpSpec; + Optional vjpSpec; + TrailingWhereClause *whereClause = nullptr; + + // Parse '('. + if (consumeIf(tok::l_paren, lParenLoc)) { + // Parse @differentiable attribute arguments. + if (parseDifferentiableAttributeArguments(linear, params, jvpSpec, vjpSpec, + whereClause)) + return makeParserError(); + // Parse ')'. + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, + /*DeclModifier=*/false); + return makeParserError(); + } + } + + return ParserResult( + DifferentiableAttr::create(Context, /*implicit*/ false, atLoc, + SourceRange(loc, rParenLoc), linear, + params, jvpSpec, vjpSpec, whereClause)); +} + +bool Parser::parseDifferentiationParametersClause( + SmallVectorImpl ¶ms, StringRef attrName) { + // Set parse error, skip until ')' and parse it. + auto errorAndSkipToEnd = [&](int parenDepth = 1) -> bool { + for (int i = 0; i < parenDepth; i++) { + skipUntil(tok::r_paren); + if (!consumeIf(tok::r_paren)) + diagnose(Tok, diag::attr_expected_rparen, attrName, + /*DeclModifier=*/false); + } + return true; + }; + + SyntaxParsingContext DiffParamsClauseContext( + SyntaxContext, SyntaxKind::DifferentiationParamsClause); + consumeToken(tok::identifier); + if (!consumeIf(tok::colon)) { + diagnose(Tok, diag::expected_colon_after_label, "wrt"); + return errorAndSkipToEnd(); + } + + // Function that parses a parameter into `params`. Returns true if error + // occurred. + auto parseParam = [&](bool parseTrailingComma = true) -> bool { + SyntaxParsingContext DiffParamContext( + SyntaxContext, SyntaxKind::DifferentiationParam); + SourceLoc paramLoc; + switch (Tok.getKind()) { + case tok::identifier: { + Identifier paramName; + if (parseIdentifier(paramName, paramLoc, + diag::diff_params_clause_expected_parameter)) + return true; + params.push_back(ParsedAutoDiffParameter::getNamedParameter( + paramLoc, paramName)); + break; + } + case tok::integer_literal: { + unsigned paramNum; + if (parseUnsignedInteger( + paramNum, paramLoc, + diag::diff_params_clause_expected_parameter)) + return true; + + params.push_back(ParsedAutoDiffParameter::getOrderedParameter( + paramLoc, paramNum)); + break; + } + case tok::kw_self: { + paramLoc = consumeToken(tok::kw_self); + params.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); + break; + } + default: + diagnose(Tok, diag::diff_params_clause_expected_parameter); + return true; + } + if (parseTrailingComma && Tok.isNot(tok::r_paren)) + return parseToken(tok::comma, diag::attr_expected_comma, attrName, + /*isDeclModifier=*/false); + return false; + }; + + // Parse opening '(' of the parameter list. + if (Tok.is(tok::l_paren)) { + SyntaxParsingContext DiffParamsContext( + SyntaxContext, SyntaxKind::DifferentiationParams); + consumeToken(tok::l_paren); + // Parse first parameter. At least one is required. + if (parseParam()) + return errorAndSkipToEnd(2); + // Parse remaining parameters until ')'. + while (Tok.isNot(tok::r_paren)) + if (parseParam()) + return errorAndSkipToEnd(2); + SyntaxContext->collectNodesInPlace(SyntaxKind::DifferentiationParamList); + // Parse closing ')' of the parameter list. + consumeToken(tok::r_paren); + } + // If no opening '(' for parameter list, parse a single parameter. + else { + if (parseParam(/*parseTrailingComma*/ false)) + return errorAndSkipToEnd(); + } + return false; +} + +bool Parser::parseDifferentiableAttributeArguments( + bool &linear, SmallVectorImpl ¶ms, + Optional &jvpSpec, Optional &vjpSpec, + TrailingWhereClause *&whereClause) { + StringRef AttrName = "differentiable"; + + // Set parse error, skip until ')' and parse it. + auto errorAndSkipToEnd = [&](int parenDepth = 1) -> bool { + for (int i = 0; i < parenDepth; i++) { + skipUntil(tok::r_paren); + if (!consumeIf(tok::r_paren)) + diagnose(Tok, diag::attr_expected_rparen, AttrName, + /*DeclModifier=*/false); + } + return true; + }; + + // Parse trailing comma, if it exists, and check for errors. + auto consumeIfTrailingComma = [&]() -> bool { + if (!consumeIf(tok::comma)) return false; + // Diagnose trailing comma before 'where' or ')'. + if (Tok.is(tok::kw_where) || Tok.is(tok::r_paren)) { + diagnose(Tok, diag::unexpected_separator, ","); + return true; + } + // Check that token after comma is 'wrt:' or a function specifier label. + if (!(isWRTIdentifier(Tok) || isJVPIdentifier(Tok) || + isVJPIdentifier(Tok))) { + diagnose(Tok, diag::attr_differentiable_expected_label); + return true; + } + return false; + }; + + // Store starting parser position. + auto startingLoc = Tok.getLoc(); + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::DifferentiableAttributeArguments); + + // Parse optional differentiation parameters. + // Parse 'linear' label (optional). + linear = false; + if (Tok.is(tok::identifier) && Tok.getText() == "linear") { + linear = true; + consumeToken(tok::identifier); + // If no trailing comma or 'where' clause, terminate parsing arguments. + if (Tok.isNot(tok::comma, tok::kw_where)) + return false; + if (consumeIfTrailingComma()) + return errorAndSkipToEnd(); + } + + // If 'withRespectTo' is used, make the user change it to 'wrt'. + if (Tok.is(tok::identifier) && Tok.getText() == "withRespectTo") { + SourceRange withRespectToRange(Tok.getLoc(), peekToken().getLoc()); + diagnose(Tok, diag::attr_differentiable_use_wrt_not_withrespectto) + .highlight(withRespectToRange) + .fixItReplace(withRespectToRange, "wrt:"); + return errorAndSkipToEnd(); + } + if (isWRTIdentifier(Tok)) { + if (parseDifferentiationParametersClause(params, AttrName)) + return true; + // If no trailing comma or 'where' clause, terminate parsing arguments. + if (Tok.isNot(tok::comma, tok::kw_where)) + return false; + if (consumeIfTrailingComma()) + return errorAndSkipToEnd(); + } + + // Function that parses a label and a function specifier, e.g. 'vjp: foo(_:)'. + // Return true on error. + auto parseFuncSpec = [&](StringRef label, DeclNameWithLoc &result, + bool &terminateParsingArgs) -> bool { + // Parse label. + if (parseSpecificIdentifier(label, + diag::attr_differentiable_missing_label, label) || + parseToken(tok::colon, diag::expected_colon_after_label, label)) + return true; + // Parse the name of the function. + SyntaxParsingContext FuncDeclNameContext( + SyntaxContext, SyntaxKind::FunctionDeclName); + Diagnostic funcDiag(diag::attr_differentiable_expected_function_name.ID, + { label }); + result.Name = + parseUnqualifiedDeclName(/*afterDot=*/false, result.Loc, + funcDiag, /*allowOperators=*/true, + /*allowZeroArgCompoundNames=*/true); + // If no trailing comma or 'where' clause, terminate parsing arguments. + if (Tok.isNot(tok::comma, tok::kw_where)) + terminateParsingArgs = true; + return !result.Name; + }; + + // Store whether to terminate parsing arguments. + bool terminateParsingArgs = false; + + // Parse 'jvp: ' (optional). + if (isJVPIdentifier(Tok)) { + SyntaxParsingContext JvpContext( + SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); + jvpSpec = DeclNameWithLoc(); + if (parseFuncSpec("jvp", *jvpSpec, terminateParsingArgs)) + return errorAndSkipToEnd(); + if (terminateParsingArgs) + return false; + if (consumeIfTrailingComma()) + return errorAndSkipToEnd(); + } + + // Parse 'vjp: ' (optional). + if (isVJPIdentifier(Tok)) { + SyntaxParsingContext VjpContext( + SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); + vjpSpec = DeclNameWithLoc(); + if (parseFuncSpec("vjp", *vjpSpec, terminateParsingArgs)) + return errorAndSkipToEnd(); + if (terminateParsingArgs) + return false; + // Note: intentionally parse trailing comma here, even though it's the last + // function specifier. `consumeIfTrailingComma` will emit an error. + if (consumeIfTrailingComma()) + return errorAndSkipToEnd(); + } + + // If parser has not advanced and token is not 'where' or ')', emit error. + if (Tok.getLoc() == startingLoc && Tok.isNot(tok::kw_where, tok::r_paren)) { + diagnose(Tok, diag::attr_differentiable_expected_label); + return errorAndSkipToEnd(); + } + + // Parse a trailing 'where' clause if any. + if (Tok.is(tok::kw_where)) { + SourceLoc whereLoc; + SmallVector requirements; + bool firstTypeInComplete; + parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete, + /*AllowLayoutConstraints*/ true); + whereClause = TrailingWhereClause::create(Context, whereLoc, requirements); + } + return false; +} + + void Parser::parseObjCSelector(SmallVector &Names, SmallVector &NameLocs, bool &IsNullarySelector) { @@ -1598,6 +1859,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_Differentiable: { + auto Attr = parseDifferentiableAttribute(AtLoc, Loc); + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } + case DAK_ProjectedValueProperty: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 8c99750f87837..2f422f7a88347 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -907,6 +907,18 @@ bool Parser::parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag, return false; } +bool Parser::parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, + const Diagnostic &D) { + auto IntTok = Tok; + if (parseToken(tok::integer_literal, Loc, D)) + return true; + if (IntTok.getText().getAsInteger(0, Result)) { + diagnose(IntTok.getLoc(), D); + return true; + } + return false; +} + SourceLoc Parser::getLocForMissingMatchingToken() const { // At present, use the same location whether it's an error or whether // the matching token is missing. diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 925c46af030cd..acf1b4882c8cb 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -111,6 +111,9 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(DisfavoredOverload) IGNORED_ATTR(ProjectedValueProperty) IGNORED_ATTR(ReferenceOwnership) + + // TODO: Changes are yet to be upstreamed from apple/tensorflow branch. + IGNORED_ATTR(Differentiable) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 73db61aa2d058..157a8ccf5891d 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1320,6 +1320,9 @@ namespace { UNINTERESTING_ATTR(DynamicReplacement) UNINTERESTING_ATTR(PrivateImport) + // Differentiation-related attributes. + UNINTERESTING_ATTR(Differentiable) + // These can't appear on overridable declarations. UNINTERESTING_ATTR(Prefix) UNINTERESTING_ATTR(Postfix) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 36337c215a366..3c8112c4f6995 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1734,7 +1734,19 @@ namespace decls_block { GenericSignatureIDField // specialized signature >; -#define SIMPLE_DECL_ATTR(X, CLASS, ...) \ + using DifferentiableDeclAttrLayout = BCRecordLayout< + Differentiable_DECL_ATTR, + BCFixed<1>, // Implicit flag. + BCFixed<1>, // Linear flag. + IdentifierIDField, // JVP name. + DeclIDField, // JVP function declaration. + IdentifierIDField, // VJP name. + DeclIDField, // VJP function declaration. + GenericSignatureIDField, // Derivative generic signature. + BCArray> // Differentiation parameter indices' bitvector. + >; + +#define SIMPLE_DECL_ATTR(X, CLASS, ...) \ using CLASS##DeclAttrLayout = BCRecordLayout< \ CLASS##_DECL_ATTR, \ BCFixed<1> /* implicit flag */ \ diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index da8395c8d586c..b11e78fbe0039 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -20,6 +20,7 @@ #include "swift/AST/FileSystem.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IndexSubset.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/LinkLibrary.h" @@ -2270,6 +2271,37 @@ class Serializer::DeclSerializer : public DeclVisitor { break; } + case DAK_Differentiable: { + auto abbrCode = S.DeclTypeAbbrCodes[DifferentiableDeclAttrLayout::Code]; + auto *attr = cast(DA); + + IdentifierID jvpName = 0; + DeclID jvpRef = 0; + if (auto jvp = attr->getJVP()) + jvpName = S.addDeclBaseNameRef(jvp->Name.getBaseName()); + if (auto jvpFunction = attr->getJVPFunction()) + jvpRef = S.addDeclRef(jvpFunction); + + IdentifierID vjpName = 0; + DeclID vjpRef = 0; + if (auto vjp = attr->getVJP()) + vjpName = S.addDeclBaseNameRef(vjp->Name.getBaseName()); + if (auto vjpFunction = attr->getVJPFunction()) + vjpRef = S.addDeclRef(vjpFunction); + + auto paramIndices = attr->getParameterIndices(); + assert(paramIndices && "Checked parameter indices must be resolved"); + SmallVector indices; + for (unsigned i : range(paramIndices->getCapacity())) + indices.push_back(paramIndices->contains(i)); + + DifferentiableDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), + attr->isLinear(), jvpName, jvpRef, vjpName, vjpRef, + S.addGenericSignatureRef(attr->getDerivativeGenericSignature()), + indices); + return; + } } } diff --git a/test/AutoDiff/differentiable_attr_parse.swift b/test/AutoDiff/differentiable_attr_parse.swift new file mode 100644 index 0000000000000..354d385ba3604 --- /dev/null +++ b/test/AutoDiff/differentiable_attr_parse.swift @@ -0,0 +1,180 @@ +// RUN: %target-swift-frontend -parse -verify %s + +/// Good + +struct Foo { + @differentiable + var x: Float +} + +@differentiable(vjp: foo(_:_:)) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(vjp: foo(_:_:) where T : FloatingPoint) // okay +func bar(_ x: T, _: T) -> T { + return 1 + x +} + +@differentiable(wrt: (self, x, y), vjp: foo(_:_:)) // okay +func bar(_ x: Float, _ y: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: (self, x, y), jvp: bar, vjp: foo(_:_:)) // okay +func bar(_ x: Float, _ y: Float) -> Float { + return 1 + x +} + +@differentiable // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: x) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: (x)) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: self) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@_transparent +@differentiable // okay +@inlinable +func playWellWithOtherAttrs(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@_transparent +@differentiable(wrt: (self), vjp: _vjpSquareRoot) // okay +public func squareRoot() -> Self { + var lhs = self + lhs.formSquareRoot() + return lhs +} + +@differentiable(linear) // okay +func identity(_ x: Float) -> Float { + return x +} + +@differentiable(linear, wrt: x) // okay +func slope2(_ x: Float) -> Float { + return 2 * x +} + +@differentiable(wrt: y) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (x, y)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (0, y)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (x, 1)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (0, 1)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +/// Bad + +@differentiable(3) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(foo(_:_:)) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(vjp: foo(_:_:), 3) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: (x), foo(_:_:)) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: x, y) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func bar(_ x: Float, _ y: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: 0, 1) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: 0, y) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: 0,) // expected-error {{unexpected ',' separator}} +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(vjp: foo(_:_:) // expected-error {{expected ')' in 'differentiable' attribute}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(vjp: foo(_:_:) where T) // expected-error {{expected ':' or '==' to indicate a conformance or same-type requirement}} +func bar(_ x: T, _: T) -> T { + return 1 + x +} + +@differentiable(,) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(vjp: foo(_:_:),) // expected-error {{unexpected ',' separator}} +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(vjp: foo(_:_:), where T) // expected-error {{unexpected ',' separator}} +func bar(_ x: T, _: T) -> T { + return 1 + x +} + +@differentiable(wrt: x, linear) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func slope4(_ x: Float) -> Float { + return 4 * x +} + +@differentiable(wrt: x, linear, vjp: const5) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func slope5(_ x: Float) -> Float { + return 5 * x +} + +@differentiable(wrt: x, vjp: const6, linear) // expected-error {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +func slope5(_ x: Float) -> Float { + return 6 * x +} diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 7ec0053cba608..0c66c5dd1f3e0 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -62,6 +62,7 @@ struct MyStruct {} // KEYWORD2-NEXT: Keyword/None: warn_unqualified_access[#Func Attribute#]; name=warn_unqualified_access{{$}} // KEYWORD2-NEXT: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // KEYWORD2-NEXT: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult +// KEYWORD2-NEXT: Keyword/None: differentiable[#Func Attribute#]; name=differentiable // KEYWORD2-NEXT: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction{{$}} // KEYWORD2-NOT: Keyword // KEYWORD2: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct @@ -124,6 +125,7 @@ struct MyStruct {} // ON_GLOBALVAR-DAG: Keyword/None: inlinable[#Var Attribute#]; name=inlinable // ON_GLOBALVAR-DAG: Keyword/None: usableFromInline[#Var Attribute#]; name=usableFromInline // ON_GLOBALVAR-DAG: Keyword/None: GKInspectable[#Var Attribute#]; name=GKInspectable +// ON_GLOBALVAR-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable // ON_GLOBALVAR-NOT: Keyword // ON_GLOBALVAR: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_GLOBALVAR: End completions @@ -153,6 +155,7 @@ struct _S { // ON_PROPERTY-DAG: Keyword/None: inlinable[#Var Attribute#]; name=inlinable // ON_PROPERTY-DAG: Keyword/None: usableFromInline[#Var Attribute#]; name=usableFromInline // ON_PROPERTY-DAG: Keyword/None: GKInspectable[#Var Attribute#]; name=GKInspectable +// ON_PROPERTY-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable // ON_PROPERTY-NOT: Keyword // ON_PROPERTY: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_PROPERTY-NOT: Decl[PrecedenceGroup] @@ -172,6 +175,7 @@ struct _S { // ON_METHOD-DAG: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // ON_METHOD-DAG: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult // ON_METHOD-DAG: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction +// ON_METHOD-DAG: Keyword/None: differentiable[#Func Attribute#]; name=differentiable // ON_METHOD-NOT: Keyword // ON_METHOD: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_METHOD: End completions @@ -226,6 +230,7 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction // ON_MEMBER_LAST-DAG: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper // ON_MEMBER_LAST-DAG: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder +// ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // ON_MEMBER_LAST-NOT: Keyword // ON_MEMBER_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_MEMBER_LAST-NOT: Decl[PrecedenceGroup] @@ -268,6 +273,7 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: frozen[#Declaration Attribute#]; name=frozen // KEYWORD_LAST-NEXT: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper // KEYWORD_LAST-NEXT: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder{{$}} +// KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}} // KEYWORD_LAST-NOT: Keyword // KEYWORD_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index dec2af37ba7c7..1967ae33805e2 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -60,6 +60,8 @@ Child('ObjCName', kind='ObjCSelector'), Child('ImplementsArguments', kind='ImplementsAttributeArguments'), + Child('DifferentiableArguments', + kind='DifferentiableAttributeArguments'), Child('NamedAttributeString', kind='NamedAttributeStringArgument'), ], description=''' @@ -197,5 +199,122 @@ ]), # objc-selector -> objc-selector-piece objc-selector? - Node('ObjCSelector', kind='SyntaxCollection', element='ObjCSelectorPiece') + Node('ObjCSelector', kind='SyntaxCollection', element='ObjCSelectorPiece'), + + # The argument of '@differentiable(...)'. + # differentiable-attr-arguments -> + # differentiation-params-clause? ','? + # differentiable-attr-func-specifier? # jvp + # differentiable-attr-func-specifier? # vjp + # generic-where-clause? + Node('DifferentiableAttributeArguments', kind='Syntax', + description=''' + The arguments for the `@differentiable` attribute: an optional \ + differentiation parameter list and associated functions. + ''', + children=[ + Child('DiffParams', kind='DifferentiationParamsClause', + is_optional=True), + Child('DiffParamsComma', kind='CommaToken', description=''' + The comma following the differentiation parameters clause, + if it exists. + ''', is_optional=True), + Child('MaybePrimal', kind='DifferentiableAttributeFuncSpecifier', + is_optional=True), + Child('MaybeAdjoint', kind='DifferentiableAttributeFuncSpecifier', + is_optional=True), + Child('MaybeJVP', kind='DifferentiableAttributeFuncSpecifier', + is_optional=True), + Child('MaybeVJP', kind='DifferentiableAttributeFuncSpecifier', + is_optional=True), + Child('WhereClause', kind='GenericWhereClause', is_optional=True), + ]), + + # differentiation-params-clause -> + # 'wrt' ':' (differentiation-param | differentiation-params) + Node('DifferentiationParamsClause', kind='Syntax', + description='A clause containing differentiation parameters.', + children=[ + Child('WrtLabel', kind='IdentifierToken', + text_choices=['wrt'], description='The "wrt" label.'), + Child('Colon', kind='ColonToken', description=''' + The colon separating "wrt" and the parameter list. + '''), + Child('Parameters', kind='Syntax', + node_choices=[ + Child('Parameter', kind='DifferentiationParam'), + Child('ParameterList', kind='DifferentiationParams'), + ]), + ]), + + # differentiation-params -> '(' differentiation-param-list ')' + Node('DifferentiationParams', kind='Syntax', + description='The differentiation parameters.', + children=[ + Child('LeftParen', kind='LeftParenToken'), + Child('DiffParams', kind='DifferentiationParamList', + collection_element_name='DifferentiationParam', + description='The parameters for differentiation.'), + Child('RightParen', kind='RightParenToken'), + ]), + + # differentiation-param-list -> + # differentiation-param differentiation-param-list? + Node('DifferentiationParamList', kind='SyntaxCollection', + element='DifferentiationParam'), + + # differentiation-param -> ('self' | identifer) ','? + Node('DifferentiationParam', kind='Syntax', + description=''' + A differentiation parameter: either the "self" identifier or a \ + function parameter name. + ''', + traits=['WithTrailingComma'], + children=[ + Child('Parameter', kind='Syntax', + node_choices=[ + Child('Self', kind='SelfToken'), + Child('Name', kind='IdentifierToken'), + ]), + Child('TrailingComma', kind='CommaToken', is_optional=True), + ]), + + # differentiable-attr-func-specifier -> + # ('jvp' | 'vjp') ':' func-decl-name ','? + Node('DifferentiableAttributeFuncSpecifier', kind='Syntax', + description=''' + A function specifier, consisting of an identifier, colon, and a \ + function declaration name (e.g. `vjp: foo(_:_:)`). + ''', + traits=['WithTrailingComma'], + children=[ + Child('Label', kind='IdentifierToken', + text_choices=['jvp', 'vjp']), + Child('Colon', kind='ColonToken'), + Child('FunctionDeclName', kind='FunctionDeclName', + description='The referenced function name.'), + Child('TrailingComma', kind='CommaToken', is_optional=True), + ]), + + # func-decl-name -> (identifier | operator) decl-name-arguments? + # NOTE: This is duplicated with `DeclName` above. Change `DeclName` + # description and use it if possible. + Node('FunctionDeclName', kind='Syntax', + description='A function declaration name (e.g. `foo(_:_:)`).', + children=[ + Child('Name', kind='Syntax', description=''' + The base name of the referenced function. + ''', + node_choices=[ + Child('Identifier', kind='IdentifierToken'), + Child('PrefixOperator', kind='PrefixOperatorToken'), + Child('SpacedBinaryOperator', + kind='SpacedBinaryOperatorToken'), + ]), + Child('Arguments', kind='DeclNameArguments', + is_optional=True, description=''' + The argument labels of the referenced function, optionally \ + specified. + '''), + ]), ] diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 574139092ab3c..748f089bd1d36 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -234,6 +234,13 @@ 'SomeType': 230, 'CustomAttribute': 231, 'GenericRequirement': 232, + 'DifferentiableAttributeArguments': 233, + 'DifferentiationParamsClause': 234, + 'DifferentiationParams': 235, + 'DifferentiationParamList': 236, + 'DifferentiationParam': 237, + 'DifferentiableAttributeFuncSpecifier': 238, + 'FunctionDeclName': 239, } From 2634841a4edc70526c035939c716ae6625e4e77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Mon, 11 Nov 2019 14:17:00 -0800 Subject: [PATCH 061/283] [windows] Quote argument for Windows compatibility. In Windows the dollar doesn't introduce variables, so it doesn't need to be escaped, and was creating an error when running the test on Windows. Howerver, the unescaped dollar will be interpreted like a variable in Unix shells, and will fail the test. The solution is quoting the inside of the argument, which disables the variable substitution on Linux, and doesn't seem to affect the Windows command line parser. --- test/IDE/print_type_interface.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/IDE/print_type_interface.swift b/test/IDE/print_type_interface.swift index 5b2f976f2139b..7af1876b8af34 100644 --- a/test/IDE/print_type_interface.swift +++ b/test/IDE/print_type_interface.swift @@ -97,7 +97,7 @@ extension Array.Inner where Element: P2 { extension Int: P2 {} // Print interface for Array.Inner -// RUN: %target-swift-ide-test -print-type-interface -usr=\$sSa20print_type_interfaceE5InnerVySi_GD -module-name print_type_interface -source-filename %s | %FileCheck %s -check-prefix=TYPE6 +// RUN: %target-swift-ide-test -print-type-interface -usr='$sSa20print_type_interfaceE5InnerVySi_GD' -module-name print_type_interface -source-filename %s | %FileCheck %s -check-prefix=TYPE6 // TYPE6-LABEL: public struct Inner { // TYPE6: public func innerFoo() From d9a575ca4d173de23a4a15de422ed8a5020cd0d6 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:07:04 -0800 Subject: [PATCH 062/283] Add TypeExpansionContext --- include/swift/AST/TypeExpansionContext.h | 114 +++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 include/swift/AST/TypeExpansionContext.h diff --git a/include/swift/AST/TypeExpansionContext.h b/include/swift/AST/TypeExpansionContext.h new file mode 100644 index 0000000000000..7c266e63d73ef --- /dev/null +++ b/include/swift/AST/TypeExpansionContext.h @@ -0,0 +1,114 @@ +//===--- TypeExpansionContext.h - Swift Type Expansion Context --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the TypeExpansionContext class. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_TYPEEXPANSIONCONTEXT_H +#define SWIFT_TYPEEXPANSIONCONTEXT_H + +#include "swift/AST/ResilienceExpansion.h" + +namespace swift { + class DeclContext; + +/// Describes the context in which SIL types should eventually be expanded. +/// Required for lowering resilient types and deciding whether to look through +/// opaque result types to their underlying type. +class TypeExpansionContext { + ResilienceExpansion expansion; + // The context (module, function, ...) we are expanding the type in. + const DeclContext *inContext; + // Is the context in which we are expanding in the whole module. + bool isContextWholeModule; + + // The minimal expansion. + TypeExpansionContext() { + inContext = nullptr; + expansion = ResilienceExpansion::Minimal; + isContextWholeModule = false; + } + +public: + + // Infer the expansion for the SIL function. + TypeExpansionContext(const SILFunction &f); + + TypeExpansionContext(ResilienceExpansion expansion, + const DeclContext *inContext, bool isWholeModuleContext) + : expansion(expansion), inContext(inContext), + isContextWholeModule(isWholeModuleContext) {} + + ResilienceExpansion getResilienceExpansion() const { return expansion; } + + const DeclContext *getContext() const { return inContext; } + + bool isWholeModuleContext() const { return isContextWholeModule; } + + bool shouldLookThroughOpaqueTypeArchetypes() const { + return inContext != nullptr; + } + + bool isMinimal() const { return *this == TypeExpansionContext(); } + + static TypeExpansionContext minimal() { + return TypeExpansionContext(); + } + + static TypeExpansionContext maximal(const DeclContext *inContext, + bool isContextWholeModule) { + return TypeExpansionContext(ResilienceExpansion::Maximal, inContext, + isContextWholeModule); + } + + static TypeExpansionContext maximalResilienceExpansionOnly() { + return maximal(nullptr, false); + } + + static TypeExpansionContext + noOpaqueTypeArchetypesSubstitution(ResilienceExpansion expansion) { + return TypeExpansionContext(expansion, nullptr, false); + } + + bool operator==(const TypeExpansionContext &other) const { + assert(other.inContext != this->inContext || + other.isContextWholeModule == this->isContextWholeModule); + return other.inContext == this->inContext && + other.expansion == this->expansion; + } + + bool operator<(const TypeExpansionContext other) const { + assert(other.inContext != this->inContext || + other.isContextWholeModule == this->isContextWholeModule); + if (this->expansion == other.expansion) + return this->inContext < other.inContext; + return this->expansion < other.expansion; + } + + bool operator>(const TypeExpansionContext other) const { + assert(other.inContext != this->inContext || + other.isContextWholeModule == this->isContextWholeModule); + if (this->expansion == other.expansion) + return this->inContext > other.inContext; + return this->expansion > other.expansion; + } + + uintptr_t getHashKey() const { + uintptr_t key = (uintptr_t)inContext | (uintptr_t)expansion; + return key; + } +}; + +} // namespace swift + +#endif From f5fe7c1868b9187adfc60bae354563cd1d5c8316 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:08:18 -0800 Subject: [PATCH 063/283] Add an API to map substitution map into a type expansion context. --- include/swift/AST/SubstitutionMap.h | 9 ++++++++- lib/AST/SubstitutionMap.cpp | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index 6d9ce0fb38ace..0ae98500eb524 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -20,6 +20,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/Type.h" +#include "swift/AST/TypeExpansionContext.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMapInfo.h" @@ -176,7 +177,13 @@ class SubstitutionMap { SubstitutionMap subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options=None) const; - + + /// Apply type expansion lowering to all types in the substitution map. Opaque + /// archetypes will be lowered to their underlying types if the type expansion + /// context allows. + SubstitutionMap mapIntoTypeExpansionContext( + TypeExpansionContext context) const; + /// Create a substitution map for a protocol conformance. static SubstitutionMap getProtocolSubstitutions(ProtocolDecl *protocol, diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 3f74f9a42a6ba..38e17b36aed58 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -715,3 +715,11 @@ bool SubstitutionMap::isIdentity() const { return !hasNonIdentityReplacement; } + +SubstitutionMap SubstitutionMap::mapIntoTypeExpansionContext( + TypeExpansionContext context) const { + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + return this->subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); +} From 8ec3a77e94b578289423be9ed46f847663320577 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:19:34 -0800 Subject: [PATCH 064/283] Add TypeExpansionContext to AST substitution functions Also add hasOpaqueArchetypePropertiesOrCases() this function will be needed by SIL type lowering to determine whether types containing opaque archetype stored properties or cases should be considered as potentially address types. --- include/swift/AST/Types.h | 34 +++++++++--- lib/AST/Type.cpp | 110 ++++++++++++++++++++++++++++++-------- 2 files changed, 116 insertions(+), 28 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 89bb823f7633c..5984a303fc945 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -27,6 +27,7 @@ #include "swift/AST/SubstitutionMap.h" #include "swift/AST/Type.h" #include "swift/AST/TypeAlignments.h" +#include "swift/AST/TypeExpansionContext.h" #include "swift/Basic/ArrayRefView.h" #include "swift/Basic/Debug.h" #include "swift/Basic/InlineBitfield.h" @@ -582,7 +583,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { bool hasOpaqueArchetype() const { return getRecursiveProperties().hasOpaqueArchetype(); } - + /// Determine whether the type has any stored properties or enum cases that + /// involve an opaque type. + bool hasOpaqueArchetypePropertiesOrCases(); + /// Determine whether the type is an opened existential type. /// /// To determine whether there is an opened existential type @@ -3687,6 +3691,17 @@ enum class SILCoroutineKind : uint8_t { class SILFunctionConventions; + +CanType substOpaqueTypesWithUnderlyingTypes(CanType type, + TypeExpansionContext context, + bool allowLoweredTypes = false); +ProtocolConformanceRef +substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, Type origType, + TypeExpansionContext context); +namespace Lowering { + class TypeConverter; +}; + /// SILFunctionType - The lowered type of a function value, suitable /// for use by SIL. /// @@ -4205,10 +4220,14 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, SILFunction &context) const; CanSILFunctionType substGenericArgs(SILModule &silModule, - SubstitutionMap subs); + SubstitutionMap subs, + TypeExpansionContext context); CanSILFunctionType substGenericArgs(SILModule &silModule, TypeSubstitutionFn subs, - LookupConformanceFn conformances); + LookupConformanceFn conformances, + TypeExpansionContext context); + CanSILFunctionType substituteOpaqueArchetypes(Lowering::TypeConverter &TC, + TypeExpansionContext context); SILType substInterfaceType(SILModule &M, SILType interfaceType) const; @@ -4963,9 +4982,12 @@ class ReplaceOpaqueTypesWithUnderlyingTypes { public: const DeclContext *inContext; ResilienceExpansion contextExpansion; + bool isContextWholeModule; ReplaceOpaqueTypesWithUnderlyingTypes(const DeclContext *inContext, - ResilienceExpansion contextExpansion) - : inContext(inContext), contextExpansion(contextExpansion) {} + ResilienceExpansion contextExpansion, + bool isWholeModuleContext) + : inContext(inContext), contextExpansion(contextExpansion), + isContextWholeModule(isWholeModuleContext) {} /// TypeSubstitutionFn Type operator()(SubstitutableType *maybeOpaqueType) const; @@ -5752,5 +5774,5 @@ struct DenseMapInfo { }; } - + #endif diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index b29f96dfe9fff..f3f486b12ca63 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2670,8 +2670,10 @@ ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( static Type substOpaqueTypesWithUnderlyingTypes(Type ty, const DeclContext *inContext, - ResilienceExpansion contextExpansion) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion); + ResilienceExpansion contextExpansion, + bool isWholeModuleContext) { + ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion, + isWholeModuleContext); return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); } @@ -2682,7 +2684,8 @@ substOpaqueTypesWithUnderlyingTypes(Type ty, const DeclContext *inContext, /// will be accessible. It's not intended to enforce any rules about what /// opaque substitutions are or are not allowed. static bool canSubstituteTypeInto(Type ty, const DeclContext *dc, - OpaqueSubstitutionKind kind) { + OpaqueSubstitutionKind kind, + bool isContextWholeModule) { auto nominal = ty->getAnyNominal(); if (!nominal) return true; @@ -2695,6 +2698,10 @@ static bool canSubstituteTypeInto(Type ty, const DeclContext *dc, return true; case OpaqueSubstitutionKind::SubstituteSameModuleMaximalResilience: + // In whole module compilation private types are okay. + if (isContextWholeModule) + return true; + // In the same file any visibility is okay. if (!dc->isModuleContext() && nominal->getDeclContext()->getParentSourceFile() == @@ -2739,27 +2746,39 @@ operator()(SubstitutableType *maybeOpaqueType) const { // Check that we are allowed to substitute the underlying type into the // context. auto inContext = this->inContext; - if (substTy.findIf([inContext, substitutionKind](Type t) -> bool { - if (!canSubstituteTypeInto(t, inContext, substitutionKind)) - return true; - return false; - })) + auto isContextWholeModule = this->isContextWholeModule; + if (substTy.findIf( + [inContext, substitutionKind, isContextWholeModule](Type t) -> bool { + if (!canSubstituteTypeInto(t, inContext, substitutionKind, + isContextWholeModule)) + return true; + return false; + })) return maybeOpaqueType; // If the type still contains opaque types, recur. if (substTy->hasOpaqueArchetype()) { - return substOpaqueTypesWithUnderlyingTypes(substTy, inContext, - contextExpansion); + return ::substOpaqueTypesWithUnderlyingTypes( + substTy, inContext, contextExpansion, isContextWholeModule); } return substTy; } -static ProtocolConformanceRef -substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, Type origType, - const DeclContext *inContext, - ResilienceExpansion contextExpansion) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion); +static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef ref, Type origType, const DeclContext *inContext, + ResilienceExpansion contextExpansion, bool isWholeModuleContext) { + ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion, + isWholeModuleContext); + return ref.subst(origType, replacer, replacer, + SubstFlags::SubstituteOpaqueArchetypes); +} + +ProtocolConformanceRef swift::substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef ref, Type origType, TypeExpansionContext context) { + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); return ref.subst(origType, replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); } @@ -2804,11 +2823,14 @@ operator()(CanType maybeOpaqueType, Type replacementType, // Check that we are allowed to substitute the underlying type into the // context. auto inContext = this->inContext; - if (substTy.findIf([inContext, substitutionKind](Type t) -> bool { - if (!canSubstituteTypeInto(t, inContext, substitutionKind)) - return true; - return false; - })) + auto isContextWholeModule = this->isContextWholeModule; + if (substTy.findIf( + [inContext, substitutionKind, isContextWholeModule](Type t) -> bool { + if (!canSubstituteTypeInto(t, inContext, substitutionKind, + isContextWholeModule)) + return true; + return false; + })) return abstractRef; auto substRef = @@ -2816,8 +2838,8 @@ operator()(CanType maybeOpaqueType, Type replacementType, // If the type still contains opaque types, recur. if (substTy->hasOpaqueArchetype()) { - return substOpaqueTypesWithUnderlyingTypes(substRef, substTy, inContext, - contextExpansion); + return ::substOpaqueTypesWithUnderlyingTypes( + substRef, substTy, inContext, contextExpansion, isContextWholeModule); } return substRef; } @@ -3436,6 +3458,11 @@ static Type substType(Type derivedType, // If we have a substitution for this type, use it. if (auto known = substitutions(substOrig)) { + if (options.contains(SubstFlags::SubstituteOpaqueArchetypes) && + isa(substOrig) && + known->getCanonicalType() == substOrig->getCanonicalType()) + return None; // Recursively process the substitutions of the opaque type + // archetype. return known; } @@ -4612,3 +4639,42 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { opened = OpenedArchetypeType::get(this); return opened; } + +bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { + if (hasOpaqueArchetype()) + return true; + + if (auto *structDecl = getStructOrBoundGenericStruct()) { + for (auto *field : structDecl->getStoredProperties()) { + auto fieldTy = field->getInterfaceType(); + if (fieldTy->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) + return true; + } + } + + if (auto *enumDecl = getEnumOrBoundGenericEnum()) { + for (auto *elt : enumDecl->getAllElements()) { + auto eltType = elt->getInterfaceType(); + if (eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) + return true; + } + } + return false; +} + +CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, + TypeExpansionContext context, + bool allowLoweredTypes) { + if (!context.shouldLookThroughOpaqueTypeArchetypes() || + !ty->hasOpaqueArchetype()) + return ty; + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + SubstOptions flags = SubstFlags::SubstituteOpaqueArchetypes; + if (allowLoweredTypes) + flags = + SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes; + return ty.subst(replacer, replacer, flags)->getCanonicalType(); +} From 6b3e1b398778cf5ec66a1eb4dfde86e676d053c3 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:29:24 -0800 Subject: [PATCH 065/283] SIL: Add TypeExpansion to SIL type lowering --- include/swift/SIL/SILType.h | 49 +++-- include/swift/SIL/TypeLowering.h | 206 +++++++++++-------- lib/SIL/SILFunctionType.cpp | 318 +++++++++++++++++++---------- lib/SIL/SILType.cpp | 75 +++---- lib/SIL/TypeLowering.cpp | 329 ++++++++++++++++++++----------- 5 files changed, 628 insertions(+), 349 deletions(-) diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index c933c0977e552..f291c86b5628a 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -244,7 +244,7 @@ class SILType { /// tc.getTypeLowering(type).isAddressOnly(). static bool isAddressOnly(CanType type, Lowering::TypeConverter &tc, CanGenericSignature sig, - ResilienceExpansion expansion); + TypeExpansionContext expansion); /// Return true if this type must be returned indirectly. /// @@ -253,7 +253,7 @@ class SILType { static bool isFormallyReturnedIndirectly(CanType type, Lowering::TypeConverter &tc, CanGenericSignature sig) { - return isAddressOnly(type, tc, sig, ResilienceExpansion::Minimal); + return isAddressOnly(type, tc, sig, TypeExpansionContext::minimal()); } /// Return true if this type must be passed indirectly. @@ -263,7 +263,7 @@ class SILType { static bool isFormallyPassedIndirectly(CanType type, Lowering::TypeConverter &tc, CanGenericSignature sig) { - return isAddressOnly(type, tc, sig, ResilienceExpansion::Minimal); + return isAddressOnly(type, tc, sig, TypeExpansionContext::minimal()); } /// True if the type, or the referenced type of an address type, is loadable. @@ -411,16 +411,20 @@ class SILType { /// the given field. Applies substitutions as necessary. The /// result will be an address type if the base type is an address /// type or a class. - SILType getFieldType(VarDecl *field, Lowering::TypeConverter &TC) const; + SILType getFieldType(VarDecl *field, Lowering::TypeConverter &TC, + TypeExpansionContext context) const; - SILType getFieldType(VarDecl *field, SILModule &M) const; + SILType getFieldType(VarDecl *field, SILModule &M, + TypeExpansionContext context) const; /// Given that this is an enum type, return the lowered type of the /// data for the given element. Applies substitutions as necessary. /// The result will have the same value category as the base type. - SILType getEnumElementType(EnumElementDecl *elt, Lowering::TypeConverter &TC) const; + SILType getEnumElementType(EnumElementDecl *elt, Lowering::TypeConverter &TC, + TypeExpansionContext context) const; - SILType getEnumElementType(EnumElementDecl *elt, SILModule &M) const; + SILType getEnumElementType(EnumElementDecl *elt, SILModule &M, + TypeExpansionContext context) const; /// Given that this is a tuple type, return the lowered type of the /// given tuple element. The result will have the same value @@ -458,11 +462,11 @@ class SILType { /// generic args with the appropriate item from the substitution. /// /// Only call this with function types! - SILType substGenericArgs(Lowering::TypeConverter &TC, - SubstitutionMap SubMap) const; + SILType substGenericArgs(Lowering::TypeConverter &TC, SubstitutionMap SubMap, + TypeExpansionContext context) const; - SILType substGenericArgs(SILModule &M, - SubstitutionMap SubMap) const; + SILType substGenericArgs(SILModule &M, SubstitutionMap SubMap, + TypeExpansionContext context) const; /// If the original type is generic, pass the signature as genericSig. /// @@ -487,8 +491,9 @@ class SILType { bool isHeapObjectReferenceType() const; /// Returns true if this SILType is an aggregate that contains \p Ty - bool aggregateContainsRecord(SILType Ty, SILModule &SILMod) const; - + bool aggregateContainsRecord(SILType Ty, SILModule &SILMod, + TypeExpansionContext context) const; + /// Returns true if this SILType is an aggregate with unreferenceable storage, /// meaning it cannot be fully destructured in SIL. bool aggregateHasUnreferenceableStorage() const; @@ -515,7 +520,8 @@ class SILType { /// Returns true if this SILType could be potentially a lowering of the given /// formal type. Meant for verification purposes/assertions. - bool isLoweringOf(SILModule &M, CanType formalType); + bool isLoweringOf(TypeExpansionContext context, SILModule &M, + CanType formalType); /// Returns the hash code for the SILType. llvm::hash_code getHashCode() const { @@ -589,8 +595,9 @@ NON_SIL_TYPE(LValue) #undef NON_SIL_TYPE CanSILFunctionType getNativeSILFunctionType( - Lowering::TypeConverter &TC, Lowering::AbstractionPattern origType, - CanAnyFunctionType substType, Optional origConstant = None, + Lowering::TypeConverter &TC, TypeExpansionContext context, + Lowering::AbstractionPattern origType, CanAnyFunctionType substType, + Optional origConstant = None, Optional constant = None, Optional reqtSubs = None, ProtocolConformanceRef witnessMethodConformance = ProtocolConformanceRef()); @@ -616,15 +623,15 @@ inline SILType SILField::getObjectType() const { return SILType::getPrimitiveObjectType(getLoweredType()); } -CanType getSILBoxFieldLoweredType(SILBoxType *type, - Lowering::TypeConverter &TC, +CanType getSILBoxFieldLoweredType(TypeExpansionContext context, + SILBoxType *type, Lowering::TypeConverter &TC, unsigned index); -inline SILType getSILBoxFieldType(SILBoxType *type, - Lowering::TypeConverter &TC, +inline SILType getSILBoxFieldType(TypeExpansionContext context, + SILBoxType *type, Lowering::TypeConverter &TC, unsigned index) { return SILType::getPrimitiveAddressType( - getSILBoxFieldLoweredType(type, TC, index)); + getSILBoxFieldLoweredType(context, type, TC, index)); } } // end swift namespace diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 1a856f8af390d..04d9fdca569c5 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -15,6 +15,7 @@ #include "swift/ABI/MetadataValues.h" #include "swift/AST/CaptureInfo.h" +#include "swift/AST/Module.h" #include "swift/SIL/AbstractionPattern.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILInstruction.h" @@ -29,6 +30,35 @@ namespace clang { class Type; } +namespace llvm { +template <> struct DenseMapInfo { + using TypeExpansionContext = swift::TypeExpansionContext; + + static TypeExpansionContext getEmptyKey() { + return TypeExpansionContext( + swift::ResilienceExpansion::Minimal, + reinterpret_cast( + DenseMapInfo::getEmptyKey()), + false); + } + static TypeExpansionContext getTombstoneKey() { + return TypeExpansionContext( + swift::ResilienceExpansion::Minimal, + reinterpret_cast( + DenseMapInfo::getTombstoneKey()), + false); + } + + static unsigned getHashValue(TypeExpansionContext val) { + return DenseMapInfo::getHashValue(val.getHashKey()); + } + + static bool isEqual(TypeExpansionContext LHS, TypeExpansionContext RHS) { + return LHS == RHS; + } +}; +} + namespace swift { class AnyFunctionRef; enum class Bridgeability : unsigned; @@ -227,11 +257,12 @@ class TypeLowering { SILType LoweredType; RecursiveProperties Properties; - unsigned ReferenceCounted : 1; /// The resilience expansion for this type lowering. /// If the type is not resilient at all, this is always Minimal. - unsigned ForExpansion : 1; + TypeExpansionContext expansionContext; + + unsigned ReferenceCounted : 1; /// A single linked list of lowerings for different resilience expansions. /// The first lowering is always for ResilientExpansion::Minimal. @@ -240,10 +271,9 @@ class TypeLowering { protected: TypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) - : LoweredType(type), Properties(properties), - ReferenceCounted(isRefCounted), - ForExpansion(unsigned(forExpansion)) {} + TypeExpansionContext expansionContext) + : LoweredType(type), Properties(properties), + expansionContext(expansionContext), ReferenceCounted(isRefCounted) {} public: TypeLowering(const TypeLowering &) = delete; @@ -257,27 +287,6 @@ class TypeLowering { /// Dump out the internal state of this type lowering to llvm::dbgs(). SWIFT_DEBUG_DUMP; - /// Are r-values of this type passed as arguments indirectly by formal - /// convention? - /// - /// This is independent of whether the SIL argument is address type. - bool isFormallyPassedIndirectly() const { - assert(!isResilient() || - getResilienceExpansion() == ResilienceExpansion::Minimal && - "calling convention uses minimal resilience expansion"); - return isAddressOnly(); - } - - /// Are r-values of this type returned indirectly by formal convention? - /// - /// This is independent of whether the SIL result is address type. - bool isFormallyReturnedIndirectly() const { - assert(!isResilient() || - getResilienceExpansion() == ResilienceExpansion::Minimal && - "calling convention uses minimal resilience expansion"); - return isAddressOnly(); - } - RecursiveProperties getRecursiveProperties() const { return Properties; } @@ -331,7 +340,11 @@ class TypeLowering { } ResilienceExpansion getResilienceExpansion() const { - return ResilienceExpansion(ForExpansion); + return expansionContext.getResilienceExpansion(); + } + + TypeExpansionContext getExpansionContext() const { + return expansionContext; } /// Produce an exact copy of the value in the given address as a @@ -595,12 +608,13 @@ class TypeConverter { CanGenericSignature Sig; AbstractionPattern::CachingKey OrigType; CanType SubstType; + TypeExpansionContext expansionContext; friend bool operator==(const CachingTypeKey &lhs, const CachingTypeKey &rhs) { - return lhs.Sig == rhs.Sig - && lhs.OrigType == rhs.OrigType - && lhs.SubstType == rhs.SubstType; + return lhs.Sig == rhs.Sig && lhs.OrigType == rhs.OrigType && + lhs.SubstType == rhs.SubstType && + lhs.expansionContext == rhs.expansionContext; } friend bool operator!=(const CachingTypeKey &lhs, const CachingTypeKey &rhs) { @@ -616,13 +630,16 @@ class TypeConverter { /// should be used in the lowered type. CanType SubstType; + TypeExpansionContext expansionContext; + CachingTypeKey getCachingKey() const { assert(isCacheable()); return { (OrigType.hasGenericSignature() ? OrigType.getGenericSignature() : nullptr), OrigType.getCachingKey(), - SubstType }; + SubstType, + expansionContext }; } bool isCacheable() const { @@ -634,14 +651,18 @@ class TypeConverter { return IsDependent; return IsNotDependent; } + TypeKey getKeyForMinimalExpansion() const { + return {OrigType, SubstType, TypeExpansionContext::minimal()}; + } }; friend struct llvm::DenseMapInfo; - - TypeKey getTypeKey(AbstractionPattern origTy, CanType substTy) { - return {origTy, substTy}; + + TypeKey getTypeKey(AbstractionPattern origTy, CanType substTy, + TypeExpansionContext context) { + return {origTy, substTy, context}; } - + struct OverrideKey { SILDeclRef derived; SILDeclRef base; @@ -664,7 +685,11 @@ class TypeConverter { const TypeLowering *find(TypeKey k); /// Insert a mapping into the cache. void insert(TypeKey k, const TypeLowering *tl); - +#ifndef NDEBUG + /// Remove the nullptr entry from the type map. + void removeNullEntry(TypeKey k); +#endif + /// Mapping for types independent on contextual generic parameters. llvm::DenseMap IndependentTypes; @@ -685,12 +710,15 @@ class TypeConverter { llvm::SmallVector DependentTypes; - llvm::DenseMap ConstantTypes; - + llvm::DenseMap, SILConstantInfo *> + ConstantTypes; + llvm::DenseMap ConstantOverrideTypes; llvm::DenseMap LoweredCaptures; + llvm::DenseMap opaqueArchetypeFields; + /// Cache of loadable SILType to number of (estimated) fields /// /// Second element is a ResilienceExpansion. @@ -705,12 +733,14 @@ class TypeConverter { const TypeLowering & getTypeLoweringForLoweredType(TypeKey key, - ResilienceExpansion forExpansion); + TypeExpansionContext forExpansion, + bool origHadOpaqueTypeArchetype); const TypeLowering * getTypeLoweringForExpansion(TypeKey key, - ResilienceExpansion forExpansion, - const TypeLowering *lowering); + TypeExpansionContext forExpansion, + const TypeLowering *lowering, + bool origHadOpaqueTypeArchetype); public: ModuleDecl &M; @@ -723,7 +753,7 @@ class TypeConverter { /// Return the CaptureKind to use when capturing a decl. CaptureKind getDeclCaptureKind(CapturedValue capture, - ResilienceExpansion expansion); + TypeExpansionContext expansion); /// Return a most-general-possible abstraction pattern. AbstractionPattern getMostGeneralAbstraction() { @@ -748,7 +778,7 @@ class TypeConverter { static ProtocolDispatchStrategy getProtocolDispatchStrategy(ProtocolDecl *P); /// Count the total number of fields inside the given SIL Type - unsigned countNumberOfFields(SILType Ty, ResilienceExpansion expansion); + unsigned countNumberOfFields(SILType Ty, TypeExpansionContext expansion); /// True if a protocol uses witness tables for dynamic dispatch. static bool protocolRequiresWitnessTable(ProtocolDecl *P) { @@ -774,7 +804,7 @@ class TypeConverter { /// Lowers a Swift type to a SILType, and returns the SIL TypeLowering /// for that type. const TypeLowering & - getTypeLowering(Type t, ResilienceExpansion forExpansion) { + getTypeLowering(Type t, TypeExpansionContext forExpansion) { AbstractionPattern pattern(getCurGenericContext(), t->getCanonicalType()); return getTypeLowering(pattern, t, forExpansion); } @@ -783,28 +813,28 @@ class TypeConverter { /// patterns of the given original type. const TypeLowering &getTypeLowering(AbstractionPattern origType, Type substType, - ResilienceExpansion forExpansion); + TypeExpansionContext forExpansion); /// Returns the SIL TypeLowering for an already lowered SILType. If the /// SILType is an address, returns the TypeLowering for the pointed-to /// type. const TypeLowering & - getTypeLowering(SILType t, ResilienceExpansion forExpansion); + getTypeLowering(SILType t, TypeExpansionContext forExpansion); // Returns the lowered SIL type for a Swift type. - SILType getLoweredType(Type t, ResilienceExpansion forExpansion) { + SILType getLoweredType(Type t, TypeExpansionContext forExpansion) { return getTypeLowering(t, forExpansion).getLoweredType(); } // Returns the lowered SIL type for a Swift type. SILType getLoweredType(AbstractionPattern origType, Type substType, - ResilienceExpansion forExpansion) { + TypeExpansionContext forExpansion) { return getTypeLowering(origType, substType, forExpansion) .getLoweredType(); } SILType getLoweredLoadableType(Type t, - ResilienceExpansion forExpansion, + TypeExpansionContext forExpansion, SILModule &M) { const TypeLowering &ti = getTypeLowering(t, forExpansion); assert( @@ -813,17 +843,13 @@ class TypeConverter { return ti.getLoweredType(); } - CanType getLoweredRValueType(Type t) { - // We're ignoring the category (object vs address), so the resilience - // expansion does not matter. - return getLoweredType(t, ResilienceExpansion::Minimal).getASTType(); + CanType getLoweredRValueType(TypeExpansionContext context, Type t) { + return getLoweredType(t, context).getASTType(); } - CanType getLoweredRValueType(AbstractionPattern origType, Type substType) { - // We're ignoring the category (object vs address), so the resilience - // expansion does not matter. - return getLoweredType(origType, substType, - ResilienceExpansion::Minimal).getASTType(); + CanType getLoweredRValueType(TypeExpansionContext context, + AbstractionPattern origType, Type substType) { + return getLoweredType(origType, substType, context).getASTType(); } AbstractionPattern getAbstractionPattern(AbstractStorageDecl *storage, @@ -836,14 +862,18 @@ class TypeConverter { CanType getLoweredTypeOfGlobal(VarDecl *var); + bool hasOpaqueArchetypePropertiesOrCases(CanType ty); + /// Return the SILFunctionType for a native function value of the /// given type. - CanSILFunctionType getSILFunctionType(AbstractionPattern origType, + CanSILFunctionType getSILFunctionType(TypeExpansionContext context, + AbstractionPattern origType, CanFunctionType substFnType); /// Returns the formal type, lowered AST type, and SILFunctionType /// for a constant reference. - const SILConstantInfo &getConstantInfo(SILDeclRef constant); + const SILConstantInfo &getConstantInfo(TypeExpansionContext context, + SILDeclRef constant); /// Get the generic environment for a constant. GenericSignature getConstantGenericSignature(SILDeclRef constant); @@ -852,26 +882,29 @@ class TypeConverter { GenericEnvironment *getConstantGenericEnvironment(SILDeclRef constant); /// Returns the SIL type of a constant reference. - SILType getConstantType(SILDeclRef constant) { - return getConstantInfo(constant).getSILType(); + SILType getConstantType(TypeExpansionContext context, SILDeclRef constant) { + return getConstantInfo(context, constant).getSILType(); } /// Returns the SILFunctionType for the given declaration. - CanSILFunctionType getConstantFunctionType(SILDeclRef constant) { - return getConstantInfo(constant).SILFnType; + CanSILFunctionType getConstantFunctionType(TypeExpansionContext context, + SILDeclRef constant) { + return getConstantInfo(context, constant).SILFnType; } /// Returns the SILParameterInfo for the given declaration's `self` parameter. /// `constant` must refer to a method. - SILParameterInfo getConstantSelfParameter(SILDeclRef constant); + SILParameterInfo getConstantSelfParameter(TypeExpansionContext context, + SILDeclRef constant); /// Returns the SILFunctionType that must be used to perform a vtable dispatch /// to the given declaration. /// /// Will be the same as getConstantFunctionType() if the declaration does not /// override anything. - CanSILFunctionType getConstantOverrideType(SILDeclRef constant) { - return getConstantOverrideInfo(constant).SILFnType; + CanSILFunctionType getConstantOverrideType(TypeExpansionContext context, + SILDeclRef constant) { + return getConstantOverrideInfo(context, constant).SILFnType; } /// Returns the SILConstantInfo that must be used to perform a vtable dispatch @@ -879,17 +912,19 @@ class TypeConverter { /// /// Will be the same as getConstantInfo() if the declaration does not /// override anything. - const SILConstantInfo &getConstantOverrideInfo(SILDeclRef constant) { + const SILConstantInfo &getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef constant) { // Fast path if the constant does not override anything. auto next = constant.getNextOverriddenVTableEntry(); if (next.isNull()) - return getConstantInfo(constant); + return getConstantInfo(context, constant); auto base = constant.getOverriddenVTableEntry(); - return getConstantOverrideInfo(constant, base); + return getConstantOverrideInfo(context, constant, base); } - const SILConstantInfo &getConstantOverrideInfo(SILDeclRef constant, + const SILConstantInfo &getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef constant, SILDeclRef base); /// Get the empty tuple type as a SILType. @@ -939,7 +974,8 @@ class TypeConverter { /// type of the storage of the value. /// /// \return - always an address type - SILType getSubstitutedStorageType(AbstractStorageDecl *value, + SILType getSubstitutedStorageType(TypeExpansionContext context, + AbstractStorageDecl *value, Type lvalueType); /// Push a generic function context. See GenericContextScope for an RAII @@ -1022,9 +1058,10 @@ class TypeConverter { /// \p constant. The result is not cached as part of the constant's normal /// ConstantInfo. CanSILFunctionType - getUncachedSILFunctionTypeForConstant(SILDeclRef constant, - CanAnyFunctionType origInterfaceType); - + getUncachedSILFunctionTypeForConstant(TypeExpansionContext expansion, + SILDeclRef constant, + CanAnyFunctionType origInterfaceType); + /// Get the boxed interface type to use for a capture of the given decl. CanSILBoxType getInterfaceBoxTypeForCapture(ValueDecl *captured, @@ -1038,11 +1075,13 @@ class TypeConverter { GenericEnvironment *env, bool isMutable); - CanSILBoxType getBoxTypeForEnumElement(SILType enumType, + CanSILBoxType getBoxTypeForEnumElement(TypeExpansionContext context, + SILType enumType, EnumElementDecl *elt); private: - CanType computeLoweredRValueType(AbstractionPattern origType, + CanType computeLoweredRValueType(TypeExpansionContext context, + AbstractionPattern origType, CanType substType); Type getLoweredCBridgedType(AbstractionPattern pattern, Type t, @@ -1103,10 +1142,12 @@ namespace llvm { // Use the second field because the first field can validly be null. static CachingTypeKey getEmptyKey() { - return {nullptr, APCachingKey(), CanTypeInfo::getEmptyKey()}; + return {nullptr, APCachingKey(), CanTypeInfo::getEmptyKey(), + swift::TypeExpansionContext::minimal()}; } static CachingTypeKey getTombstoneKey() { - return {nullptr, APCachingKey(), CanTypeInfo::getTombstoneKey()}; + return {nullptr, APCachingKey(), CanTypeInfo::getTombstoneKey(), + swift::TypeExpansionContext::minimal()}; } static unsigned getHashValue(CachingTypeKey val) { auto hashSig = @@ -1115,7 +1156,10 @@ namespace llvm { CachingKeyInfo::getHashValue(val.OrigType); auto hashSubst = DenseMapInfo::getHashValue(val.SubstType); - return hash_combine(hashSig, hashOrig, hashSubst); + auto hashContext = + DenseMapInfo::getHashValue( + val.expansionContext); + return hash_combine(hashSig, hashOrig, hashSubst, hashContext); } static bool isEqual(CachingTypeKey LHS, CachingTypeKey RHS) { return LHS == RHS; diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index b6399ec306434..67ea7f9ec0428 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -338,10 +338,13 @@ class DestructureResults { TypeConverter &TC; const Conventions &Convs; SmallVectorImpl &Results; + TypeExpansionContext context; + public: - DestructureResults(TypeConverter &TC, const Conventions &conventions, + DestructureResults(TypeExpansionContext context, TypeConverter &TC, + const Conventions &conventions, SmallVectorImpl &results) - : TC(TC), Convs(conventions), Results(results) {} + : TC(TC), Convs(conventions), Results(results), context(context) {} void destructure(AbstractionPattern origType, CanType substType) { // Recurse into tuples. @@ -356,15 +359,19 @@ class DestructureResults { return; } + auto &substResultTLForConvention = TC.getTypeLowering( + origType, substType, TypeExpansionContext::minimal()); auto &substResultTL = TC.getTypeLowering(origType, substType, - ResilienceExpansion::Minimal); + context); + // Determine the result convention. ResultConvention convention; - if (isFormallyReturnedIndirectly(origType, substType, substResultTL)) { + if (isFormallyReturnedIndirectly(origType, substType, + substResultTLForConvention)) { convention = ResultConvention::Indirect; } else { - convention = Convs.getResult(substResultTL); + convention = Convs.getResult(substResultTLForConvention); // Reduce conventions for trivial types to an unowned convention. if (substResultTL.isTrivial()) { @@ -507,6 +514,7 @@ static bool isFormallyPassedIndirectly(TypeConverter &TC, /// /// See the comment in AbstractionPattern.h for details. class DestructureInputs { + TypeExpansionContext expansion; TypeConverter &TC; const Conventions &Convs; const ForeignInfo &Foreign; @@ -514,10 +522,11 @@ class DestructureInputs { SmallVectorImpl &Inputs; unsigned NextOrigParamIndex = 0; public: - DestructureInputs(TypeConverter &TC, const Conventions &conventions, - const ForeignInfo &foreign, + DestructureInputs(TypeExpansionContext expansion, TypeConverter &TC, + const Conventions &conventions, const ForeignInfo &foreign, SmallVectorImpl &inputs) - : TC(TC), Convs(conventions), Foreign(foreign), Inputs(inputs) {} + : expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign), + Inputs(inputs) {} void destructure(AbstractionPattern origType, CanAnyFunctionType::CanParamArrayRef params, @@ -628,20 +637,21 @@ class DestructureInputs { unsigned origParamIndex = NextOrigParamIndex++; - auto &substTL = TC.getTypeLowering(origType, substType, - ResilienceExpansion::Minimal); + auto &substTLConv = TC.getTypeLowering(origType, substType, + TypeExpansionContext::minimal()); + auto &substTL = TC.getTypeLowering(origType, substType, expansion); ParameterConvention convention; if (ownership == ValueOwnership::InOut) { convention = ParameterConvention::Indirect_Inout; - } else if (isFormallyPassedIndirectly(origType, substType, substTL)) { + } else if (isFormallyPassedIndirectly(origType, substType, substTLConv)) { convention = Convs.getIndirect(ownership, forSelf, origParamIndex, - origType, substTL); + origType, substTLConv); assert(isIndirectFormalParameter(convention)); } else if (substTL.isTrivial()) { convention = ParameterConvention::Direct_Unowned; } else { convention = Convs.getDirect(ownership, forSelf, origParamIndex, origType, - substTL); + substTLConv); assert(!isIndirectFormalParameter(convention)); } auto loweredType = substTL.getLoweredType().getASTType(); @@ -665,8 +675,8 @@ class DestructureInputs { NextOrigParamIndex != Foreign.Error->getErrorParameterIndex()) return false; - auto foreignErrorTy = - TC.getLoweredRValueType(Foreign.Error->getErrorParameterType()); + auto foreignErrorTy = TC.getLoweredRValueType( + expansion, Foreign.Error->getErrorParameterType()); // Assume the error parameter doesn't have interesting lowering. Inputs.push_back(SILParameterInfo(foreignErrorTy, @@ -755,7 +765,7 @@ static std::pair updateResultTypeForForeignError( static void lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, CanGenericSignature genericSig, - ResilienceExpansion expansion, + TypeExpansionContext expansion, SmallVectorImpl &inputs) { // NB: The generic signature may be elided from the lowered function type @@ -827,9 +837,14 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, break; } case CaptureKind::Box: { + // The type in the box is lowered in the minimal context. + auto minimalLoweredTy = + TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType, + TypeExpansionContext::minimal()) + .getLoweredType(); // Lvalues are captured as a box that owns the captured value. auto boxTy = TC.getInterfaceBoxTypeForCapture( - VD, loweredTy.getASTType(), + VD, minimalLoweredTy.getASTType(), /*mutable*/ true); auto convention = ParameterConvention::Direct_Guaranteed; auto param = SILParameterInfo(boxTy, convention); @@ -850,6 +865,7 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, } static void destructureYieldsForReadAccessor(TypeConverter &TC, + TypeExpansionContext expansion, AbstractionPattern origType, CanType valueType, SmallVectorImpl &yields) { @@ -859,17 +875,20 @@ static void destructureYieldsForReadAccessor(TypeConverter &TC, for (auto i : indices(valueTupleType.getElementTypes())) { auto origEltType = origType.getTupleElementType(i); auto valueEltType = valueTupleType.getElementType(i); - destructureYieldsForReadAccessor(TC, origEltType, valueEltType, yields); + destructureYieldsForReadAccessor(TC, expansion, origEltType, valueEltType, + yields); } return; } - auto &tl = TC.getTypeLowering(origType, valueType, - ResilienceExpansion::Minimal); + auto &tlConv = + TC.getTypeLowering(origType, valueType, TypeExpansionContext::minimal()); + auto &tl = + TC.getTypeLowering(origType, valueType, expansion); auto convention = [&] { - if (isFormallyPassedIndirectly(TC, origType, valueType, tl)) + if (isFormallyPassedIndirectly(TC, origType, valueType, tlConv)) return ParameterConvention::Indirect_In_Guaranteed; - if (tl.isTrivial()) + if (tlConv.isTrivial()) return ParameterConvention::Direct_Unowned; return ParameterConvention::Direct_Guaranteed; }(); @@ -878,6 +897,7 @@ static void destructureYieldsForReadAccessor(TypeConverter &TC, } static void destructureYieldsForCoroutine(TypeConverter &TC, + TypeExpansionContext expansion, Optional origConstant, Optional constant, Optional reqtSubs, @@ -916,7 +936,8 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, // 'modify' yields an inout of the target type. if (accessor->getAccessorKind() == AccessorKind::Modify) { - auto loweredValueTy = TC.getLoweredRValueType(origType, canValueType); + auto loweredValueTy = + TC.getLoweredRValueType(expansion, origType, canValueType); yields.push_back(SILYieldInfo(loweredValueTy, ParameterConvention::Indirect_Inout)); return; @@ -925,7 +946,8 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, // 'read' yields a borrowed value of the target type, destructuring // tuples as necessary. assert(accessor->getAccessorKind() == AccessorKind::Read); - destructureYieldsForReadAccessor(TC, origType, canValueType, yields); + destructureYieldsForReadAccessor(TC, expansion, origType, canValueType, + yields); } /// Create the appropriate SIL function type for the given formal type @@ -964,7 +986,7 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, /// /// \param conventions - conventions as expressed for the original type static CanSILFunctionType getSILFunctionType( - TypeConverter &TC, AbstractionPattern origType, + TypeConverter &TC, TypeExpansionContext expansionContext, AbstractionPattern origType, CanAnyFunctionType substFnInterfaceType, AnyFunctionType::ExtInfo extInfo, const Conventions &conventions, const ForeignInfo &foreignInfo, Optional origConstant, Optional constant, @@ -1016,14 +1038,15 @@ static CanSILFunctionType getSILFunctionType( // Destructure the result tuple type. SmallVector results; { - DestructureResults destructurer(TC, conventions, results); + DestructureResults destructurer(expansionContext, TC, conventions, results); destructurer.destructure(origResultType, substFormalResultType); } // Destructure the input tuple type. SmallVector inputs; { - DestructureInputs destructurer(TC, conventions, foreignInfo, inputs); + DestructureInputs destructurer(expansionContext, TC, conventions, + foreignInfo, inputs); destructurer.destructure(origType, substFnInterfaceType.getParams(), extInfo); @@ -1032,16 +1055,16 @@ static CanSILFunctionType getSILFunctionType( // Destructure the coroutine yields. SILCoroutineKind coroutineKind = SILCoroutineKind::None; SmallVector yields; - destructureYieldsForCoroutine(TC, origConstant, constant, reqtSubs, - yields, coroutineKind); - + destructureYieldsForCoroutine(TC, expansionContext, origConstant, constant, + reqtSubs, yields, coroutineKind); + // Lower the capture context parameters, if any. if (constant && constant->getAnyFunctionRef()) { - auto expansion = ResilienceExpansion::Maximal; + auto expansion = TypeExpansionContext::maximal( + expansionContext.getContext(), expansionContext.isWholeModuleContext()); if (constant->isSerialized()) - expansion = ResilienceExpansion::Minimal; - lowerCaptureContextParameters(TC, *constant, genericSig, expansion, - inputs); + expansion = TypeExpansionContext::minimal(); + lowerCaptureContextParameters(TC, *constant, genericSig, expansion, inputs); } auto calleeConvention = ParameterConvention::Direct_Unowned; @@ -1434,7 +1457,7 @@ getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, Optional constant); static CanSILFunctionType getNativeSILFunctionType( - TypeConverter &TC, AbstractionPattern origType, + TypeConverter &TC, TypeExpansionContext context, AbstractionPattern origType, CanAnyFunctionType substInterfaceType, AnyFunctionType::ExtInfo extInfo, Optional origConstant, Optional constant, Optional reqtSubs, @@ -1456,23 +1479,23 @@ static CanSILFunctionType getNativeSILFunctionType( switch (constant ? constant->kind : SILDeclRef::Kind::Func) { case SILDeclRef::Kind::Initializer: case SILDeclRef::Kind::EnumElement: - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DefaultInitializerConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultInitializerConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, witnessMethodConformance); case SILDeclRef::Kind::Allocator: - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DefaultAllocatorConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultAllocatorConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, witnessMethodConformance); case SILDeclRef::Kind::Func: // If we have a setter, use the special setter convention. This ensures // that we take normal parameters at +1. if (constant && constant->isSetter()) { - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DefaultSetterConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, - witnessMethodConformance); + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultSetterConventions(), + ForeignInfo(), origConstant, constant, + reqtSubs, witnessMethodConformance); } LLVM_FALLTHROUGH; case SILDeclRef::Kind::Destroyer: @@ -1483,14 +1506,14 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: { auto conv = DefaultConventions(NormalParameterConvention::Guaranteed); - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, conv, - ForeignInfo(), origConstant, constant, reqtSubs, - witnessMethodConformance); + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, conv, ForeignInfo(), origConstant, + constant, reqtSubs, witnessMethodConformance); } case SILDeclRef::Kind::Deallocator: - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DeallocatorConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DeallocatorConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, witnessMethodConformance); } } @@ -1500,9 +1523,10 @@ static CanSILFunctionType getNativeSILFunctionType( } CanSILFunctionType swift::getNativeSILFunctionType( - TypeConverter &TC, AbstractionPattern origType, - CanAnyFunctionType substType, Optional origConstant, - Optional substConstant, Optional reqtSubs, + TypeConverter &TC, TypeExpansionContext context, + AbstractionPattern origType, CanAnyFunctionType substType, + Optional origConstant, Optional substConstant, + Optional reqtSubs, ProtocolConformanceRef witnessMethodConformance) { AnyFunctionType::ExtInfo extInfo; @@ -1515,7 +1539,7 @@ CanSILFunctionType swift::getNativeSILFunctionType( extInfo = substType->getExtInfo(); } - return ::getNativeSILFunctionType(TC, origType, substType, extInfo, + return ::getNativeSILFunctionType(TC, context, origType, substType, extInfo, origConstant, substConstant, reqtSubs, witnessMethodConformance); } @@ -1862,7 +1886,8 @@ getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, if (auto method = dyn_cast(clangDecl)) { auto origPattern = AbstractionPattern::getObjCMethod(origType, method, foreignInfo.Error); - return getSILFunctionType(TC, origPattern, substInterfaceType, extInfo, + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, ObjCMethodConventions(method), foreignInfo, constant, constant, None, ProtocolConformanceRef()); @@ -1872,9 +1897,10 @@ getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, AbstractionPattern origPattern = AbstractionPattern::getCXXMethod(origType, method); auto conventions = CXXMethodConventions(method); - return getSILFunctionType(TC, origPattern, substInterfaceType, extInfo, - conventions, foreignInfo, constant, constant, - None, ProtocolConformanceRef()); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, conventions, + foreignInfo, constant, constant, None, + ProtocolConformanceRef()); } if (auto func = dyn_cast(clangDecl)) { @@ -1884,7 +1910,8 @@ getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, ? AbstractionPattern::getCFunctionAsMethod(origType, clangType, foreignInfo.Self) : AbstractionPattern(origType, clangType); - return getSILFunctionType(TC, origPattern, substInterfaceType, extInfo, + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, CFunctionConventions(func), foreignInfo, constant, constant, None, ProtocolConformanceRef()); } @@ -1914,15 +1941,17 @@ getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, } if (fnType) { return getSILFunctionType( - TC, origType, substType, extInfo, CFunctionTypeConventions(fnType), - ForeignInfo(), constant, constant, None, ProtocolConformanceRef()); + TC, TypeExpansionContext::minimal(), origType, substType, extInfo, + CFunctionTypeConventions(fnType), ForeignInfo(), constant, constant, + None, ProtocolConformanceRef()); } } // TODO: Ought to support captures in block funcs. - return getSILFunctionType(TC, origType, substType, extInfo, - DefaultBlockConventions(), ForeignInfo(), constant, - constant, None, ProtocolConformanceRef()); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origType, + substType, extInfo, DefaultBlockConventions(), + ForeignInfo(), constant, constant, None, + ProtocolConformanceRef()); } /// Try to find a clang method declaration for the given function. @@ -2087,8 +2116,9 @@ getSILFunctionTypeForObjCSelectorFamily(TypeConverter &TC, ObjCSelectorFamily fa const ForeignInfo &foreignInfo, Optional constant) { return getSILFunctionType( - TC, AbstractionPattern(origType), substInterfaceType, extInfo, - ObjCSelectorFamilyConventions(family), foreignInfo, constant, constant, + TC, TypeExpansionContext::minimal(), AbstractionPattern(origType), + substInterfaceType, extInfo, ObjCSelectorFamilyConventions(family), + foreignInfo, constant, constant, /*requirement subs*/ None, ProtocolConformanceRef()); } @@ -2110,10 +2140,9 @@ static bool isImporterGeneratedAccessor(const clang::Decl *clangDecl, return true; } -static CanSILFunctionType -getUncachedSILFunctionTypeForConstant(TypeConverter &TC, - SILDeclRef constant, - CanAnyFunctionType origLoweredInterfaceType) { +static CanSILFunctionType getUncachedSILFunctionTypeForConstant( + TypeConverter &TC, TypeExpansionContext context, SILDeclRef constant, + CanAnyFunctionType origLoweredInterfaceType) { assert(origLoweredInterfaceType->getExtInfo().getSILRepresentation() != SILFunctionTypeRepresentation::Thick && origLoweredInterfaceType->getExtInfo().getSILRepresentation() @@ -2131,9 +2160,9 @@ getUncachedSILFunctionTypeForConstant(TypeConverter &TC, } return ::getNativeSILFunctionType( - TC, AbstractionPattern(origLoweredInterfaceType), - origLoweredInterfaceType, extInfo, - constant, constant, None, witnessMethodConformance); + TC, context, AbstractionPattern(origLoweredInterfaceType), + origLoweredInterfaceType, extInfo, constant, constant, None, + witnessMethodConformance); } ForeignInfo foreignInfo; @@ -2173,12 +2202,12 @@ getUncachedSILFunctionTypeForConstant(TypeConverter &TC, extInfo, foreignInfo, constant); } -CanSILFunctionType TypeConverter:: -getUncachedSILFunctionTypeForConstant(SILDeclRef constant, - CanAnyFunctionType origInterfaceType) { +CanSILFunctionType TypeConverter::getUncachedSILFunctionTypeForConstant( + TypeExpansionContext context, SILDeclRef constant, + CanAnyFunctionType origInterfaceType) { auto origLoweredInterfaceType = getLoweredFormalTypes(constant, origInterfaceType).Uncurried; - return ::getUncachedSILFunctionTypeForConstant(*this, constant, + return ::getUncachedSILFunctionTypeForConstant(*this, context, constant, origLoweredInterfaceType); } @@ -2258,9 +2287,11 @@ static llvm::cl::opt DisableConstantInfoCache("sil-disable-typelowering-constantinfo-cache", llvm::cl::init(false)); -const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { +const SILConstantInfo & +TypeConverter::getConstantInfo(TypeExpansionContext expansion, + SILDeclRef constant) { if (!DisableConstantInfoCache) { - auto found = ConstantTypes.find(constant); + auto found = ConstantTypes.find(std::make_pair(expansion, constant)); if (found != ConstantTypes.end()) return *found->second; } @@ -2281,7 +2312,7 @@ const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { // The SIL type encodes conventions according to the original type. CanSILFunctionType silFnType = - ::getUncachedSILFunctionTypeForConstant(*this, constant, + ::getUncachedSILFunctionTypeForConstant(*this, expansion, constant, loweredInterfaceType); LLVM_DEBUG(llvm::dbgs() << "lowering type for constant "; @@ -2304,7 +2335,8 @@ const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { if (DisableConstantInfoCache) return *result; - auto inserted = ConstantTypes.insert({constant, result}); + auto inserted = + ConstantTypes.insert({std::make_pair(expansion, constant), result}); assert(inserted.second); (void)inserted; return *result; @@ -2312,8 +2344,10 @@ const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { /// Returns the SILParameterInfo for the given declaration's `self` parameter. /// `constant` must refer to a method. -SILParameterInfo TypeConverter::getConstantSelfParameter(SILDeclRef constant) { - auto ty = getConstantFunctionType(constant); +SILParameterInfo +TypeConverter::getConstantSelfParameter(TypeExpansionContext context, + SILDeclRef constant) { + auto ty = getConstantFunctionType(context, constant); // In most cases the "self" parameter is lowered as the back parameter. // The exception is C functions imported as methods. @@ -2417,10 +2451,11 @@ static CanType copyOptionalityFromDerivedToBase(TypeConverter &tc, /// Returns the ConstantInfo corresponding to the VTable thunk for overriding. /// Will be the same as getConstantInfo if the declaration does not override. const SILConstantInfo & -TypeConverter::getConstantOverrideInfo(SILDeclRef derived, SILDeclRef base) { +TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef derived, SILDeclRef base) { // Foreign overrides currently don't need reabstraction. if (derived.isForeign) - return getConstantInfo(derived); + return getConstantInfo(context, derived); auto found = ConstantOverrideTypes.find({derived, base}); if (found != ConstantOverrideTypes.end()) @@ -2428,8 +2463,8 @@ TypeConverter::getConstantOverrideInfo(SILDeclRef derived, SILDeclRef base) { assert(base.requiresNewVTableEntry() && "base must not be an override"); - auto baseInfo = getConstantInfo(base); - auto derivedInfo = getConstantInfo(derived); + auto baseInfo = getConstantInfo(context, base); + auto derivedInfo = getConstantInfo(context, derived); // If the derived method is ABI-compatible with the base method, give the // vtable thunk the same signature as the derived method. @@ -2476,7 +2511,7 @@ TypeConverter::getConstantOverrideInfo(SILDeclRef derived, SILDeclRef base) { // Build the SILFunctionType for the vtable thunk. CanSILFunctionType fnTy = getNativeSILFunctionType( - *this, basePattern, overrideLoweredInterfaceTy, base, derived, + *this, context, basePattern, overrideLoweredInterfaceTy, base, derived, /*reqt subs*/ None, ProtocolConformanceRef()); // Build the SILConstantInfo and cache it. @@ -2509,10 +2544,13 @@ class SILTypeSubstituter : // context signature. CanGenericSignature Sig; + TypeExpansionContext typeExpansionContext; + bool shouldSubstituteOpaqueArchetypes; public: SILTypeSubstituter(TypeConverter &TC, + TypeExpansionContext context, TypeSubstitutionFn Subst, LookupConformanceFn Conformances, CanGenericSignature Sig, @@ -2521,6 +2559,7 @@ class SILTypeSubstituter : Subst(Subst), Conformances(Conformances), Sig(Sig), + typeExpansionContext(context), shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) {} @@ -2584,13 +2623,31 @@ class SILTypeSubstituter : } witnessMethodConformance = conformance.subst(selfType, Subst, Conformances); + + // Substitute the underlying conformance of opaque type archetypes if we + // should look through opaque archetypes. + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + SubstOptions substOptions(None); + auto substType = selfType.subst(Subst, Conformances, substOptions) + ->getCanonicalType(); + if (substType->hasOpaqueArchetype()) { + witnessMethodConformance = substOpaqueTypesWithUnderlyingTypes( + witnessMethodConformance, substType, typeExpansionContext); + } + } } // The substituted type is no longer generic, so it'd never be // pseudogeneric. - auto extInfo = origType->getExtInfo().withIsPseudogeneric(false); + auto extInfo = origType->getExtInfo(); + if (!shouldSubstituteOpaqueArchetypes) + extInfo = extInfo.withIsPseudogeneric(false); - return SILFunctionType::get(nullptr, extInfo, + auto genericSig = shouldSubstituteOpaqueArchetypes + ? origType->getSubstGenericSignature() + : nullptr; + + return SILFunctionType::get(genericSig, extInfo, origType->getCoroutineKind(), origType->getCalleeConvention(), substParams, substYields, substResults, substErrorResult, @@ -2670,7 +2727,14 @@ class SILTypeSubstituter : } AbstractionPattern abstraction(Sig, origType); - return TC.getLoweredRValueType(abstraction, substType); + // If we looked through an opaque archetype to a function type we need to + // use the function type's abstraction. + if (isa(origType) && + isa(substType)) + abstraction = AbstractionPattern(Sig, substType); + + return TC.getLoweredRValueType(typeExpansionContext, abstraction, + substType); } }; @@ -2687,8 +2751,9 @@ SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, if (!genericSig) genericSig = tc.getCurGenericContext(); - SILTypeSubstituter STST(tc, subs, conformances, - genericSig, shouldSubstituteOpaqueArchetypes); + SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, + conformances, genericSig, + shouldSubstituteOpaqueArchetypes); return STST.subst(*this); } @@ -2712,8 +2777,8 @@ SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ /// it has the form of the normal SILFunctionType for the substituted /// type, except using the original conventions. CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, - SubstitutionMap subs) { +SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, + TypeExpansionContext context) { if (!isPolymorphic()) { return CanSILFunctionType(this); } @@ -2724,20 +2789,49 @@ SILFunctionType::substGenericArgs(SILModule &silModule, return substGenericArgs(silModule, QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs)); + LookUpConformanceInSubstitutionMap(subs), + context); } CanSILFunctionType SILFunctionType::substGenericArgs(SILModule &silModule, TypeSubstitutionFn subs, - LookupConformanceFn conformances) { + LookupConformanceFn conformances, + TypeExpansionContext context) { if (!isPolymorphic()) return CanSILFunctionType(this); - SILTypeSubstituter substituter(silModule.Types, subs, conformances, + SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, getSubstGenericSignature(), /*shouldSubstituteOpaqueTypes*/ false); return substituter.substSILFunctionType(CanSILFunctionType(this)); } +CanSILFunctionType +SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, + TypeExpansionContext context) { + if (!hasOpaqueArchetype() || + !context.shouldLookThroughOpaqueTypeArchetypes()) + return CanSILFunctionType(this); + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + + assert(!(getSubstGenericSignature() && TC.getCurGenericContext())); + + auto genericSig = getSubstGenericSignature(); + GenericContextScope genericContext(TC, genericSig); + if (!genericSig) { + genericSig = TC.getCurGenericContext(); + } + + SILTypeSubstituter substituter(TC, context, replacer, replacer, genericSig, + /*shouldSubstituteOpaqueTypes*/ true); + auto resTy = + substituter.substSILFunctionType(CanSILFunctionType(this)); + + return resTy; +} + /// Fast path for bridging types in a function type without uncurrying. CanAnyFunctionType TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, @@ -3009,7 +3103,8 @@ static bool areABICompatibleParamsOrReturns(SILType a, SILType b, if (!dc || !dc->isChildContextOf(currentModule)) dc = currentModule; ReplaceOpaqueTypesWithUnderlyingTypes replacer( - dc, inFunction->getResilienceExpansion()); + dc, inFunction->getResilienceExpansion(), + inFunction->getModule().isWholeModule()); if (aa.getASTType()->hasOpaqueArchetype()) opaqueTypesSubsituted = aa.subst(inFunction->getModule(), replacer, replacer, CanGenericSignature(), true); @@ -3205,3 +3300,26 @@ SILFunctionType::withSubstitutions(SubstitutionMap subs) const { subs, isGenericSignatureImplied(), const_cast(this)->getASTContext()); } + +static DeclContext *getDeclContextForExpansion(const SILFunction &f) { + auto *dc = f.getDeclContext(); + if (!dc) + dc = f.getModule().getSwiftModule(); + auto *currentModule = f.getModule().getSwiftModule(); + if (!dc || !dc->isChildContextOf(currentModule)) + dc = currentModule; + return dc; +} + +TypeExpansionContext::TypeExpansionContext(const SILFunction &f) + : expansion(f.getResilienceExpansion()), + inContext(getDeclContextForExpansion(f)), + isContextWholeModule(f.getModule().isWholeModule()) {} + +CanSILFunctionType SILFunction::getLoweredFunctionTypeInContext( + TypeExpansionContext context) const { + auto origFunTy = getLoweredFunctionType(); + auto &M = getModule(); + auto funTy = M.Types.getLoweredType(origFunTy , context); + return cast(funTy.getASTType()); +} diff --git a/lib/SIL/SILType.cpp b/lib/SIL/SILType.cpp index 3b89dbc7852e9..60eceb46c333b 100644 --- a/lib/SIL/SILType.cpp +++ b/lib/SIL/SILType.cpp @@ -86,7 +86,7 @@ bool SILType::isTrivial(const SILFunction &F) const { bool SILType::isReferenceCounted(SILModule &M) const { return M.Types.getTypeLowering(*this, - ResilienceExpansion::Minimal) + TypeExpansionContext::minimal()) .isReferenceCounted(); } @@ -133,8 +133,8 @@ bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) { && toTy.isHeapObjectReferenceType(); } -SILType SILType::getFieldType(VarDecl *field, - TypeConverter &TC) const { +SILType SILType::getFieldType(VarDecl *field, TypeConverter &TC, + TypeExpansionContext context) const { AbstractionPattern origFieldTy = TC.getAbstractionPattern(field); CanType substFieldTy; if (field->hasClangNode()) { @@ -144,7 +144,8 @@ SILType SILType::getFieldType(VarDecl *field, getASTType()->getTypeOfMember(&TC.M, field, nullptr)->getCanonicalType(); } - auto loweredTy = TC.getLoweredRValueType(origFieldTy, substFieldTy); + auto loweredTy = + TC.getLoweredRValueType(context, origFieldTy, substFieldTy); if (isAddress() || getClassOrBoundGenericClass() != nullptr) { return SILType::getPrimitiveAddressType(loweredTy); } else { @@ -152,12 +153,13 @@ SILType SILType::getFieldType(VarDecl *field, } } -SILType SILType::getFieldType(VarDecl *field, SILModule &M) const { - return getFieldType(field, M.Types); +SILType SILType::getFieldType(VarDecl *field, SILModule &M, + TypeExpansionContext context) const { + return getFieldType(field, M.Types, context); } -SILType SILType::getEnumElementType(EnumElementDecl *elt, - TypeConverter &TC) const { +SILType SILType::getEnumElementType(EnumElementDecl *elt, TypeConverter &TC, + TypeExpansionContext context) const { assert(elt->getDeclContext() == getEnumOrBoundGenericEnum()); assert(elt->hasAssociatedValues()); @@ -168,7 +170,7 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, // If the case is indirect, then the payload is boxed. if (elt->isIndirect() || elt->getParentEnum()->isIndirect()) { - auto box = TC.getBoxTypeForEnumElement(*this, elt); + auto box = TC.getBoxTypeForEnumElement(context, *this, elt); return SILType(SILType::getPrimitiveObjectType(box).getASTType(), getCategory()); } @@ -176,15 +178,15 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, auto substEltTy = getASTType()->getTypeOfMember(&TC.M, elt, elt->getArgumentInterfaceType()); - auto loweredTy = - TC.getLoweredRValueType(TC.getAbstractionPattern(elt), - substEltTy); + auto loweredTy = TC.getLoweredRValueType( + context, TC.getAbstractionPattern(elt), substEltTy); return SILType(loweredTy, getCategory()); } -SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M) const { - return getEnumElementType(elt, M.Types); +SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M, + TypeExpansionContext context) const { + return getEnumElementType(elt, M.Types, context); } bool SILType::isLoadableOrOpaque(const SILFunction &F) const { @@ -196,10 +198,10 @@ bool SILType::isAddressOnly(const SILFunction &F) const { return F.getTypeLowering(*this).isAddressOnly(); } -SILType SILType::substGenericArgs(SILModule &M, - SubstitutionMap SubMap) const { +SILType SILType::substGenericArgs(SILModule &M, SubstitutionMap SubMap, + TypeExpansionContext context) const { auto fnTy = castTo(); - auto canFnTy = CanSILFunctionType(fnTy->substGenericArgs(M, SubMap)); + auto canFnTy = CanSILFunctionType(fnTy->substGenericArgs(M, SubMap, context)); return SILType::getPrimitiveObjectType(canFnTy); } @@ -217,7 +219,8 @@ bool SILType::isHeapObjectReferenceType() const { return false; } -bool SILType::aggregateContainsRecord(SILType Record, SILModule &Mod) const { +bool SILType::aggregateContainsRecord(SILType Record, SILModule &Mod, + TypeExpansionContext context) const { assert(!hasArchetype() && "Agg should be proven to not be generic " "before passed to this function."); assert(!Record.hasArchetype() && "Record should be proven to not be generic " @@ -246,14 +249,14 @@ bool SILType::aggregateContainsRecord(SILType Record, SILModule &Mod) const { if (EnumDecl *E = Ty.getEnumOrBoundGenericEnum()) { for (auto Elt : E->getAllElements()) if (Elt->hasAssociatedValues()) - Worklist.push_back(Ty.getEnumElementType(Elt, Mod)); + Worklist.push_back(Ty.getEnumElementType(Elt, Mod, context)); continue; } // Then if we have a struct address... if (StructDecl *S = Ty.getStructOrBoundGenericStruct()) for (VarDecl *Var : S->getStoredProperties()) - Worklist.push_back(Ty.getFieldType(Var, Mod)); + Worklist.push_back(Ty.getFieldType(Var, Mod, context)); // If we have a class address, it is a pointer so it cannot contain other // types. @@ -387,21 +390,22 @@ SILType SILType::mapTypeOutOfContext() const { getCategory()); } -CanType -swift::getSILBoxFieldLoweredType(SILBoxType *type, TypeConverter &TC, - unsigned index) { +CanType swift::getSILBoxFieldLoweredType(TypeExpansionContext context, + SILBoxType *type, TypeConverter &TC, + unsigned index) { auto fieldTy = type->getLayout()->getFields()[index].getLoweredType(); // Apply generic arguments if the layout is generic. if (auto subMap = type->getSubstitutions()) { auto sig = type->getLayout()->getGenericSignature(); - return SILType::getPrimitiveObjectType(fieldTy) + fieldTy = SILType::getPrimitiveObjectType(fieldTy) .subst(TC, QuerySubstitutionMap{subMap}, LookUpConformanceInSubstitutionMap(subMap), sig) .getASTType(); } + fieldTy = TC.getLoweredType(fieldTy, context).getASTType(); return fieldTy; } @@ -440,9 +444,8 @@ SILModuleConventions::SILModuleConventions(SILModule &M) bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type, SILModule &M) { if (SILModuleConventions(M).loweredAddresses) { - return M.Types.getTypeLowering(type, - ResilienceExpansion::Minimal) - .isAddressOnly(); + return M.Types.getTypeLowering(type, TypeExpansionContext::minimal()) + .isAddressOnly(); } return false; @@ -450,9 +453,8 @@ bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type, bool SILModuleConventions::isPassedIndirectlyInSIL(SILType type, SILModule &M) { if (SILModuleConventions(M).loweredAddresses) { - return M.Types.getTypeLowering(type, - ResilienceExpansion::Minimal) - .isAddressOnly(); + return M.Types.getTypeLowering(type, TypeExpansionContext::minimal()) + .isAddressOnly(); } return false; @@ -546,8 +548,14 @@ bool SILType::hasAbstractionDifference(SILFunctionTypeRepresentation rep, return (*this != type2); } -bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { +bool SILType::isLoweringOf(TypeExpansionContext context, SILModule &Mod, + CanType formalType) { SILType loweredType = *this; + if (formalType->hasOpaqueArchetype() && + context.shouldLookThroughOpaqueTypeArchetypes() && + loweredType.getASTType() == + Mod.Types.getLoweredRValueType(context, formalType)) + return true; // Optional lowers its contained type. SILType loweredObjectType = loweredType.getOptionalObjectType(); @@ -555,7 +563,7 @@ bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { if (loweredObjectType) { return formalObjectType && - loweredObjectType.isLoweringOf(Mod, formalObjectType); + loweredObjectType.isLoweringOf(context, Mod, formalObjectType); } // Metatypes preserve their instance type through lowering. @@ -586,7 +594,8 @@ bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { for (unsigned i = 0, e = loweredTT->getNumElements(); i < e; ++i) { auto loweredTTEltType = SILType::getPrimitiveAddressType(loweredTT.getElementType(i)); - if (!loweredTTEltType.isLoweringOf(Mod, formalTT.getElementType(i))) + if (!loweredTTEltType.isLoweringOf(context, Mod, + formalTT.getElementType(i))) return false; } return true; diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 16b373c9e6447..18c07ce960026 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -92,7 +92,7 @@ static bool hasSingletonMetatype(CanType instanceType) { } CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture, - ResilienceExpansion expansion) { + TypeExpansionContext expansion) { auto decl = capture.getDecl(); auto *var = cast(decl); assert(var->hasStorage() && @@ -103,7 +103,11 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture, // by its address (like a var) instead. if (!var->supportsMutation() && (Context.LangOpts.EnableSILOpaqueValues || - !getTypeLowering(var->getType(), expansion).isAddressOnly())) + !getTypeLowering( + var->getType(), + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( + expansion.getResilienceExpansion())) + .isAddressOnly())) return CaptureKind::Constant; // In-out parameters are captured by address. @@ -132,7 +136,7 @@ using RecursiveProperties = TypeLowering::RecursiveProperties; static RecursiveProperties classifyType(CanType type, TypeConverter &TC, CanGenericSignature sig, - ResilienceExpansion expansion); + TypeExpansionContext expansion); namespace { /// A CRTP helper class for doing things that depends on type @@ -143,9 +147,9 @@ namespace { protected: TypeConverter &TC; CanGenericSignature Sig; - ResilienceExpansion Expansion; + TypeExpansionContext Expansion; TypeClassifierBase(TypeConverter &TC, CanGenericSignature Sig, - ResilienceExpansion Expansion) + TypeExpansionContext Expansion) : TC(TC), Sig(Sig), Expansion(Expansion) {} public: @@ -326,7 +330,7 @@ namespace { auto referentType = type->getReferentType(); \ auto concreteType = getConcreteReferenceStorageReferent(referentType); \ if (Name##StorageType::get(concreteType, TC.Context) \ - ->isLoadable(Expansion)) { \ + ->isLoadable(Expansion.getResilienceExpansion())) { \ return asImpl().visitLoadable##Name##StorageType(type); \ } else { \ return asImpl().visitAddressOnly##Name##StorageType(type); \ @@ -338,6 +342,13 @@ namespace { } #include "swift/AST/ReferenceStorage.def" + RetTy visitOpaqueTypeArchetypeType(CanOpaqueTypeArchetypeType ty) { + auto replacedTy = substOpaqueTypesWithUnderlyingTypes(ty, Expansion); + if (replacedTy == ty) + return visitArchetypeType(ty); + return this->visit(replacedTy); + } + RetTy visitArchetypeType(CanArchetypeType type) { if (type->requiresClass()) { return asImpl().handleReference(type); @@ -448,7 +459,7 @@ namespace { public TypeClassifierBase { public: TypeClassifier(TypeConverter &TC, CanGenericSignature Sig, - ResilienceExpansion Expansion) + TypeExpansionContext Expansion) : TypeClassifierBase(TC, Sig, Expansion) {} RecursiveProperties handle(CanType type, RecursiveProperties properties) { @@ -502,7 +513,7 @@ namespace { static RecursiveProperties classifyType(CanType type, TypeConverter &tc, CanGenericSignature sig, - ResilienceExpansion expansion) { + TypeExpansionContext expansion) { return TypeClassifier(tc, sig, expansion).visit(type); } @@ -511,7 +522,7 @@ static RecursiveProperties classifyType(CanType type, TypeConverter &tc, /// something of unknown size. bool SILType::isAddressOnly(CanType type, TypeConverter &tc, CanGenericSignature sig, - ResilienceExpansion expansion) { + TypeExpansionContext expansion) { return classifyType(type, tc, sig, expansion).isAddressOnly(); } @@ -523,7 +534,7 @@ namespace { protected: LoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : TypeLowering(type, properties, isRefCounted, forExpansion) {} public: @@ -550,7 +561,7 @@ namespace { class TrivialTypeLowering final : public LoadableTypeLowering { public: TrivialTypeLowering(SILType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableTypeLowering(type, properties, IsNotReferenceCounted, forExpansion) { assert(properties.isFixedABI()); @@ -620,7 +631,7 @@ namespace { NonTrivialLoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableTypeLowering(type, properties, isRefCounted, forExpansion) { assert(!properties.isTrivial()); } @@ -713,7 +724,7 @@ namespace { public: LoadableAggTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), properties, IsNotReferenceCounted, forExpansion) { @@ -832,7 +843,7 @@ namespace { : public LoadableAggTypeLowering { public: LoadableTupleTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableAggTypeLowering(type, properties, forExpansion) {} SILValue emitRValueProject(SILBuilder &B, SILLocation loc, @@ -857,7 +868,7 @@ namespace { unsigned index = 0; for (auto elt : tupleTy.getElementTypes()) { auto silElt = SILType::getPrimitiveType(elt, silTy.getCategory()); - auto &eltTL = TC.getTypeLowering(silElt, getResilienceExpansion()); + auto &eltTL = TC.getTypeLowering(silElt, getExpansionContext()); children.push_back(Child{index, eltTL}); ++index; } @@ -869,7 +880,7 @@ namespace { : public LoadableAggTypeLowering { public: LoadableStructTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableAggTypeLowering(type, properties, forExpansion) {} SILValue emitRValueProject(SILBuilder &B, SILLocation loc, @@ -892,8 +903,8 @@ namespace { assert(structDecl); for (auto prop : structDecl->getStoredProperties()) { - SILType propTy = silTy.getFieldType(prop, TC); - auto &propTL = TC.getTypeLowering(propTy, getResilienceExpansion()); + SILType propTy = silTy.getFieldType(prop, TC, getExpansionContext()); + auto &propTL = TC.getTypeLowering(propTy, getExpansionContext()); children.push_back(Child{prop, propTL}); } } @@ -903,7 +914,7 @@ namespace { class LoadableEnumTypeLowering final : public NonTrivialLoadableTypeLowering { public: LoadableEnumTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), properties, IsNotReferenceCounted, @@ -946,7 +957,7 @@ namespace { public: LeafLoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : NonTrivialLoadableTypeLowering(type, properties, isRefCounted, forExpansion) {} @@ -966,7 +977,7 @@ namespace { /// loadable. class ReferenceTypeLowering : public LeafLoadableTypeLowering { public: - ReferenceTypeLowering(SILType type, ResilienceExpansion forExpansion) + ReferenceTypeLowering(SILType type, TypeExpansionContext forExpansion) : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), IsReferenceCounted, forExpansion) {} @@ -998,7 +1009,7 @@ namespace { class Loadable##Name##TypeLowering final : public LeafLoadableTypeLowering { \ public: \ Loadable##Name##TypeLowering(SILType type, \ - ResilienceExpansion forExpansion) \ + TypeExpansionContext forExpansion) \ : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), \ IsReferenceCounted, \ forExpansion) {} \ @@ -1024,7 +1035,7 @@ namespace { class AddressOnlyTypeLowering : public TypeLowering { public: AddressOnlyTypeLowering(SILType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : TypeLowering(type, properties, IsNotReferenceCounted, forExpansion) { assert(properties.isAddressOnly()); @@ -1096,7 +1107,7 @@ namespace { class UnsafeValueBufferTypeLowering : public AddressOnlyTypeLowering { public: UnsafeValueBufferTypeLowering(SILType type, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : AddressOnlyTypeLowering(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, IsNotResilient}, @@ -1127,7 +1138,7 @@ namespace { class OpaqueValueTypeLowering : public LeafLoadableTypeLowering { public: OpaqueValueTypeLowering(SILType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LeafLoadableTypeLowering(type, properties, IsNotReferenceCounted, forExpansion) {} @@ -1169,7 +1180,7 @@ namespace { IsDependent_t Dependent; public: LowerType(TypeConverter &TC, CanGenericSignature Sig, - ResilienceExpansion Expansion, IsDependent_t Dependent) + TypeExpansionContext Expansion, IsDependent_t Dependent) : TypeClassifierBase(TC, Sig, Expansion), Dependent(Dependent) {} TypeLowering *handleTrivial(CanType type) { @@ -1249,7 +1260,8 @@ namespace { // Note: if the type is in a different module, the lowering does // not depend on the resilience expansion, so we do not need to set // the isResilent() flag above. - if (!sameModule || Expansion == ResilienceExpansion::Minimal) { + if (!sameModule || Expansion.getResilienceExpansion() == + ResilienceExpansion::Minimal) { properties.addSubobject(RecursiveProperties::forOpaque()); return true; } @@ -1379,10 +1391,34 @@ const TypeLowering *TypeConverter::find(TypeKey k) { if (found == types->end()) return nullptr; - assert(found->second && "type recursion not caught in Sema"); + assert((found->second || k.expansionContext.isMinimal()) && + "type recursion not caught in Sema"); return found->second; } +#ifndef NDEBUG +void TypeConverter::removeNullEntry(TypeKey k) { + if (!k.isCacheable()) + return; + + auto ck = k.getCachingKey(); + + llvm::DenseMap *types; + if (k.isDependent()) { + auto &state = DependentTypes.back(); + types = &state.Map; + } else { + types = &IndependentTypes; + } + + auto found = types->find(ck); + if (found == types->end() || found->second != nullptr) + return; + + types->erase(ck); +} +#endif + void TypeConverter::insert(TypeKey k, const TypeLowering *tl) { if (!k.isCacheable()) return; @@ -1393,13 +1429,13 @@ void TypeConverter::insert(TypeKey k, const TypeLowering *tl) { } else { types = &IndependentTypes; } - (*types)[k.getCachingKey()] = tl; } /// Lower each of the elements of the substituted type according to /// the abstraction pattern of the given original type. static CanTupleType computeLoweredTupleType(TypeConverter &tc, + TypeExpansionContext context, AbstractionPattern origType, CanTupleType substType) { assert(origType.matchesTuple(substType)); @@ -1422,7 +1458,7 @@ static CanTupleType computeLoweredTupleType(TypeConverter &tc, assert(!Flags.isVariadic()); CanType loweredSubstEltType = - tc.getLoweredRValueType(origEltType, substEltType); + tc.getLoweredRValueType(context, origEltType, substEltType); changed = (changed || substEltType != loweredSubstEltType || !Flags.isNone()); @@ -1444,14 +1480,14 @@ static CanTupleType computeLoweredTupleType(TypeConverter &tc, } static CanType computeLoweredOptionalType(TypeConverter &tc, + TypeExpansionContext context, AbstractionPattern origType, CanType substType, CanType substObjectType) { assert(substType.getOptionalObjectType() == substObjectType); - CanType loweredObjectType = - tc.getLoweredRValueType(origType.getOptionalObjectType(), - substObjectType); + CanType loweredObjectType = tc.getLoweredRValueType( + context, origType.getOptionalObjectType(), substObjectType); // If the object type didn't change, we don't have to rebuild anything. if (loweredObjectType == substObjectType) { @@ -1464,11 +1500,12 @@ static CanType computeLoweredOptionalType(TypeConverter &tc, static CanType computeLoweredReferenceStorageType(TypeConverter &tc, + TypeExpansionContext context, AbstractionPattern origType, CanReferenceStorageType substType) { - CanType loweredReferentType = - tc.getLoweredRValueType(origType.getReferenceStorageReferentType(), - substType.getReferentType()); + CanType loweredReferentType = tc.getLoweredRValueType( + context, origType.getReferenceStorageReferentType(), + substType.getReferentType()); if (loweredReferentType == substType.getReferentType()) return substType; @@ -1478,36 +1515,54 @@ computeLoweredReferenceStorageType(TypeConverter &tc, } CanSILFunctionType -TypeConverter::getSILFunctionType(AbstractionPattern origType, +TypeConverter::getSILFunctionType(TypeExpansionContext context, + AbstractionPattern origType, CanFunctionType substType) { return cast( - getLoweredRValueType(origType, substType)); + getLoweredRValueType(context, origType, substType)); +} + +bool TypeConverter::hasOpaqueArchetypePropertiesOrCases(CanType ty) { + if (ty->hasOpaqueArchetype()) + return true; + + auto it = opaqueArchetypeFields.find(ty); + if (it == opaqueArchetypeFields.end()) { + bool res = ty->hasOpaqueArchetypePropertiesOrCases(); + opaqueArchetypeFields[ty] = res; + return res; + } + return it->second; } const TypeLowering & TypeConverter::getTypeLowering(AbstractionPattern origType, Type origSubstType, - ResilienceExpansion forExpansion) { + TypeExpansionContext forExpansion) { CanType substType = origSubstType->getCanonicalType(); - auto key = getTypeKey(origType, substType); - + auto origHadOpaqueTypeArchetype = + origSubstType->hasOpaqueArchetype() || + hasOpaqueArchetypePropertiesOrCases(origSubstType->getCanonicalType()); + auto key = getTypeKey(origType, substType, forExpansion); assert((!key.isDependent() || getCurGenericContext()) && "dependent type outside of generic context?!"); assert(!substType->is()); - auto *prev = find(key); - auto *lowering = getTypeLoweringForExpansion(key, forExpansion, prev); + auto *candidateLowering = find(key); + auto *lowering = getTypeLoweringForExpansion( + key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); if (lowering != nullptr) return *lowering; #ifndef NDEBUG // Catch reentrancy bugs. - if (prev == nullptr) - insert(key, nullptr); + if (candidateLowering == nullptr) + insert(key.getKeyForMinimalExpansion(), nullptr); #endif // Lower the type. - auto loweredSubstType = computeLoweredRValueType(origType, substType); + auto loweredSubstType = + computeLoweredRValueType(forExpansion, origType, substType); // If that didn't change the type and the key is cachable, there's no // point in re-checking the table, so just construct a type lowering @@ -1524,24 +1579,29 @@ TypeConverter::getTypeLowering(AbstractionPattern origType, } else { AbstractionPattern origTypeForCaching = AbstractionPattern(getCurGenericContext(), loweredSubstType); - auto loweredKey = getTypeKey(origTypeForCaching, loweredSubstType); + auto loweredKey = + getTypeKey(origTypeForCaching, loweredSubstType, forExpansion); lowering = &getTypeLoweringForLoweredType(loweredKey, - forExpansion); + forExpansion, + origHadOpaqueTypeArchetype); } - if (prev == nullptr) - insert(key, lowering); + if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) + insert(key.getKeyForMinimalExpansion(), lowering); else { - prev->NextExpansion = lowering; - assert(prev->isResilient() == lowering->isResilient()); + insert(key, lowering); +#ifndef NDEBUG + removeNullEntry(key.getKeyForMinimalExpansion()); +#endif } - return *lowering; } -CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, - CanType substType) { +CanType +TypeConverter::computeLoweredRValueType(TypeExpansionContext forExpansion, + AbstractionPattern origType, + CanType substType) { // AST function types are turned into SIL function types: // - the type is uncurried as desired // - types are turned into their unbridged equivalents, depending @@ -1576,12 +1636,12 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, } } - return getNativeSILFunctionType(*this, origType, substFnType); + return getNativeSILFunctionType(*this, forExpansion, origType, substFnType); } // Ignore dynamic self types. if (auto selfType = dyn_cast(substType)) { - return selfType.getSelfType(); + return getLoweredRValueType(forExpansion, selfType.getSelfType()); } // Static metatypes are unitary and can optimized to a "thin" empty @@ -1592,7 +1652,7 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, // representation. if (substMeta->hasRepresentation()) { assert(substMeta->isLegalSILType()); - return substMeta; + return substOpaqueTypesWithUnderlyingTypes(substMeta, forExpansion); } MetatypeRepresentation repr; @@ -1611,8 +1671,9 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, else repr = MetatypeRepresentation::Thick; } - - CanType instanceType = substMeta.getInstanceType(); + + CanType instanceType = substOpaqueTypesWithUnderlyingTypes( + substMeta.getInstanceType(), forExpansion); // Regardless of thinness, metatypes are always trivial. return CanMetatypeType::get(instanceType, repr); @@ -1631,61 +1692,101 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, // Lower tuple element types. if (auto substTupleType = dyn_cast(substType)) { - return computeLoweredTupleType(*this, origType, substTupleType); + return computeLoweredTupleType(*this, forExpansion, origType, + substTupleType); } // Lower the referent type of reference storage types. if (auto substRefType = dyn_cast(substType)) { - return computeLoweredReferenceStorageType(*this, origType, substRefType); + return computeLoweredReferenceStorageType(*this, forExpansion, origType, + substRefType); } // Lower the object type of optional types. if (auto substObjectType = substType.getOptionalObjectType()) { - return computeLoweredOptionalType(*this, origType, + return computeLoweredOptionalType(*this, forExpansion, origType, substType, substObjectType); } + if (auto silFnTy = dyn_cast(substType)) { + if (!substType->hasOpaqueArchetype() || + !forExpansion.shouldLookThroughOpaqueTypeArchetypes()) + return substType; + return silFnTy->substituteOpaqueArchetypes(*this, forExpansion); + } + // The Swift type directly corresponds to the lowered type. - return substType; + auto underlyingTy = + substOpaqueTypesWithUnderlyingTypes(substType, forExpansion, + /*allowLoweredTypes*/ true); + if (underlyingTy != substType) { + if (auto underlyingFnTy = dyn_cast(underlyingTy)) { + underlyingTy = computeLoweredRValueType( + forExpansion, AbstractionPattern::getOpaque(), underlyingTy); + } else + underlyingTy = computeLoweredRValueType( + forExpansion, + AbstractionPattern(getCurGenericContext(), underlyingTy), + underlyingTy); + } + + return underlyingTy; } const TypeLowering & -TypeConverter::getTypeLowering(SILType type, ResilienceExpansion forExpansion) { +TypeConverter::getTypeLowering(SILType type, + TypeExpansionContext forExpansion) { auto loweredType = type.getASTType(); auto key = getTypeKey(AbstractionPattern(getCurGenericContext(), loweredType), - loweredType); + loweredType, forExpansion); + + auto origHadOpaqueTypeArchetype = + loweredType->hasOpaqueArchetype() || + hasOpaqueArchetypePropertiesOrCases(loweredType); - return getTypeLoweringForLoweredType(key, forExpansion); + return getTypeLoweringForLoweredType(key, forExpansion, + origHadOpaqueTypeArchetype); } const TypeLowering & TypeConverter::getTypeLoweringForLoweredType(TypeKey key, - ResilienceExpansion forExpansion) { + TypeExpansionContext forExpansion, + bool origHadOpaqueTypeArchetype) { auto type = key.SubstType; assert(type->isLegalSILType() && "type is not lowered!"); (void)type; - auto *prev = find(key); - auto *lowering = getTypeLoweringForExpansion(key, forExpansion, prev); + auto *candidateLowering = find(key.getKeyForMinimalExpansion()); + auto *lowering = getTypeLoweringForExpansion( + key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); if (lowering != nullptr) return *lowering; #ifndef NDEBUG // Catch reentrancy bugs. - if (prev == nullptr) - insert(key, nullptr); + if (candidateLowering == nullptr) + insert(key.getKeyForMinimalExpansion(), nullptr); #endif - lowering = LowerType(*this, - CanGenericSignature(), - forExpansion, - key.isDependent()).visit(key.SubstType); + if (forExpansion.shouldLookThroughOpaqueTypeArchetypes() && + type->hasOpaqueArchetype()) { + type = computeLoweredRValueType( + forExpansion, AbstractionPattern(getCurGenericContext(), type), type); + } + + lowering = + LowerType(*this, CanGenericSignature(), forExpansion, key.isDependent()) + .visit(type); - if (prev) { - prev->NextExpansion = lowering; - assert(prev->isResilient() == lowering->isResilient()); - } else + if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) + insert(key.getKeyForMinimalExpansion(), lowering); + else { insert(key, lowering); +#ifndef NDEBUG + removeNullEntry(key.getKeyForMinimalExpansion()); +#endif + } + return *lowering; } @@ -1695,28 +1796,25 @@ TypeConverter::getTypeLoweringForLoweredType(TypeKey key, /// go ahead and lower the type with the correct expansion. const TypeLowering *TypeConverter:: getTypeLoweringForExpansion(TypeKey key, - ResilienceExpansion forExpansion, - const TypeLowering *lowering) { + TypeExpansionContext forExpansion, + const TypeLowering *lowering, + bool origHadOpaqueTypeArchetype) { if (lowering == nullptr) return nullptr; - if (!lowering->isResilient()) { + if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { // Don't try to refine the lowering for other resilience expansions if - // we don't expect to get a different lowering anyway. + // we don't expect to get a different lowering anyway. Similar if the + // original type did not have opaque type archetypes. // // See LowerType::handleResilience() for the gory details; we only // set this flag if the type is resilient *and* inside our module. return lowering; } - // Search for a matching lowering in the linked list of lowerings. - while (lowering) { - if (lowering->getResilienceExpansion() == forExpansion) - return lowering; - - // Continue searching. - lowering = lowering->NextExpansion; - } + auto *exactLowering = find(key); + if (exactLowering) + return exactLowering; // We have to create a new one. return nullptr; @@ -2020,7 +2118,8 @@ TypeConverter::getConstantGenericEnvironment(SILDeclRef c) { return nullptr; } -SILType TypeConverter::getSubstitutedStorageType(AbstractStorageDecl *value, +SILType TypeConverter::getSubstitutedStorageType(TypeExpansionContext context, + AbstractStorageDecl *value, Type lvalueType) { // The l-value type is the result of applying substitutions to // the type-of-reference. Essentially, we want to apply those @@ -2039,7 +2138,7 @@ SILType TypeConverter::getSubstitutedStorageType(AbstractStorageDecl *value, substType = substType.getReferenceStorageReferent(); } - CanType substLoweredType = getLoweredRValueType(origType, substType); + CanType substLoweredType = getLoweredRValueType(context, origType, substType); // Type substitution preserves structural type structure, and the // type-of-reference is only different in the outermost structural @@ -2605,10 +2704,11 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured, env->mapTypeIntoContext(contextBoxTy) ->getCanonicalType()); } - assert(contextBoxTy->getLayout()->getFields().size() == 1 - && getSILBoxFieldType(contextBoxTy, *this, 0).getASTType() - == loweredContextType - && "box field type doesn't match capture!"); + assert(contextBoxTy->getLayout()->getFields().size() == 1 && + getSILBoxFieldType(TypeExpansionContext::minimal(), contextBoxTy, + *this, 0) + .getASTType() == loweredContextType && + "box field type doesn't match capture!"); #endif return boxTy; } @@ -2638,8 +2738,8 @@ TypeConverter::getContextBoxTypeForCapture(ValueDecl *captured, return boxType; } -CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, - EnumElementDecl *elt) { +CanSILBoxType TypeConverter::getBoxTypeForEnumElement( + TypeExpansionContext context, SILType enumType, EnumElementDecl *elt) { auto *enumDecl = enumType.getEnumOrBoundGenericEnum(); @@ -2652,8 +2752,7 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, if (boxSignature == CanGenericSignature()) { auto eltIntfTy = elt->getArgumentInterfaceType(); - - auto boxVarTy = getLoweredRValueType(eltIntfTy); + auto boxVarTy = getLoweredRValueType(context, eltIntfTy); auto layout = SILLayout::get(C, nullptr, SILField(boxVarTy, true)); return SILBoxType::get(C, layout, {}); } @@ -2665,8 +2764,8 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, auto eltIntfTy = elt->getArgumentInterfaceType(); GenericContextScope scope(*this, boxSignature); - auto boxVarTy = getLoweredRValueType(getAbstractionPattern(elt), - eltIntfTy); + auto boxVarTy = getLoweredRValueType(context, + getAbstractionPattern(elt), eltIntfTy); auto layout = SILLayout::get(C, boxSignature, SILField(boxVarTy, true)); // Instantiate the layout with enum's substitution list. @@ -2678,13 +2777,15 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, } static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, - SILType Ty, ResilienceExpansion expansion) { + SILType Ty, + TypeExpansionContext expansion) { if (auto *structDecl = Ty.getStructOrBoundGenericStruct()) { - assert(!structDecl->isResilient(&TC.M, expansion) && - " FSO should not be trying to explode resilient (ie address-only) " - "types at all"); + assert( + !structDecl->isResilient(&TC.M, expansion.getResilienceExpansion()) && + " FSO should not be trying to explode resilient (ie address-only) " + "types at all"); for (auto *prop : structDecl->getStoredProperties()) { - SILType propTy = Ty.getFieldType(prop, TC); + SILType propTy = Ty.getFieldType(prop, TC, expansion); unsigned fieldsCountBefore = fieldsCount; countNumberOfInnerFields(fieldsCount, TC, propTy, expansion); if (fieldsCount == fieldsCountBefore) { @@ -2706,7 +2807,7 @@ static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, if (enumDecl->isIndirect()) { return; } - assert(!enumDecl->isResilient(&TC.M, expansion) && + assert(!enumDecl->isResilient(&TC.M, expansion.getResilienceExpansion()) && " FSO should not be trying to explode resilient (ie address-only) " "types at all"); unsigned fieldsCountBefore = fieldsCount; @@ -2723,7 +2824,7 @@ static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, // (we shouldn't expand enums) // Number of fields > 1 as "future proof" for this heuristic: // In case it is used by a pass that tries to explode enums. - auto payloadTy = Ty.getEnumElementType(elt, TC); + auto payloadTy = Ty.getEnumElementType(elt, TC, expansion); fieldsCount = 0; countNumberOfInnerFields(fieldsCount, TC, payloadTy, expansion); if (fieldsCount > maxEnumCount) { @@ -2736,8 +2837,8 @@ static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, } unsigned TypeConverter::countNumberOfFields(SILType Ty, - ResilienceExpansion expansion) { - auto key = std::make_pair(Ty, unsigned(expansion)); + TypeExpansionContext expansion) { + auto key = std::make_pair(Ty, unsigned(expansion.getResilienceExpansion())); auto Iter = TypeFields.find(key); if (Iter != TypeFields.end()) { return std::max(Iter->second, 1U); @@ -2755,7 +2856,7 @@ void TypeLowering::print(llvm::raw_ostream &os) const { return "false"; }; os << "Type Lowering for lowered type: " << LoweredType << ".\n" - << "Expansion: " << ResilienceExpansion(ForExpansion) << "\n" + << "Expansion: " << getResilienceExpansion() << "\n" << "isTrivial: " << BOOL(Properties.isTrivial()) << ".\n" << "isFixedABI: " << BOOL(Properties.isFixedABI()) << ".\n" << "isAddressOnly: " << BOOL(Properties.isAddressOnly()) << ".\n" From e67b96139d59170e26f3e046306e8ea4e6c0e979 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:37:53 -0800 Subject: [PATCH 066/283] SIL: Add getTypeExpansionContext to SILFunction --- include/swift/SIL/SILFunction.h | 13 +++++++++++++ lib/SIL/SILFunction.cpp | 14 +++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 3e8f92d571983..040b95db67b39 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -342,6 +342,14 @@ class SILFunction CanSILFunctionType getLoweredFunctionType() const { return LoweredType; } + CanSILFunctionType + getLoweredFunctionTypeInContext(TypeExpansionContext context) const; + + SILType getLoweredTypeInContext(TypeExpansionContext context) const { + return SILType::getPrimitiveObjectType( + getLoweredFunctionTypeInContext(context)); + } + SILFunctionConventions getConventions() const { return SILFunctionConventions(LoweredType, getModule()); } @@ -491,6 +499,11 @@ class SILFunction : ResilienceExpansion::Maximal); } + // Returns the type expansion context to be used inside this function. + TypeExpansionContext getTypeExpansionContext() const { + return TypeExpansionContext(*this); + } + const Lowering::TypeLowering & getTypeLowering(Lowering::AbstractionPattern orig, Type subst); diff --git a/lib/SIL/SILFunction.cpp b/lib/SIL/SILFunction.cpp index 8fdf99b6878dc..9129fdea5ccc6 100644 --- a/lib/SIL/SILFunction.cpp +++ b/lib/SIL/SILFunction.cpp @@ -235,34 +235,34 @@ bool SILFunction::isNoReturnFunction() const { const TypeLowering & SILFunction::getTypeLowering(AbstractionPattern orig, Type subst) { return getModule().Types.getTypeLowering(orig, subst, - getResilienceExpansion()); + TypeExpansionContext(*this)); } const TypeLowering &SILFunction::getTypeLowering(Type t) const { - return getModule().Types.getTypeLowering(t, getResilienceExpansion()); + return getModule().Types.getTypeLowering(t, TypeExpansionContext(*this)); } SILType SILFunction::getLoweredType(AbstractionPattern orig, Type subst) const { return getModule().Types.getLoweredType(orig, subst, - getResilienceExpansion()); + TypeExpansionContext(*this)); } SILType SILFunction::getLoweredType(Type t) const { - return getModule().Types.getLoweredType(t, getResilienceExpansion()); + return getModule().Types.getLoweredType(t, TypeExpansionContext(*this)); } SILType SILFunction::getLoweredLoadableType(Type t) const { auto &M = getModule(); - return M.Types.getLoweredLoadableType(t, getResilienceExpansion(), M); + return M.Types.getLoweredLoadableType(t, TypeExpansionContext(*this), M); } const TypeLowering &SILFunction::getTypeLowering(SILType type) const { - return getModule().Types.getTypeLowering(type, getResilienceExpansion()); + return getModule().Types.getTypeLowering(type, TypeExpansionContext(*this)); } bool SILFunction::isTypeABIAccessible(SILType type) const { - return getModule().isTypeABIAccessible(type, getResilienceExpansion()); + return getModule().isTypeABIAccessible(type, TypeExpansionContext(*this)); } bool SILFunction::isWeakImported() const { From 9ecda0c574174a21169cf4f3b462787d0f84b94c Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:56:44 -0800 Subject: [PATCH 067/283] SIL: Plumb TypeExpansionContext through SIL --- include/swift/SIL/Projection.h | 27 +++--- include/swift/SIL/SILBasicBlock.h | 3 +- include/swift/SIL/SILBuilder.h | 65 ++++++++------ include/swift/SIL/SILCloner.h | 34 +++++--- include/swift/SIL/SILFunction.h | 2 + include/swift/SIL/SILGlobalVariable.h | 7 +- include/swift/SIL/SILInstruction.h | 23 +++-- include/swift/SIL/SILModule.h | 2 +- lib/SIL/AbstractionPattern.cpp | 2 + lib/SIL/Bridging.cpp | 3 +- lib/SIL/DynamicCasts.cpp | 7 +- lib/SIL/MemoryLifetime.cpp | 3 +- lib/SIL/Projection.cpp | 93 ++++++++++---------- lib/SIL/SIL.cpp | 2 +- lib/SIL/SILBasicBlock.cpp | 8 +- lib/SIL/SILBuilder.cpp | 32 +++---- lib/SIL/SILFunction.cpp | 3 + lib/SIL/SILFunctionBuilder.cpp | 3 +- lib/SIL/SILGlobalVariable.cpp | 11 +++ lib/SIL/SILInstructions.cpp | 68 +++++++++------ lib/SIL/SILVerifier.cpp | 119 +++++++++++++++----------- 21 files changed, 315 insertions(+), 202 deletions(-) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 1e4324e07b29f..1ccc0fad703bc 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -293,7 +293,8 @@ class Projection { /// /// WARNING: This is not a constant time operation because it is implemented /// in terms of getVarDecl, which requests all BaseType's stored properties. - SILType getType(SILType BaseType, SILModule &M) const; + SILType getType(SILType BaseType, SILModule &M, + TypeExpansionContext context) const; VarDecl *getVarDecl(SILType BaseType) const { assert(isValid()); @@ -402,6 +403,7 @@ class Projection { /// Given a specific SILType, return all first level projections if it is an /// aggregate. static void getFirstLevelProjections(SILType V, SILModule &Mod, + TypeExpansionContext context, llvm::SmallVectorImpl &Out); /// Is this cast which only allows for equality? @@ -584,6 +586,7 @@ class ProjectionPath { /// is a leaf node in the type tree. static void expandTypeIntoLeafProjectionPaths(SILType BaseType, SILModule *Mod, + TypeExpansionContext context, ProjectionPathList &P); /// Return true if the given projection paths in \p CPaths does not cover @@ -626,26 +629,27 @@ class ProjectionPath { SILType getBaseType() const { return BaseType; } /// Returns the most derived type of the projection path. - SILType getMostDerivedType(SILModule &M) { + SILType getMostDerivedType(SILModule &M, TypeExpansionContext context) { if (Path.empty()) return getBaseType(); if (MostDerivedType) return MostDerivedType; - MostDerivedType = getDerivedType(Path.size(), M); + MostDerivedType = getDerivedType(Path.size(), M, context); return MostDerivedType; } /// Returns the ith derived type of the path. This is zero indexed with 0 /// being the base type and n consisting of applying the up to n projections /// to the base type. - SILType getDerivedType(unsigned i, SILModule &M) const { + SILType getDerivedType(unsigned i, SILModule &M, + TypeExpansionContext context) const { assert(i <= Path.size()); SILType IterTy = getBaseType(); if (i == 0) return IterTy; for (unsigned j : range(i)) { auto &Proj = Path[j]; - IterTy = Proj.getType(IterTy, M); + IterTy = Proj.getType(IterTy, M, context); } return IterTy; } @@ -671,10 +675,11 @@ class ProjectionPath { const_reverse_iterator rbegin() const { return Path.rbegin(); } const_reverse_iterator rend() const { return Path.rend(); } - void verify(SILModule &M); + void verify(SILModule &M, TypeExpansionContext context); - raw_ostream &print(raw_ostream &OS, SILModule &M) const; - void dump(SILModule &M) const; + raw_ostream &print(raw_ostream &OS, SILModule &M, + TypeExpansionContext context) const; + void dump(SILModule &M, TypeExpansionContext context) const; }; /// Returns the hashcode for the new projection path. @@ -813,9 +818,11 @@ class ProjectionTreeNode { llvm::SmallVectorImpl &Worklist, SILValue Value); - void createNextLevelChildren(ProjectionTree &Tree); + void createNextLevelChildren(ProjectionTree &Tree, TypeExpansionContext context); - void createNextLevelChildrenForStruct(ProjectionTree &Tree, StructDecl *SD); + void createNextLevelChildrenForStruct(ProjectionTree &Tree, + TypeExpansionContext context, + StructDecl *SD); void createNextLevelChildrenForTuple(ProjectionTree &Tree, TupleType *TT); }; diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index db7f53cd6a5b9..8e2a37cc088ae 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -214,7 +214,8 @@ public llvm::ilist_node, public SILAllocated { /// Allocate a new argument of type \p Ty and append it to the argument /// list. Optionally you can pass in a value decl parameter. SILFunctionArgument *createFunctionArgument(SILType Ty, - const ValueDecl *D = nullptr); + const ValueDecl *D = nullptr, + bool disableEntryBlockVerification = false); SILFunctionArgument *insertFunctionArgument(unsigned Index, SILType Ty, ValueOwnershipKind OwnershipKind, diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index aa3da71165d5c..6d818b060baf0 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -197,16 +197,23 @@ class SILBuilder { assert(F && "cannot create this instruction without a function context"); return *F; } + + TypeExpansionContext getTypeExpansionContext() const { + return TypeExpansionContext(getFunction()); + } + SILBuilderContext &getBuilderContext() const { return C; } SILModule &getModule() const { return C.Module; } ASTContext &getASTContext() const { return getModule().getASTContext(); } const Lowering::TypeLowering &getTypeLowering(SILType T) const { - auto expansion = ResilienceExpansion::Maximal; + + auto expansion = TypeExpansionContext::maximal(getModule().getSwiftModule(), + getModule().isWholeModule()); // If there's no current SILFunction, we're inserting into a global // variable initializer. - if (F) - expansion = F->getResilienceExpansion(); - + if (F) { + expansion = TypeExpansionContext(getFunction()); + } return getModule().Types.getTypeLowering(T, expansion); } @@ -336,12 +343,11 @@ class SILBuilder { // Type remapping //===--------------------------------------------------------------------===// - static SILType - getPartialApplyResultType(SILType Ty, unsigned ArgCount, SILModule &M, - SubstitutionMap subs, - ParameterConvention calleeConvention, - PartialApplyInst::OnStackKind onStack = - PartialApplyInst::OnStackKind::NotOnStack); + static SILType getPartialApplyResultType( + TypeExpansionContext context, SILType Ty, unsigned ArgCount, SILModule &M, + SubstitutionMap subs, ParameterConvention calleeConvention, + PartialApplyInst::OnStackKind onStack = + PartialApplyInst::OnStackKind::NotOnStack); //===--------------------------------------------------------------------===// // CFG Manipulation @@ -574,7 +580,8 @@ class SILBuilder { FunctionRefBaseInst *createFunctionRefFor(SILLocation Loc, SILFunction *f) { if (f->isDynamicallyReplaceable()) return createDynamicFunctionRef(Loc, f); - else return createFunctionRef(Loc, f); + else + return createFunctionRef(Loc, f); } FunctionRefBaseInst *createFunctionRef(SILLocation Loc, SILFunction *f, @@ -590,20 +597,20 @@ class SILBuilder { } FunctionRefInst *createFunctionRef(SILLocation Loc, SILFunction *f) { - return insert(new (getModule()) - FunctionRefInst(getSILDebugLocation(Loc), f)); + return insert(new (getModule()) FunctionRefInst(getSILDebugLocation(Loc), f, + getTypeExpansionContext())); } DynamicFunctionRefInst * createDynamicFunctionRef(SILLocation Loc, SILFunction *f) { return insert(new (getModule()) DynamicFunctionRefInst( - getSILDebugLocation(Loc), f)); + getSILDebugLocation(Loc), f, getTypeExpansionContext())); } PreviousDynamicFunctionRefInst * createPreviousDynamicFunctionRef(SILLocation Loc, SILFunction *f) { return insert(new (getModule()) PreviousDynamicFunctionRefInst( - getSILDebugLocation(Loc), f)); + getSILDebugLocation(Loc), f, getTypeExpansionContext())); } AllocGlobalInst *createAllocGlobal(SILLocation Loc, SILGlobalVariable *g) { @@ -611,16 +618,16 @@ class SILBuilder { AllocGlobalInst(getSILDebugLocation(Loc), g)); } GlobalAddrInst *createGlobalAddr(SILLocation Loc, SILGlobalVariable *g) { - return insert(new (getModule()) - GlobalAddrInst(getSILDebugLocation(Loc), g)); + return insert(new (getModule()) GlobalAddrInst(getSILDebugLocation(Loc), g, + getTypeExpansionContext())); } GlobalAddrInst *createGlobalAddr(SILLocation Loc, SILType Ty) { return insert(new (F->getModule()) GlobalAddrInst(getSILDebugLocation(Loc), Ty)); } GlobalValueInst *createGlobalValue(SILLocation Loc, SILGlobalVariable *g) { - return insert(new (getModule()) - GlobalValueInst(getSILDebugLocation(Loc), g)); + return insert(new (getModule()) GlobalValueInst(getSILDebugLocation(Loc), g, + getTypeExpansionContext())); } IntegerLiteralInst *createIntegerLiteral(IntegerLiteralExpr *E); @@ -1285,8 +1292,8 @@ class SILBuilder { UncheckedEnumDataInst *createUncheckedEnumData(SILLocation Loc, SILValue Operand, EnumElementDecl *Element) { - SILType EltType = - Operand->getType().getEnumElementType(Element, getModule()); + SILType EltType = Operand->getType().getEnumElementType( + Element, getModule(), getTypeExpansionContext()); return createUncheckedEnumData(Loc, Operand, Element, EltType); } @@ -1307,8 +1314,8 @@ class SILBuilder { UncheckedTakeEnumDataAddrInst * createUncheckedTakeEnumDataAddr(SILLocation Loc, SILValue Operand, EnumElementDecl *Element) { - SILType EltType = - Operand->getType().getEnumElementType(Element, getModule()); + SILType EltType = Operand->getType().getEnumElementType( + Element, getModule(), getTypeExpansionContext()); return createUncheckedTakeEnumDataAddr(Loc, Operand, Element, EltType); } @@ -1383,7 +1390,8 @@ class SILBuilder { StructExtractInst *createStructExtract(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto type = Operand->getType().getFieldType(Field, getModule()); + auto type = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return createStructExtract(Loc, Operand, Field, type); } @@ -1397,7 +1405,8 @@ class SILBuilder { StructElementAddrInst * createStructElementAddr(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto ResultTy = Operand->getType().getFieldType(Field, getModule()); + auto ResultTy = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return createStructElementAddr(Loc, Operand, Field, ResultTy); } @@ -1408,7 +1417,8 @@ class SILBuilder { } RefElementAddrInst *createRefElementAddr(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto ResultTy = Operand->getType().getFieldType(Field, getModule()); + auto ResultTy = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return createRefElementAddr(Loc, Operand, Field, ResultTy); } @@ -2131,7 +2141,8 @@ class SILBuilder { SILValue emitStructExtract(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto type = Operand->getType().getFieldType(Field, getModule()); + auto type = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return emitStructExtract(Loc, Operand, Field, type); } diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 91fc6f2dd0e4a..272557603d4cf 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -128,14 +128,15 @@ class SILCloner : protected SILInstructionVisitor { /// blocks. /// /// This is used to clone an entire function and should not mutate the - /// original function. + /// original function except if \p replaceOriginalFunctionInPlace is true. /// /// entryArgs must have a SILValue from the cloned function corresponding to /// each argument in the original function `F`. /// /// Cloned instructions are inserted starting at the end of clonedEntryBB. void cloneFunctionBody(SILFunction *F, SILBasicBlock *clonedEntryBB, - ArrayRef entryArgs); + ArrayRef entryArgs, + bool replaceOriginalFunctionInPlace = false); /// MARK: Callback utilities used from CRTP extensions during cloning. /// These should only be called from within an instruction cloning visitor. @@ -354,8 +355,14 @@ class SILCloner : protected SILInstructionVisitor { SILLocation remapLocation(SILLocation Loc) { return Loc; } const SILDebugScope *remapScope(const SILDebugScope *DS) { return DS; } - SILType remapType(SILType Ty) { return Ty; } - CanType remapASTType(CanType Ty) { return Ty; } + SILType remapType(SILType Ty) { + return Ty; + } + + CanType remapASTType(CanType Ty) { + return Ty; + } + ProtocolConformanceRef remapConformance(Type Ty, ProtocolConformanceRef C) { return C; } @@ -612,9 +619,11 @@ void SILCloner::cloneReachableBlocks( template void SILCloner::cloneFunctionBody(SILFunction *F, SILBasicBlock *clonedEntryBB, - ArrayRef entryArgs) { + ArrayRef entryArgs, + bool replaceOriginalFunctionInPlace) { - assert(F != clonedEntryBB->getParent() && "Must clone into a new function."); + assert((replaceOriginalFunctionInPlace || F != clonedEntryBB->getParent()) && + "Must clone into a new function."); assert(BBMap.empty() && "This API does not allow clients to map blocks."); assert(ValueMap.empty() && "Stale ValueMap."); @@ -972,9 +981,9 @@ SILCloner::visitFunctionRefInst(FunctionRefInst *Inst) { getOpLocation(Inst->getLoc()), OpFunction)); } -template -void -SILCloner::visitDynamicFunctionRefInst(DynamicFunctionRefInst *Inst) { +template +void SILCloner::visitDynamicFunctionRefInst( + DynamicFunctionRefInst *Inst) { SILFunction *OpFunction = getOpFunction(Inst->getInitiallyReferencedFunction()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); @@ -2049,8 +2058,11 @@ SILCloner::visitWitnessMethodInst(WitnessMethodInst *Inst) { CanType Ty = conformance.getConcrete()->getType()->getCanonicalType(); if (Ty != newLookupType) { - assert(Ty->isExactSuperclassOf(newLookupType) && - "Should only create upcasts for sub class."); + assert( + (Ty->isExactSuperclassOf(newLookupType) || + getBuilder().getModule().Types.getLoweredRValueType( + getBuilder().getTypeExpansionContext(), Ty) == newLookupType) && + "Should only create upcasts for sub class."); // We use the super class as the new look up type. newLookupType = Ty; diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 040b95db67b39..58eeff9bb053f 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -515,6 +515,8 @@ class SILFunction SILType getLoweredLoadableType(Type t) const; + SILType getLoweredType(SILType t) const; + const Lowering::TypeLowering &getTypeLowering(SILType type) const; bool isTypeABIAccessible(SILType type) const; diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index eedc850458e3a..c40ba2c5766a1 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -105,7 +105,12 @@ class SILGlobalVariable CanSILFunctionType getLoweredFunctionType() const { return LoweredType.castTo(); } - + SILType getLoweredTypeInContext(TypeExpansionContext context) const; + CanSILFunctionType + getLoweredFunctionTypeInContext(TypeExpansionContext context) const { + return getLoweredTypeInContext(context).castTo(); + } + StringRef getName() const { return Name; } void setDeclaration(bool isD) { IsDeclaration = isD; } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 551eeae5a9928..3e64bce702fb4 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -2357,7 +2357,7 @@ class FunctionRefBaseInst : public LiteralInst { protected: FunctionRefBaseInst(SILInstructionKind Kind, SILDebugLocation DebugLoc, - SILFunction *F); + SILFunction *F, TypeExpansionContext context); public: ~FunctionRefBaseInst(); @@ -2416,7 +2416,9 @@ class FunctionRefInst : public FunctionRefBaseInst { /// /// \param DebugLoc The location of the reference. /// \param F The function being referenced. - FunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + /// \param context The type expansion context of the function reference. + FunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F, + TypeExpansionContext context); public: static bool classof(const SILNode *node) { @@ -2434,7 +2436,9 @@ class DynamicFunctionRefInst : public FunctionRefBaseInst { /// /// \param DebugLoc The location of the reference. /// \param F The function being referenced. - DynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + /// \param context The type expansion context of the function reference. + DynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F, + TypeExpansionContext context); public: static bool classof(const SILNode *node) { @@ -2452,7 +2456,9 @@ class PreviousDynamicFunctionRefInst : public FunctionRefBaseInst { /// /// \param DebugLoc The location of the reference. /// \param F The function being referenced. - PreviousDynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + /// \param context The type expansion context of the function reference. + PreviousDynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F, + TypeExpansionContext context); public: static bool classof(const SILNode *node) { @@ -3117,14 +3123,16 @@ class GlobalAddrInst GlobalAccessInst> { friend SILBuilder; - GlobalAddrInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global); + GlobalAddrInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global, + TypeExpansionContext context); + public: // FIXME: This constructor should be private but is currently used // in the SILParser. /// Create a placeholder instruction with an unset global reference. GlobalAddrInst(SILDebugLocation DebugLoc, SILType Ty) - : InstructionBase(DebugLoc, Ty, nullptr) { } + : InstructionBase(DebugLoc, Ty, nullptr) {} }; /// Gives the value of a global variable. @@ -3136,7 +3144,8 @@ class GlobalValueInst GlobalAccessInst> { friend SILBuilder; - GlobalValueInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global); + GlobalValueInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global, + TypeExpansionContext context); }; /// IntegerLiteralInst - Encapsulates an integer constant, as defined originally diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 802bb85041b32..c251bf9f0b3bf 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -595,7 +595,7 @@ class SILModule { /// Can value operations (copies and destroys) on the given lowered type /// be performed in this module? bool isTypeABIAccessible(SILType type, - ResilienceExpansion forExpansion); + TypeExpansionContext forExpansion); /// Can type metadata for the given formal type be fetched in /// the given module? diff --git a/lib/SIL/AbstractionPattern.cpp b/lib/SIL/AbstractionPattern.cpp index ff1b499c68120..ed058b3a899e8 100644 --- a/lib/SIL/AbstractionPattern.cpp +++ b/lib/SIL/AbstractionPattern.cpp @@ -596,6 +596,8 @@ AbstractionPattern AbstractionPattern::getOptionalObjectType() const { case Kind::Type: if (isTypeParameter()) return AbstractionPattern::getOpaque(); + if (isa(getType())) + return AbstractionPattern::getOpaque(); return AbstractionPattern(getGenericSignature(), ::getOptionalObjectType(getType())); diff --git a/lib/SIL/Bridging.cpp b/lib/SIL/Bridging.cpp index ab2fc6bab7aef..c5760f1805eda 100644 --- a/lib/SIL/Bridging.cpp +++ b/lib/SIL/Bridging.cpp @@ -33,7 +33,8 @@ using namespace swift::Lowering; CanType TypeConverter::getLoweredTypeOfGlobal(VarDecl *var) { AbstractionPattern origType = getAbstractionPattern(var); assert(!origType.isTypeParameter()); - return getLoweredRValueType(origType, origType.getType()); + return getLoweredRValueType(TypeExpansionContext::minimal(), origType, + origType.getType()); } AnyFunctionType::Param diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp index 3f83785d1bbbb..f294dddecb59b 100644 --- a/lib/SIL/DynamicCasts.cpp +++ b/lib/SIL/DynamicCasts.cpp @@ -918,7 +918,8 @@ namespace { auto sourceSomeDecl = Ctx.getOptionalSomeDecl(); SILType loweredSourceObjectType = - source.Value->getType().getEnumElementType(sourceSomeDecl, M); + source.Value->getType().getEnumElementType( + sourceSomeDecl, M, B.getTypeExpansionContext()); // Form the target for the optional object. EmitSomeState state; @@ -991,8 +992,8 @@ namespace { auto someDecl = Ctx.getOptionalSomeDecl(); state.SomeDecl = someDecl; - SILType loweredObjectType = - target.LoweredType.getEnumElementType(someDecl, M); + SILType loweredObjectType = target.LoweredType.getEnumElementType( + someDecl, M, B.getTypeExpansionContext()); if (target.isAddress()) { SILValue objectAddr = diff --git a/lib/SIL/MemoryLifetime.cpp b/lib/SIL/MemoryLifetime.cpp index 8c8688086b0ca..39af8027f878f 100644 --- a/lib/SIL/MemoryLifetime.cpp +++ b/lib/SIL/MemoryLifetime.cpp @@ -374,7 +374,8 @@ void MemoryLocations::initFieldsCounter(Location &loc) { } SILModule &module = function->getModule(); for (VarDecl *field : decl->getStoredProperties()) { - loc.updateFieldCounters(ty.getFieldType(field, module), +1); + loc.updateFieldCounters( + ty.getFieldType(field, module, TypeExpansionContext(*function)), +1); } return; } diff --git a/lib/SIL/Projection.cpp b/lib/SIL/Projection.cpp index 3e090189a21bf..2881469102ea1 100644 --- a/lib/SIL/Projection.cpp +++ b/lib/SIL/Projection.cpp @@ -177,17 +177,18 @@ Projection::Projection(SingleValueInstruction *I) : Value() { /// /// WARNING: This is not a constant time operation because it is implemented /// in terms of getVarDecl, which requests all BaseType's stored properties. -SILType Projection::getType(SILType BaseType, SILModule &M) const { +SILType Projection::getType(SILType BaseType, SILModule &M, + TypeExpansionContext context) const { assert(isValid()); switch (getKind()) { case ProjectionKind::Struct: case ProjectionKind::Class: - return BaseType.getFieldType(getVarDecl(BaseType), M); + return BaseType.getFieldType(getVarDecl(BaseType), M, context); case ProjectionKind::Enum: - return BaseType.getEnumElementType(getEnumElementDecl(BaseType), M); + return BaseType.getEnumElementType(getEnumElementDecl(BaseType), M, context); case ProjectionKind::Box: - return getSILBoxFieldType(BaseType.castTo(), - M.Types, getIndex()); + return getSILBoxFieldType(context, BaseType.castTo(), M.Types, + getIndex()); case ProjectionKind::Tuple: return BaseType.getTupleElementType(getIndex()); case ProjectionKind::Upcast: @@ -285,18 +286,20 @@ Projection::createAddressProjection(SILBuilder &B, SILLocation Loc, llvm_unreachable("Unhandled ProjectionKind in switch."); } -void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, - llvm::SmallVectorImpl &Out) { +void Projection::getFirstLevelProjections( + SILType Ty, SILModule &Mod, TypeExpansionContext context, + llvm::SmallVectorImpl &Out) { if (auto *S = Ty.getStructOrBoundGenericStruct()) { unsigned Count = 0; for (auto *VDecl : S->getStoredProperties()) { (void) VDecl; Projection P(ProjectionKind::Struct, Count++); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); - assert(X.getMostDerivedType(Mod)==Ty.getFieldType(VDecl, Mod)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == + Ty.getFieldType(VDecl, Mod, context)); + X.verify(Mod, context);); Out.push_back(P); } return; @@ -306,10 +309,11 @@ void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { Projection P(ProjectionKind::Tuple, i); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); - assert(X.getMostDerivedType(Mod) == Ty.getTupleElementType(i)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == + Ty.getTupleElementType(i)); + X.verify(Mod, context);); Out.push_back(P); } return; @@ -321,10 +325,11 @@ void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, (void) VDecl; Projection P(ProjectionKind::Class, Count++); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); - assert(X.getMostDerivedType(Mod)==Ty.getFieldType(VDecl, Mod)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == + Ty.getFieldType(VDecl, Mod, context)); + X.verify(Mod, context);); Out.push_back(P); } return; @@ -334,11 +339,10 @@ void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, for (unsigned field : indices(Box->getLayout()->getFields())) { Projection P(ProjectionKind::Box, field); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); - X.append(P); - assert(X.getMostDerivedType(Mod) - == getSILBoxFieldType(Box, Mod.Types, field)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); + assert(X.getMostDerivedType(Mod, context) == + getSILBoxFieldType(context, Box, Mod.Types, field)); + X.verify(Mod, context);); (void)Box; Out.push_back(P); } @@ -580,12 +584,13 @@ void Projection::print(raw_ostream &os, SILType baseType) const { os << ""; } -raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) const { +raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M, + TypeExpansionContext context) const { os << "Projection Path ["; SILType IterType = getBaseType(); for (const Projection &IterProj : Path) { SILType BaseType = IterType; - IterType = IterProj.getType(IterType, M); + IterType = IterProj.getType(IterType, M, context); os << BaseType.getAddressType() << "\n "; @@ -596,16 +601,16 @@ raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) const { return os; } -void ProjectionPath::dump(SILModule &M) const { - print(llvm::dbgs(), M); +void ProjectionPath::dump(SILModule &M, TypeExpansionContext context) const { + print(llvm::dbgs(), M, context); } -void ProjectionPath::verify(SILModule &M) { +void ProjectionPath::verify(SILModule &M, TypeExpansionContext context) { #ifndef NDEBUG SILType IterTy = getBaseType(); assert(IterTy); for (auto &Proj : Path) { - IterTy = Proj.getType(IterTy, M); + IterTy = Proj.getType(IterTy, M, context); assert(IterTy); } #endif @@ -613,6 +618,7 @@ void ProjectionPath::verify(SILModule &M) { void ProjectionPath::expandTypeIntoLeafProjectionPaths(SILType B, SILModule *Mod, + TypeExpansionContext context, ProjectionPathList &Paths) { // Perform a BFS to expand the given type into projectionpath each of // which contains 1 field from the type. @@ -626,7 +632,7 @@ ProjectionPath::expandTypeIntoLeafProjectionPaths(SILType B, SILModule *Mod, // Get the next level projections based on current projection's type. ProjectionPath PP = Worklist.pop_back_val(); // Get the current type to process. - SILType Ty = PP.getMostDerivedType(*Mod); + SILType Ty = PP.getMostDerivedType(*Mod, context); LLVM_DEBUG(llvm::dbgs() << "Visiting type: " << Ty << "\n"); @@ -655,7 +661,7 @@ ProjectionPath::expandTypeIntoLeafProjectionPaths(SILType B, SILModule *Mod, // Get the first level projection of the current type. Projections.clear(); - Projection::getFirstLevelProjections(Ty, *Mod, Projections); + Projection::getFirstLevelProjections(Ty, *Mod, context, Projections); // Reached the end of the projection tree, this field can not be expanded // anymore. @@ -695,11 +701,12 @@ bool ProjectionPath::hasUncoveredNonTrivials(SILType B, const SILFunction &F, continue; // Get the current type to process. - SILType Ty = PP.getMostDerivedType(Mod); + SILType Ty = PP.getMostDerivedType(Mod, F.getTypeExpansionContext()); // Get the first level projection of the current type. llvm::SmallVector Projections; - Projection::getFirstLevelProjections(Ty, Mod, Projections); + Projection::getFirstLevelProjections(Ty, Mod, F.getTypeExpansionContext(), + Projections); // Reached the end of the projection tree, this field can not be expanded // anymore. @@ -719,7 +726,8 @@ bool ProjectionPath::hasUncoveredNonTrivials(SILType B, const SILFunction &F, for (auto &P : Projections) { ProjectionPath X(B); X.append(PP); - assert(PP.getMostDerivedType(Mod) == X.getMostDerivedType(Mod)); + assert(PP.getMostDerivedType(Mod, F.getTypeExpansionContext()) == + X.getMostDerivedType(Mod, F.getTypeExpansionContext())); X.append(P); Worklist.push_back(X); } @@ -728,8 +736,8 @@ bool ProjectionPath::hasUncoveredNonTrivials(SILType B, const SILFunction &F, // Check whether any path leads to a non-trivial type. for (auto &X : Paths) { - if (!X.getMostDerivedType(Mod).isTrivial(F)) - return true; + if (!X.getMostDerivedType(Mod, F.getTypeExpansionContext()).isTrivial(F)) + return true; } return false; } @@ -937,7 +945,8 @@ processUsersOfValue(ProjectionTree &Tree, // we have a projection to the next level children, create the next // level children nodes lazily. if (!Initialized) - createNextLevelChildren(Tree); + createNextLevelChildren( + Tree, TypeExpansionContext(*projectionInst->getFunction())); // Look up the Node for this projection add {User, ChildNode} to the // worklist. @@ -962,15 +971,14 @@ processUsersOfValue(ProjectionTree &Tree, } } -void -ProjectionTreeNode:: -createNextLevelChildrenForStruct(ProjectionTree &Tree, StructDecl *SD) { +void ProjectionTreeNode::createNextLevelChildrenForStruct( + ProjectionTree &Tree, TypeExpansionContext context, StructDecl *SD) { SILModule &Mod = Tree.getModule(); unsigned ChildIndex = 0; SILType Ty = getType(); for (VarDecl *VD : SD->getStoredProperties()) { assert(Tree.getNode(Index) == this && "Node is not mapped to itself?"); - SILType NodeTy = Ty.getFieldType(VD, Mod); + SILType NodeTy = Ty.getFieldType(VD, Mod, context); auto *Node = Tree.createChildForStruct(this, NodeTy, VD, ChildIndex++); LLVM_DEBUG(llvm::dbgs() << " Creating child for: " < elts) { return createTuple(loc, tupleType, elts); } -SILType SILBuilder::getPartialApplyResultType(SILType origTy, unsigned argCount, - SILModule &M, - SubstitutionMap subs, - ParameterConvention calleeConvention, - PartialApplyInst::OnStackKind onStack) { +SILType SILBuilder::getPartialApplyResultType( + TypeExpansionContext context, SILType origTy, unsigned argCount, + SILModule &M, SubstitutionMap subs, ParameterConvention calleeConvention, + PartialApplyInst::OnStackKind onStack) { CanSILFunctionType FTI = origTy.castTo(); if (!subs.empty()) - FTI = FTI->substGenericArgs(M, subs); - + FTI = FTI->substGenericArgs(M, subs, context); + assert(!FTI->isPolymorphic() && "must provide substitutions for generic partial_apply"); auto params = FTI->getParameters(); @@ -104,7 +103,8 @@ ProjectBoxInst *SILBuilder::createProjectBox(SILLocation Loc, SILValue boxOperand, unsigned index) { auto boxTy = boxOperand->getType().castTo(); - auto fieldTy = getSILBoxFieldType(boxTy, getModule().Types, index); + auto fieldTy = getSILBoxFieldType(getTypeExpansionContext(), boxTy, + getModule().Types, index); return insert(new (getModule()) ProjectBoxInst( getSILDebugLocation(Loc), boxOperand, index, fieldTy)); @@ -511,11 +511,11 @@ void SILBuilder::addOpenedArchetypeOperands(SILInstruction *I) { ValueMetatypeInst *SILBuilder::createValueMetatype(SILLocation Loc, SILType MetatypeTy, SILValue Base) { - assert( - Base->getType().isLoweringOf( - getModule(), MetatypeTy.castTo().getInstanceType()) && - "value_metatype result must be formal metatype of the lowered operand " - "type"); + assert(Base->getType().isLoweringOf( + getTypeExpansionContext(), getModule(), + MetatypeTy.castTo().getInstanceType()) && + "value_metatype result must be formal metatype of the lowered operand " + "type"); return insert(new (getModule()) ValueMetatypeInst(getSILDebugLocation(Loc), MetatypeTy, Base)); } @@ -541,7 +541,8 @@ void SILBuilder::emitDestructureValueOperation( // In non qualified ownership SIL, drop back to using projection code. SmallVector projections; - Projection::getFirstLevelProjections(v->getType(), getModule(), projections); + Projection::getFirstLevelProjections(v->getType(), getModule(), + getTypeExpansionContext(), projections); llvm::transform(projections, std::back_inserter(results), [&](const Projection &p) -> SILValue { return p.createObjectProjection(*this, loc, v).get(); @@ -562,7 +563,8 @@ void SILBuilder::emitDestructureAddressOperation( } SmallVector projections; - Projection::getFirstLevelProjections(v->getType(), getModule(), projections); + Projection::getFirstLevelProjections(v->getType(), getModule(), + getTypeExpansionContext(), projections); llvm::transform(projections, std::back_inserter(results), [&](const Projection &p) -> SILValue { return p.createAddressProjection(*this, loc, v).get(); diff --git a/lib/SIL/SILFunction.cpp b/lib/SIL/SILFunction.cpp index 9129fdea5ccc6..44905ea1b1504 100644 --- a/lib/SIL/SILFunction.cpp +++ b/lib/SIL/SILFunction.cpp @@ -261,6 +261,9 @@ const TypeLowering &SILFunction::getTypeLowering(SILType type) const { return getModule().Types.getTypeLowering(type, TypeExpansionContext(*this)); } +SILType SILFunction::getLoweredType(SILType t) const { + return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); +} bool SILFunction::isTypeABIAccessible(SILType type) const { return getModule().isTypeABIAccessible(type, TypeExpansionContext(*this)); } diff --git a/lib/SIL/SILFunctionBuilder.cpp b/lib/SIL/SILFunctionBuilder.cpp index e2abe75068a65..85c962470be37 100644 --- a/lib/SIL/SILFunctionBuilder.cpp +++ b/lib/SIL/SILFunctionBuilder.cpp @@ -107,7 +107,8 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant, ForDefinition_t forDefinition, ProfileCounter entryCount) { auto nameTmp = constant.mangle(); - auto constantType = mod.Types.getConstantFunctionType(constant); + auto constantType = mod.Types.getConstantFunctionType( + TypeExpansionContext::minimal(), constant); SILLinkage linkage = constant.getLinkage(forDefinition); if (auto fn = mod.lookUpFunction(nameTmp)) { diff --git a/lib/SIL/SILGlobalVariable.cpp b/lib/SIL/SILGlobalVariable.cpp index 48d66dd76e3de..733ad528dcd52 100644 --- a/lib/SIL/SILGlobalVariable.cpp +++ b/lib/SIL/SILGlobalVariable.cpp @@ -310,3 +310,14 @@ swift::getVariableOfStaticInitializer(SILFunction *InitFunc, return nullptr; return GVar; } + +SILType +SILGlobalVariable::getLoweredTypeInContext(TypeExpansionContext context) const { + auto ty = getLoweredType(); + if (!ty.getASTType()->hasOpaqueArchetype() || + !context.shouldLookThroughOpaqueTypeArchetypes()) + return ty; + auto resultTy = + getModule().Types.getTypeLowering(ty, context).getLoweredType(); + return resultTy.getCategoryType(ty.getCategory()); +} diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 49e627dd18fd2..31e17ef61c79c 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -285,7 +285,9 @@ AllocBoxInst *AllocBoxInst::create(SILDebugLocation Loc, } SILType AllocBoxInst::getAddressType() const { - return getSILBoxFieldType(getBoxType(), getModule().Types, 0).getAddressType(); + return getSILBoxFieldType(TypeExpansionContext(*this->getFunction()), + getBoxType(), getModule().Types, 0) + .getAddressType(); } VarDecl *AllocBoxInst::getDecl() const { @@ -416,8 +418,8 @@ ApplyInst::create(SILDebugLocation Loc, SILValue Callee, SubstitutionMap Subs, Optional ModuleConventions, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo) { - SILType SubstCalleeSILTy = - Callee->getType().substGenericArgs(F.getModule(), Subs); + SILType SubstCalleeSILTy = Callee->getType().substGenericArgs( + F.getModule(), Subs, F.getTypeExpansionContext()); auto SubstCalleeTy = SubstCalleeSILTy.getAs(); SILFunctionConventions Conv(SubstCalleeTy, ModuleConventions.hasValue() @@ -462,8 +464,8 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee, SILFunction &F, SILOpenedArchetypesState &openedArchetypes, const GenericSpecializationInformation *specializationInfo) { - SILType substCalleeSILType = - callee->getType().substGenericArgs(F.getModule(), subs); + SILType substCalleeSILType = callee->getType().substGenericArgs( + F.getModule(), subs, F.getTypeExpansionContext()); auto substCalleeType = substCalleeSILType.castTo(); SILFunctionConventions conv(substCalleeType, @@ -553,10 +555,11 @@ PartialApplyInst *PartialApplyInst::create( SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo, OnStackKind onStack) { - SILType SubstCalleeTy = - Callee->getType().substGenericArgs(F.getModule(), Subs); + SILType SubstCalleeTy = Callee->getType().substGenericArgs( + F.getModule(), Subs, F.getTypeExpansionContext()); SILType ClosureType = SILBuilder::getPartialApplyResultType( - SubstCalleeTy, Args.size(), F.getModule(), {}, CalleeConvention, onStack); + F.getTypeExpansionContext(), SubstCalleeTy, Args.size(), F.getModule(), {}, + CalleeConvention, onStack); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, @@ -591,8 +594,8 @@ TryApplyInst *TryApplyInst::create( ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB, SILFunction &F, SILOpenedArchetypesState &openedArchetypes, const GenericSpecializationInformation *specializationInfo) { - SILType substCalleeTy = - callee->getType().substGenericArgs(F.getModule(), subs); + SILType substCalleeTy = callee->getType().substGenericArgs( + F.getModule(), subs, F.getTypeExpansionContext()); SmallVector typeDependentOperands; collectTypeDependentOperands(typeDependentOperands, openedArchetypes, F, @@ -608,8 +611,9 @@ TryApplyInst *TryApplyInst::create( FunctionRefBaseInst::FunctionRefBaseInst(SILInstructionKind Kind, SILDebugLocation DebugLoc, - SILFunction *F) - : LiteralInst(Kind, DebugLoc, F->getLoweredType()), f(F) { + SILFunction *F, + TypeExpansionContext context) + : LiteralInst(Kind, DebugLoc, F->getLoweredTypeInContext(context)), f(F) { F->incrementRefCount(); } @@ -624,21 +628,25 @@ FunctionRefBaseInst::~FunctionRefBaseInst() { getInitiallyReferencedFunction()->decrementRefCount(); } -FunctionRefInst::FunctionRefInst(SILDebugLocation Loc, SILFunction *F) - : FunctionRefBaseInst(SILInstructionKind::FunctionRefInst, Loc, F) { +FunctionRefInst::FunctionRefInst(SILDebugLocation Loc, SILFunction *F, + TypeExpansionContext context) + : FunctionRefBaseInst(SILInstructionKind::FunctionRefInst, Loc, F, + context) { assert(!F->isDynamicallyReplaceable()); } DynamicFunctionRefInst::DynamicFunctionRefInst(SILDebugLocation Loc, - SILFunction *F) - : FunctionRefBaseInst(SILInstructionKind::DynamicFunctionRefInst, Loc, F) { + SILFunction *F, + TypeExpansionContext context) + : FunctionRefBaseInst(SILInstructionKind::DynamicFunctionRefInst, Loc, F, + context) { assert(F->isDynamicallyReplaceable()); } PreviousDynamicFunctionRefInst::PreviousDynamicFunctionRefInst( - SILDebugLocation Loc, SILFunction *F) + SILDebugLocation Loc, SILFunction *F, TypeExpansionContext context) : FunctionRefBaseInst(SILInstructionKind::PreviousDynamicFunctionRefInst, - Loc, F) { + Loc, F, context) { assert(!F->isDynamicallyReplaceable()); } @@ -648,16 +656,19 @@ AllocGlobalInst::AllocGlobalInst(SILDebugLocation Loc, Global(Global) {} GlobalAddrInst::GlobalAddrInst(SILDebugLocation DebugLoc, - SILGlobalVariable *Global) - : InstructionBase(DebugLoc, Global->getLoweredType().getAddressType(), + SILGlobalVariable *Global, + TypeExpansionContext context) + : InstructionBase(DebugLoc, + Global->getLoweredTypeInContext(context).getAddressType(), Global) {} GlobalValueInst::GlobalValueInst(SILDebugLocation DebugLoc, - SILGlobalVariable *Global) - : InstructionBase(DebugLoc, Global->getLoweredType().getObjectType(), + SILGlobalVariable *Global, + TypeExpansionContext context) + : InstructionBase(DebugLoc, + Global->getLoweredTypeInContext(context).getObjectType(), Global) {} - const IntrinsicInfo &BuiltinInst::getIntrinsicInfo() const { return getModule().getIntrinsicInfo(getName()); } @@ -1087,7 +1098,8 @@ bool StructExtractInst::isTrivialFieldOfOneRCIDStruct() const { // Otherwise check if we have a non-trivial type. If we don't have one, // continue. - if (StructTy.getFieldType(D, F->getModule()).isTrivial(*F)) + if (StructTy.getFieldType(D, F->getModule(), TypeExpansionContext(*F)) + .isTrivial(*F)) continue; // Ok, this type is non-trivial. If we have not seen a non-trivial field @@ -1132,7 +1144,8 @@ bool StructExtractInst::isFieldOnlyNonTrivialField() const { // Ok, we have a field that is not equal to the field we are // extracting. If that field is trivial, we do not care about // it... continue. - if (StructTy.getFieldType(D, F->getModule()).isTrivial(*F)) + if (StructTy.getFieldType(D, F->getModule(), TypeExpansionContext(*F)) + .isTrivial(*F)) continue; // We have found a non trivial member that is not the member we are @@ -2450,11 +2463,12 @@ static void computeAggregateFirstLevelSubtypeInfo( // TODO: Create an iterator for accessing first level projections to eliminate // this SmallVector. llvm::SmallVector Projections; - Projection::getFirstLevelProjections(OpType, M, Projections); + Projection::getFirstLevelProjections(OpType, M, F.getTypeExpansionContext(), + Projections); auto OpOwnershipKind = Operand.getOwnershipKind(); for (auto &P : Projections) { - SILType ProjType = P.getType(OpType, M); + SILType ProjType = P.getType(OpType, M, F.getTypeExpansionContext()); Types.emplace_back(ProjType); OwnershipKinds.emplace_back( OpOwnershipKind.getProjectedOwnershipKind(F, ProjType)); diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 610649ac1c1b3..7aafd4d378133 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -134,14 +134,15 @@ void verifyKeyPathComponent(SILModule &M, bool forPropertyDescriptor, bool hasIndices) { auto &C = M.getASTContext(); - + auto typeExpansionContext = + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion); auto opaque = AbstractionPattern::getOpaque(); auto loweredBaseTy = - M.Types.getLoweredType(opaque, baseTy, expansion); + M.Types.getLoweredType(opaque, baseTy, typeExpansionContext); auto componentTy = component.getComponentType().subst(patternSubs) ->getCanonicalType(); auto loweredComponentTy = - M.Types.getLoweredType(opaque, componentTy, expansion); + M.Types.getLoweredType(opaque, componentTy, typeExpansionContext); auto checkIndexEqualsAndHash = [&]{ if (!component.getSubscriptIndices().empty()) { @@ -153,7 +154,7 @@ void verifyKeyPathComponent(SILModule &M, "operator"); auto substEqualsType = equals->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substEqualsType->getParameters().size() == 2, "must have two arguments"); @@ -186,7 +187,7 @@ void verifyKeyPathComponent(SILModule &M, "operator"); auto substHashType = hash->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substHashType->getParameters().size() == 1, "must have two arguments"); @@ -234,7 +235,8 @@ void verifyKeyPathComponent(SILModule &M, require(property->hasStorage(), "property must be stored"); require(!property->isResilient(M.getSwiftModule(), expansion), "cannot access storage of resilient property"); - auto propertyTy = loweredBaseTy.getFieldType(property, M); + auto propertyTy = + loweredBaseTy.getFieldType(property, M, typeExpansionContext); require(propertyTy.getObjectType() == loweredComponentTy.getObjectType(), "component type should match the maximal abstraction of the " @@ -269,8 +271,8 @@ void verifyKeyPathComponent(SILModule &M, "less visible getters"); } - auto substGetterType = getter->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + auto substGetterType = getter->getLoweredFunctionType()->substGenericArgs( + M, patternSubs, TypeExpansionContext::minimal()); require(substGetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, "getter should be a thin function"); @@ -317,7 +319,7 @@ void verifyKeyPathComponent(SILModule &M, } auto substSetterType = setter->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substSetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, @@ -366,8 +368,9 @@ void verifyKeyPathComponent(SILModule &M, require(contextType == operands[opIndex].get()->getType(), "operand must match type required by pattern"); SILType loweredType = index.LoweredType; - require(loweredType.isLoweringOf(M, index.FormalType), - "pattern index formal type doesn't match lowered type"); + require( + loweredType.isLoweringOf(typeExpansionContext, M, index.FormalType), + "pattern index formal type doesn't match lowered type"); } checkIndexEqualsAndHash(); @@ -1301,7 +1304,7 @@ class SILVerifier : public SILVerifierBase { } // Apply the substitutions. - return fnTy->substGenericArgs(F.getModule(), subs); + return fnTy->substGenericArgs(F.getModule(), subs, F.getTypeExpansionContext()); } /// Check that for each opened archetype or dynamic self type in substitutions @@ -1743,7 +1746,8 @@ class SILVerifier : public SILVerifierBase { void checkGlobalAccessInst(GlobalAccessInst *GAI) { SILGlobalVariable *RefG = GAI->getReferencedGlobal(); - require(GAI->getType().getObjectType() == RefG->getLoweredType(), + require(GAI->getType().getObjectType() == + RefG->getLoweredTypeInContext(F.getTypeExpansionContext()), "global_addr/value must be the type of the variable it references"); if (auto *VD = RefG->getDecl()) { require(!VD->isResilient(F.getModule().getSwiftModule(), @@ -2324,7 +2328,8 @@ class SILVerifier : public SILVerifierBase { "project_box operand should be a value"); auto boxTy = I->getOperand()->getType().getAs(); require(boxTy, "project_box operand should be a @box type"); - require(I->getType() == getSILBoxFieldType(boxTy, F.getModule().Types, + require(I->getType() == getSILBoxFieldType(F.getTypeExpansionContext(), boxTy, + F.getModule().Types, I->getFieldIndex()), "project_box result should be address of boxed type"); @@ -2393,7 +2398,8 @@ class SILVerifier : public SILVerifierBase { "number of struct operands does not match number of stored " "member variables of struct"); - SILType loweredType = structTy.getFieldType(field, F.getModule()); + SILType loweredType = + structTy.getFieldType(field, F.getModule(), F.getTypeExpansionContext()); if (SI->getModule().getStage() != SILStage::Lowered) { require((*opi)->getType() == loweredType, "struct operand type does not match field type"); @@ -2415,8 +2421,8 @@ class SILVerifier : public SILVerifierBase { if (UI->getElement()->hasAssociatedValues()) { require(UI->getOperand()->getType().isObject(), "EnumInst operand must be an object"); - SILType caseTy = UI->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { require(caseTy == UI->getOperand()->getType(), "EnumInst operand type does not match type of case"); @@ -2436,9 +2442,8 @@ class SILVerifier : public SILVerifierBase { require(UI->getType().isAddress(), "InitEnumDataAddrInst must produce an address"); - SILType caseTy = - UI->getOperand()->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getOperand()->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { requireSameType( @@ -2459,9 +2464,8 @@ class SILVerifier : public SILVerifierBase { require(UI->getType().isObject(), "UncheckedEnumData must produce an address"); - SILType caseTy = - UI->getOperand()->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getOperand()->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { require(caseTy == UI->getType(), @@ -2481,9 +2485,8 @@ class SILVerifier : public SILVerifierBase { require(UI->getType().isAddress(), "UncheckedTakeEnumDataAddrInst must produce an address"); - SILType caseTy = - UI->getOperand()->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getOperand()->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { require(caseTy == UI->getType(), "UncheckedTakeEnumDataAddrInst result " @@ -2519,7 +2522,8 @@ class SILVerifier : public SILVerifierBase { // Is a SIL type a potential lowering of a formal type? bool isLoweringOf(SILType loweredType, CanType formalType) { - return loweredType.isLoweringOf(F.getModule(), formalType); + return loweredType.isLoweringOf(F.getTypeExpansionContext(), F.getModule(), + formalType); } void checkMetatypeInst(MetatypeInst *MI) { @@ -2609,9 +2613,9 @@ class SILVerifier : public SILVerifierBase { require(AI->getType().isObject(), "result of alloc_box must be an object"); for (unsigned field : indices(AI->getBoxType()->getLayout()->getFields())) { - verifyOpenedArchetype(AI, - getSILBoxFieldLoweredType(AI->getBoxType(), F.getModule().Types, - field)); + verifyOpenedArchetype(AI, getSILBoxFieldLoweredType( + F.getTypeExpansionContext(), AI->getBoxType(), + F.getModule().Types, field)); } // An alloc_box with a mark_uninitialized user can not have any other users. @@ -2705,8 +2709,8 @@ class SILVerifier : public SILVerifierBase { "struct_extract field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { - SILType loweredFieldTy = - operandTy.getFieldType(EI->getField(), F.getModule()); + SILType loweredFieldTy = operandTy.getFieldType( + EI->getField(), F.getModule(), F.getTypeExpansionContext()); require(loweredFieldTy == EI->getType(), "result of struct_extract does not match type of field"); } @@ -2751,8 +2755,8 @@ class SILVerifier : public SILVerifierBase { "struct_element_addr field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { - SILType loweredFieldTy = - operandTy.getFieldType(EI->getField(), F.getModule()); + SILType loweredFieldTy = operandTy.getFieldType( + EI->getField(), F.getModule(), F.getTypeExpansionContext()); require(loweredFieldTy == EI->getType(), "result of struct_element_addr does not match type of field"); } @@ -2777,8 +2781,8 @@ class SILVerifier : public SILVerifierBase { "ref_element_addr field must be a member of the class"); if (EI->getModule().getStage() != SILStage::Lowered) { - SILType loweredFieldTy = - operandTy.getFieldType(EI->getField(), F.getModule()); + SILType loweredFieldTy = operandTy.getFieldType( + EI->getField(), F.getModule(), F.getTypeExpansionContext()); require(loweredFieldTy == EI->getType(), "result of ref_element_addr does not match type of field"); } @@ -2876,7 +2880,8 @@ class SILVerifier : public SILVerifierBase { // The type of the dynamic method must match the usual type of the method, // but with the more opaque Self type. - auto constantInfo = F.getModule().Types.getConstantInfo(method); + auto constantInfo = + F.getModule().Types.getConstantInfo(F.getTypeExpansionContext(), method); auto methodTy = constantInfo.SILFnType; assert(!methodTy->isCoroutine()); @@ -2884,7 +2889,8 @@ class SILVerifier : public SILVerifierBase { // Map interface types to archetypes. if (auto *env = F.getModule().Types.getConstantGenericEnvironment(method)) { auto subs = env->getForwardingSubstitutionMap(); - methodTy = methodTy->substGenericArgs(F.getModule(), subs); + methodTy = methodTy->substGenericArgs(F.getModule(), subs, + F.getTypeExpansionContext()); } assert(!methodTy->isPolymorphic()); @@ -2954,7 +2960,8 @@ class SILVerifier : public SILVerifierBase { void checkClassMethodInst(ClassMethodInst *CMI) { auto member = CMI->getMember(); - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (CMI->getModule().getStage() != SILStage::Lowered) { requireSameType( CMI->getType(), SILType::getPrimitiveObjectType(overrideTy), @@ -2982,7 +2989,8 @@ class SILVerifier : public SILVerifierBase { void checkSuperMethodInst(SuperMethodInst *CMI) { auto member = CMI->getMember(); - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (CMI->getModule().getStage() != SILStage::Lowered) { requireSameType( CMI->getType(), SILType::getPrimitiveObjectType(overrideTy), @@ -3033,7 +3041,8 @@ class SILVerifier : public SILVerifierBase { operandInstanceType = metatypeType.getInstanceType(); if (operandInstanceType.getClassOrBoundGenericClass()) { - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); requireSameType( OMI->getType(), SILType::getPrimitiveObjectType(overrideTy), "result type of objc_method must match abstracted type of method"); @@ -3058,7 +3067,8 @@ class SILVerifier : public SILVerifierBase { void checkObjCSuperMethodInst(ObjCSuperMethodInst *OMI) { auto member = OMI->getMember(); - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (OMI->getModule().getStage() != SILStage::Lowered) { requireSameType( OMI->getType(), SILType::getPrimitiveObjectType(overrideTy), @@ -3901,7 +3911,9 @@ class SILVerifier : public SILVerifierBase { LLVM_DEBUG(RI->print(llvm::dbgs())); SILType functionResultType = - F.mapTypeIntoContext(fnConv.getSILResultType()); + F.getLoweredType( + F.mapTypeIntoContext(fnConv.getSILResultType()).getASTType()) + .getCategoryType(fnConv.getSILResultType().getCategory()); SILType instResultType = RI->getOperand()->getType(); LLVM_DEBUG(llvm::dbgs() << "function return type: "; functionResultType.dump(); @@ -3914,11 +3926,15 @@ class SILVerifier : public SILVerifierBase { void checkThrowInst(ThrowInst *TI) { LLVM_DEBUG(TI->print(llvm::dbgs())); - CanSILFunctionType fnType = F.getLoweredFunctionType(); + CanSILFunctionType fnType = + F.getLoweredFunctionTypeInContext(F.getTypeExpansionContext()); require(fnType->hasErrorResult(), "throw in function that doesn't have an error result"); - SILType functionResultType = F.mapTypeIntoContext(fnConv.getSILErrorType()); + SILType functionResultType = + F.getLoweredType( + F.mapTypeIntoContext(fnConv.getSILErrorType()).getASTType()) + .getCategoryType(fnConv.getSILErrorType().getCategory()); SILType instResultType = TI->getOperand()->getType(); LLVM_DEBUG(llvm::dbgs() << "function error result type: "; functionResultType.dump(); @@ -3934,7 +3950,8 @@ class SILVerifier : public SILVerifierBase { } void checkYieldInst(YieldInst *YI) { - CanSILFunctionType fnType = F.getLoweredFunctionType(); + CanSILFunctionType fnType = + F.getLoweredFunctionTypeInContext(F.getTypeExpansionContext()); require(fnType->isCoroutine(), "yield in non-coroutine function"); @@ -4134,7 +4151,8 @@ class SILVerifier : public SILVerifierBase { } if (dest->getArguments().size() == 1) { - SILType eltArgTy = uTy.getEnumElementType(elt, F.getModule()); + SILType eltArgTy = uTy.getEnumElementType(elt, F.getModule(), + F.getTypeExpansionContext()); SILType bbArgTy = dest->getArguments()[0]->getType(); if (F.getModule().getStage() != SILStage::Lowered) { // During the lowered stage, a function type might have different @@ -4540,7 +4558,9 @@ class SILVerifier : public SILVerifierBase { auto mappedTy = F.mapTypeIntoContext(ty); SILArgument *bbarg = *argI; ++argI; - if (bbarg->getType() != mappedTy) { + if (bbarg->getType() != mappedTy && + bbarg->getType() != F.getLoweredType(mappedTy.getASTType()) + .getCategoryType(mappedTy.getCategory())) { llvm::errs() << what << " type mismatch!\n"; llvm::errs() << " argument: "; bbarg->dump(); llvm::errs() << " expected: "; mappedTy.dump(); @@ -5145,7 +5165,8 @@ void SILVTable::verify(const SILModule &M) const { for (auto &entry : getEntries()) { // All vtable entries must be decls in a class context. assert(entry.Method.hasDecl() && "vtable entry is not a decl"); - auto baseInfo = M.Types.getConstantInfo(entry.Method); + auto baseInfo = + M.Types.getConstantInfo(TypeExpansionContext::minimal(), entry.Method); ValueDecl *decl = entry.Method.getDecl(); assert((!isa(decl) From e9971100d91ae1e3470daf5960f2e79a5104311f Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 11:58:43 -0800 Subject: [PATCH 068/283] Add TypeExpansionContext to SIL serialization and parsing --- lib/ParseSIL/ParseSIL.cpp | 15 +- lib/Serialization/DeserializeSIL.cpp | 641 ++++++++++++++------------- lib/Serialization/DeserializeSIL.h | 3 + 3 files changed, 346 insertions(+), 313 deletions(-) diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index de34626cd11e3..706aa218fcd52 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -4082,8 +4082,9 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; EnumElementDecl *Elt = cast(EltRef.getDecl()); - auto ResultTy = Operand->getType().getEnumElementType(Elt, SILMod); - + auto ResultTy = Operand->getType().getEnumElementType( + Elt, SILMod, B.getTypeExpansionContext()); + switch (Opcode) { case swift::SILInstructionKind::InitEnumDataAddrInst: ResultVal = B.createInitEnumDataAddr(InstLoc, Operand, Elt, ResultTy); @@ -4431,7 +4432,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { // FIXME: substitution means this type should be explicit to improve // performance. - auto ResultTy = Val->getType().getFieldType(Field, SILMod); + auto ResultTy = + Val->getType().getFieldType(Field, SILMod, B.getTypeExpansionContext()); if (Opcode == SILInstructionKind::StructElementAddrInst) ResultVal = B.createStructElementAddr(InstLoc, Val, Field, ResultTy.getAddressType()); @@ -4453,7 +4455,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } VarDecl *Field = cast(FieldV); - auto ResultTy = Val->getType().getFieldType(Field, SILMod); + auto ResultTy = + Val->getType().getFieldType(Field, SILMod, B.getTypeExpansionContext()); ResultVal = B.createRefElementAddr(InstLoc, Val, Field, ResultTy); break; } @@ -5136,8 +5139,8 @@ bool SILParser::parseCallInstruction(SILLocation InstLoc, CanSILFunctionType substFTI = FTI; if (!subs.empty()) { auto silFnTy = FnTy.castTo(); - substFTI - = silFnTy->substGenericArgs(SILMod, subs); + substFTI = + silFnTy->substGenericArgs(SILMod, subs, B.getTypeExpansionContext()); FnTy = SILType::getPrimitiveObjectType(substFTI); } SILFunctionConventions substConv(substFTI, B.getModule()); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 3eeae17f1c77b..751a2ca9c169c 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -326,10 +326,15 @@ SILBasicBlock *SILDeserializer::getBBForReference(SILFunction *Fn, } /// Helper function to convert from Type to SILType. -static SILType getSILType(Type Ty, SILValueCategory Category) { +SILType SILDeserializer::getSILType(Type Ty, SILValueCategory Category, + SILFunction *inContext) { auto TyLoc = TypeLoc::withoutLoc(Ty); - return SILType::getPrimitiveType(TyLoc.getType()->getCanonicalType(), - Category); + if (!inContext) { + return SILType::getPrimitiveType(TyLoc.getType()->getCanonicalType(), + Category); + } + return inContext->getLoweredType(TyLoc.getType()->getCanonicalType()) + .getCategoryType(Category); } /// Helper function to find a SILFunction, given its name and type. @@ -494,7 +499,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, llvm::consumeError(astType.takeError()); return existingFn; } - auto ty = getSILType(astType.get(), SILValueCategory::Object); + auto ty = getSILType(astType.get(), SILValueCategory::Object, nullptr); if (!ty.is()) { LLVM_DEBUG(llvm::dbgs() << "not a function type for SILFunction\n"); MF->fatal(); @@ -806,7 +811,7 @@ SILBasicBlock *SILDeserializer::readSILBasicBlock(SILFunction *Fn, auto ArgTy = MF->getType(TyID); SILArgument *Arg; auto ValueCategory = SILValueCategory(Args[I + 1] & 0xF); - SILType SILArgTy = getSILType(ArgTy, ValueCategory); + SILType SILArgTy = getSILType(ArgTy, ValueCategory, Fn); if (IsEntry) { Arg = CurrentBB->createFunctionArgument(SILArgTy); } else { @@ -1101,68 +1106,68 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, break; case SILInstructionKind::AllocStackInst: assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); - ResultVal = Builder.createAllocStack(Loc, - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), - None, /*bool hasDynamicLifetime*/ Attr != 0); + ResultVal = Builder.createAllocStack( + Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), + None, /*bool hasDynamicLifetime*/ Attr != 0); break; case SILInstructionKind::MetatypeInst: assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); - ResultVal = Builder.createMetatype(Loc, - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); - break; - -#define ONETYPE_ONEOPERAND_INST(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ - "Layout should be OneTypeOneOperand."); \ - ResultVal = Builder.create##ID(Loc, \ - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), \ - getLocalValue(ValID, \ - getSILType(MF->getType(TyID2), \ - (SILValueCategory)TyCategory2))); \ - break; - ONETYPE_ONEOPERAND_INST(ValueMetatype) - ONETYPE_ONEOPERAND_INST(ExistentialMetatype) - ONETYPE_ONEOPERAND_INST(AllocValueBuffer) - ONETYPE_ONEOPERAND_INST(ProjectValueBuffer) - ONETYPE_ONEOPERAND_INST(ProjectExistentialBox) - ONETYPE_ONEOPERAND_INST(DeallocValueBuffer) + ResultVal = Builder.createMetatype( + Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + break; + +#define ONETYPE_ONEOPERAND_INST(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ + "Layout should be OneTypeOneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), \ + getLocalValue(ValID, getSILType(MF->getType(TyID2), \ + (SILValueCategory)TyCategory2, Fn))); \ + break; + ONETYPE_ONEOPERAND_INST(ValueMetatype) + ONETYPE_ONEOPERAND_INST(ExistentialMetatype) + ONETYPE_ONEOPERAND_INST(AllocValueBuffer) + ONETYPE_ONEOPERAND_INST(ProjectValueBuffer) + ONETYPE_ONEOPERAND_INST(ProjectExistentialBox) + ONETYPE_ONEOPERAND_INST(DeallocValueBuffer) #undef ONETYPE_ONEOPERAND_INST case SILInstructionKind::DeallocBoxInst: assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); - ResultVal = Builder.createDeallocBox(Loc, - getLocalValue(ValID, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2))); + ResultVal = Builder.createDeallocBox( + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn))); break; case SILInstructionKind::OpenExistentialAddrInst: assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); ResultVal = Builder.createOpenExistentialAddr( - Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), Attr == 0 ? OpenedExistentialAccess::Immutable : OpenedExistentialAccess::Mutable); break; -#define ONEOPERAND_ONETYPE_INST(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ - "Layout should be OneTypeOneOperand."); \ - ResultVal = Builder.create##ID(Loc, \ - getLocalValue(ValID, \ - getSILType(MF->getType(TyID2), \ - (SILValueCategory)TyCategory2)), \ - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory));\ - break; - ONEOPERAND_ONETYPE_INST(OpenExistentialRef) - ONEOPERAND_ONETYPE_INST(OpenExistentialMetatype) - ONEOPERAND_ONETYPE_INST(OpenExistentialBox) - ONEOPERAND_ONETYPE_INST(OpenExistentialValue) - ONEOPERAND_ONETYPE_INST(OpenExistentialBoxValue) - // Conversion instructions. +#define ONEOPERAND_ONETYPE_INST(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ + "Layout should be OneTypeOneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, \ + getLocalValue(ValID, getSILType(MF->getType(TyID2), \ + (SILValueCategory)TyCategory2, Fn)), \ + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); \ + break; + ONEOPERAND_ONETYPE_INST(OpenExistentialRef) + ONEOPERAND_ONETYPE_INST(OpenExistentialMetatype) + ONEOPERAND_ONETYPE_INST(OpenExistentialBox) + ONEOPERAND_ONETYPE_INST(OpenExistentialValue) + ONEOPERAND_ONETYPE_INST(OpenExistentialBoxValue) + // Conversion instructions. #define LOADABLE_REF_STORAGE(Name, ...) \ ONEOPERAND_ONETYPE_INST(RefTo##Name) \ ONEOPERAND_ONETYPE_INST(Name##ToRef) @@ -1190,11 +1195,11 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::ProjectBoxInst: { assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); - ResultVal = Builder.createProjectBox(Loc, - getLocalValue(ValID, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - TyID); + ResultVal = Builder.createProjectBox( + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + TyID); break; } case SILInstructionKind::ConvertEscapeToNoEscapeInst: { @@ -1204,8 +1209,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ResultVal = Builder.createConvertEscapeToNoEscape( Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), isLifetimeGuaranteed); break; } @@ -1216,8 +1221,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ResultVal = Builder.createConvertFunction( Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), withoutActuallyEscaping); break; } @@ -1227,29 +1232,30 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, bool isStrict = Attr & 0x01; bool isInvariant = Attr & 0x02; ResultVal = Builder.createPointerToAddress( - Loc, - getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), - isStrict, isInvariant); + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), isStrict, + isInvariant); break; } case SILInstructionKind::DeallocExistentialBoxInst: { assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); - ResultVal = Builder.createDeallocExistentialBox(Loc, - MF->getType(TyID)->getCanonicalType(), - getLocalValue(ValID, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2))); + ResultVal = Builder.createDeallocExistentialBox( + Loc, MF->getType(TyID)->getCanonicalType(), + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::RefToBridgeObjectInst: { - auto RefTy = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto RefTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Ref = getLocalValue(ValID, RefTy); - auto BitsTy = getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2); + auto BitsTy = + getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2, Fn); auto Bits = getLocalValue(ValID2, BitsTy); ResultVal = Builder.createRefToBridgeObject(Loc, Ref, Bits); @@ -1257,7 +1263,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::ObjCProtocolInst: { - auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Proto = MF->getDecl(ValID); ResultVal = Builder.createObjCProtocol(Loc, cast(Proto), Ty); break; @@ -1269,15 +1275,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::InitExistentialRefInst: case SILInstructionKind::AllocExistentialBoxInst: { - auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Ty2 = MF->getType(TyID2); CanType ConcreteTy; if (OpCode != SILInstructionKind::InitExistentialMetatypeInst) ConcreteTy = MF->getType(ConcreteTyID)->getCanonicalType(); SILValue operand; if (OpCode != SILInstructionKind::AllocExistentialBoxInst) - operand = getLocalValue(ValID, - getSILType(Ty2, (SILValueCategory)TyCategory2)); + operand = getLocalValue( + ValID, getSILType(Ty2, (SILValueCategory)TyCategory2, Fn)); SmallVector conformances; while (NumConformances--) { @@ -1325,16 +1331,17 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, unsigned Flags = ListOfValues[0]; bool isObjC = (bool)(Flags & 1); bool canAllocOnStack = (bool)((Flags >> 1) & 1); - SILType ClassTy = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType ClassTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SmallVector Counts; SmallVector TailTypes; unsigned i = 1; for (; i + 2 < NumVals; i += 3) { SILType TailType = getSILType(MF->getType(ListOfValues[i]), - SILValueCategory::Object); + SILValueCategory::Object, Fn); TailTypes.push_back(TailType); - SILType CountType = getSILType(MF->getType(ListOfValues[i+2]), - SILValueCategory::Object); + SILType CountType = getSILType(MF->getType(ListOfValues[i + 2]), + SILValueCategory::Object, Fn); SILValue CountVal = getLocalValue(ListOfValues[i+1], CountType); Counts.push_back(CountVal); } @@ -1342,7 +1349,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, assert(i + 2 == NumVals); assert(!canAllocOnStack); SILType MetadataType = getSILType(MF->getType(ListOfValues[i+1]), - SILValueCategory::Object); + SILValueCategory::Object, Fn); SILValue MetadataOp = getLocalValue(ListOfValues[i], MetadataType); ResultVal = Builder.createAllocRefDynamic(Loc, MetadataOp, ClassTy, isObjC, TailTypes, Counts); @@ -1360,8 +1367,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // is represented with 2 IDs: ValueID and ValueResultNumber. auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object); - SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); + SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object, Fn); SILFunctionConventions substConventions(SubstFnTy.castTo(), Builder.getModule()); assert(substConventions.getNumSILArguments() == ListOfValues.size() @@ -1390,8 +1397,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // two values in the list are the basic block identifiers. auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object); - SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, nullptr); + SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object, Fn); SILBasicBlock *errorBB = getBBForReference(Fn, ListOfValues.back()); ListOfValues = ListOfValues.drop_back(); @@ -1416,14 +1423,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::PartialApplyInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object); - SILType closureTy = getSILType(Ty2, SILValueCategory::Object); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, nullptr); + SILType closureTy = getSILType(Ty2, SILValueCategory::Object, Fn); SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); auto SubstFnTy = SILType::getPrimitiveObjectType( - FnTy.castTo() - ->substGenericArgs(Builder.getModule(), Substitutions)); + FnTy.castTo()->substGenericArgs( + Builder.getModule(), Substitutions, + Builder.getTypeExpansionContext())); SILFunctionConventions fnConv(SubstFnTy.castTo(), Builder.getModule()); @@ -1448,12 +1456,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::BuiltinInst: { auto ASTTy = MF->getType(TyID); - auto ResultTy = getSILType(ASTTy, (SILValueCategory)(unsigned)TyID2); + auto ResultTy = getSILType(ASTTy, (SILValueCategory)(unsigned)TyID2, Fn); SmallVector Args; for (unsigned i = 0, e = ListOfValues.size(); i < e; i += 3) { auto ArgASTTy = MF->getType(ListOfValues[i+1]); - auto ArgTy = getSILType(ArgASTTy, - (SILValueCategory)(unsigned)ListOfValues[i+2]); + auto ArgTy = getSILType( + ArgASTTy, (SILValueCategory)(unsigned)ListOfValues[i + 2], Fn); Args.push_back(getLocalValue(ListOfValues[i], ArgTy)); } SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); @@ -1483,10 +1491,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // Find the global variable. SILGlobalVariable *g = getGlobalForReference(Name); assert(g && "Can't deserialize global variable"); - SILType expectedType = (OpCode == SILInstructionKind::GlobalAddrInst ? - g->getLoweredType().getAddressType() : - g->getLoweredType()); - assert(expectedType == getSILType(Ty, (SILValueCategory)TyCategory) && + SILType expectedType = + (OpCode == SILInstructionKind::GlobalAddrInst + ? g->getLoweredTypeInContext(TypeExpansionContext(*Fn)) + .getAddressType() + : g->getLoweredTypeInContext(TypeExpansionContext(*Fn))); + assert(expectedType == getSILType(Ty, (SILValueCategory)TyCategory, Fn) && "Type of a global variable does not match GlobalAddr."); (void)Ty; (void)expectedType; @@ -1499,17 +1509,18 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::DeallocStackInst: { auto Ty = MF->getType(TyID); - ResultVal = Builder.createDeallocStack(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory))); + ResultVal = Builder.createDeallocStack( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn))); break; } case SILInstructionKind::DeallocRefInst: { auto Ty = MF->getType(TyID); bool OnStack = (bool)Attr; - ResultVal = Builder.createDeallocRef(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), OnStack); + ResultVal = Builder.createDeallocRef( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), + OnStack); break; } case SILInstructionKind::DeallocPartialRefInst: { @@ -1517,81 +1528,87 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto Ty2 = MF->getType(TyID2); ResultVal = Builder.createDeallocPartialRef(Loc, getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::FunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); ResultVal = Builder.createFunctionRef( - Loc, getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + Loc, + getFuncForReference( + FuncName, getSILType(Ty, (SILValueCategory)TyCategory, nullptr))); break; } case SILInstructionKind::DynamicFunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); ResultVal = Builder.createDynamicFunctionRef( - Loc, getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + Loc, + getFuncForReference( + FuncName, getSILType(Ty, (SILValueCategory)TyCategory, nullptr))); break; } case SILInstructionKind::PreviousDynamicFunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); ResultVal = Builder.createPreviousDynamicFunctionRef( - Loc, getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + Loc, + getFuncForReference( + FuncName, getSILType(Ty, (SILValueCategory)TyCategory, nullptr))); break; } case SILInstructionKind::MarkDependenceInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - ResultVal = Builder.createMarkDependence(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + ResultVal = Builder.createMarkDependence( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::CopyBlockWithoutEscapingInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); ResultVal = Builder.createCopyBlockWithoutEscaping( - Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), - getLocalValue(ValID2, getSILType(Ty2, (SILValueCategory)TyCategory2))); + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), + getLocalValue(ValID2, + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::IndexAddrInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - ResultVal = Builder.createIndexAddr(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + ResultVal = Builder.createIndexAddr( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::TailAddrInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); auto ResultTy = MF->getType(TyID3); - ResultVal = Builder.createTailAddr(Loc, - getLocalValue(ValID, getSILType(Ty, SILValueCategory::Address)), - getLocalValue(ValID2, getSILType(Ty2, SILValueCategory::Object)), - getSILType(ResultTy, SILValueCategory::Address)); + ResultVal = Builder.createTailAddr( + Loc, + getLocalValue(ValID, getSILType(Ty, SILValueCategory::Address, Fn)), + getLocalValue(ValID2, getSILType(Ty2, SILValueCategory::Object, Fn)), + getSILType(ResultTy, SILValueCategory::Address, Fn)); break; } case SILInstructionKind::IndexRawPointerInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - ResultVal = Builder.createIndexRawPointer(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + ResultVal = Builder.createIndexRawPointer( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::IntegerLiteralInst: { @@ -1601,9 +1618,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, bool negate = text[0] == '-'; if (negate) text = text.drop_front(); APInt value = intTy->getWidth().parse(text, 10, negate); - ResultVal = Builder.createIntegerLiteral(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - value); + ResultVal = Builder.createIntegerLiteral( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), value); break; } case SILInstructionKind::FloatLiteralInst: { @@ -1617,9 +1633,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, APFloat value(floatTy->getAPFloatSemantics(), bits); - ResultVal = Builder.createFloatLiteral(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - value); + ResultVal = Builder.createFloatLiteral( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), value); break; } case SILInstructionKind::StringLiteralInst: { @@ -1631,8 +1646,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, break; } case SILInstructionKind::CondFailInst: { - SILValue Op = getLocalValue(ValID, getSILType(MF->getType(TyID), \ - (SILValueCategory)TyCategory)); + SILValue Op = getLocalValue( + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); StringRef StringVal = MF->getIdentifierText(ValID2); ResultVal = Builder.createCondFail(Loc, Op, StringVal); break; @@ -1643,39 +1658,41 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector OpList; for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { auto EltTy = MF->getType(ListOfValues[I]); - OpList.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(EltTy, (SILValueCategory)ListOfValues[I+1]))); + OpList.push_back(getLocalValue( + ListOfValues[I + 2], + getSILType(EltTy, (SILValueCategory)ListOfValues[I + 1], Fn))); } ResultVal = Builder.createMarkFunctionEscape(Loc, OpList); break; } // Checked Conversion instructions. case SILInstructionKind::UnconditionalCheckedCastInst: { - SILValue Val = getLocalValue(ValID, - getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); - SILType Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILValue Val = + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)); + SILType Ty = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); ResultVal = Builder.createUnconditionalCheckedCast(Loc, Val, Ty); break; } -#define UNARY_INSTRUCTION(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_OPERAND && \ - "Layout should be OneOperand."); \ - ResultVal = Builder.create##ID(Loc, getLocalValue(ValID, \ - getSILType(MF->getType(TyID), \ - (SILValueCategory)TyCategory))); \ - break; +#define UNARY_INSTRUCTION(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, \ + getLocalValue(ValID, getSILType(MF->getType(TyID), \ + (SILValueCategory)TyCategory, Fn))); \ + break; -#define REFCOUNTING_INSTRUCTION(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_OPERAND && \ - "Layout should be OneOperand."); \ - ResultVal = Builder.create##ID(Loc, getLocalValue(ValID, \ - getSILType(MF->getType(TyID), \ - (SILValueCategory)TyCategory)), \ - (Atomicity)Attr); \ +#define REFCOUNTING_INSTRUCTION(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, \ + getLocalValue(ValID, getSILType(MF->getType(TyID), \ + (SILValueCategory)TyCategory, Fn)), \ + (Atomicity)Attr); \ break; #define UNCHECKED_REF_STORAGE(Name, ...) \ @@ -1723,8 +1740,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, unsigned verificationType = Attr; ResultVal = Builder.createIsEscapingClosure( Loc, - getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)), + getLocalValue(ValID, getSILType(MF->getType(TyID), + (SILValueCategory)TyCategory, Fn)), verificationType); break; } @@ -1732,14 +1749,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::DestructureTupleInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); SILValue Operand = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); ResultVal = Builder.createDestructureTuple(Loc, Operand); break; } case SILInstructionKind::DestructureStructInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); SILValue Operand = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); ResultVal = Builder.createDestructureStruct(Loc, Operand); break; } @@ -1747,7 +1764,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto Ty = MF->getType(TyID); auto ResultKind = ValueOwnershipKind(Attr); ResultVal = Builder.createUncheckedOwnershipConversion( - Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), ResultKind); break; } @@ -1756,35 +1774,35 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto Ty = MF->getType(TyID); auto Qualifier = LoadOwnershipQualifier(Attr); ResultVal = Builder.createLoad( - Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), Qualifier); break; } -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Load##Name##Inst: { \ - auto Ty = MF->getType(TyID); \ - bool isTake = (Attr > 0); \ - auto Val = getLocalValue(ValID, getSILType(Ty, \ - SILValueCategory(TyCategory)));\ - ResultVal = Builder.createLoad##Name(Loc, Val, IsTake_t(isTake)); \ - break; \ - } \ - case SILInstructionKind::Store##Name##Inst: { \ - auto Ty = MF->getType(TyID); \ - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); \ - auto refType = addrType.castTo(); \ +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Load##Name##Inst: { \ + auto Ty = MF->getType(TyID); \ + bool isTake = (Attr > 0); \ + auto Val = getLocalValue( \ + ValID, getSILType(Ty, SILValueCategory(TyCategory), Fn)); \ + ResultVal = Builder.createLoad##Name(Loc, Val, IsTake_t(isTake)); \ + break; \ + } \ + case SILInstructionKind::Store##Name##Inst: { \ + auto Ty = MF->getType(TyID); \ + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); \ + auto refType = addrType.castTo(); \ auto ValType = SILType::getPrimitiveObjectType(refType.getReferentType()); \ - bool isInit = (Attr > 0); \ - ResultVal = Builder.createStore##Name(Loc, \ - getLocalValue(ValID, ValType), \ - getLocalValue(ValID2, addrType), \ - IsInitialization_t(isInit)); \ - break; \ + bool isInit = (Attr > 0); \ + ResultVal = Builder.createStore##Name(Loc, getLocalValue(ValID, ValType), \ + getLocalValue(ValID2, addrType), \ + IsInitialization_t(isInit)); \ + break; \ } #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::MarkUninitializedInst: { - auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Kind = (MarkUninitializedInst::Kind)Attr; auto Val = getLocalValue(ValID, Ty); ResultVal = Builder.createMarkUninitialized(Loc, Val, Kind); @@ -1792,7 +1810,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::StoreInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); SILType ValType = addrType.getObjectType(); auto Qualifier = StoreOwnershipQualifier(Attr); ResultVal = Builder.createStore(Loc, getLocalValue(ValID, ValType), @@ -1801,7 +1819,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::StoreBorrowInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); SILType ValType = addrType.getObjectType(); ResultVal = Builder.createStoreBorrow(Loc, getLocalValue(ValID, ValType), getLocalValue(ValID2, addrType)); @@ -1809,7 +1827,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::BeginAccessInst: { SILValue op = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); auto accessKind = SILAccessKind(Attr & 0x3); auto enforcement = SILAccessEnforcement((Attr >> 2) & 0x3); bool noNestedConflict = (Attr >> 4) & 0x01; @@ -1821,16 +1839,17 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::EndAccessInst: { SILValue op = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); bool aborted = Attr & 0x1; ResultVal = Builder.createEndAccess(Loc, op, aborted); break; } case SILInstructionKind::BeginUnpairedAccessInst: { SILValue source = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); - SILValue buffer = getLocalValue( - ValID2, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + SILValue buffer = + getLocalValue(ValID2, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)); auto accessKind = SILAccessKind(Attr & 0x3); auto enforcement = SILAccessEnforcement((Attr >> 2) & 0x03); bool noNestedConflict = (Attr >> 4) & 0x01; @@ -1842,7 +1861,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::EndUnpairedAccessInst: { SILValue op = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); bool aborted = Attr & 0x1; auto enforcement = SILAccessEnforcement((Attr >> 1) & 0x03); bool fromBuiltin = (Attr >> 3) & 0x01; @@ -1852,7 +1871,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::CopyAddrInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); bool isInit = (Attr & 0x2) > 0; bool isTake = (Attr & 0x1) > 0; ResultVal = Builder.createCopyAddr(Loc, @@ -1864,7 +1883,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::AssignInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); SILType valType = addrType.getObjectType(); auto qualifier = AssignOwnershipQualifier(Attr); ResultVal = Builder.createAssign(Loc, @@ -1880,14 +1899,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, "Layout should be OneTypeValues."); auto Ty = MF->getType(TyID); // BoundTy ResultVal = Builder.createBindMemory( - Loc, - getLocalValue(ListOfValues[2], - getSILType(MF->getType(ListOfValues[0]), - (SILValueCategory)ListOfValues[1])), - getLocalValue(ListOfValues[5], - getSILType(MF->getType(ListOfValues[3]), - (SILValueCategory)ListOfValues[4])), - getSILType(Ty, (SILValueCategory)TyCategory)); + Loc, + getLocalValue(ListOfValues[2], + getSILType(MF->getType(ListOfValues[0]), + (SILValueCategory)ListOfValues[1], Fn)), + getLocalValue(ListOfValues[5], + getSILType(MF->getType(ListOfValues[3]), + (SILValueCategory)ListOfValues[4], Fn)), + getSILType(Ty, (SILValueCategory)TyCategory, Fn)); break; } case SILInstructionKind::StructElementAddrInst: @@ -1895,9 +1914,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // Use SILOneValueOneOperandLayout. VarDecl *Field = cast(MF->getDecl(ValID)); auto Ty = MF->getType(TyID); - auto Val = getLocalValue(ValID2, - getSILType(Ty, (SILValueCategory)TyCategory)); - auto ResultTy = Val->getType().getFieldType(Field, SILMod); + auto Val = + getLocalValue(ValID2, getSILType(Ty, (SILValueCategory)TyCategory, Fn)); + auto ResultTy = Val->getType().getFieldType( + Field, SILMod, Builder.getTypeExpansionContext()); if (OpCode == SILInstructionKind::StructElementAddrInst) ResultVal = Builder.createStructElementAddr(Loc, Val, Field, ResultTy.getAddressType()); @@ -1913,35 +1933,33 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector OpList; for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { auto EltTy = MF->getType(ListOfValues[I]); - OpList.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(EltTy, (SILValueCategory)ListOfValues[I+1]))); + OpList.push_back(getLocalValue( + ListOfValues[I + 2], + getSILType(EltTy, (SILValueCategory)ListOfValues[I + 1], Fn))); } - ResultVal = Builder.createStruct(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - OpList); + ResultVal = Builder.createStruct( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), OpList); break; } case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::TupleExtractInst: { // Use OneTypeOneOperand layout where the field number is stored in TypeID. auto Ty2 = MF->getType(TyID2); - SILType ST = getSILType(Ty2, (SILValueCategory)TyCategory2); + SILType ST = getSILType(Ty2, (SILValueCategory)TyCategory2, Fn); TupleType *TT = ST.castTo(); auto ResultTy = TT->getElement(TyID).getType(); switch (OpCode) { default: llvm_unreachable("Out of sync with parent switch"); case SILInstructionKind::TupleElementAddrInst: - ResultVal = Builder.createTupleElementAddr(Loc, - getLocalValue(ValID, ST), - TyID, getSILType(ResultTy, SILValueCategory::Address)); + ResultVal = Builder.createTupleElementAddr( + Loc, getLocalValue(ValID, ST), TyID, + getSILType(ResultTy, SILValueCategory::Address, Fn)); break; case SILInstructionKind::TupleExtractInst: - ResultVal = Builder.createTupleExtract(Loc, - getLocalValue(ValID,ST), - TyID, - getSILType(ResultTy, SILValueCategory::Object)); + ResultVal = Builder.createTupleExtract( + Loc, getLocalValue(ValID, ST), TyID, + getSILType(ResultTy, SILValueCategory::Object, Fn)); break; } break; @@ -1957,11 +1975,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, Type EltTy = TT->getElement(I).getType(); OpList.push_back( getLocalValue(ListOfValues[I], - getSILType(EltTy, SILValueCategory::Object))); + getSILType(EltTy, SILValueCategory::Object, Fn))); } - ResultVal = Builder.createTuple(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - OpList); + ResultVal = Builder.createTuple( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), OpList); break; } case SILInstructionKind::ObjectInst: { @@ -1971,9 +1988,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector Args; for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) Args.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(MF->getType(ListOfValues[I]), - (SILValueCategory)ListOfValues[I+1]))); + getLocalValue(ListOfValues[I + 2], + getSILType(MF->getType(ListOfValues[I]), + (SILValueCategory)ListOfValues[I + 1], Fn))); ResultVal = Builder.createBranch(Loc, getBBForReference(Fn, TyID), Args); @@ -1985,9 +2002,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // for condition, the list has value for condition, true basic block ID, // false basic block ID, number of true arguments, and a list of true|false // arguments. - SILValue Cond = getLocalValue(ListOfValues[0], - getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); unsigned NumTrueArgs = ListOfValues[3]; unsigned StartOfTrueArg = 4; @@ -1995,16 +2012,16 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector TrueArgs; for (unsigned I = StartOfTrueArg, E = StartOfFalseArg; I < E; I += 3) TrueArgs.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(MF->getType(ListOfValues[I]), - (SILValueCategory)ListOfValues[I+1]))); + getLocalValue(ListOfValues[I + 2], + getSILType(MF->getType(ListOfValues[I]), + (SILValueCategory)ListOfValues[I + 1], Fn))); SmallVector FalseArgs; for (unsigned I = StartOfFalseArg, E = ListOfValues.size(); I < E; I += 3) FalseArgs.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(MF->getType(ListOfValues[I]), - (SILValueCategory)ListOfValues[I+1]))); + getLocalValue(ListOfValues[I + 2], + getSILType(MF->getType(ListOfValues[I]), + (SILValueCategory)ListOfValues[I + 1], Fn))); ResultVal = Builder.createCondBranch(Loc, Cond, getBBForReference(Fn, ListOfValues[1]), TrueArgs, @@ -2017,9 +2034,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // default basic block ID. Use SILOneTypeValuesLayout: the type is // for condition, the list has value for condition, hasDefault, default // basic block ID, a list of (DeclID, BasicBlock ID). - SILValue Cond = getLocalValue(ListOfValues[0], - getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); SILBasicBlock *DefaultBB = nullptr; if (ListOfValues[1]) @@ -2046,12 +2063,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // basic block ID, a list of (DeclID, BasicBlock ID). SILValue Cond = getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + (SILValueCategory)TyCategory, Fn)); Type ResultLoweredTy = MF->getType(ListOfValues[1]); SILValueCategory ResultCategory = (SILValueCategory)ListOfValues[2]; - SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory); - + SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory, Fn); + SILValue DefaultVal = nullptr; if (ListOfValues[3]) DefaultVal = getLocalValue(ListOfValues[4], ResultTy); @@ -2076,9 +2093,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // for condition, the list contains value for condition, hasDefault, default // basic block ID, a list of (Value ID, BasicBlock ID). SILType ResultTy = getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory); - SILValue Cond = getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + (SILValueCategory)TyCategory, Fn); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); SILBasicBlock *DefaultBB = nullptr; if (ListOfValues[1]) @@ -2098,12 +2116,13 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // for condition, the list has value for condition, result type, // hasDefault, default, // basic block ID, a list of (Value ID, Value ID). - SILValue Cond = getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); Type ResultLoweredTy = MF->getType(ListOfValues[1]); SILValueCategory ResultCategory = (SILValueCategory)ListOfValues[2]; - SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory); + SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory, Fn); SILValue DefaultVal = nullptr; if (ListOfValues[3]) @@ -2125,21 +2144,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // (DeclID + hasOperand), and an operand. SILValue Operand; if (Attr) - Operand = getLocalValue(ValID2, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)); - ResultVal = Builder.createEnum(Loc, Operand, - cast(MF->getDecl(ValID)), - getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + Operand = + getLocalValue(ValID2, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)); + ResultVal = Builder.createEnum( + Loc, Operand, cast(MF->getDecl(ValID)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); break; } case SILInstructionKind::InitEnumDataAddrInst: { // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); SILType OperandTy = getSILType(MF->getType(TyID), - (SILValueCategory) TyCategory); - SILType ResultTy = OperandTy.getEnumElementType(Elt, SILMod); + (SILValueCategory) TyCategory, Fn); + SILType ResultTy = OperandTy.getEnumElementType( + Elt, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createInitEnumDataAddr(Loc, getLocalValue(ValID2, OperandTy), Elt, ResultTy); @@ -2148,9 +2167,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::UncheckedEnumDataInst: { // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); - SILType OperandTy = getSILType(MF->getType(TyID), - (SILValueCategory) TyCategory); - SILType ResultTy = OperandTy.getEnumElementType(Elt, SILMod); + SILType OperandTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILType ResultTy = OperandTy.getEnumElementType( + Elt, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createUncheckedEnumData(Loc, getLocalValue(ValID2, OperandTy), Elt, ResultTy); @@ -2159,9 +2179,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); - SILType OperandTy = getSILType(MF->getType(TyID), - (SILValueCategory) TyCategory); - SILType ResultTy = OperandTy.getEnumElementType(Elt, SILMod); + SILType OperandTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILType ResultTy = OperandTy.getEnumElementType( + Elt, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createUncheckedTakeEnumDataAddr(Loc, getLocalValue(ValID2, OperandTy), Elt, ResultTy); @@ -2171,10 +2192,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); auto Ty = MF->getType(TyID); - ResultVal = Builder.createInjectEnumAddr(Loc, - getLocalValue(ValID2, - getSILType(Ty, (SILValueCategory)TyCategory)), - Elt); + ResultVal = Builder.createInjectEnumAddr( + Loc, + getLocalValue(ValID2, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), + Elt); break; } case SILInstructionKind::RefElementAddrInst: { @@ -2182,8 +2203,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, VarDecl *Field = cast(MF->getDecl(ValID)); auto Ty = MF->getType(TyID); auto Val = getLocalValue(ValID2, - getSILType(Ty, (SILValueCategory)TyCategory)); - auto ResultTy = Val->getType().getFieldType(Field, SILMod); + getSILType(Ty, (SILValueCategory)TyCategory, Fn)); + auto ResultTy = Val->getType().getFieldType( + Field, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createRefElementAddr(Loc, Val, Field, ResultTy); break; @@ -2194,10 +2216,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, assert(Attr == 0); assert((SILValueCategory)TyCategory == SILValueCategory::Address); ResultVal = Builder.createRefTailAddr( - Loc, - getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), SILValueCategory::Address)); + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), SILValueCategory::Address, Fn)); break; } case SILInstructionKind::ClassMethodInst: @@ -2208,11 +2230,13 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // type, Attr, SILDeclRef (DeclID, Kind, uncurryLevel), and an operand. unsigned NextValueIndex = 0; SILDeclRef DRef = getSILDeclRef(MF, ListOfValues, NextValueIndex); - SILType Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType Ty = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); assert(ListOfValues.size() >= NextValueIndex + 2 && "Out of entries for MethodInst"); - SILType operandTy = getSILType(MF->getType(ListOfValues[NextValueIndex]), - (SILValueCategory)ListOfValues[NextValueIndex+1]); + SILType operandTy = + getSILType(MF->getType(ListOfValues[NextValueIndex]), + (SILValueCategory)ListOfValues[NextValueIndex + 1], Fn); NextValueIndex += 2; switch (OpCode) { @@ -2247,15 +2271,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, "Out of entries for MethodInst"); CanType Ty = MF->getType(TyID)->getCanonicalType(); - SILType OperandTy = getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2); + SILType OperandTy = + getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2, Fn); auto Conformance = MF->readConformance(SILCursor); // Read the optional opened existential. SILValue ExistentialOperand; if (TyID3) { SILType ExistentialOperandTy = - getSILType(MF->getType(TyID3), (SILValueCategory)TyCategory3); + getSILType(MF->getType(TyID3), (SILValueCategory)TyCategory3, Fn); if (ValID3) ExistentialOperand = getLocalValue(ValID3, ExistentialOperandTy); } @@ -2270,11 +2294,13 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SILDeclRef DRef = getSILDeclRef(MF, ListOfValues, NextValueIndex); assert(ListOfValues.size() == NextValueIndex + 2 && "Wrong number of entries for DynamicMethodBranchInst"); - ResultVal = Builder.createDynamicMethodBranch(Loc, - getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)), - DRef, getBBForReference(Fn, ListOfValues[NextValueIndex]), - getBBForReference(Fn, ListOfValues[NextValueIndex+1])); + ResultVal = Builder.createDynamicMethodBranch( + Loc, + getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)), + DRef, getBBForReference(Fn, ListOfValues[NextValueIndex]), + getBBForReference(Fn, ListOfValues[NextValueIndex + 1])); break; } case SILInstructionKind::CheckedCastBranchInst: { @@ -2284,10 +2310,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, "expect 7 numbers for CheckedCastBranchInst"); bool isExact = ListOfValues[0] != 0; SILType opTy = getSILType(MF->getType(ListOfValues[2]), - (SILValueCategory)ListOfValues[3]); + (SILValueCategory)ListOfValues[3], Fn); SILValue op = getLocalValue(ListOfValues[1], opTy); - SILType castTy = getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory); + SILType castTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto *successBB = getBBForReference(Fn, ListOfValues[4]); auto *failureBB = getBBForReference(Fn, ListOfValues[5]); @@ -2301,10 +2327,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, assert(ListOfValues.size() == 5 && "expect 6 numbers for CheckedCastValueBranchInst"); SILType opTy = getSILType(MF->getType(ListOfValues[1]), - (SILValueCategory)ListOfValues[2]); + (SILValueCategory)ListOfValues[2], Fn); SILValue op = getLocalValue(ListOfValues[0], opTy); SILType castTy = - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto *successBB = getBBForReference(Fn, ListOfValues[3]); auto *failureBB = getBBForReference(Fn, ListOfValues[4]); @@ -2314,8 +2340,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::UnconditionalCheckedCastValueInst: { SILValue Val = getLocalValue( - ValID, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); - SILType Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + ValID, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2, Fn)); + SILType Ty = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); ResultVal = Builder.createUnconditionalCheckedCastValue(Loc, Val, Ty); break; } @@ -2323,12 +2350,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // ignore attr. CanType sourceType = MF->getType(ListOfValues[0])->getCanonicalType(); SILType srcAddrTy = getSILType(MF->getType(ListOfValues[2]), - (SILValueCategory)ListOfValues[3]); + (SILValueCategory)ListOfValues[3], Fn); SILValue src = getLocalValue(ListOfValues[1], srcAddrTy); CanType targetType = MF->getType(ListOfValues[4])->getCanonicalType(); SILType destAddrTy = - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SILValue dest = getLocalValue(ListOfValues[5], destAddrTy); ResultVal = Builder.createUnconditionalCheckedCastAddr(Loc, src, sourceType, @@ -2340,12 +2367,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, CanType sourceType = MF->getType(ListOfValues[1])->getCanonicalType(); SILType srcAddrTy = getSILType(MF->getType(ListOfValues[3]), - (SILValueCategory)ListOfValues[4]); + (SILValueCategory)ListOfValues[4], Fn); SILValue src = getLocalValue(ListOfValues[2], srcAddrTy); CanType targetType = MF->getType(ListOfValues[5])->getCanonicalType(); SILType destAddrTy = - getSILType(MF->getType(TyID), (SILValueCategory) TyCategory); + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SILValue dest = getLocalValue(ListOfValues[6], destAddrTy); auto *successBB = getBBForReference(Fn, ListOfValues[7]); @@ -2360,12 +2387,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, CanType sourceType = MF->getType(ListOfValues[0])->getCanonicalType(); // ignore attr. SILType srcAddrTy = getSILType(MF->getType(ListOfValues[2]), - (SILValueCategory)ListOfValues[3]); + (SILValueCategory)ListOfValues[3], Fn); SILValue src = getLocalValue(ListOfValues[1], srcAddrTy); CanType targetType = MF->getType(ListOfValues[4])->getCanonicalType(); SILType destAddrTy = - getSILType(MF->getType(TyID), (SILValueCategory) TyCategory); + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SILValue dest = getLocalValue(ListOfValues[5], destAddrTy); ResultVal = Builder.createUncheckedRefCastAddr(Loc, src, sourceType, @@ -2375,16 +2402,16 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::InitBlockStorageHeaderInst: { assert(ListOfValues.size() == 5 && "expected 5 values for InitBlockStorageHeader"); - SILType blockTy - = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType blockTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SILType storageTy = getSILType(MF->getType(ListOfValues[1]), - SILValueCategory::Address); + SILValueCategory::Address, Fn); SILValue storage = getLocalValue(ListOfValues[0], storageTy); - SILType invokeTy = getSILType(MF->getType(ListOfValues[3]), - SILValueCategory::Object); + SILType invokeTy = + getSILType(MF->getType(ListOfValues[3]), SILValueCategory::Object, Fn); SILValue invoke = getLocalValue(ListOfValues[2], invokeTy); @@ -2412,8 +2439,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { auto valueTy = MF->getType(ListOfValues[I]); auto valueCategory = (SILValueCategory) ListOfValues[I+1]; - yieldedValues.push_back( - getLocalValue(ListOfValues[I+2], getSILType(valueTy, valueCategory))); + yieldedValues.push_back(getLocalValue( + ListOfValues[I + 2], getSILType(valueTy, valueCategory, Fn))); } ResultVal = Builder.createYield(Loc, yieldedValues, resumeBB, unwindBB); @@ -2421,8 +2448,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::KeyPathInst: { unsigned nextValue = 0; - SILType kpTy - = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType kpTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto rootTy = MF->getType(ListOfValues[nextValue++]); auto valueTy = MF->getType(ListOfValues[nextValue++]); @@ -2465,7 +2492,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto opValue = ListOfValues[nextValue++]; auto opTy = MF->getType(ListOfValues[nextValue++]); auto opCat = (SILValueCategory)ListOfValues[nextValue++]; - operands.push_back(getLocalValue(opValue, getSILType(opTy, opCat))); + operands.push_back(getLocalValue(opValue, getSILType(opTy, opCat, Fn))); } ResultVal = Builder.createKeyPath(Loc, pattern, subMap, operands, kpTy); @@ -2659,7 +2686,7 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { SILGlobalVariable *v = SILGlobalVariable::create( SILMod, linkage.getValue(), isSerialized ? IsSerialized : IsNotSerialized, - Name.str(), getSILType(Ty, SILValueCategory::Object), + Name.str(), getSILType(Ty, SILValueCategory::Object, nullptr), None, dID ? cast(MF->getDecl(dID)): nullptr); v->setLet(IsLet); diff --git a/lib/Serialization/DeserializeSIL.h b/lib/Serialization/DeserializeSIL.h index c2790c01e48c2..d94de0c3e178d 100644 --- a/lib/Serialization/DeserializeSIL.h +++ b/lib/Serialization/DeserializeSIL.h @@ -110,6 +110,9 @@ namespace swift { SILValue getLocalValue(serialization::ValueID Id, SILType Type); + SILType getSILType(Type ty, SILValueCategory category, + SILFunction *inContext); + SILFunction *getFuncForReference(StringRef Name, SILType Ty); SILFunction *getFuncForReference(StringRef Name); SILVTable *readVTable(serialization::DeclID); From 33f4f57cc47f5a13a91fec7c4cb30254a53b53b3 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 12:01:11 -0800 Subject: [PATCH 069/283] SILGen: Add TypeExpansionContext to SILGen --- lib/SILGen/SILGen.cpp | 23 +++--- lib/SILGen/SILGenApply.cpp | 128 ++++++++++++++++++------------ lib/SILGen/SILGenBridging.cpp | 36 ++++++--- lib/SILGen/SILGenConstructor.cpp | 21 ++--- lib/SILGen/SILGenDecl.cpp | 18 +++-- lib/SILGen/SILGenDestructor.cpp | 3 +- lib/SILGen/SILGenExpr.cpp | 62 ++++++++++----- lib/SILGen/SILGenForeignError.cpp | 3 +- lib/SILGen/SILGenFunction.cpp | 82 +++++++++++++------ lib/SILGen/SILGenFunction.h | 37 +++++++-- lib/SILGen/SILGenLValue.cpp | 83 ++++++++++--------- lib/SILGen/SILGenPattern.cpp | 6 +- lib/SILGen/SILGenPoly.cpp | 45 +++++++---- lib/SILGen/SILGenProlog.cpp | 47 ++++++----- lib/SILGen/SILGenThunk.cpp | 18 +++-- lib/SILGen/SILGenType.cpp | 29 ++++--- lib/SILGen/SwitchEnumBuilder.cpp | 4 +- 17 files changed, 404 insertions(+), 241 deletions(-) diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index b1e5e9c22937f..248e10c4613bd 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -110,11 +110,12 @@ getBridgingFn(Optional &cacheSlot, // Check that the function takes the expected arguments and returns the // expected result type. SILDeclRef c(fd); - auto funcTy = SGM.Types.getConstantFunctionType(c); + auto funcTy = + SGM.Types.getConstantFunctionType(TypeExpansionContext::minimal(), c); SILFunctionConventions fnConv(funcTy, SGM.M); auto toSILType = [&SGM](Type ty) { - return SGM.Types.getLoweredType(ty, ResilienceExpansion::Minimal); + return SGM.Types.getLoweredType(ty, TypeExpansionContext::minimal()); }; if (inputTypes) { @@ -916,7 +917,6 @@ SILFunction *SILGenModule::emitClosure(AbstractClosureExpr *ce) { // initializer of the containing type. if (!f->isExternalDeclaration()) return f; - preEmitFunction(constant, ce, f, ce); PrettyStackTraceSILFunction X("silgen closureexpr", f); SILGenFunction(*this, *f, ce).emitClosure(ce); @@ -947,8 +947,8 @@ bool SILGenModule::hasNonTrivialIVars(ClassDecl *cd) { auto *vd = dyn_cast(member); if (!vd || !vd->hasStorage()) continue; - auto &ti = Types.getTypeLowering(vd->getType(), - ResilienceExpansion::Maximal); + auto &ti = Types.getTypeLowering( + vd->getType(), TypeExpansionContext::maximalResilienceExpansionOnly()); if (!ti.isTrivial()) return true; } @@ -1170,7 +1170,7 @@ SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, Type initType = FunctionType::get({}, TupleType::getEmpty(C), type->getExtInfo()); auto initSILType = cast( - Types.getLoweredRValueType(initType)); + Types.getLoweredRValueType(TypeExpansionContext::minimal(), initType)); SILGenFunctionBuilder builder(*this); auto *f = builder.createFunction( @@ -1354,11 +1354,12 @@ SILGenModule::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl, if (auto genericEnv = decl->getInnermostDeclContext()->getGenericEnvironmentOfContext()) componentObjTy = genericEnv->mapTypeIntoContext(componentObjTy); - auto storageTy = M.Types.getSubstitutedStorageType(decl, componentObjTy); - auto opaqueTy = - M.Types.getLoweredRValueType(AbstractionPattern::getOpaque(), - componentObjTy); - + auto storageTy = M.Types.getSubstitutedStorageType( + TypeExpansionContext::minimal(), decl, componentObjTy); + auto opaqueTy = M.Types.getLoweredRValueType( + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion), + AbstractionPattern::getOpaque(), componentObjTy); + return storageTy.getASTType() == opaqueTy; } case AccessStrategy::DirectToAccessor: diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 00be4b4e930fa..c0f7ce664ff24 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -90,7 +90,8 @@ getIndirectApplyAbstractionPattern(SILGenFunction &SGF, static CanFunctionType getPartialApplyOfDynamicMethodFormalType(SILGenModule &SGM, SILDeclRef member, ConcreteDeclRef memberRef) { - auto memberCI = SGM.Types.getConstantInfo(member); + auto memberCI = + SGM.Types.getConstantInfo(TypeExpansionContext::minimal(), member); // Construct a non-generic version of the formal type. // This works because we're only using foreign members, where presumably @@ -133,7 +134,8 @@ getDynamicMethodLoweredType(SILModule &M, auto objcFormalTy = substMemberTy.withExtInfo(substMemberTy->getExtInfo() .withSILRepresentation(SILFunctionTypeRepresentation::ObjCMethod)); return SILType::getPrimitiveObjectType( - M.Types.getUncachedSILFunctionTypeForConstant(constant, objcFormalTy)); + M.Types.getUncachedSILFunctionTypeForConstant( + TypeExpansionContext::minimal(), constant, objcFormalTy)); } /// Check if we can perform a dynamic dispatch on a super method call. @@ -382,34 +384,39 @@ class Callee { SubstitutionMap subs, SILLocation l, bool callPreviousDynamicReplaceableImpl = false) { - auto &ci = SGF.getConstantInfo(c); - return Callee(SGF, c, ci.FormalPattern, ci.FormalType, subs, l, - callPreviousDynamicReplaceableImpl); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l, + callPreviousDynamicReplaceableImpl); } static Callee forEnumElement(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { assert(isa(c.getDecl())); - auto &ci = SGF.getConstantInfo(c); - return Callee(Kind::EnumElement, SGF, c, ci.FormalPattern, - ci.FormalType, subs, l); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::EnumElement, SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forClassMethod(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { auto base = c.getOverriddenVTableEntry(); - auto &baseCI = SGF.getConstantInfo(base); - auto &derivedCI = SGF.getConstantInfo(c); - return Callee(Kind::ClassMethod, SGF, c, - baseCI.FormalPattern, derivedCI.FormalType, subs, l); + auto &baseCI = SGF.getConstantInfo(SGF.getTypeExpansionContext(), base); + auto &derivedCI = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::ClassMethod, SGF, c, baseCI.FormalPattern, derivedCI.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forSuperMethod(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { - auto &ci = SGF.getConstantInfo(c); - return Callee(Kind::SuperMethod, SGF, c, - ci.FormalPattern, ci.FormalType, subs, l); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::SuperMethod, SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forWitnessMethod(SILGenFunction &SGF, CanType protocolSelfType, @@ -430,15 +437,16 @@ class Callee { subs); } - auto &ci = SGF.getConstantInfo(c); - return Callee(Kind::WitnessMethod, SGF, c, ci.FormalPattern, - ci.FormalType, subs, l); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::WitnessMethod, SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forDynamic(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap constantSubs, CanAnyFunctionType substFormalType, SubstitutionMap subs, SILLocation l) { - auto &ci = SGF.getConstantInfo(c); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); AbstractionPattern origFormalType = ci.FormalPattern; // Replace the original self type with the partially-applied subst type. @@ -456,8 +464,9 @@ class Callee { } origFormalType.rewriteType(CanGenericSignature(), origFormalFnType); - return Callee(Kind::DynamicMethod, SGF, c, origFormalType, - substFormalType, subs, l); + return Callee( + Kind::DynamicMethod, SGF, c, origFormalType, substFormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } Callee(Callee &&) = default; @@ -534,8 +543,8 @@ class Callee { CalleeTypeInfo result; result.substFnType = - formalFnType.castTo()->substGenericArgs(SGF.SGM.M, - Substitutions); + formalFnType.castTo()->substGenericArgs( + SGF.SGM.M, Substitutions, SGF.getTypeExpansionContext()); if (!constant || !constant->isForeign) return result; @@ -578,7 +587,8 @@ class Callee { // If the call is curried, emit a direct call to the curry thunk. if (constant->isCurried) { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo); return ManagedValue::forUnmanaged(ref); } @@ -590,18 +600,21 @@ class Callee { return IndirectValue; case Kind::EnumElement: case Kind::StandaloneFunction: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo); return ManagedValue::forUnmanaged(ref); } case Kind::StandaloneFunctionDynamicallyReplaceableImpl: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo, true); return ManagedValue::forUnmanaged(ref); } case Kind::ClassMethod: { - auto methodTy = SGF.SGM.Types.getConstantOverrideType(*constant); + auto methodTy = SGF.SGM.Types.getConstantOverrideType( + SGF.getTypeExpansionContext(), *constant); // Otherwise, do the dynamic dispatch inline. ArgumentScope S(SGF, Loc); @@ -626,8 +639,8 @@ class Callee { SGF, Loc, *borrowedSelf); auto base = constant->getOverriddenVTableEntry(); - auto constantInfo = - SGF.SGM.Types.getConstantOverrideInfo(*constant, base); + auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo( + SGF.getTypeExpansionContext(), *constant, base); ManagedValue fn; if (!constant->isForeign) { @@ -641,8 +654,10 @@ class Callee { return fn; } case Kind::WitnessMethod: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); + // TODO: substOpaqueTypesWithUnderlyingTypes ... auto proto = cast(Constant.getDecl()->getDeclContext()); auto selfType = proto->getSelfInterfaceType()->getCanonicalType(); auto lookupType = selfType.subst(Substitutions)->getCanonicalType(); @@ -687,7 +702,8 @@ class Callee { // If the call is curried, emit a direct call to the curry thunk. if (constant->isCurried) { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } } @@ -699,26 +715,30 @@ class Callee { case Kind::StandaloneFunctionDynamicallyReplaceableImpl: case Kind::StandaloneFunction: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::EnumElement: { // Emit a direct call to the element constructor thunk. - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::ClassMethod: { - auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo(*constant); + auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo( + SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::SuperMethod: { auto base = constant->getOverriddenVTableEntry(); - auto constantInfo = - SGF.SGM.Types.getConstantOverrideInfo(*constant, base); + auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo( + SGF.getTypeExpansionContext(), *constant, base); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::WitnessMethod: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::DynamicMethod: { @@ -3951,8 +3971,8 @@ CallEmission::applyPartiallyAppliedSuperMethod(SGFContext C) { // because that's what the partially applied super method expects; firstLevelResult.formalType = callee.getSubstFormalType(); auto origFormalType = AbstractionPattern(firstLevelResult.formalType); - auto substFnType = - SGF.getSILFunctionType(origFormalType, firstLevelResult.formalType); + auto substFnType = SGF.getSILFunctionType( + SGF.getTypeExpansionContext(), origFormalType, firstLevelResult.formalType); // Emit the arguments. SmallVector uncurriedArgs; @@ -3978,7 +3998,8 @@ CallEmission::applyPartiallyAppliedSuperMethod(SGFContext C) { // partial_apply. upcastedSelf = upcastedSelf.ensurePlusOne(SGF, loc); - auto constantInfo = SGF.getConstantInfo(callee.getMethodName()); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), callee.getMethodName()); auto functionTy = constantInfo.getSILType(); ManagedValue superMethod; { @@ -4019,8 +4040,8 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, // expect. firstLevelResult.formalType = callee.getSubstFormalType(); auto origFormalType = AbstractionPattern(firstLevelResult.formalType); - auto substFnType = - SGF.getSILFunctionType(origFormalType, firstLevelResult.formalType); + auto substFnType = SGF.getSILFunctionType( + SGF.getTypeExpansionContext(), origFormalType, firstLevelResult.formalType); // If we have an early emitter, just let it take over for the // uncurried call site. @@ -4352,7 +4373,8 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) { if (method->isStatic()) return false; - auto fnType = M.Types.getConstantFunctionType(methodRef); + auto fnType = M.Types.getConstantFunctionType(TypeExpansionContext::minimal(), + methodRef); auto importAsMember = method->getImportAsMemberStatus(); SILParameterInfo self; @@ -4640,7 +4662,7 @@ void SILGenFunction::emitYield(SILLocation loc, SmallVector yieldArgs; SmallVector delayedArgs; - auto fnType = F.getLoweredFunctionType(); + auto fnType = F.getLoweredFunctionTypeInContext(getTypeExpansionContext()); SmallVector substYieldTys; for (auto origYield : fnType->getYields()) { substYieldTys.push_back({ @@ -4740,7 +4762,8 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc, // careful to stage the cleanups so that if the expression // throws, we know to deallocate the uninitialized box. if (element->isIndirect() || element->getParentEnum()->isIndirect()) { - auto boxTy = SGM.M.Types.getBoxTypeForEnumElement(enumTy, element); + auto boxTy = SGM.M.Types.getBoxTypeForEnumElement(getTypeExpansionContext(), + enumTy, element); auto *box = B.createAllocBox(loc, boxTy); auto *addr = B.createProjectBox(loc, box, 0); @@ -4868,7 +4891,7 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, // Form the reference to the allocating initializer. auto initRef = SILDeclRef(ctor, SILDeclRef::Kind::Allocator) .asForeign(requiresForeignEntryPoint(ctor)); - auto initConstant = getConstantInfo(initRef); + auto initConstant = getConstantInfo(getTypeExpansionContext(), initRef); auto subs = init.getSubstitutions(); // Scope any further writeback just within this operation. @@ -4879,8 +4902,8 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, SILType selfMetaTy; { // Determine the self metatype type. - CanSILFunctionType substFnType = - initConstant.SILFnType->substGenericArgs(SGM.M, subs); + CanSILFunctionType substFnType = initConstant.SILFnType->substGenericArgs( + SGM.M, subs, getTypeExpansionContext()); SILType selfParamMetaTy = getSILType(substFnType->getSelfParameter(), substFnType); @@ -4972,7 +4995,7 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, // Form the reference to the method. auto callRef = SILDeclRef(call, SILDeclRef::Kind::Func) .asForeign(requiresForeignEntryPoint(declRef.getDecl())); - auto declRefConstant = getConstantInfo(callRef); + auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef); auto subs = declRef.getSubstitutions(); // Scope any further writeback just within this operation. @@ -4984,7 +5007,8 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, { // Determine the self metatype type. CanSILFunctionType substFnType = - declRefConstant.SILFnType->substGenericArgs(SGM.M, subs); + declRefConstant.SILFnType->substGenericArgs(SGM.M, subs, + getTypeExpansionContext()); SILType selfParamMetaTy = getSILType(substFnType->getSelfParameter(), substFnType); selfMetaTy = selfParamMetaTy; @@ -5490,8 +5514,8 @@ AccessorBaseArgPreparer::AccessorBaseArgPreparer(SILGenFunction &SGF, CanType baseFormalType, SILDeclRef accessor) : SGF(SGF), loc(loc), base(base), baseFormalType(baseFormalType), - accessor(accessor), - selfParam(SGF.SGM.Types.getConstantSelfParameter(accessor)), + accessor(accessor), selfParam(SGF.SGM.Types.getConstantSelfParameter( + SGF.getTypeExpansionContext(), accessor)), baseLoweredType(base.getType()) { assert(!base.isInContext()); assert(!base.isLValue() || !base.hasCleanup()); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 5520e6f15536f..bd88f7a2ad42b 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -123,7 +123,8 @@ emitBridgeNativeToObjectiveC(SILGenFunction &SGF, SGF.SGM.SwiftModule, dc); // Substitute into the witness function type. - witnessFnTy = witnessFnTy.substGenericArgs(SGF.SGM.M, typeSubMap); + witnessFnTy = witnessFnTy.substGenericArgs(SGF.SGM.M, typeSubMap, + SGF.getTypeExpansionContext()); // We might have to re-abstract the 'self' value if it is an // Optional. @@ -204,7 +205,8 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF, SubstitutionMap typeSubMap = witness.getSubstitutions(); // Substitute into the witness function type. - witnessFnTy = witnessFnTy->substGenericArgs(SGF.SGM.M, typeSubMap); + witnessFnTy = witnessFnTy->substGenericArgs(SGF.SGM.M, typeSubMap, + SGF.getTypeExpansionContext()); // The witness takes an _ObjectiveCType?, so convert to that type. CanType desiredValueType = OptionalType::get(objcType)->getCanonicalType(); @@ -220,7 +222,8 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF, SGF.B.createMetatype(loc, metatypeParam.getSILStorageType(SGF.SGM.M, witnessFnTy)); - auto witnessCI = SGF.getConstantInfo(witnessConstant); + auto witnessCI = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), witnessConstant); CanType formalResultTy = witnessCI.LoweredType.getResult(); auto subs = witness.getSubstitutions(); @@ -954,7 +957,8 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, if (thunkTy->getInvocationGenericSignature()) { substFnTy = thunkTy->substGenericArgs(F.getModule(), - interfaceSubs); + interfaceSubs, + getTypeExpansionContext()); } // Create it in the current function. @@ -1263,12 +1267,16 @@ static SILFunctionType *emitObjCThunkArguments(SILGenFunction &SGF, auto subs = SGF.F.getForwardingSubstitutionMap(); - auto objcInfo = SGF.SGM.Types.getConstantInfo(thunk); - auto objcFnTy = objcInfo.SILFnType->substGenericArgs(SGF.SGM.M, subs); + auto objcInfo = + SGF.SGM.Types.getConstantInfo(SGF.getTypeExpansionContext(), thunk); + auto objcFnTy = objcInfo.SILFnType->substGenericArgs( + SGF.SGM.M, subs, SGF.getTypeExpansionContext()); auto objcFormalFnTy = substGenericArgs(objcInfo.LoweredType, subs); - auto swiftInfo = SGF.SGM.Types.getConstantInfo(native); - auto swiftFnTy = swiftInfo.SILFnType->substGenericArgs(SGF.SGM.M, subs); + auto swiftInfo = + SGF.SGM.Types.getConstantInfo(SGF.getTypeExpansionContext(), native); + auto swiftFnTy = swiftInfo.SILFnType->substGenericArgs( + SGF.SGM.M, subs, SGF.getTypeExpansionContext()); auto swiftFormalFnTy = substGenericArgs(swiftInfo.LoweredType, subs); SILFunctionConventions swiftConv(swiftFnTy, SGF.SGM.M); @@ -1402,9 +1410,10 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) { } } - auto nativeInfo = getConstantInfo(native); + auto nativeInfo = getConstantInfo(getTypeExpansionContext(), native); auto subs = F.getForwardingSubstitutionMap(); - auto substTy = nativeInfo.SILFnType->substGenericArgs(SGM.M, subs); + auto substTy = nativeInfo.SILFnType->substGenericArgs( + SGM.M, subs, getTypeExpansionContext()); SILFunctionConventions substConv(substTy, SGM.M); // Use the same generic environment as the native entry point. @@ -1605,7 +1614,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { // Wrap the function in its original form. auto fd = cast(thunk.getDecl()); - auto nativeCI = getConstantInfo(thunk); + auto nativeCI = getConstantInfo(getTypeExpansionContext(), thunk); auto nativeFnTy = F.getLoweredFunctionType(); assert(nativeFnTy == nativeCI.SILFnType); @@ -1613,7 +1622,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(thunk)); SILDeclRef foreignDeclRef = thunk.asForeign(true); - SILConstantInfo foreignCI = getConstantInfo(foreignDeclRef); + SILConstantInfo foreignCI = + getConstantInfo(getTypeExpansionContext(), foreignDeclRef); auto foreignFnTy = foreignCI.SILFnType; // Find the foreign error convention and 'self' parameter index. @@ -1780,7 +1790,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { foreignCI); auto fnType = fn->getType().castTo(); - fnType = fnType->substGenericArgs(SGM.M, subs); + fnType = fnType->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); CanType nativeFormalResultType = fd->mapTypeIntoContext(nativeCI.LoweredType.getResult()) diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index f43baef1ba102..c46970ecf36e4 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -49,7 +49,8 @@ static SILValue emitConstructorMetatypeArg(SILGenFunction &SGF, VD->setInterfaceType(metatype); SGF.AllocatorMetatype = SGF.F.begin()->createFunctionArgument( - SGF.getLoweredType(DC->mapTypeIntoContext(metatype)), VD); + SGF.getLoweredTypeForFunctionArgument(DC->mapTypeIntoContext(metatype)), + VD); return SGF.AllocatorMetatype; } @@ -78,8 +79,7 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, VD->setSpecifier(ParamSpecifier::Default); VD->setInterfaceType(interfaceType); - auto argType = SGF.SGM.Types.getLoweredType(type, - ResilienceExpansion::Minimal); + auto argType = SGF.getLoweredTypeForFunctionArgument(type); auto *arg = SGF.F.begin()->createFunctionArgument(argType, VD); ManagedValue mvArg; if (arg->getArgumentConvention().isOwnedConvention()) { @@ -129,7 +129,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, auto *paramList = ctor->getParameters(); auto *selfDecl = ctor->getImplicitSelfDecl(); auto selfIfaceTy = selfDecl->getInterfaceType(); - SILType selfTy = SGF.getLoweredType(selfDecl->getType()); + SILType selfTy = SGF.getLoweredTypeForFunctionArgument(selfDecl->getType()); // Emit the indirect return argument, if any. SILValue resultSlot; @@ -142,7 +142,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, ctor); VD->setSpecifier(ParamSpecifier::InOut); VD->setInterfaceType(selfIfaceTy); - resultSlot = SGF.F.begin()->createFunctionArgument(selfTy.getAddressType(), VD); + resultSlot = + SGF.F.begin()->createFunctionArgument(selfTy.getAddressType(), VD); } // Emit the elementwise arguments. @@ -164,7 +165,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, if (resultSlot) { auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { - auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M); + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue slot = SGF.B.createStructElementAddr(Loc, resultSlot, field, fieldTy.getAddressType()); @@ -201,7 +203,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { - auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M); + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue v; // If it's memberwise initialized, do so now. @@ -416,8 +419,8 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) { Type enumIfaceTy = element->getParentEnum()->getDeclaredInterfaceType(); Type enumTy = F.mapTypeIntoContext(enumIfaceTy); - auto &enumTI = SGM.Types.getTypeLowering(enumTy, - ResilienceExpansion::Minimal); + auto &enumTI = + SGM.Types.getTypeLowering(enumTy, TypeExpansionContext::minimal()); RegularLocation Loc(element); CleanupLocation CleanupLoc(element); diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 8ef1cf2c6af67..ace1e9e71c359 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -371,12 +371,13 @@ class LocalVariableInitialization : public SingleBufferInitialization { "can't emit a local var for a non-local var decl"); assert(decl->hasStorage() && "can't emit storage for a computed variable"); assert(!SGF.VarLocs.count(decl) && "Already have an entry for this decl?"); - - auto boxType = SGF.SGM.Types - .getContextBoxTypeForCapture(decl, - SGF.SGM.Types.getLoweredRValueType(decl->getType()), - SGF.F.getGenericEnvironment(), - /*mutable*/ true); + // The box type's context is lowered in the minimal resilience domain. + auto boxType = SGF.SGM.Types.getContextBoxTypeForCapture( + decl, + SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + decl->getType()), + SGF.F.getGenericEnvironment(), + /*mutable*/ true); // The variable may have its lifetime extended by a closure, heap-allocate // it using a box. @@ -850,7 +851,8 @@ void EnumElementPatternInitialization::emitEnumMatch( } // Otherwise, the bound value for the enum case is available. - SILType eltTy = value.getType().getEnumElementType(eltDecl, SGF.SGM.M); + SILType eltTy = value.getType().getEnumElementType( + eltDecl, SGF.SGM.M, SGF.getTypeExpansionContext()); auto &eltTL = SGF.getTypeLowering(eltTy); if (mv.getType().isAddress()) { @@ -1237,7 +1239,7 @@ SILValue SILGenFunction::emitOSVersionRangeCheck(SILLocation loc, auto silDeclRef = SILDeclRef(versionQueryDecl); SILValue availabilityGTEFn = emitGlobalFunctionRef( - loc, silDeclRef, getConstantInfo(silDeclRef)); + loc, silDeclRef, getConstantInfo(getTypeExpansionContext(), silDeclRef)); SILValue args[] = {majorValue, minorValue, subminorValue}; return B.createApply(loc, availabilityGTEFn, SubstitutionMap(), args); diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 1c816b3314ce5..3041fb58404ee 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -232,7 +232,8 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { auto superclassDtor = SILDeclRef(superclassDtorDecl, SILDeclRef::Kind::Deallocator) .asForeign(); - auto superclassDtorType = SGM.Types.getConstantType(superclassDtor); + auto superclassDtorType = + SGM.Types.getConstantType(getTypeExpansionContext(), superclassDtor); SILValue superclassDtorValue = B.createObjCSuperMethod( cleanupLoc, selfValue, superclassDtor, superclassDtorType); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 65f4e81ffc9a7..05cedbb18d0df 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1442,10 +1442,13 @@ RValueEmitter::visitBridgeToObjCExpr(BridgeToObjCExpr *E, SGFContext C) { RValue RValueEmitter::visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, SGFContext C) { ManagedValue archetype = SGF.emitRValueAsSingleValue(E->getSubExpr()); + auto loweredTy = SGF.getLoweredLoadableType(E->getType()); + if (loweredTy == archetype.getType()) + return RValue(SGF, E, archetype); + // Replace the cleanup with a new one on the superclass value so we always use // concrete retain/release operations. - auto base = SGF.B.createUpcast(E, archetype, - SGF.getLoweredLoadableType(E->getType())); + auto base = SGF.B.createUpcast(E, archetype, loweredTy); return RValue(SGF, E, base); } @@ -1539,7 +1542,8 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF, // Produce a reference to the C-compatible entry point for the function. SILDeclRef constant(loc, /*curried*/ false, /*foreign*/ true); - SILConstantInfo constantInfo = SGF.getConstantInfo(constant); + SILConstantInfo constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), constant); // C function pointers cannot capture anything from their context. auto captures = SGF.SGM.Types.getLoweredLocalCaptures(constant); @@ -1968,7 +1972,11 @@ RValue RValueEmitter::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, E->getSubExpr()->getType()); auto &underlyingSubstTL = SGF.getTypeLowering(E->getSubExpr()->getType()); - + + if (underlyingSubstTL.getLoweredType() == opaqueTL.getLoweredType()) { + return SGF.emitRValue(E->getSubExpr(), C); + } + // If the opaque type is address only, initialize in place. if (opaqueTL.getLoweredType().isAddress()) { auto opaqueAddr = SGF.getBufferForExprResult( @@ -2007,15 +2015,18 @@ RValue RValueEmitter::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, // If the opaque type is loadable, emit the subexpression and bitcast it. auto value = SGF.emitRValueAsSingleValue(E->getSubExpr()); - if (underlyingSubstTL.getLoweredType() == underlyingTL.getLoweredType()) { + if (underlyingSubstTL.getLoweredType() != underlyingTL.getLoweredType()) { value = SGF.emitSubstToOrigValue(E, value, AbstractionPattern::getOpaque(), E->getSubExpr()->getType()->getCanonicalType()); } - + + if (value.getType() == opaqueTL.getLoweredType()) + return RValue(SGF, E, value); + auto cast = SGF.B.createUncheckedBitCast(E, value.forward(SGF), - opaqueTL.getLoweredType()); + opaqueTL.getLoweredType()); value = SGF.emitManagedRValueWithCleanup(cast); - + return RValue(SGF, E, value); } @@ -2198,7 +2209,8 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, if (fnType->isPolymorphic()) subs = defaultArgsOwner.getSubstitutions(); - auto substFnType = fnType->substGenericArgs(SGM.M, subs); + auto substFnType = + fnType->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); CalleeTypeInfo calleeTypeInfo(substFnType, origResultType, resultType); ResultPlanPtr resultPtr = @@ -2225,7 +2237,8 @@ RValue SILGenFunction::emitApplyOfStoredPropertyInitializer( auto fnRef = ManagedValue::forUnmanaged(emitGlobalFunctionRef(loc, constant)); auto fnType = fnRef.getType().castTo(); - auto substFnType = fnType->substGenericArgs(SGM.M, subs); + auto substFnType = + fnType->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); CalleeTypeInfo calleeTypeInfo(substFnType, origResultType, resultType); ResultPlanPtr resultPlan = @@ -2650,8 +2663,10 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, GenericContextScope scope(SGM.Types, genericSig); AbstractionPattern opaque = AbstractionPattern::getOpaque(); - loweredBaseTy = SGM.Types.getLoweredRValueType(opaque, baseType); - loweredPropTy = SGM.Types.getLoweredRValueType(opaque, propertyType); + loweredBaseTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, baseType); + loweredPropTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, propertyType); } auto paramConvention = ParameterConvention::Indirect_In_Guaranteed; @@ -2786,8 +2801,10 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, GenericContextScope scope(SGM.Types, genericSig); AbstractionPattern opaque = AbstractionPattern::getOpaque(); - loweredBaseTy = SGM.Types.getLoweredRValueType(opaque, baseType); - loweredPropTy = SGM.Types.getLoweredRValueType(opaque, propertyType); + loweredBaseTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, baseType); + loweredPropTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, propertyType); } auto &C = SGM.getASTContext(); @@ -2968,8 +2985,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, RValue indexValue(indexTupleTy); auto indexLoweredTy = - SILType::getPrimitiveAddressType( - SGM.Types.getLoweredRValueType(indexTupleTy)); + SILType::getPrimitiveAddressType(SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), indexTupleTy)); // Get or create the equals witness [unsafeRawPointerTy, boolTy, genericSig, &C, &indexTypes, &equals, loc, @@ -3031,8 +3048,9 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto equalsMethod = equatableProtocol->getSingleRequirement( C.Id_EqualsOperator); auto equalsRef = SILDeclRef(equalsMethod); - auto equalsTy = subSGF.SGM.Types.getConstantType(equalsRef); - + auto equalsTy = subSGF.SGM.Types.getConstantType( + TypeExpansionContext(subSGF.F), equalsRef); + auto isFalseBB = subSGF.createBasicBlock(); auto i1Ty = SILType::getBuiltinIntegerType(1, C); for (unsigned i : indices(indexes)) { @@ -3064,8 +3082,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, = SubstitutionMap::getProtocolSubstitutions(equatableProtocol, formalCanTy, equatable); - auto equalsSubstTy = equalsTy.castTo() - ->substGenericArgs(SGM.M, equatableSub); + auto equalsSubstTy = equalsTy.castTo()->substGenericArgs( + SGM.M, equatableSub, TypeExpansionContext(subSGF.F)); auto equalsInfo = CalleeTypeInfo(equalsSubstTy, AbstractionPattern(boolTy), boolTy, None, @@ -3310,8 +3328,8 @@ lowerKeyPathSubscriptIndexTypes( } auto indexLoweredTy = SGM.Types.getLoweredType( - AbstractionPattern::getOpaque(), - indexTy, expansion); + AbstractionPattern::getOpaque(), indexTy, + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion)); indexLoweredTy = indexLoweredTy.mapTypeOutOfContext(); indexPatterns.push_back({indexTy->mapTypeOutOfContext() ->getCanonicalType(), diff --git a/lib/SILGen/SILGenForeignError.cpp b/lib/SILGen/SILGenForeignError.cpp index 41acb7b8646da..ad33042cd1cf5 100644 --- a/lib/SILGen/SILGenForeignError.cpp +++ b/lib/SILGen/SILGenForeignError.cpp @@ -113,7 +113,8 @@ static SILValue emitIntValue(SILGenFunction &SGF, SILLocation loc, if (auto structDecl = type.getStructOrBoundGenericStruct()) { auto properties = structDecl->getStoredProperties(); assert(properties.size() == 1); - SILType fieldType = type.getFieldType(properties[0], SGF.SGM.M); + SILType fieldType = type.getFieldType(properties[0], SGF.SGM.M, + SGF.getTypeExpansionContext()); SILValue fieldValue = emitIntValue(SGF, loc, fieldType, value); return SGF.B.createStruct(loc, type, fieldValue); } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 9fca470e3feb4..307ef69050cf2 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -153,10 +153,12 @@ SILGenFunction::emitSiblingMethodRef(SILLocation loc, // dispatch (viz. objc_msgSend for now). if (methodConstant.hasDecl() && methodConstant.getDecl()->isObjCDynamic()) { - methodValue = emitDynamicMethodRef( - loc, methodConstant, - SGM.Types.getConstantInfo(methodConstant).SILFnType) - .getValue(); + methodValue = + emitDynamicMethodRef( + loc, methodConstant, + SGM.Types.getConstantInfo(getTypeExpansionContext(), methodConstant) + .SILFnType) + .getValue(); } else { methodValue = emitGlobalFunctionRef(loc, methodConstant); } @@ -164,7 +166,8 @@ SILGenFunction::emitSiblingMethodRef(SILLocation loc, SILType methodTy = methodValue->getType(); // Specialize the generic method. - methodTy = methodTy.substGenericArgs(SGM.M, subMap); + methodTy = + methodTy.substGenericArgs(SGM.M, subMap, getTypeExpansionContext()); return std::make_tuple(ManagedValue::forUnmanaged(methodValue), methodTy); @@ -190,8 +193,8 @@ void SILGenFunction::emitCaptures(SILLocation loc, canGuarantee = true; break; } - - auto expansion = F.getResilienceExpansion(); + + auto expansion = getTypeExpansionContext(); for (auto capture : captureInfo.getCaptures()) { if (capture.isDynamicSelfMetadata()) { @@ -262,10 +265,12 @@ void SILGenFunction::emitCaptures(SILLocation loc, capturedArgs.push_back(emitUndef(getLoweredType(type).getAddressType())); break; case CaptureKind::Box: { - auto boxTy = SGM.Types.getContextBoxTypeForCapture(vd, - getLoweredType(type).getASTType(), - FunctionDC->getGenericEnvironmentOfContext(), - /*mutable*/ true); + auto boxTy = SGM.Types.getContextBoxTypeForCapture( + vd, + SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + type), + FunctionDC->getGenericEnvironmentOfContext(), + /*mutable*/ true); capturedArgs.push_back(emitUndef(boxTy)); break; } @@ -273,8 +278,28 @@ void SILGenFunction::emitCaptures(SILLocation loc, continue; } - auto Entry = found->second; + // Get an address value for a SILValue if it is address only in an type + // expansion context without opaque archetype substitution. + auto getAddressValue = [&](SILValue entryValue) -> SILValue { + if (SGM.Types + .getTypeLowering( + valueType, + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( + expansion.getResilienceExpansion())) + .isAddressOnly() && + !entryValue->getType().isAddress()) { + + auto addr = emitTemporaryAllocation(vd, entryValue->getType()); + auto val = B.emitCopyValueOperation(vd, entryValue); + auto &lowering = getTypeLowering(entryValue->getType()); + lowering.emitStore(B, vd, val, addr, StoreOwnershipQualifier::Init); + entryValue = addr; + enterDestroyCleanup(addr); + } + return entryValue; + }; + auto Entry = found->second; switch (SGM.Types.getDeclCaptureKind(capture, expansion)) { case CaptureKind::Constant: { // let declarations. @@ -309,20 +334,25 @@ void SILGenFunction::emitCaptures(SILLocation loc, } case CaptureKind::StorageAddress: { + auto entryValue = getAddressValue(Entry.value); // No-escaping stored declarations are captured as the // address of the value. - assert(Entry.value->getType().isAddress() && "no address for captured var!"); - capturedArgs.push_back(ManagedValue::forLValue(Entry.value)); + assert(entryValue->getType().isAddress() && "no address for captured var!"); + capturedArgs.push_back(ManagedValue::forLValue(entryValue)); break; } case CaptureKind::Box: { + auto entryValue = getAddressValue(Entry.value); // LValues are captured as both the box owning the value and the // address of the value. - assert(Entry.value->getType().isAddress() && "no address for captured var!"); - + assert(entryValue->getType().isAddress() && "no address for captured var!"); + // Boxes of opaque return values stay opaque. + auto minimalLoweredType = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), type->getCanonicalType()); // If this is a boxed variable, we can use it directly. - if (Entry.box) { + if (Entry.box && + entryValue->getType().getASTType() == minimalLoweredType) { // We can guarantee our own box to the callee. if (canGuarantee) { capturedArgs.push_back( @@ -330,7 +360,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, } else { capturedArgs.push_back(emitManagedRetain(loc, Entry.box)); } - escapesToMark.push_back(Entry.value); + escapesToMark.push_back(entryValue); } else { // Address only 'let' values are passed by box. This isn't great, in // that a variable captured by multiple closures will be boxed for each @@ -343,14 +373,13 @@ void SILGenFunction::emitCaptures(SILLocation loc, // closure context and pass it down to the partially applied function // in-place. // TODO: Use immutable box for immutable captures. - auto boxTy = SGM.Types.getContextBoxTypeForCapture(vd, - Entry.value->getType().getASTType(), - FunctionDC->getGenericEnvironmentOfContext(), - /*mutable*/ true); - + auto boxTy = SGM.Types.getContextBoxTypeForCapture( + vd, minimalLoweredType, FunctionDC->getGenericEnvironmentOfContext(), + /*mutable*/ true); + AllocBoxInst *allocBox = B.createAllocBox(loc, boxTy); ProjectBoxInst *boxAddress = B.createProjectBox(loc, allocBox, 0); - B.createCopyAddr(loc, Entry.value, boxAddress, IsNotTake, + B.createCopyAddr(loc, entryValue, boxAddress, IsNotTake, IsInitialization); if (canGuarantee) capturedArgs.push_back( @@ -377,7 +406,7 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant, SubstitutionMap subs) { auto loweredCaptureInfo = SGM.Types.getLoweredLocalCaptures(constant); - auto constantInfo = getConstantInfo(constant); + auto constantInfo = getConstantInfo(getTypeExpansionContext(), constant); SILValue functionRef = emitGlobalFunctionRef(loc, constant, constantInfo); SILType functionTy = functionRef->getType(); @@ -400,7 +429,8 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant, bool wasSpecialized = false; if (!subs.empty()) { - auto specialized = pft->substGenericArgs(F.getModule(), subs); + auto specialized = + pft->substGenericArgs(F.getModule(), subs, getTypeExpansionContext()); functionTy = SILType::getPrimitiveObjectType(specialized); wasSpecialized = true; } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 0887461ea7992..165a09e91442a 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -494,22 +494,35 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SILGenBuilder &getBuilder() { return B; } SILOptions &getOptions() { return getModule().getOptions(); } + // Returns the type expansion context for types in this function. + TypeExpansionContext getTypeExpansionContext() { + return TypeExpansionContext(getFunction()); + } + const TypeLowering &getTypeLowering(AbstractionPattern orig, Type subst) { return F.getTypeLowering(orig, subst); } const TypeLowering &getTypeLowering(Type t) { return F.getTypeLowering(t); } - CanSILFunctionType getSILFunctionType(AbstractionPattern orig, + CanSILFunctionType getSILFunctionType(TypeExpansionContext context, + AbstractionPattern orig, CanFunctionType substFnType) { - return SGM.Types.getSILFunctionType(orig, substFnType); + return SGM.Types.getSILFunctionType(context, orig, substFnType); } - SILType getLoweredType(AbstractionPattern orig, Type subst) { + SILType getLoweredType(AbstractionPattern orig, + Type subst) { return F.getLoweredType(orig, subst); } SILType getLoweredType(Type t) { return F.getLoweredType(t); } + SILType getLoweredTypeForFunctionArgument(Type t) { + auto typeForConv = + SGM.Types.getLoweredType(t, TypeExpansionContext::minimal()); + return getLoweredType(t).getCategoryType(typeForConv.getCategory()); + } + SILType getLoweredLoadableType(Type t) { return F.getLoweredLoadableType(t); } @@ -524,8 +537,19 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction return silConv.getSILType(result, fnTy); } - const SILConstantInfo &getConstantInfo(SILDeclRef constant) { - return SGM.Types.getConstantInfo(constant); + SILType getSILTypeInContext(SILResultInfo result, CanSILFunctionType fnTy) { + auto t = F.mapTypeIntoContext(getSILType(result, fnTy)); + return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); + } + + SILType getSILTypeInContext(SILParameterInfo param, CanSILFunctionType fnTy) { + auto t = F.mapTypeIntoContext(getSILType(param, fnTy)); + return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); + } + + const SILConstantInfo &getConstantInfo(TypeExpansionContext context, + SILDeclRef constant) { + return SGM.Types.getConstantInfo(context, constant); } Optional getStaticEnforcement(VarDecl *var = nullptr); @@ -1162,7 +1186,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Returns a reference to a constant in global context. For local func decls /// this returns the function constant with unapplied closure context. SILValue emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant) { - return emitGlobalFunctionRef(loc, constant, getConstantInfo(constant)); + return emitGlobalFunctionRef( + loc, constant, getConstantInfo(getTypeExpansionContext(), constant)); } SILValue emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index caf7acfc56ffa..747c6a97d1922 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -201,35 +201,37 @@ static CanType getSubstFormalRValueType(Expr *expr) { return expr->getType()->getRValueType()->getCanonicalType(); } -static LValueTypeData getAbstractedTypeData(SILGenModule &SGM, +static LValueTypeData getAbstractedTypeData(TypeExpansionContext context, + SILGenModule &SGM, SGFAccessKind accessKind, AbstractionPattern origFormalType, CanType substFormalType) { return { - accessKind, - origFormalType, - substFormalType, - SGM.Types.getLoweredRValueType(origFormalType, substFormalType) - }; + accessKind, origFormalType, substFormalType, + SGM.Types.getLoweredRValueType(context, origFormalType, substFormalType)}; } -static LValueTypeData getLogicalStorageTypeData(SILGenModule &SGM, +static LValueTypeData getLogicalStorageTypeData(TypeExpansionContext context, + SILGenModule &SGM, SGFAccessKind accessKind, CanType substFormalType) { assert(!isa(substFormalType)); AbstractionPattern origFormalType( substFormalType.getReferenceStorageReferent()); - return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType); + return getAbstractedTypeData(context, SGM, accessKind, origFormalType, + substFormalType); } -static LValueTypeData getPhysicalStorageTypeData(SILGenModule &SGM, +static LValueTypeData getPhysicalStorageTypeData(TypeExpansionContext context, + SILGenModule &SGM, SGFAccessKind accessKind, AbstractStorageDecl *storage, CanType substFormalType) { assert(!isa(substFormalType)); auto origFormalType = SGM.Types.getAbstractionPattern(storage) .getReferenceStorageReferentType(); - return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType); + return getAbstractedTypeData(context, SGM, accessKind, origFormalType, + substFormalType); } static bool shouldUseUnsafeEnforcement(VarDecl *var) { @@ -1208,7 +1210,8 @@ namespace { bool doesAccessorMutateSelf(SILGenFunction &SGF, SILDeclRef accessor) const { - auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor); + auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter( + SGF.getTypeExpansionContext(), accessor); return accessorSelf.getInterfaceType() && accessorSelf.isIndirectMutating(); } @@ -1396,10 +1399,12 @@ namespace { CanType ValType = SGF.F.mapTypeIntoContext(backingVar->getInterfaceType()) ->getCanonicalType(); - SILType varStorageType = - SGF.SGM.Types.getSubstitutedStorageType(backingVar, ValType); + // TODO: revist minimal + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + TypeExpansionContext::minimal(), backingVar, ValType); auto typeData = - getLogicalStorageTypeData(SGF.SGM, getTypeData().AccessKind, ValType); + getLogicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + getTypeData().AccessKind, ValType); // Get the address of the storage property. ManagedValue proj; @@ -1441,11 +1446,13 @@ namespace { ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI); // Create the allocating setter function. It captures the base address. - auto setterInfo = SGF.getConstantInfo(setter); + auto setterInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), setter); SILValue setterFRef; if (setter.hasDecl() && setter.getDecl()->isObjCDynamic()) { auto methodTy = SILType::getPrimitiveObjectType( - SGF.SGM.Types.getConstantFunctionType(setter)); + SGF.SGM.Types.getConstantFunctionType(SGF.getTypeExpansionContext(), + setter)); setterFRef = SGF.B.createObjCMethod( loc, base.getValue(), setter, methodTy); } else @@ -2016,7 +2023,8 @@ namespace { projectFnType->getInvocationGenericSignature(), keyPathTy->getGenericArgs(), {}); - auto substFnType = projectFnType->substGenericArgs(SGF.SGM.M, subs); + auto substFnType = projectFnType->substGenericArgs( + SGF.SGM.M, subs, SGF.getTypeExpansionContext()); // Perform the begin_apply. SmallVector yields; @@ -2484,8 +2492,9 @@ namespace { void emitUsingStrategy(AccessStrategy strategy) { switch (strategy.getKind()) { case AccessStrategy::Storage: { - auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, - FormalRValueType); + auto typeData = + getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + AccessKind, Storage, FormalRValueType); return asImpl().emitUsingStorage(typeData); } @@ -2496,8 +2505,8 @@ namespace { return asImpl().emitUsingAccessor(strategy.getAccessor(), false); case AccessStrategy::MaterializeToTemporary: { - auto typeData = - getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType); + auto typeData = getLogicalStorageTypeData( + SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType); return asImpl().emitUsingMaterialization(strategy.getReadStrategy(), strategy.getWriteStrategy(), typeData); @@ -2513,22 +2522,24 @@ namespace { switch (accessorKind) { case AccessorKind::Get: case AccessorKind::Set: { - auto typeData = - getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType); + auto typeData = getLogicalStorageTypeData( + SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType); return asImpl().emitUsingGetterSetter(accessor, isDirect, typeData); } case AccessorKind::Address: case AccessorKind::MutableAddress: { - auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, - FormalRValueType); + auto typeData = + getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + AccessKind, Storage, FormalRValueType); return asImpl().emitUsingAddressor(accessor, isDirect, typeData); } case AccessorKind::Read: case AccessorKind::Modify: { - auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, - FormalRValueType); + auto typeData = + getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + AccessKind, Storage, FormalRValueType); return asImpl().emitUsingCoroutineAccessor(accessor, isDirect, typeData); } @@ -3052,8 +3063,8 @@ struct MemberStorageAccessEmitter : AccessEmitter { void emitUsingAddressor(SILDeclRef addressor, bool isDirect, LValueTypeData typeData) { - SILType varStorageType = - SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType); + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + SGF.getTypeExpansionContext(), Storage, FormalRValueType); LV.add(Storage, addressor, IsSuper, isDirect, Subs, BaseFormalType, typeData, varStorageType, @@ -3117,8 +3128,8 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, } // Otherwise, it's a physical member. - SILType varStorageType = - SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType); + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + SGF.getTypeExpansionContext(), Storage, FormalRValueType); if (BaseFormalType->mayHaveSuperclass()) { LV.add(Storage, Options, varStorageType, typeData); @@ -3254,8 +3265,8 @@ LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, }(); if (useLogical) { - auto typeData = getLogicalStorageTypeData(SGF.SGM, accessKind, - substFormalType); + auto typeData = getLogicalStorageTypeData( + SGF.getTypeExpansionContext(), SGF.SGM, accessKind, substFormalType); Type baseFormalType = e->getBase()->getType()->getRValueType(); lv.add(typeData, keyPathKind, keyPath, @@ -3265,9 +3276,9 @@ LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, // in the opaque AbstractionPattern and push an OrigToSubstComponent here // so it can be peepholed. } else { - auto typeData = getAbstractedTypeData(SGF.SGM, accessKind, - AbstractionPattern::getOpaque(), - substFormalType); + auto typeData = getAbstractedTypeData( + TypeExpansionContext::minimal(), SGF.SGM, accessKind, + AbstractionPattern::getOpaque(), substFormalType); lv.add(typeData, keyPathKind, keyPath); diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index f54c4f90a4128..3b95cc66d571e 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -1878,7 +1878,8 @@ void PatternMatchEmission::emitEnumElementObjectDispatch( bool hasNonVoidAssocValue = false; bool hasAssocValue = elt->hasAssociatedValues(); if (hasAssocValue) { - eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M); + eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M, + SGF.getTypeExpansionContext()); hasNonVoidAssocValue = !eltTy.getASTType()->isVoid(); } @@ -2060,7 +2061,8 @@ void PatternMatchEmission::emitEnumElementDispatch( SILType eltTy; bool hasElt = false; if (elt->hasAssociatedValues()) { - eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M); + eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M, + SGF.getTypeExpansionContext()); hasElt = !eltTy.getASTType()->isVoid(); } diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index a62315dc41e03..9fea54ed37d55 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -787,7 +787,11 @@ void SILGenFunction::collectThunkParams( // Add the indirect results. for (auto resultTy : F.getConventions().getIndirectSILResultTypes()) { auto paramTy = F.mapTypeIntoContext(resultTy); - SILArgument *arg = F.begin()->createFunctionArgument(paramTy); + // Lower result parameters in the context of the function: opaque result + // types will be lowered to their underlying type if allowed by resilience. + auto inContextParamTy = F.getLoweredType(paramTy.getASTType()) + .getCategoryType(paramTy.getCategory()); + SILArgument *arg = F.begin()->createFunctionArgument(inContextParamTy); if (indirectResults) indirectResults->push_back(arg); } @@ -796,7 +800,11 @@ void SILGenFunction::collectThunkParams( auto paramTypes = F.getLoweredFunctionType()->getParameters(); for (auto param : paramTypes) { auto paramTy = F.mapTypeIntoContext(F.getConventions().getSILType(param)); - params.push_back(B.createInputFunctionArgument(paramTy, loc)); + // Lower parameters in the context of the function: opaque result types will + // be lowered to their underlying type if allowed by resilience. + auto inContextParamTy = F.getLoweredType(paramTy.getASTType()) + .getCategoryType(paramTy.getCategory()); + params.push_back(B.createInputFunctionArgument(inContextParamTy, loc)); } } @@ -2689,8 +2697,8 @@ void ResultPlanner::execute(ArrayRef innerDirectResults, // A helper function to add an outer direct result. auto addOuterDirectResult = [&](ManagedValue resultValue, SILResultInfo result) { - assert(resultValue.getType() - == SGF.F.mapTypeIntoContext(SGF.getSILType(result, CanSILFunctionType()))); + assert(resultValue.getType() == + SGF.getSILTypeInContext(result, CanSILFunctionType())); outerDirectResults.push_back(resultValue.forward(SGF)); }; @@ -3563,16 +3571,19 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, CanSILFunctionType derivedFTy; if (baseLessVisibleThanDerived) { - derivedFTy = SGM.Types.getConstantOverrideType(derived); + derivedFTy = + SGM.Types.getConstantOverrideType(getTypeExpansionContext(), derived); } else { - derivedFTy = SGM.Types.getConstantInfo(derived).SILFnType; + derivedFTy = + SGM.Types.getConstantInfo(getTypeExpansionContext(), derived).SILFnType; } SubstitutionMap subs; if (auto *genericEnv = fd->getGenericEnvironment()) { F.setGenericEnvironment(genericEnv); subs = getForwardingSubstitutionMap(); - derivedFTy = derivedFTy->substGenericArgs(SGM.M, subs); + derivedFTy = + derivedFTy->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); inputSubstType = cast( cast(inputSubstType) @@ -3622,7 +3633,8 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, if (baseLessVisibleThanDerived) { // See the comment in SILVTableVisitor.h under maybeAddMethod(). auto selfValue = thunkArgs.back().getValue(); - auto derivedTy = SGM.Types.getConstantOverrideType(derived); + auto derivedTy = + SGM.Types.getConstantOverrideType(getTypeExpansionContext(), derived); derivedRef = emitClassMethodRef(loc, selfValue, derived, derivedTy); } else { derivedRef = B.createFunctionRefFor(loc, implFn); @@ -3744,16 +3756,15 @@ static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness, } static CanSILFunctionType -getWitnessFunctionType(SILGenModule &SGM, - SILDeclRef witness, - WitnessDispatchKind witnessKind) { +getWitnessFunctionType(TypeExpansionContext context, SILGenModule &SGM, + SILDeclRef witness, WitnessDispatchKind witnessKind) { switch (witnessKind) { case WitnessDispatchKind::Static: case WitnessDispatchKind::Dynamic: case WitnessDispatchKind::Witness: - return SGM.Types.getConstantInfo(witness).SILFnType; + return SGM.Types.getConstantInfo(context, witness).SILFnType; case WitnessDispatchKind::Class: - return SGM.Types.getConstantOverrideType(witness); + return SGM.Types.getConstantOverrideType(context, witness); } llvm_unreachable("Unhandled WitnessDispatchKind in switch."); @@ -3838,7 +3849,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, collectThunkParams(loc, origParams); // Get the type of the witness. - auto witnessInfo = getConstantInfo(witness); + auto witnessInfo = getConstantInfo(getTypeExpansionContext(), witness); CanAnyFunctionType witnessSubstTy = witnessInfo.LoweredType; if (auto genericFnType = dyn_cast(witnessSubstTy)) { witnessSubstTy = cast(genericFnType @@ -3857,10 +3868,12 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, } // Get the lowered type of the witness. - auto origWitnessFTy = getWitnessFunctionType(SGM, witness, witnessKind); + auto origWitnessFTy = getWitnessFunctionType(getTypeExpansionContext(), SGM, + witness, witnessKind); auto witnessFTy = origWitnessFTy; if (!witnessSubs.empty()) - witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs); + witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs, + getTypeExpansionContext()); auto reqtSubstParams = reqtSubstTy.getParams(); auto witnessSubstParams = witnessSubstTy.getParams(); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index d3b9edb10248b..4c57bf2e96ee1 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -98,9 +98,14 @@ class EmitBBArguments : public CanTypeVisitor elements; - auto &tl = SGF.SGM.Types.getTypeLowering(t, ResilienceExpansion::Minimal); + auto &tl = SGF.SGM.Types.getTypeLowering(t, SGF.getTypeExpansionContext()); bool canBeGuaranteed = tl.isLoadable(); // Collect the exploded elements. @@ -226,9 +231,10 @@ struct ArgumentInitHelper { uint16_t ArgNo = 0; ArgumentInitHelper(SILGenFunction &SGF, SILFunction &f) - : SGF(SGF), f(f), initB(SGF.B), - parameters(f.getLoweredFunctionType()->getParameters()) { - } + : SGF(SGF), f(f), initB(SGF.B), + parameters( + f.getLoweredFunctionTypeInContext(SGF.B.getTypeExpansionContext()) + ->getParameters()) {} unsigned getNumArgs() const { return ArgNo; } @@ -320,8 +326,7 @@ static void makeArgument(Type ty, ParamDecl *decl, for (auto fieldType : tupleTy->getElementTypes()) makeArgument(fieldType, decl, args, SGF); } else { - auto loweredTy = SGF.SGM.Types.getLoweredType(ty, - ResilienceExpansion::Minimal); + auto loweredTy = SGF.getLoweredTypeForFunctionArgument(ty); if (decl->isInOut()) loweredTy = SILType::getPrimitiveAddressType(loweredTy.getASTType()); auto arg = SGF.F.begin()->createFunctionArgument(loweredTy, decl); @@ -358,7 +363,7 @@ static void emitCaptureArguments(SILGenFunction &SGF, return SGF.F.mapTypeIntoContext(interfaceType); }; - auto expansion = SGF.F.getResilienceExpansion(); + auto expansion = SGF.getTypeExpansionContext(); switch (SGF.SGM.Types.getDeclCaptureKind(capture, expansion)) { case CaptureKind::Constant: { auto type = getVarTypeInCaptureContext(); @@ -400,9 +405,13 @@ static void emitCaptureArguments(SILGenFunction &SGF, // LValues are captured as a retained @box that owns // the captured value. auto type = getVarTypeInCaptureContext(); - auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(VD, - SGF.SGM.Types.getLoweredRValueType(type), - SGF.F.getGenericEnvironment(), /*mutable*/ true); + // Get the content for the box in the minimal resilience domain because we + // are declaring a type. + auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture( + VD, + SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + type), + SGF.F.getGenericEnvironment(), /*mutable*/ true); SILValue box = SGF.F.begin()->createFunctionArgument( SILType::getPrimitiveObjectType(boxTy), VD); SILValue addr = SGF.B.createProjectBox(VD, box, 0); @@ -499,9 +508,12 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType, // The calling convention always uses minimal resilience expansion. auto &resultTI = SGF.SGM.Types.getTypeLowering(DC->mapTypeIntoContext(resultType), - ResilienceExpansion::Minimal); + SGF.getTypeExpansionContext()); + auto &resultTIConv = SGF.SGM.Types.getTypeLowering( + DC->mapTypeIntoContext(resultType), TypeExpansionContext::minimal()); + if (!SILModuleConventions::isReturnedIndirectlyInSIL( - resultTI.getLoweredType(), SGF.SGM.M)) { + resultTIConv.getLoweredType(), SGF.SGM.M)) { return; } auto &ctx = SGF.getASTContext(); @@ -511,9 +523,8 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType, DC); var->setSpecifier(ParamSpecifier::InOut); var->setInterfaceType(resultType); - - auto *arg = - SGF.F.begin()->createFunctionArgument(resultTI.getLoweredType(), var); + auto *arg = SGF.F.begin()->createFunctionArgument( + resultTI.getLoweredType().getAddressType(), var); (void)arg; } diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index 37f9557a8ce53..ca420cb1a55aa 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -97,7 +97,8 @@ getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk, SILDeclRef next = SILDeclRef(vd, thunk.kind); assert(!next.isCurried); - auto constantInfo = SGF.SGM.Types.getConstantInfo(next); + auto constantInfo = + SGF.SGM.Types.getConstantInfo(SGF.getTypeExpansionContext(), next); // If the function is natively foreign, reference its foreign entry point. if (requiresForeignToNativeThunk(vd)) @@ -118,7 +119,8 @@ getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk, next}; } - auto methodTy = SGF.SGM.Types.getConstantOverrideType(next); + auto methodTy = SGF.SGM.Types.getConstantOverrideType( + SGF.getTypeExpansionContext(), next); SILValue result = SGF.emitClassMethodRef(loc, selfArg.getValue(), next, methodTy); return {ManagedValue::forUnmanaged(result), @@ -158,7 +160,7 @@ void SILGenFunction::emitCurryThunk(SILDeclRef thunk) { SILLocation loc(vd); Scope S(*this, vd); - auto thunkInfo = SGM.Types.getConstantInfo(thunk); + auto thunkInfo = SGM.Types.getConstantInfo(getTypeExpansionContext(), thunk); auto thunkFnTy = thunkInfo.SILFnType; SILFunctionConventions fromConv(thunkFnTy, SGM.M); @@ -192,8 +194,9 @@ void SILGenFunction::emitCurryThunk(SILDeclRef thunk) { // just grab the pattern for the curried fn ref and "call" it. assert(!calleeRef.isCurried); calleeRef.isCurried = true; - auto appliedFnPattern = SGM.Types.getConstantInfo(calleeRef).FormalPattern - .getFunctionResultType(); + auto appliedFnPattern = + SGM.Types.getConstantInfo(getTypeExpansionContext(), calleeRef) + .FormalPattern.getFunctionResultType(); auto appliedThunkPattern = thunkInfo.FormalPattern.getFunctionResultType(); @@ -265,7 +268,7 @@ SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, SILConstantInfo constantInfo, bool callPreviousDynamicReplaceableImpl) { - assert(constantInfo == getConstantInfo(constant)); + assert(constantInfo == getConstantInfo(getTypeExpansionContext(), constant)); // Builtins must be fully applied at the point of reference. if (constant.hasDecl() && @@ -289,7 +292,8 @@ SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, } auto f = SGM.getFunction(constant, NotForDefinition); - assert(f->getLoweredFunctionType() == constantInfo.SILFnType); + assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext()) == + constantInfo.SILFnType); if (callPreviousDynamicReplaceableImpl) return B.createPreviousDynamicFunctionRef(loc, f); else diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 9e634105d3594..d226f30b7d109 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -87,7 +87,9 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, derived.kind != SILDeclRef::Kind::Allocator); if (usesObjCDynamicDispatch) { - implFn = getDynamicThunk(derived, Types.getConstantInfo(derived).SILFnType); + implFn = getDynamicThunk( + derived, Types.getConstantInfo(TypeExpansionContext::minimal(), derived) + .SILFnType); } else { implFn = getFunction(derived, NotForDefinition); } @@ -105,11 +107,13 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, // Determine the derived thunk type by lowering the derived type against the // abstraction pattern of the base. - auto baseInfo = Types.getConstantInfo(base); - auto derivedInfo = Types.getConstantInfo(derived); + auto baseInfo = Types.getConstantInfo(TypeExpansionContext::minimal(), base); + auto derivedInfo = + Types.getConstantInfo(TypeExpansionContext::minimal(), derived); auto basePattern = AbstractionPattern(baseInfo.LoweredType); - - auto overrideInfo = M.Types.getConstantOverrideInfo(derived, base); + + auto overrideInfo = M.Types.getConstantOverrideInfo( + TypeExpansionContext::minimal(), derived, base); // If base method's generic requirements are not satisfied by the derived // method then we need a thunk. @@ -588,7 +592,8 @@ SILFunction *SILGenModule::emitProtocolWitness( ProtocolConformanceRef conformance, SILLinkage linkage, IsSerialized_t isSerialized, SILDeclRef requirement, SILDeclRef witnessRef, IsFreeFunctionWitness_t isFree, Witness witness) { - auto requirementInfo = Types.getConstantInfo(requirement); + auto requirementInfo = + Types.getConstantInfo(TypeExpansionContext::minimal(), requirement); // Work out the lowered function type of the SIL witness thunk. auto reqtOrigTy = cast(requirementInfo.LoweredType); @@ -652,8 +657,9 @@ SILFunction *SILGenModule::emitProtocolWitness( // Lower the witness thunk type with the requirement's abstraction level. auto witnessSILFnType = getNativeSILFunctionType( - M.Types, AbstractionPattern(reqtOrigTy), reqtSubstTy, - requirement, witnessRef, witnessSubsForTypeLowering, conformance); + M.Types, TypeExpansionContext::minimal(), AbstractionPattern(reqtOrigTy), + reqtSubstTy, requirement, witnessRef, witnessSubsForTypeLowering, + conformance); // Mangle the name of the witness thunk. Mangle::ASTMangler NewMangler; @@ -705,7 +711,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, SelfProtocolConformance *conformance, SILLinkage linkage, SILDeclRef requirement) { - auto requirementInfo = SGM.Types.getConstantInfo(requirement); + auto requirementInfo = + SGM.Types.getConstantInfo(TypeExpansionContext::minimal(), requirement); // Work out the lowered function type of the SIL witness thunk. auto reqtOrigTy = cast(requirementInfo.LoweredType); @@ -734,8 +741,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, cast(reqtOrigTy.subst(reqtSubs)->getCanonicalType()); // Substitute into the requirement type to get the type of the thunk. - auto witnessSILFnType = - requirementInfo.SILFnType->substGenericArgs(SGM.M, reqtSubs); + auto witnessSILFnType = requirementInfo.SILFnType->substGenericArgs( + SGM.M, reqtSubs, TypeExpansionContext::minimal()); // Mangle the name of the witness thunk. std::string name = [&] { diff --git a/lib/SILGen/SwitchEnumBuilder.cpp b/lib/SILGen/SwitchEnumBuilder.cpp index c230ec981a713..4704d95fae200 100644 --- a/lib/SILGen/SwitchEnumBuilder.cpp +++ b/lib/SILGen/SwitchEnumBuilder.cpp @@ -144,8 +144,8 @@ void SwitchEnumBuilder::emit() && { ManagedValue input; if (decl->hasAssociatedValues()) { // Pull the payload out if we have one. - SILType inputType = - optional.getType().getEnumElementType(decl, builder.getModule()); + SILType inputType = optional.getType().getEnumElementType( + decl, builder.getModule(), builder.getFunction()); input = optional; if (!isAddressOnly) { input = builder.createOwnedPhiArgument(inputType); From 8aaa7b4dc11bb85a39512d6abbb2737be0f329ea Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 12:02:21 -0800 Subject: [PATCH 070/283] SILOptimizer: Pipe through TypeExpansionContext --- include/swift/SIL/TypeSubstCloner.h | 35 +- .../Analysis/AccessSummaryAnalysis.h | 9 +- .../Analysis/TypeExpansionAnalysis.h | 3 +- .../swift/SILOptimizer/PassManager/Passes.def | 2 - include/swift/SILOptimizer/Utils/Generics.h | 20 +- .../SILOptimizer/Utils/LoadStoreOptUtils.h | 33 +- .../Analysis/AccessSummaryAnalysis.cpp | 23 +- lib/SILOptimizer/Analysis/AliasAnalysis.cpp | 4 +- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 11 +- .../Analysis/TypeExpansionAnalysis.cpp | 11 +- .../ExistentialTransform.cpp | 3 +- .../FunctionSignatureOpts.cpp | 4 +- lib/SILOptimizer/IPO/CapturePromotion.cpp | 19 +- lib/SILOptimizer/IPO/CapturePropagation.cpp | 9 +- lib/SILOptimizer/IPO/ClosureSpecializer.cpp | 5 +- lib/SILOptimizer/IPO/EagerSpecializer.cpp | 10 +- lib/SILOptimizer/IPO/GlobalOpt.cpp | 3 +- lib/SILOptimizer/IPO/LetPropertiesOpts.cpp | 2 +- lib/SILOptimizer/IPO/UsePrespecialized.cpp | 3 +- .../Mandatory/DIMemoryUseCollector.cpp | 85 ++- .../Mandatory/DiagnoseStaticExclusivity.cpp | 9 +- .../Mandatory/OSLogOptimization.cpp | 6 +- .../Mandatory/OwnershipModelEliminator.cpp | 3 +- .../Mandatory/PMOMemoryUseCollector.cpp | 4 +- .../Mandatory/PredictableMemOpt.cpp | 72 ++- lib/SILOptimizer/PassManager/PassPipeline.cpp | 4 - .../Transforms/AllocBoxToStack.cpp | 14 +- lib/SILOptimizer/Transforms/CMakeLists.txt | 1 - .../Transforms/DeadStoreElimination.cpp | 7 +- .../Transforms/ObjectOutliner.cpp | 8 +- .../Transforms/RedundantLoadElimination.cpp | 44 +- .../Transforms/ReleaseDevirtualizer.cpp | 7 +- lib/SILOptimizer/Transforms/SILCodeMotion.cpp | 3 +- lib/SILOptimizer/Transforms/SILSROA.cpp | 5 +- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 27 +- .../Transforms/SpecializeOpaqueArchetypes.cpp | 558 ------------------ .../UtilityPasses/LSLocationPrinter.cpp | 34 +- .../UtilityPasses/SerializeSILPass.cpp | 380 ++++++++++++ lib/SILOptimizer/Utils/CastOptimizer.cpp | 3 +- lib/SILOptimizer/Utils/Devirtualize.cpp | 15 +- lib/SILOptimizer/Utils/Generics.cpp | 49 +- lib/SILOptimizer/Utils/InstOptUtils.cpp | 2 +- lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp | 91 +-- 43 files changed, 817 insertions(+), 823 deletions(-) delete mode 100644 lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp diff --git a/include/swift/SIL/TypeSubstCloner.h b/include/swift/SIL/TypeSubstCloner.h index f450827afa47f..22b8dfb679256 100644 --- a/include/swift/SIL/TypeSubstCloner.h +++ b/include/swift/SIL/TypeSubstCloner.h @@ -90,7 +90,8 @@ class TypeSubstCloner : public SILClonerWithScopes { // Use the new set of substitutions to compute the new // substituted callee type. RecursiveSubstCalleeSILType = LoweredFnTy->substGenericArgs( - AI.getModule(), RecursiveSubs); + AI.getModule(), RecursiveSubs, + Builder.getTypeExpansionContext()); } // The specialized recursive function may have different calling @@ -109,7 +110,8 @@ class TypeSubstCloner : public SILClonerWithScopes { assert(Subs.empty() || SubstCalleeSILType == - Callee->getType().substGenericArgs(AI.getModule(), Subs)); + Callee->getType().substGenericArgs( + AI.getModule(), Subs, Builder.getTypeExpansionContext())); } ArrayRef getArguments() const { @@ -168,17 +170,40 @@ class TypeSubstCloner : public SILClonerWithScopes { SILType &Sty = TypeCache[Ty]; if (!Sty) { Sty = Ty.subst(Original.getModule(), SubsMap); + if (!Sty.getASTType()->hasOpaqueArchetype() || + !getBuilder() + .getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return Sty; + // Remap types containing opaque result types in the current context. + Sty = getBuilder().getTypeLowering(Sty).getLoweredType().getCategoryType( + Sty.getCategory()); } return Sty; } CanType remapASTType(CanType ty) { - return ty.subst(SubsMap)->getCanonicalType(); + auto substTy = ty.subst(SubsMap)->getCanonicalType(); + if (!substTy->hasOpaqueArchetype() || + !getBuilder().getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return substTy; + // Remap types containing opaque result types in the current context. + return getBuilder().getModule().Types.getLoweredRValueType( + TypeExpansionContext(getBuilder().getFunction()), substTy); } - ProtocolConformanceRef remapConformance(Type type, + ProtocolConformanceRef remapConformance(Type ty, ProtocolConformanceRef conf) { - return conf.subst(type, SubsMap); + auto conformance = conf.subst(ty, SubsMap); + auto substTy = ty.subst(SubsMap)->getCanonicalType(); + auto context = getBuilder().getTypeExpansionContext(); + if (substTy->hasOpaqueArchetype() && + context.shouldLookThroughOpaqueTypeArchetypes()) { + conformance = + substOpaqueTypesWithUnderlyingTypes(conformance, substTy, context); + } + return conformance; } SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { diff --git a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h index 1c19552cd5f5f..e71b6c58fea36 100644 --- a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h @@ -62,7 +62,8 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { /// Returns a description of the summary. For debugging and testing /// purposes. - std::string getDescription(SILType BaseType, SILModule &M) const; + std::string getDescription(SILType BaseType, SILModule &M, + TypeExpansionContext context) const; }; typedef llvm::SmallDenseMap @@ -85,7 +86,8 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { /// Returns a description of the summary. For debugging and testing /// purposes. - std::string getDescription(SILType BaseType, SILModule &M) const; + std::string getDescription(SILType BaseType, SILModule &M, + TypeExpansionContext context) const; /// Returns the accesses that the function performs to subpaths of the /// argument. @@ -208,7 +210,8 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { /// The base type must be the type of the root of the path. static std::string getSubPathDescription(SILType BaseType, const IndexTrieNode *SubPath, - SILModule &M); + SILModule &M, + TypeExpansionContext context); /// Performs a lexicographic comparison of two subpaths, first by path length /// and then by index of the last path component. Returns true when lhs diff --git a/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h b/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h index bc70dd7a31292..46be7f8af2bd0 100644 --- a/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h @@ -32,7 +32,8 @@ class TypeExpansionAnalysis : public SILAnalysis { } /// Return ProjectionPath to every leaf or intermediate node of the given type. - const ProjectionPathList &getTypeExpansion(SILType B, SILModule *Mod); + const ProjectionPathList &getTypeExpansion(SILType B, SILModule *Mod, + TypeExpansionContext context); /// Invalidate all information in this analysis. virtual void invalidate() override { diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 96f891f770501..bfcf1254014cb 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -230,8 +230,6 @@ PASS(NoReturnFolding, "noreturn-folding", "Prune Control Flow at No-Return Calls Using SIL unreachable") PASS(ObjectOutliner, "object-outliner", "Outlining of Global Objects") -PASS(OpaqueArchetypeSpecializer, "opaque-archetype-specializer", - "Opaque archetype specializer") PASS(Outliner, "outliner", "Function Outlining Optimization") PASS(OwnershipModelEliminator, "ownership-model-eliminator", diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 50c80b51578d3..d4c3f3d325040 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -100,6 +100,11 @@ class ReabstractionInfo { // Reference to the original generic non-specialized callee function. SILFunction *Callee; + // The module the specialization is created in. + ModuleDecl *TargetModule = nullptr; + + bool isWholeModule = false; + // The apply site which invokes the generic function. ApplySite Apply; @@ -140,7 +145,9 @@ class ReabstractionInfo { /// substitutions \p ParamSubs. /// If specialization is not possible getSpecializedType() will return an /// invalid type. - ReabstractionInfo(ApplySite Apply, SILFunction *Callee, + ReabstractionInfo(ModuleDecl *targetModule, + bool isModuleWholeModule, + ApplySite Apply, SILFunction *Callee, SubstitutionMap ParamSubs, IsSerialized_t Serialized, bool ConvertIndirectToDirect = true, @@ -148,16 +155,17 @@ class ReabstractionInfo { /// Constructs the ReabstractionInfo for generic function \p Callee with /// a specialization signature. - ReabstractionInfo(SILFunction *Callee, GenericSignature SpecializedSig); + ReabstractionInfo(ModuleDecl *targetModule, bool isModuleWholeModule, + SILFunction *Callee, GenericSignature SpecializedSig); IsSerialized_t isSerialized() const { return Serialized; } - ResilienceExpansion getResilienceExpansion() const { - return (Serialized - ? ResilienceExpansion::Minimal - : ResilienceExpansion::Maximal); + TypeExpansionContext getResilienceExpansion() const { + auto resilience = (Serialized ? ResilienceExpansion::Minimal + : ResilienceExpansion::Maximal); + return TypeExpansionContext(resilience, TargetModule, isWholeModule); } /// Returns true if the \p ParamIdx'th (non-result) formal parameter is diff --git a/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h b/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h index f11fbcd3042af..5a25557693b06 100644 --- a/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h +++ b/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h @@ -169,12 +169,15 @@ class LSBase { } /// Print the LSBase. - virtual void print(llvm::raw_ostream &os, SILModule *Mod) { + virtual void print(llvm::raw_ostream &os, SILModule *Mod, + TypeExpansionContext context) { os << Base; - Path.getValue().print(os, *Mod); + Path.getValue().print(os, *Mod, context); } - virtual void dump(SILModule *Mod) { print(llvm::dbgs(), Mod); } + virtual void dump(SILModule *Mod, TypeExpansionContext context) { + print(llvm::dbgs(), Mod, context); + } }; static inline llvm::hash_code hash_value(const LSBase &S) { @@ -257,16 +260,18 @@ class LSValue : public LSBase { return Path.getValue().createExtract(Base, Inst, true); } - void print(llvm::raw_ostream &os, SILModule *Mod) { + void print(llvm::raw_ostream &os, SILModule *Mod, + TypeExpansionContext context) { if (CoveringValue) { os << "Covering Value"; return; } - LSBase::print(os, Mod); + LSBase::print(os, Mod, context); } /// Expand this SILValue to all individual fields it contains. - static void expand(SILValue Base, SILModule *Mod, LSValueList &Vals, + static void expand(SILValue Base, SILModule *Mod, + TypeExpansionContext context, LSValueList &Vals, TypeExpansionAnalysis *TE); /// Given a memory location and a map between the expansions of the location @@ -329,13 +334,14 @@ class LSLocation : public LSBase { } /// Returns the type of the object the LSLocation represents. - SILType getType(SILModule *M) { - return Path.getValue().getMostDerivedType(*M); + SILType getType(SILModule *M, TypeExpansionContext context) { + return Path.getValue().getMostDerivedType(*M, context); } /// Get the first level locations based on this location's first level /// projection. - void getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod); + void getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod, + TypeExpansionContext context); /// Check whether the 2 LSLocations may alias each other or not. bool isMayAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA); @@ -351,15 +357,18 @@ class LSLocation : public LSBase { /// In SIL, we can have a store to an aggregate and loads from its individual /// fields. Therefore, we expand all the operations on aggregates onto /// individual fields and process them separately. - static void expand(LSLocation Base, SILModule *Mod, LSLocationList &Locs, + static void expand(LSLocation Base, SILModule *Mod, + TypeExpansionContext context, LSLocationList &Locs, TypeExpansionAnalysis *TE); /// Given a set of locations derived from the same base, try to merge/reduce /// them into smallest number of LSLocations possible. - static void reduce(LSLocation Base, SILModule *Mod, LSLocationList &Locs); + static void reduce(LSLocation Base, SILModule *Mod, + TypeExpansionContext context, LSLocationList &Locs); /// Enumerate the given Mem LSLocation. - static void enumerateLSLocation(SILModule *M, SILValue Mem, + static void enumerateLSLocation(TypeExpansionContext context, SILModule *M, + SILValue Mem, std::vector &LSLocationVault, LSLocationIndexMap &LocToBit, LSLocationBaseMap &BaseToLoc, diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index 928106140beb0..fbd23936c2238 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -380,13 +380,13 @@ void AccessSummaryAnalysis::recompute(FunctionInfo *initial) { } while (needAnotherIteration); } -std::string -AccessSummaryAnalysis::SubAccessSummary::getDescription(SILType BaseType, - SILModule &M) const { +std::string AccessSummaryAnalysis::SubAccessSummary::getDescription( + SILType BaseType, SILModule &M, TypeExpansionContext context) const { std::string sbuf; llvm::raw_string_ostream os(sbuf); - os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M); + os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M, + context); if (!SubPath->isRoot()) os << " "; @@ -409,9 +409,8 @@ void AccessSummaryAnalysis::ArgumentSummary::getSortedSubAccesses( assert(storage.size() == SubAccesses.size()); } -std::string -AccessSummaryAnalysis::ArgumentSummary::getDescription(SILType BaseType, - SILModule &M) const { +std::string AccessSummaryAnalysis::ArgumentSummary::getDescription( + SILType BaseType, SILModule &M, TypeExpansionContext context) const { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "["; @@ -425,7 +424,7 @@ AccessSummaryAnalysis::ArgumentSummary::getDescription(SILType BaseType, if (index > 0) { os << ", "; } - os << subAccess.getDescription(BaseType, M); + os << subAccess.getDescription(BaseType, M, context); ++index; } os << "]"; @@ -551,7 +550,8 @@ AccessSummaryAnalysis::findSubPathAccessed(BeginAccessInst *BAI) { /// that stored-property relaxation supports: struct stored properties /// and tuple elements. std::string AccessSummaryAnalysis::getSubPathDescription( - SILType baseType, const IndexTrieNode *subPath, SILModule &M) { + SILType baseType, const IndexTrieNode *subPath, SILModule &M, + TypeExpansionContext context) { // Walk the trie to the root to collect the sequence (in reverse order). llvm::SmallVector reversedIndices; const IndexTrieNode *I = subPath; @@ -570,7 +570,7 @@ std::string AccessSummaryAnalysis::getSubPathDescription( if (StructDecl *D = containingType.getStructOrBoundGenericStruct()) { VarDecl *var = D->getStoredProperties()[index]; os << var->getBaseName(); - containingType = containingType.getFieldType(var, M); + containingType = containingType.getFieldType(var, M, context); continue; } @@ -635,7 +635,8 @@ void AccessSummaryAnalysis::FunctionSummary::print(raw_ostream &os, } SILArgument *arg = fn->getArgument(i); SILModule &m = fn->getModule(); - os << getAccessForArgument(i).getDescription(arg->getType(), m); + os << getAccessForArgument(i).getDescription(arg->getType(), m, + TypeExpansionContext(*fn)); } os << ")"; diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index e2db274668799..90e304d0a5084 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -475,8 +475,8 @@ static bool typedAccessTBAAMayAlias(SILType LTy, SILType RTy, // If one type is an aggregate and it contains the other type then the record // reference may alias the aggregate reference. - if (LTy.aggregateContainsRecord(RTy, Mod) || - RTy.aggregateContainsRecord(LTy, Mod)) + if (LTy.aggregateContainsRecord(RTy, Mod, F.getTypeExpansionContext()) || + RTy.aggregateContainsRecord(LTy, Mod, F.getTypeExpansionContext())) return true; // FIXME: All the code following could be made significantly more aggressive diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index ce6af396ff29d..fbe37439c5eb7 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -70,7 +70,9 @@ static bool findRecursiveRefType(SILType Ty, const SILFunction &F, if (auto *Str = Ty.getStructOrBoundGenericStruct()) { for (auto *Field : Str->getStoredProperties()) { - if (findRecursiveRefType(Ty.getFieldType(Field, Mod), F, mustBeRef)) + if (findRecursiveRefType( + Ty.getFieldType(Field, Mod, F.getTypeExpansionContext()), F, + mustBeRef)) return true; } return false; @@ -84,9 +86,10 @@ static bool findRecursiveRefType(SILType Ty, const SILFunction &F, } if (auto En = Ty.getEnumOrBoundGenericEnum()) { for (auto *ElemDecl : En->getAllElements()) { - if (ElemDecl->hasAssociatedValues() - && findRecursiveRefType(Ty.getEnumElementType(ElemDecl, Mod), F, - mustBeRef)) + if (ElemDecl->hasAssociatedValues() && + findRecursiveRefType( + Ty.getEnumElementType(ElemDecl, Mod, F.getTypeExpansionContext()), + F, mustBeRef)) return true; } return false; diff --git a/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp b/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp index 203e7cca65921..657022a286b3d 100644 --- a/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp @@ -24,10 +24,14 @@ using namespace swift; // memory usage of this cache. static const int TypeExpansionAnalysisMaxCacheSize = 4096; -const ProjectionPathList& -TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod) { +const ProjectionPathList & +TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod, + TypeExpansionContext context) { // Check whether we have the type expansion. auto Iter = ExpansionCache.find(B); + // TODO: FIXME: ExpansionCache.find((B,context)) + // + // if (Iter != ExpansionCache.end()) { return Iter->second; } @@ -46,7 +50,8 @@ TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod) { } // Build the type expansion for the leaf nodes. - ProjectionPath::expandTypeIntoLeafProjectionPaths(B, Mod, ExpansionCache[B]); + ProjectionPath::expandTypeIntoLeafProjectionPaths(B, Mod, context, + ExpansionCache[B]); return ExpansionCache[B]; } diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index b4d3be9dbda36..41671b27609de 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -517,7 +517,8 @@ void ExistentialTransform::populateThunkBody() { MakeAbstractConformanceForGenericType()); /// Perform the substitutions. - auto SubstCalleeType = GenCalleeType->substGenericArgs(M, SubMap); + auto SubstCalleeType = GenCalleeType->substGenericArgs( + M, SubMap, Builder.getTypeExpansionContext()); /// Obtain the Result Type. SILValue ReturnValue; diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 620d02b9c3be1..69089e5f54770 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -581,8 +581,8 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Produce a substitutions list and a set of substituted SIL types // required for creating a new SIL function. Subs = F->getForwardingSubstitutionMap(); - auto SubstCalleeType = - GenCalleeType->substGenericArgs(M, Subs); + auto SubstCalleeType = GenCalleeType->substGenericArgs( + M, Subs, Builder.getTypeExpansionContext()); SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); SILFunctionConventions Conv(SubstCalleeType, M); ResultType = Conv.getSILResultType(); diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index dff5e4489c9e0..db161542862f7 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -368,8 +368,11 @@ computeNewArgInterfaceTypes(SILFunction *F, IndicesSet &PromotableIndices, auto paramBoxTy = paramTy.castTo(); assert(paramBoxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented yet"); - auto paramBoxedTy = getSILBoxFieldType(paramBoxTy, Types, 0); - auto ¶mTL = Types.getTypeLowering(paramBoxedTy, expansion); + auto paramBoxedTy = + getSILBoxFieldType(TypeExpansionContext(*F), paramBoxTy, Types, 0); + assert(expansion == F->getResilienceExpansion()); + auto ¶mTL = + Types.getTypeLowering(paramBoxedTy, TypeExpansionContext(*F)); ParameterConvention convention; if (paramTL.isAddressOnly()) { convention = ParameterConvention::Indirect_In; @@ -480,8 +483,9 @@ ClosureCloner::populateCloned() { auto BoxTy = (*I)->getType().castTo(); assert(BoxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented"); - auto BoxedTy = getSILBoxFieldType(BoxTy, Cloned->getModule().Types, 0) - .getObjectType(); + auto BoxedTy = getSILBoxFieldType(TypeExpansionContext(*Cloned), BoxTy, + Cloned->getModule().Types, 0) + .getObjectType(); SILValue MappedValue = ClonedEntryBB->createFunctionArgument(BoxedTy, (*I)->getDecl()); @@ -999,7 +1003,8 @@ bool isPartialApplyNonEscapingUser(Operand *CurrentOp, PartialApplyInst *PAI, auto BoxTy = BoxArg->getType().castTo(); assert(BoxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented yet"); - if (getSILBoxFieldType(BoxTy, M.Types, 0).isAddressOnly(*F)) { + if (getSILBoxFieldType(TypeExpansionContext(*Fn), BoxTy, M.Types, 0) + .isAddressOnly(*F)) { LLVM_DEBUG(llvm::dbgs() << " FAIL! Box is an address only " "argument!\n"); return false; @@ -1273,8 +1278,8 @@ processPartialApplyInst(SILOptFunctionBuilder &FuncBuilder, auto CalleeFunctionTy = PAI->getCallee()->getType().castTo(); auto SubstCalleeFunctionTy = CalleeFunctionTy; if (PAI->hasSubstitutions()) - SubstCalleeFunctionTy = - CalleeFunctionTy->substGenericArgs(M, PAI->getSubstitutionMap()); + SubstCalleeFunctionTy = CalleeFunctionTy->substGenericArgs( + M, PAI->getSubstitutionMap(), TypeExpansionContext(*F)); SILFunctionConventions calleeConv(SubstCalleeFunctionTy, M); auto CalleePInfo = SubstCalleeFunctionTy->getParameters(); SILFunctionConventions paConv(PAI->getType().castTo(), M); diff --git a/lib/SILOptimizer/IPO/CapturePropagation.cpp b/lib/SILOptimizer/IPO/CapturePropagation.cpp index 0b2ff3827e4d5..d482e8b01ecac 100644 --- a/lib/SILOptimizer/IPO/CapturePropagation.cpp +++ b/lib/SILOptimizer/IPO/CapturePropagation.cpp @@ -421,10 +421,11 @@ static SILFunction *getSpecializedWithDeadParams( return nullptr; // Perform a generic specialization of the Specialized function. - ReabstractionInfo ReInfo(ApplySite(), Specialized, - PAI->getSubstitutionMap(), - Specialized->isSerialized(), - /* ConvertIndirectToDirect */ false); + ReabstractionInfo ReInfo( + FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), ApplySite(), Specialized, + PAI->getSubstitutionMap(), Specialized->isSerialized(), + /* ConvertIndirectToDirect */ false); GenericFuncSpecializer FuncSpecializer(FuncBuilder, Specialized, ReInfo.getClonerParamSubstitutionMap(), diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index 6395f744aa76b..d9395b29b8f92 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -800,8 +800,9 @@ void ClosureSpecCloner::populateCloned() { } // Otherwise, create a new argument which copies the original argument + auto typeInContext = Cloned->getLoweredType(Arg->getType()); SILValue MappedValue = - ClonedEntryBB->createFunctionArgument(Arg->getType(), Arg->getDecl()); + ClonedEntryBB->createFunctionArgument(typeInContext, Arg->getDecl()); entryArgs.push_back(MappedValue); } @@ -821,6 +822,8 @@ void ClosureSpecCloner::populateCloned() { unsigned idx = 0; for (auto &PInfo : ClosedOverFunConv.getParameters().slice(NumNotCaptured)) { auto paramTy = ClosedOverFunConv.getSILType(PInfo); + // Get the type in context of the new function. + paramTy = Cloned->getLoweredType(paramTy); SILValue MappedValue = ClonedEntryBB->createFunctionArgument(paramTy); NewPAIArgs.push_back(MappedValue); auto CapturedVal = diff --git a/lib/SILOptimizer/IPO/EagerSpecializer.cpp b/lib/SILOptimizer/IPO/EagerSpecializer.cpp index ca0df01328595..a56bd3c8bdacd 100644 --- a/lib/SILOptimizer/IPO/EagerSpecializer.cpp +++ b/lib/SILOptimizer/IPO/EagerSpecializer.cpp @@ -231,7 +231,8 @@ emitInvocation(SILBuilder &Builder, if (ReInfo.getSpecializedType()->isPolymorphic()) { Subs = ReInfo.getCallerParamSubstitutionMap(); CalleeSubstFnTy = CanSILFuncTy->substGenericArgs( - Builder.getModule(), ReInfo.getCallerParamSubstitutionMap()); + Builder.getModule(), ReInfo.getCallerParamSubstitutionMap(), + Builder.getTypeExpansionContext()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && @@ -628,7 +629,8 @@ emitArgumentConversion(SmallVectorImpl &CallArgs) { auto CalleeSubstFnTy = CanSILFuncTy; if (CanSILFuncTy->isPolymorphic()) { CalleeSubstFnTy = CanSILFuncTy->substGenericArgs( - Builder.getModule(), ReInfo.getCallerParamSubstitutionMap()); + Builder.getModule(), ReInfo.getCallerParamSubstitutionMap(), + Builder.getTypeExpansionContext()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && @@ -763,7 +765,9 @@ void EagerSpecializerTransform::run() { // TODO: Use a decision-tree to reduce the amount of dynamic checks being // performed. for (auto *SA : F.getSpecializeAttrs()) { - ReInfoVec.emplace_back(&F, SA->getSpecializedSignature()); + ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), &F, + SA->getSpecializedSignature()); auto *NewFunc = eagerSpecialize(FuncBuilder, &F, *SA, ReInfoVec.back()); SpecializedFuncs.push_back(NewFunc); diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 4581f7faa0cf8..0152388b7d9f7 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -264,7 +264,8 @@ static SILFunction *getGlobalGetterFunction(SILOptFunctionBuilder &FunctionBuild Serialized = IsSerialized; } - auto refType = M.Types.getLoweredRValueType(varDecl->getInterfaceType()); + auto refType = M.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + varDecl->getInterfaceType()); // Function takes no arguments and returns refType SILResultInfo Results[] = { SILResultInfo(refType, diff --git a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp index 6fc43adc5fe52..5b1dab1e9ed3c 100644 --- a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp +++ b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp @@ -376,7 +376,7 @@ bool LetPropertiesOpt::isConstantLetProperty(VarDecl *Property) { // FIXME: Expansion auto &TL = Module->Types.getTypeLowering(Property->getType(), - ResilienceExpansion::Minimal); + TypeExpansionContext::minimal()); if (!TL.isTrivial()) { LLVM_DEBUG(llvm::dbgs() << "Property '" << *Property << "' is not of trivial type\n"); diff --git a/lib/SILOptimizer/IPO/UsePrespecialized.cpp b/lib/SILOptimizer/IPO/UsePrespecialized.cpp index 2b1e8f58aa42b..b2f7bfde4e40f 100644 --- a/lib/SILOptimizer/IPO/UsePrespecialized.cpp +++ b/lib/SILOptimizer/IPO/UsePrespecialized.cpp @@ -90,7 +90,8 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) { if (Subs.hasArchetypes()) continue; - ReabstractionInfo ReInfo(AI, ReferencedF, Subs, IsNotSerialized); + ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), AI, + ReferencedF, Subs, IsNotSerialized); if (!ReInfo.canBeSpecialized()) continue; diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index a8138c942ddc3..5188411efe102 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -63,7 +63,8 @@ static void gatherDestroysOfContainer(const MarkUninitializedInst *MUI, // DIMemoryObjectInfo Implementation //===----------------------------------------------------------------------===// -static unsigned getElementCountRec(SILModule &Module, SILType T, +static unsigned getElementCountRec(TypeExpansionContext context, + SILModule &Module, SILType T, bool IsSelfOfNonDelegatingInitializer) { // If this is a tuple, it is always recursively flattened. if (CanTupleType TT = T.getAs()) { @@ -71,7 +72,7 @@ static unsigned getElementCountRec(SILModule &Module, SILType T, unsigned NumElements = 0; for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) NumElements += - getElementCountRec(Module, T.getTupleElementType(i), false); + getElementCountRec(context, Module, T.getTupleElementType(i), false); return NumElements; } @@ -83,8 +84,8 @@ static unsigned getElementCountRec(SILModule &Module, SILType T, if (auto *NTD = T.getNominalOrBoundGenericNominal()) { unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) - NumElements += - getElementCountRec(Module, T.getFieldType(VD, Module), false); + NumElements += getElementCountRec( + context, Module, T.getFieldType(VD, Module, context), false); return NumElements; } } @@ -134,7 +135,8 @@ DIMemoryObjectInfo::DIMemoryObjectInfo(MarkUninitializedInst *MI) // Otherwise, we break down the initializer. NumElements = - getElementCountRec(Module, MemorySILType, isNonDelegatingInit()); + getElementCountRec(TypeExpansionContext(*MI->getFunction()), Module, + MemorySILType, isNonDelegatingInit()); // If this is a derived class init method, track an extra element to determine // whether super.init has been called at each program point. @@ -152,16 +154,18 @@ SILInstruction *DIMemoryObjectInfo::getFunctionEntryPoint() const { } /// Given a symbolic element number, return the type of the element. -static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo, +static SILType getElementTypeRec(TypeExpansionContext context, + SILModule &Module, SILType T, unsigned EltNo, bool IsSelfOfNonDelegatingInitializer) { // If this is a tuple type, walk into it. if (CanTupleType TT = T.getAs()) { assert(!IsSelfOfNonDelegatingInitializer && "self never has tuple type"); for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) { auto FieldType = T.getTupleElementType(i); - unsigned NumFieldElements = getElementCountRec(Module, FieldType, false); + unsigned NumFieldElements = + getElementCountRec(context, Module, FieldType, false); if (EltNo < NumFieldElements) - return getElementTypeRec(Module, FieldType, EltNo, false); + return getElementTypeRec(context, Module, FieldType, EltNo, false); EltNo -= NumFieldElements; } // This can only happen if we look at a symbolic element number of an empty @@ -177,11 +181,11 @@ static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo, bool HasStoredProperties = false; for (auto *VD : NTD->getStoredProperties()) { HasStoredProperties = true; - auto FieldType = T.getFieldType(VD, Module); + auto FieldType = T.getFieldType(VD, Module, context); unsigned NumFieldElements = - getElementCountRec(Module, FieldType, false); + getElementCountRec(context, Module, FieldType, false); if (EltNo < NumFieldElements) - return getElementTypeRec(Module, FieldType, EltNo, false); + return getElementTypeRec(context, Module, FieldType, EltNo, false); EltNo -= NumFieldElements; } @@ -202,7 +206,8 @@ static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo, /// getElementTypeRec - Return the swift type of the specified element. SILType DIMemoryObjectInfo::getElementType(unsigned EltNo) const { auto &Module = MemoryInst->getModule(); - return getElementTypeRec(Module, MemorySILType, EltNo, isNonDelegatingInit()); + return getElementTypeRec(TypeExpansionContext(*MemoryInst->getFunction()), + Module, MemorySILType, EltNo, isNonDelegatingInit()); } /// computeTupleElementAddress - Given a tuple element number (in the flattened @@ -225,7 +230,8 @@ SILValue DIMemoryObjectInfo::emitElementAddress( unsigned FieldNo = 0; for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) { auto EltTy = PointeeType.getTupleElementType(i); - unsigned NumSubElt = getElementCountRec(Module, EltTy, false); + unsigned NumSubElt = getElementCountRec( + TypeExpansionContext(B.getFunction()), Module, EltTy, false); if (EltNo < NumSubElt) { Ptr = B.createTupleElementAddr(Loc, Ptr, FieldNo); PointeeType = EltTy; @@ -255,10 +261,11 @@ SILValue DIMemoryObjectInfo::emitElementAddress( EndBorrowList.emplace_back(Borrowed, Original); } } - - auto FieldType = PointeeType.getFieldType(VD, Module); + auto expansionContext = TypeExpansionContext(B.getFunction()); + auto FieldType = + PointeeType.getFieldType(VD, Module, expansionContext); unsigned NumFieldElements = - getElementCountRec(Module, FieldType, false); + getElementCountRec(expansionContext, Module, FieldType, false); if (EltNo < NumFieldElements) { if (isa(NTD)) { Ptr = B.createStructElementAddr(Loc, Ptr, VD); @@ -300,7 +307,8 @@ SILValue DIMemoryObjectInfo::emitElementAddress( /// Push the symbolic path name to the specified element number onto the /// specified std::string. -static void getPathStringToElementRec(SILModule &Module, SILType T, +static void getPathStringToElementRec(TypeExpansionContext context, + SILModule &Module, SILType T, unsigned EltNo, std::string &Result) { CanTupleType TT = T.getAs(); if (!TT) { @@ -313,7 +321,7 @@ static void getPathStringToElementRec(SILModule &Module, SILType T, for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) { auto Field = TT->getElement(i); SILType FieldTy = T.getTupleElementType(i); - unsigned NumFieldElements = getElementCountRec(Module, FieldTy, false); + unsigned NumFieldElements = getElementCountRec(context, Module, FieldTy, false); if (EltNo < NumFieldElements) { Result += '.'; @@ -321,7 +329,7 @@ static void getPathStringToElementRec(SILModule &Module, SILType T, Result += Field.getName().str(); else Result += llvm::utostr(FieldNo); - return getPathStringToElementRec(Module, FieldTy, EltNo, Result); + return getPathStringToElementRec(context, Module, FieldTy, EltNo, Result); } EltNo -= NumFieldElements; @@ -346,14 +354,16 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, Result = ""; // If this is indexing into a field of 'self', look it up. + auto expansionContext = TypeExpansionContext(*MemoryInst->getFunction()); if (isNonDelegatingInit() && !isDerivedClassSelfOnly()) { if (auto *NTD = MemorySILType.getNominalOrBoundGenericNominal()) { bool HasStoredProperty = false; for (auto *VD : NTD->getStoredProperties()) { HasStoredProperty = true; - auto FieldType = MemorySILType.getFieldType(VD, Module); + auto FieldType = + MemorySILType.getFieldType(VD, Module, expansionContext); unsigned NumFieldElements = - getElementCountRec(Module, FieldType, false); + getElementCountRec(expansionContext, Module, FieldType, false); if (Element < NumFieldElements) { Result += '.'; auto originalProperty = VD->getOriginalWrappedProperty(); @@ -362,7 +372,8 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, } else { Result += VD->getName().str(); } - getPathStringToElementRec(Module, FieldType, Element, Result); + getPathStringToElementRec(expansionContext, Module, FieldType, + Element, Result); return VD; } Element -= NumFieldElements; @@ -375,7 +386,8 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, } // Get the path through a tuple, if relevant. - getPathStringToElementRec(Module, MemorySILType, Element, Result); + getPathStringToElementRec(expansionContext, Module, MemorySILType, Element, + Result); // If we are analyzing a variable, we can generally get the decl associated // with it. @@ -402,9 +414,11 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const { return false; } + auto expansionContext = TypeExpansionContext(*MemoryInst->getFunction()); for (auto *VD : NTD->getStoredProperties()) { - auto FieldType = MemorySILType.getFieldType(VD, Module); - unsigned NumFieldElements = getElementCountRec(Module, FieldType, false); + auto FieldType = MemorySILType.getFieldType(VD, Module, expansionContext); + unsigned NumFieldElements = + getElementCountRec(expansionContext, Module, FieldType, false); if (Element < NumFieldElements) return VD->isLet(); Element -= NumFieldElements; @@ -591,7 +605,8 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, unsigned NumElements = 1; if (TheMemory.NumElements != 1 && !InStructSubElement && !InEnumSubElement) NumElements = - getElementCountRec(Module, UseTy, IsSelfOfNonDelegatingInitializer); + getElementCountRec(TypeExpansionContext(*User->getFunction()), Module, + UseTy, IsSelfOfNonDelegatingInitializer); trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements)); } @@ -617,7 +632,8 @@ void ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, if (T.is()) { for (unsigned i = 0; i != FieldNo; ++i) { SILType EltTy = T.getTupleElementType(i); - BaseEltNo += getElementCountRec(Module, EltTy, false); + BaseEltNo += getElementCountRec(TypeExpansionContext(*TEAI->getFunction()), + Module, EltTy, false); } } @@ -644,7 +660,8 @@ void ElementUseCollector::collectDestructureTupleResultUses( if (T.is()) { for (unsigned i = 0; i != FieldNo; ++i) { SILType EltTy = T.getTupleElementType(i); - BaseEltNo += getElementCountRec(Module, EltTy, false); + BaseEltNo += getElementCountRec(TypeExpansionContext(*DTR->getFunction()), + Module, EltTy, false); } } @@ -670,8 +687,9 @@ void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, if (SEAI->getField() == VD) break; - auto FieldType = SEAI->getOperand()->getType().getFieldType(VD, Module); - BaseEltNo += getElementCountRec(Module, FieldType, false); + auto expansionContext = TypeExpansionContext(*SEAI->getFunction()); + auto FieldType = SEAI->getOperand()->getType().getFieldType(VD, Module, expansionContext); + BaseEltNo += getElementCountRec(expansionContext, Module, FieldType, false); } collectUses(SEAI, BaseEltNo); @@ -1119,8 +1137,11 @@ void ElementUseCollector::collectClassSelfUses() { unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) { EltNumbering[VD] = NumElements; - NumElements += - getElementCountRec(Module, T.getFieldType(VD, Module), false); + auto expansionContext = + TypeExpansionContext(*TheMemory.MemoryInst->getFunction()); + NumElements += getElementCountRec( + expansionContext, Module, + T.getFieldType(VD, Module, expansionContext), false); } } diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index f4f6247e4aad5..0dec95dd11056 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -499,12 +499,14 @@ static void addSwapAtFixit(InFlightDiagnostic &Diag, CallExpr *&FoundCall, /// and tuple elements. static std::string getPathDescription(DeclName BaseName, SILType BaseType, const IndexTrieNode *SubPath, - SILModule &M) { + SILModule &M, + TypeExpansionContext context) { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "'" << BaseName; - os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M); + os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M, + context); os << "'"; return os.str(); @@ -547,7 +549,8 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation, SILType BaseType = FirstAccess.getInstruction()->getType().getAddressType(); SILModule &M = FirstAccess.getInstruction()->getModule(); std::string PathDescription = getPathDescription( - VD->getBaseName(), BaseType, MainAccess.getSubPath(), M); + VD->getBaseName(), BaseType, MainAccess.getSubPath(), M, + TypeExpansionContext(*FirstAccess.getInstruction()->getFunction())); // Determine whether we can safely suggest replacing the violation with // a call to MutableCollection.swapAt(). diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 091ab93943547..4d2c1233b3a08 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -753,9 +753,11 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, StructDecl *structDecl = osLogMessageType.getStructOrBoundGenericStruct(); assert(structDecl); + auto typeExpansionContext = + TypeExpansionContext(*osLogMessage->getFunction()); VarDecl *interpolationPropDecl = structDecl->getStoredProperties().front(); - SILType osLogInterpolationType = - osLogMessageType.getFieldType(interpolationPropDecl, module); + SILType osLogInterpolationType = osLogMessageType.getFieldType( + interpolationPropDecl, module, typeExpansionContext); StructDecl *interpolationStruct = osLogInterpolationType.getStructOrBoundGenericStruct(); assert(interpolationStruct); diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 402d40edf5c77..40c47fd45f47d 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -258,7 +258,8 @@ static void splitDestructure(SILBuilder &B, SILInstruction *I, SILValue Op) { SILType OpType = Op->getType(); llvm::SmallVector Projections; - Projection::getFirstLevelProjections(OpType, M, Projections); + Projection::getFirstLevelProjections(OpType, M, B.getTypeExpansionContext(), + Projections); assert(Projections.size() == I->getNumResults()); auto Results = I->getResults(); diff --git a/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp index a333d41114343..5aa0d49fa6034 100644 --- a/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp @@ -34,7 +34,9 @@ PMOMemoryObjectInfo::PMOMemoryObjectInfo(AllocationInst *allocation) if (auto *abi = dyn_cast(MemoryInst)) { assert(abi->getBoxType()->getLayout()->getFields().size() == 1 && "analyzing multi-field boxes not implemented"); - MemorySILType = getSILBoxFieldType(abi->getBoxType(), module.Types, 0); + MemorySILType = + getSILBoxFieldType(TypeExpansionContext(*abi->getFunction()), + abi->getBoxType(), module.Types, 0); } else { MemorySILType = cast(MemoryInst)->getElementType(); } diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 2a1934ed35193..4586e03bb8da1 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -47,19 +47,22 @@ getFullyReferenceableStruct(SILType Ty) { return SD; } -static unsigned getNumSubElements(SILType T, SILModule &M) { +static unsigned getNumSubElements(SILType T, SILModule &M, + TypeExpansionContext context) { if (auto TT = T.getAs()) { unsigned NumElements = 0; for (auto index : indices(TT.getElementTypes())) - NumElements += getNumSubElements(T.getTupleElementType(index), M); + NumElements += + getNumSubElements(T.getTupleElementType(index), M, context); return NumElements; } if (auto *SD = getFullyReferenceableStruct(T)) { unsigned NumElements = 0; for (auto *D : SD->getStoredProperties()) - NumElements += getNumSubElements(T.getFieldType(D, M), M); + NumElements += + getNumSubElements(T.getFieldType(D, M, context), M, context); return NumElements; } @@ -127,7 +130,9 @@ static unsigned computeSubelement(SILValue Pointer, // Keep track of what subelement is being referenced. for (unsigned i = 0, e = TEAI->getFieldNo(); i != e; ++i) { - SubElementNumber += getNumSubElements(TT.getTupleElementType(i), M); + SubElementNumber += + getNumSubElements(TT.getTupleElementType(i), M, + TypeExpansionContext(*RootInst->getFunction())); } Pointer = TEAI->getOperand(); continue; @@ -140,7 +145,9 @@ static unsigned computeSubelement(SILValue Pointer, StructDecl *SD = SEAI->getStructDecl(); for (auto *D : SD->getStoredProperties()) { if (D == SEAI->getField()) break; - SubElementNumber += getNumSubElements(ST.getFieldType(D, M), M); + auto context = TypeExpansionContext(*RootInst->getFunction()); + SubElementNumber += + getNumSubElements(ST.getFieldType(D, M, context), M, context); } Pointer = SEAI->getOperand(); @@ -316,7 +323,8 @@ static SILValue nonDestructivelyExtractSubElement(const AvailableValue &Val, for (unsigned EltNo : indices(TT.getElementTypes())) { // Keep track of what subelement is being referenced. SILType EltTy = ValTy.getTupleElementType(EltNo); - unsigned NumSubElt = getNumSubElements(EltTy, B.getModule()); + unsigned NumSubElt = getNumSubElements( + EltTy, B.getModule(), TypeExpansionContext(B.getFunction())); if (SubElementNumber < NumSubElt) { auto BorrowedVal = Val.emitBeginBorrow(B, Loc); auto NewVal = @@ -338,9 +346,11 @@ static SILValue nonDestructivelyExtractSubElement(const AvailableValue &Val, // Extract struct elements. if (auto *SD = getFullyReferenceableStruct(ValTy)) { for (auto *D : SD->getStoredProperties()) { - auto fieldType = ValTy.getFieldType(D, B.getModule()); - unsigned NumSubElt = getNumSubElements(fieldType, B.getModule()); - + auto fieldType = ValTy.getFieldType( + D, B.getModule(), TypeExpansionContext(B.getFunction())); + unsigned NumSubElt = getNumSubElements( + fieldType, B.getModule(), TypeExpansionContext(B.getFunction())); + if (SubElementNumber < NumSubElt) { auto BorrowedVal = Val.emitBeginBorrow(B, Loc); auto NewVal = @@ -490,7 +500,8 @@ bool AvailableValueAggregator::isFullyAvailable(SILType loadTy, if (!firstVal || firstVal.getType() != loadTy) return false; - return llvm::all_of(range(getNumSubElements(loadTy, M)), + return llvm::all_of(range(getNumSubElements( + loadTy, M, TypeExpansionContext(B.getFunction()))), [&](unsigned index) -> bool { auto &val = AvailableValueList[firstElt + index]; return val.getValue() == firstVal.getValue() && @@ -516,7 +527,8 @@ bool AvailableValueAggregator::canTake(SILType loadTy, if (TupleType *tt = loadTy.getAs()) { return llvm::all_of(indices(tt->getElements()), [&](unsigned eltNo) { SILType eltTy = loadTy.getTupleElementType(eltNo); - unsigned numSubElt = getNumSubElements(eltTy, M); + unsigned numSubElt = + getNumSubElements(eltTy, M, TypeExpansionContext(B.getFunction())); bool success = canTake(eltTy, firstElt); firstElt += numSubElt; return success; @@ -525,8 +537,9 @@ bool AvailableValueAggregator::canTake(SILType loadTy, if (auto *sd = getFullyReferenceableStruct(loadTy)) { return llvm::all_of(sd->getStoredProperties(), [&](VarDecl *decl) -> bool { - SILType eltTy = loadTy.getFieldType(decl, M); - unsigned numSubElt = getNumSubElements(eltTy, M); + auto context = TypeExpansionContext(B.getFunction()); + SILType eltTy = loadTy.getFieldType(decl, M, context); + unsigned numSubElt = getNumSubElements(eltTy, M, context); bool success = canTake(eltTy, firstElt); firstElt += numSubElt; return success; @@ -650,7 +663,8 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, for (unsigned EltNo : indices(TT->getElements())) { SILType EltTy = LoadTy.getTupleElementType(EltNo); - unsigned NumSubElt = getNumSubElements(EltTy, M); + unsigned NumSubElt = + getNumSubElements(EltTy, M, TypeExpansionContext(B.getFunction())); // If we are missing any of the available values in this struct element, // compute an address to load from. @@ -676,8 +690,9 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, SmallVector resultElts; for (auto *decl : sd->getStoredProperties()) { - SILType eltTy = loadTy.getFieldType(decl, M); - unsigned numSubElt = getNumSubElements(eltTy, M); + auto context = TypeExpansionContext(B.getFunction()); + SILType eltTy = loadTy.getFieldType(decl, M, context); + unsigned numSubElt = getNumSubElements(eltTy, M, context); // If we are missing any of the available values in this struct element, // compute an address to load from. @@ -1094,8 +1109,9 @@ static inline void updateAvailableValuesHelper( // TODO: Is this needed now? assert(startSubElt != ~0U && "Store within enum projection not handled"); - for (unsigned i : - range(getNumSubElements(address->getType().getObjectType(), mod))) { + for (unsigned i : range(getNumSubElements( + address->getType().getObjectType(), mod, + TypeExpansionContext(*theMemory->getFunction())))) { // If this element is not required, don't fill it in. if (!requiredElts[startSubElt + i]) continue; @@ -1221,12 +1237,13 @@ void AvailableValueDataflowContext::updateAvailableValues( SILType ValTy = CAI->getDest()->getType(); bool AnyRequired = false; - for (unsigned i : range(getNumSubElements(ValTy, getModule()))) { + for (unsigned i : range(getNumSubElements( + ValTy, getModule(), TypeExpansionContext(*CAI->getFunction())))) { // If this element is not required, don't fill it in. AnyRequired = RequiredElts[StartSubElt+i]; if (AnyRequired) break; } - + // If this is a copy addr that doesn't intersect the loaded subelements, // just continue with an unmodified load mask. if (!AnyRequired) @@ -1513,7 +1530,8 @@ static SILType getMemoryType(AllocationInst *memory) { if (auto *abi = dyn_cast(memory)) { assert(abi->getBoxType()->getLayout()->getFields().size() == 1 && "optimizing multi-field boxes not implemented"); - return getSILBoxFieldType(abi->getBoxType(), abi->getModule().Types, 0); + return getSILBoxFieldType(TypeExpansionContext(*abi->getFunction()), + abi->getBoxType(), abi->getModule().Types, 0); } assert(isa(memory)); @@ -1552,8 +1570,9 @@ class AllocOptimize { DeadEndBlocks &deadEndBlocks) : Module(memory->getModule()), TheMemory(memory), MemoryType(getMemoryType(memory)), - NumMemorySubElements(getNumSubElements(MemoryType, Module)), Uses(uses), - Releases(releases), deadEndBlocks(deadEndBlocks), + NumMemorySubElements(getNumSubElements( + MemoryType, Module, TypeExpansionContext(*memory->getFunction()))), + Uses(uses), Releases(releases), deadEndBlocks(deadEndBlocks), DataflowContext(TheMemory, NumMemorySubElements, uses) {} bool optimizeMemoryAccesses(); @@ -1598,7 +1617,8 @@ Optional> AllocOptimize::computeAvailableValues( if (FirstElt == ~0U) return None; - unsigned NumLoadSubElements = getNumSubElements(LoadTy, Module); + unsigned NumLoadSubElements = getNumSubElements( + LoadTy, Module, TypeExpansionContext(*TheMemory->getFunction())); // Set up the bitvector of elements being demanded by the load. SmallBitVector RequiredElts(NumMemorySubElements); @@ -1825,7 +1845,9 @@ bool AllocOptimize::canPromoteTake( // def/use behavior. unsigned firstElt = computeSubelement(address, TheMemory); assert(firstElt != ~0U && "destroy within enum projection is not valid"); - unsigned numLoadSubElements = getNumSubElements(loadTy, Module); + auto expansionContext = TypeExpansionContext(*inst->getFunction()); + unsigned numLoadSubElements = + getNumSubElements(loadTy, Module, expansionContext); // Find out if we have any available values. If no bits are demanded, we // trivially succeed. This can happen when there is a load of an empty struct. diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index cf118006bd076..d666cee5a14da 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -292,10 +292,6 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { // Mainly for Array.append(contentsOf) optimization. P.addArrayElementPropagation(); - // Specialize opaque archetypes. - // This can expose oportunities for the generic specializer. - P.addOpaqueArchetypeSpecializer(); - // Run the devirtualizer, specializer, and inliner. If any of these // makes a change we'll end up restarting the function passes on the // current function (after optimizing any new callees). diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 42752a6acda1e..71bf97da9f221 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -436,7 +436,8 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) { && "rewriting multi-field box not implemented"); auto *ASI = Builder.createAllocStack( ABI->getLoc(), - getSILBoxFieldType(ABI->getBoxType(), ABI->getModule().Types, 0), + getSILBoxFieldType(TypeExpansionContext(*ABI->getFunction()), + ABI->getBoxType(), ABI->getModule().Types, 0), ABI->getVarInfo(), ABI->hasDynamicLifetime()); // Transfer a mark_uninitialized if we have one. @@ -452,9 +453,9 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) { assert(ABI->getBoxType()->getLayout()->getFields().size() == 1 && "promoting multi-field box not implemented"); - auto &Lowering = ABI->getFunction() - ->getTypeLowering( - getSILBoxFieldType(ABI->getBoxType(), ABI->getModule().Types, 0)); + auto &Lowering = ABI->getFunction()->getTypeLowering( + getSILBoxFieldType(TypeExpansionContext(*ABI->getFunction()), + ABI->getBoxType(), ABI->getModule().Types, 0)); auto Loc = CleanupLocation::get(ABI->getLoc()); for (auto LastRelease : FinalReleases) { @@ -585,7 +586,7 @@ SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, auto &TC = Orig->getModule().Types; Lowering::GenericContextScope scope(TC, OrigFTI->getSubstGenericSignature()); - paramTy = getSILBoxFieldType(boxTy, TC, 0); + paramTy = getSILBoxFieldType(TypeExpansionContext(*Orig), boxTy, TC, 0); } auto promotedParam = SILParameterInfo(paramTy.getASTType(), ParameterConvention::Indirect_InoutAliasable); @@ -650,7 +651,8 @@ PromotedParamCloner::populateCloned() { auto boxTy = (*I)->getType().castTo(); assert(boxTy->getLayout()->getFields().size() == 1 && "promoting multi-field boxes not implemented yet"); - auto promotedTy = getSILBoxFieldType(boxTy, Cloned->getModule().Types, 0); + auto promotedTy = getSILBoxFieldType(TypeExpansionContext(*Cloned), boxTy, + Cloned->getModule().Types, 0); auto *promotedArg = ClonedEntryBB->createFunctionArgument(promotedTy, (*I)->getDecl()); OrigPromotedParameters.insert(*I); diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index fbba6b0fcac59..b0146133a9a32 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -31,7 +31,6 @@ silopt_register_sources( SILSROA.cpp SimplifyCFG.cpp Sink.cpp - SpecializeOpaqueArchetypes.cpp SpeculativeDevirtualizer.cpp StackPromotion.cpp UnsafeGuaranteedPeephole.cpp diff --git a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp index 182d6d642e094..266859152516a 100644 --- a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp @@ -855,7 +855,8 @@ void DSEContext::processRead(SILInstruction *I, SILValue Mem, DSEKind Kind) { // Expand the given Mem into individual fields and process them as separate // reads. LSLocationList Locs; - LSLocation::expand(L, &I->getModule(), Locs, TE); + LSLocation::expand(L, &I->getModule(), + TypeExpansionContext(*I->getFunction()), Locs, TE); // Are we building the genset and killset. if (isBuildingGenKillSet(Kind)) { @@ -940,7 +941,7 @@ void DSEContext::processWrite(SILInstruction *I, SILValue Val, SILValue Mem, // writes. bool Dead = true; LSLocationList Locs; - LSLocation::expand(L, Mod, Locs, TE); + LSLocation::expand(L, Mod, TypeExpansionContext(*I->getFunction()), Locs, TE); SmallBitVector V(Locs.size()); // Are we computing max store set. @@ -997,7 +998,7 @@ void DSEContext::processWrite(SILInstruction *I, SILValue Val, SILValue Mem, } // Try to create as few aggregated stores as possible out of the locations. - LSLocation::reduce(L, Mod, Alives); + LSLocation::reduce(L, Mod, TypeExpansionContext(*I->getFunction()), Alives); // Oops, we have too many smaller stores generated, bail out. if (Alives.size() > MaxPartialStoreCount) diff --git a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp index 08f3bc0c54483..dd35ec91fbe74 100644 --- a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp +++ b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp @@ -488,7 +488,8 @@ bool ObjectOutliner::optimizeObjectAllocation(AllocRefInst *ARI) { void ObjectOutliner::replaceFindStringCall(ApplyInst *FindStringCall) { // Find the replacement function in the swift stdlib. SmallVector results; - SILModule *Module = &FindStringCall->getFunction()->getModule(); + auto &F = *FindStringCall->getFunction(); + SILModule *Module = &F.getModule(); Module->getASTContext().lookupInSwiftModule("_findStringSwitchCaseWithCache", results); if (results.size() != 1) @@ -517,8 +518,9 @@ void ObjectOutliner::replaceFindStringCall(ApplyInst *FindStringCall) { assert(!cacheDecl->isResilient(Module->getSwiftModule(), ResilienceExpansion::Minimal)); - SILType wordTy = cacheType.getFieldType( - cacheDecl->getStoredProperties().front(), *Module); + SILType wordTy = + cacheType.getFieldType(cacheDecl->getStoredProperties().front(), *Module, + F.getTypeExpansionContext()); GlobalVariableMangler Mangler; std::string GlobName = diff --git a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp index 0f610308babfa..d34370660a95b 100644 --- a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp +++ b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp @@ -677,7 +677,8 @@ SILValue BlockState::reduceValuesAtEndOfBlock(RLEContext &Ctx, LSLocation &L) { LSLocationValueMap Values; LSLocationList Locs; - LSLocation::expand(L, &BB->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &BB->getModule(), + TypeExpansionContext(*BB->getParent()), Locs, Ctx.getTE()); // Find the values that this basic block defines and the locations which // we do not have a concrete value in the current basic block. @@ -689,8 +690,8 @@ SILValue BlockState::reduceValuesAtEndOfBlock(RLEContext &Ctx, LSLocation &L) { // Second, reduce the available values into a single SILValue we can use to // forward. SILValue TheForwardingValue; - TheForwardingValue = LSValue::reduce(L, &BB->getModule(), Values, - BB->getTerminator()); + TheForwardingValue = + LSValue::reduce(L, &BB->getModule(), Values, BB->getTerminator()); /// Return the forwarding value. return TheForwardingValue; } @@ -856,7 +857,9 @@ void BlockState::processWrite(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Expand the given location and val into individual fields and process // them as separate writes. LSLocationList Locs; - LSLocation::expand(L, &I->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &I->getModule(), + TypeExpansionContext(*I->getFunction()), Locs, + Ctx.getTE()); if (isComputeAvailSetMax(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { @@ -875,7 +878,8 @@ void BlockState::processWrite(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Are we computing available value or performing RLE? LSValueList Vals; - LSValue::expand(Val, &I->getModule(), Vals, Ctx.getTE()); + LSValue::expand(Val, &I->getModule(), TypeExpansionContext(*I->getFunction()), + Vals, Ctx.getTE()); if (isComputeAvailValue(Kind) || isPerformingRLE(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { updateForwardSetAndValForWrite(Ctx, Ctx.getLocationBit(Locs[i]), @@ -907,7 +911,9 @@ void BlockState::processRead(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Expand the given LSLocation and Val into individual fields and process // them as separate reads. LSLocationList Locs; - LSLocation::expand(L, &I->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &I->getModule(), + TypeExpansionContext(*I->getFunction()), Locs, + Ctx.getTE()); if (isComputeAvailSetMax(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { @@ -927,7 +933,8 @@ void BlockState::processRead(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Are we computing available values ?. bool CanForward = true; LSValueList Vals; - LSValue::expand(Val, &I->getModule(), Vals, Ctx.getTE()); + LSValue::expand(Val, &I->getModule(), TypeExpansionContext(*I->getFunction()), + Vals, Ctx.getTE()); if (isComputeAvailValue(Kind) || isPerformingRLE(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { if (isTrackingLocation(ForwardSetIn, Ctx.getLocationBit(Locs[i]))) @@ -1245,7 +1252,8 @@ BlockState::ValueState BlockState::getValueStateAtEndOfBlock(RLEContext &Ctx, // expanded from the given location. unsigned CSCount = 0, CTCount = 0; LSLocationList Locs; - LSLocation::expand(L, &BB->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &BB->getModule(), + TypeExpansionContext(*BB->getParent()), Locs, Ctx.getTE()); ValueTableMap &OTM = getForwardValOut(); for (auto &X : Locs) { @@ -1319,12 +1327,15 @@ SILValue RLEContext::computePredecessorLocationValue(SILBasicBlock *BB, // Reduce the available values into a single SILValue we can use to forward SILInstruction *IPt = CurBB->getTerminator(); - Values.push_back({CurBB, LSValue::reduce(L, &BB->getModule(), LSValues, IPt)}); + Values.push_back( + {CurBB, LSValue::reduce(L, &BB->getModule(), LSValues, IPt)}); } // Finally, collect all the values for the SILArgument, materialize it using // the SSAUpdater. - Updater.Initialize(L.getType(&BB->getModule()).getObjectType()); + Updater.Initialize( + L.getType(&BB->getModule(), TypeExpansionContext(*BB->getParent())) + .getObjectType()); for (auto V : Values) { Updater.AddAvailableValue(V.first, V.second); } @@ -1337,7 +1348,8 @@ bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, ValueTableMap &VM) { LSLocationList CSLocs; LSLocationList Locs; - LSLocation::expand(L, &BB->getModule(), Locs, TE); + LSLocation::expand(L, &BB->getModule(), + TypeExpansionContext(*BB->getParent()), Locs, TE); auto *Mod = &BB->getModule(); // Find the locations that this basic block defines and the locations which @@ -1352,7 +1364,7 @@ bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, // For locations which we do not have concrete values for in this basic // block, try to reduce it to the minimum # of locations possible, this // will help us to generate as few SILArguments as possible. - LSLocation::reduce(L, Mod, CSLocs); + LSLocation::reduce(L, Mod, TypeExpansionContext(*BB->getParent()), CSLocs); // To handle covering value, we need to go to the predecessors and // materialize them there. @@ -1365,8 +1377,9 @@ bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, // collect the newly created forwardable values. LSLocationList Locs; LSValueList Vals; - LSLocation::expand(X, Mod, Locs, TE); - LSValue::expand(V, Mod, Vals, TE); + auto expansionContext = TypeExpansionContext(*BB->getParent()); + LSLocation::expand(X, Mod, expansionContext, Locs, TE); + LSValue::expand(V, Mod, expansionContext, Vals, TE); for (unsigned i = 0; i < Locs.size(); ++i) { Values[Locs[i]] = Vals[i]; @@ -1570,7 +1583,8 @@ bool RLEContext::run() { LLVM_DEBUG(for (unsigned i = 0; i < LocationVault.size(); ++i) { llvm::dbgs() << "LSLocation #" << i; - getLocation(i).print(llvm::dbgs(), &Fn->getModule()); + getLocation(i).print(llvm::dbgs(), &Fn->getModule(), + TypeExpansionContext(*Fn)); }); if (Optimistic) diff --git a/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp b/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp index e52ce639db7d0..3642e337053fc 100644 --- a/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp @@ -142,13 +142,14 @@ bool ReleaseDevirtualizer::createDeallocCall(SILType AllocType, SILFunction *Dealloc = M.lookUpFunction(DeallocRef); if (!Dealloc) return false; - - CanSILFunctionType DeallocType = Dealloc->getLoweredFunctionType(); + TypeExpansionContext context(*ReleaseInst->getFunction()); + CanSILFunctionType DeallocType = + Dealloc->getLoweredFunctionTypeInContext(context); auto *NTD = AllocType.getASTType()->getAnyNominal(); auto AllocSubMap = AllocType.getASTType() ->getContextSubstitutionMap(M.getSwiftModule(), NTD); - DeallocType = DeallocType->substGenericArgs(M, AllocSubMap); + DeallocType = DeallocType->substGenericArgs(M, AllocSubMap, context); SILBuilder B(ReleaseInst); if (object->getType() != AllocType) diff --git a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp index 61d1efa68b7c6..d7bae9dd2a835 100644 --- a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp @@ -60,7 +60,8 @@ static void createRefCountOpForPayload(SILBuilder &Builder, SILInstruction *I, // argument to the refcount instruction. SILValue EnumVal = DefOfEnum ? DefOfEnum : I->getOperand(0); - SILType ArgType = EnumVal->getType().getEnumElementType(EnumDecl, Mod); + SILType ArgType = EnumVal->getType().getEnumElementType( + EnumDecl, Mod, TypeExpansionContext(Builder.getFunction())); auto *UEDI = Builder.createUncheckedEnumData(I->getLoc(), EnumVal, EnumDecl, ArgType); diff --git a/lib/SILOptimizer/Transforms/SILSROA.cpp b/lib/SILOptimizer/Transforms/SILSROA.cpp index eafc9d41b120f..dcfb8d6803e78 100644 --- a/lib/SILOptimizer/Transforms/SILSROA.cpp +++ b/lib/SILOptimizer/Transforms/SILSROA.cpp @@ -228,8 +228,9 @@ createAllocas(llvm::SmallVector &NewAllocations) { "this point."); SILModule &M = AI->getModule(); for (auto *D : SD->getStoredProperties()) - NewAllocations.push_back( - B.createAllocStack(Loc, Type.getFieldType(D, M), {})); + NewAllocations.push_back(B.createAllocStack( + Loc, Type.getFieldType(D, M, TypeExpansionContext(B.getFunction())), + {})); } } diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 4a4530142072a..617456bf340a9 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -313,7 +313,8 @@ class ThreadInfo { auto EnumVal = SEI->getOperand(); auto EnumTy = EnumVal->getType(); auto Loc = SEI->getLoc(); - auto Ty = EnumTy.getEnumElementType(EnumCase, SEI->getModule()); + auto Ty = EnumTy.getEnumElementType(EnumCase, SEI->getModule(), + Builder.getTypeExpansionContext()); SILValue UED( Builder.createUncheckedEnumData(Loc, EnumVal, EnumCase, Ty)); assert(UED->getType() == @@ -358,7 +359,8 @@ static SILValue createEnumElement(SILBuilder &Builder, // Do we have a payload. auto EnumTy = EnumVal->getType(); if (EnumElement->hasAssociatedValues()) { - auto Ty = EnumTy.getEnumElementType(EnumElement, SEI->getModule()); + auto Ty = EnumTy.getEnumElementType(EnumElement, SEI->getModule(), + Builder.getTypeExpansionContext()); SILValue UED(Builder.createUncheckedEnumData(SEI->getLoc(), EnumVal, EnumElement, Ty)); return Builder.createEnum(SEI->getLoc(), UED, EnumElement, EnumTy); @@ -1702,7 +1704,8 @@ bool SimplifyCFG::simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI) { auto &Mod = SEI->getModule(); auto OpndTy = SEI->getOperand()->getType(); - auto Ty = OpndTy.getEnumElementType(Element, Mod); + auto Ty = OpndTy.getEnumElementType( + Element, Mod, TypeExpansionContext(*SEI->getFunction())); auto *UED = SILBuilderWithScope(SEI) .createUncheckedEnumData(SEI->getLoc(), SEI->getOperand(), Element, Ty); @@ -2388,15 +2391,17 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) { auto TargetFnTy = CalleeFnTy; if (TargetFnTy->isPolymorphic()) { - TargetFnTy = TargetFnTy->substGenericArgs(TAI->getModule(), - TAI->getSubstitutionMap()); + TargetFnTy = TargetFnTy->substGenericArgs( + TAI->getModule(), TAI->getSubstitutionMap(), + Builder.getTypeExpansionContext()); } SILFunctionConventions targetConv(TargetFnTy, TAI->getModule()); auto OrigFnTy = TAI->getCallee()->getType().getAs(); if (OrigFnTy->isPolymorphic()) { OrigFnTy = OrigFnTy->substGenericArgs(TAI->getModule(), - TAI->getSubstitutionMap()); + TAI->getSubstitutionMap(), + Builder.getTypeExpansionContext()); } SILFunctionConventions origConv(OrigFnTy, TAI->getModule()); @@ -2857,7 +2862,8 @@ bool ArgumentSplitter::createNewArguments() { return false; // Get the first level projection for the struct or tuple type. - Projection::getFirstLevelProjections(Arg->getType(), Mod, Projections); + Projection::getFirstLevelProjections(Arg->getType(), Mod, + TypeExpansionContext(*F), Projections); // We do not want to split arguments with less than 2 projections. if (Projections.size() < 2) @@ -2866,7 +2872,7 @@ bool ArgumentSplitter::createNewArguments() { // We do not want to split arguments that have less than 2 non-trivial // projections. if (count_if(Projections, [&](const Projection &P) { - return !P.getType(Ty, Mod).isTrivial(*F); + return !P.getType(Ty, Mod, TypeExpansionContext(*F)).isTrivial(*F); }) < 2) return false; @@ -2878,8 +2884,9 @@ bool ArgumentSplitter::createNewArguments() { // old one. llvm::SmallVector NewArgumentValues; for (auto &P : Projections) { - auto *NewArg = ParentBB->createPhiArgument(P.getType(Ty, Mod), - ValueOwnershipKind::Owned); + auto *NewArg = ParentBB->createPhiArgument( + P.getType(Ty, Mod, TypeExpansionContext(*F)), + ValueOwnershipKind::Owned); // This is unfortunate, but it feels wrong to put in an API into SILBuilder // that only takes in arguments. // diff --git a/lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp b/lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp deleted file mode 100644 index 11f66eb2666bf..0000000000000 --- a/lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp +++ /dev/null @@ -1,558 +0,0 @@ -//===--- SpecializeOpaqueArchetypes.cpp - Specialize opaque archetypes ---===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// A pass to specialize opaque archetypes -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "opaque-archetype-specializer" - -#include "swift/AST/Types.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILInstruction.h" -#include "swift/SIL/TypeSubstCloner.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" - -#include "llvm/Support/CommandLine.h" - -llvm::cl::opt - EnableOpaqueArchetypeSpecializer("enable-opaque-archetype-specializer", - llvm::cl::init(true)); - -using namespace swift; - -static const DeclContext * -getDeclContextIfInCurrentModule(const SILFunction &fn) { - auto *dc = fn.getDeclContext(); - auto *currentModule = fn.getModule().getSwiftModule(); - if (dc && dc->isChildContextOf(currentModule)) - return dc; - return currentModule; -} - -static Type substOpaqueTypesWithUnderlyingTypes( - Type ty, SILFunction *context) { - auto *dc = getDeclContextIfInCurrentModule(*context); - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - dc, context->getResilienceExpansion()); - return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); -} - -static SubstitutionMap -substOpaqueTypesWithUnderlyingTypes(SubstitutionMap map, SILFunction *context) { - auto *dc = getDeclContextIfInCurrentModule(*context); - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - dc, context->getResilienceExpansion()); - return map.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); -} - -namespace { -class OpaqueSpecializerCloner - : public SILCloner { - - using SuperTy = SILCloner; - - SILBasicBlock *entryBlock; - SILBasicBlock *cloneFromBlock; - - /// Cache for substituted types. - llvm::DenseMap TypeCache; - - SILFunction &Original; - -public: - friend class SILCloner; - friend class SILCloner; - friend class SILInstructionVisitor; - - OpaqueSpecializerCloner(SILFunction &fun) : SuperTy(fun), Original(fun) { - entryBlock = fun.getEntryBlock(); - cloneFromBlock = entryBlock->split(entryBlock->begin()); - } - - void clone(); - -protected: - void insertOpaqueToConcreteAddressCasts(SILInstruction *orig, - SILInstruction *cloned); - - void postProcess(SILInstruction *orig, SILInstruction *cloned) { - SILCloner::postProcess(orig, cloned); - insertOpaqueToConcreteAddressCasts(orig, cloned); - } - - void visitTerminator(SILBasicBlock *BB) { - visit(BB->getTerminator()); - } - - void visitReturnInst(ReturnInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto origResult = Inst->getOperand(); - auto clonedResult = getOpValue(Inst->getOperand()); - if (clonedResult->getType().getASTType() != - origResult->getType().getASTType()) { - clonedResult = createCast(RegularLocation::getAutoGeneratedLocation(), - clonedResult, origResult->getType()); - } - recordClonedInstruction( - Inst, - getBuilder().createReturn(getOpLocation(Inst->getLoc()), clonedResult)); - } - - void visitStructInst(StructInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto elements = getOpValueArray<8>(Inst->getElements()); - auto structTy = getOpType(Inst->getType()); - auto *structDecl = structTy.getStructOrBoundGenericStruct(); - unsigned idx = 0; - // Adjust field types if neccessary. - for (VarDecl *field : structDecl->getStoredProperties()) { - SILType loweredType = structTy.getFieldType( - field, getBuilder().getFunction().getModule()); - if (elements[idx]->getType() != loweredType) { - elements[idx] = createCast(getOpLocation(Inst->getLoc()), elements[idx], - loweredType); - } - idx++; - } - recordClonedInstruction( - Inst, getBuilder().createStruct(getOpLocation(Inst->getLoc()), - getOpType(Inst->getType()), elements)); - } - - void visitTupleInst(TupleInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto elements = getOpValueArray<8>(Inst->getElements()); - auto tupleTy = getOpType(Inst->getType()); - for (size_t i = 0, size = Inst->getElements().size(); i < size; ++i) { - auto elementTy = tupleTy.getTupleElementType(i); - if (Inst->getElement(i)->getType() != elementTy) { - elements[i] = - createCast(getOpLocation(Inst->getLoc()), elements[i], elementTy); - } - } - recordClonedInstruction( - Inst, getBuilder().createTuple(getOpLocation(Inst->getLoc()), - getOpType(Inst->getType()), elements)); - } - - void visitEnumInst(EnumInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - SILValue opd = SILValue(); - auto newTy = getOpType(Inst->getType()); - if (Inst->hasOperand()) { - opd = getOpValue(Inst->getOperand()); - SILType newCaseTy = newTy.getEnumElementType( - Inst->getElement(), getBuilder().getFunction().getModule()); - if (opd->getType() != newCaseTy) - opd = createCast(getOpLocation(Inst->getLoc()), opd, newCaseTy); - } - recordClonedInstruction( - Inst, getBuilder().createEnum(getOpLocation(Inst->getLoc()), opd, - Inst->getElement(), newTy)); - } - - void visitInitEnumDataAddrInst(InitEnumDataAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto opd = getOpValue(Inst->getOperand()); - auto caseTy = opd->getType().getEnumElementType( - Inst->getElement(), getBuilder().getFunction().getModule()); - auto expectedTy = getOpType(Inst->getType()); - if (expectedTy != caseTy) - expectedTy = caseTy; - recordClonedInstruction(Inst, getBuilder().createInitEnumDataAddr( - getOpLocation(Inst->getLoc()), opd, - Inst->getElement(), expectedTy)); - } - - /// Projections should not change the type if the type is not specialized. - void visitStructElementAddrInst(StructElementAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto opd = getOpValue(Inst->getOperand()); - recordClonedInstruction( - Inst, getBuilder().createStructElementAddr( - getOpLocation(Inst->getLoc()), opd, Inst->getField())); - } - - /// Projections should not change the type if the type is not specialized. - void visitStructExtractInst(StructExtractInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto opd = getOpValue(Inst->getOperand()); - recordClonedInstruction( - Inst, getBuilder().createStructExtract(getOpLocation(Inst->getLoc()), - opd, Inst->getField())); - } - /// Projections should not change the type if the type is not specialized. - void visitTupleElementAddrInst(TupleElementAddrInst *Inst) { - auto opd = getOpValue(Inst->getOperand()); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction(Inst, getBuilder().createTupleElementAddr( - getOpLocation(Inst->getLoc()), opd, - Inst->getFieldNo())); - } - /// Projections should not change the type if the type is not specialized. - void visitTupleExtractInst(TupleExtractInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createTupleExtract(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), - Inst->getFieldNo())); - } - /// Projections should not change the type if the type is not specialized. - void visitRefElementAddrInst(RefElementAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createRefElementAddr( - getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - Inst->getField())); - } - - /// Projections should not change the type if the type is not specialized. - void visitRefTailAddrInst(RefTailAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createRefTailAddr(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), - Inst->getType())); - } - - void visitYieldInst(YieldInst *Inst) { - auto OrigValues = Inst->getYieldedValues(); - auto Values = getOpValueArray<8>(Inst->getYieldedValues()); - auto ResumeBB = getOpBasicBlock(Inst->getResumeBB()); - auto UnwindBB = getOpBasicBlock(Inst->getUnwindBB()); - for (auto idx : indices(Values)) { - if (OrigValues[idx]->getType().getASTType() != - Values[idx]->getType().getASTType()) { - Values[idx] = createCast(RegularLocation::getAutoGeneratedLocation(), - Values[idx], OrigValues[idx]->getType()); - } - } - - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createYield(getOpLocation(Inst->getLoc()), Values, - ResumeBB, UnwindBB)); - } - - void visitCopyAddrInst(CopyAddrInst *Inst) { - auto src = getOpValue(Inst->getSrc()); - auto dst = getOpValue(Inst->getDest()); - auto srcType = src->getType(); - auto destType = dst->getType(); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - // If the types mismatch cast the operands to the non opaque archetype. - if (destType.getASTType() != srcType.getASTType()) { - if (srcType.getASTType()->hasOpaqueArchetype()) { - src = getBuilder().createUncheckedAddrCast( - getOpLocation(Inst->getLoc()), src, destType); - } else if (destType.getASTType()->hasOpaqueArchetype()) { - dst = getBuilder().createUncheckedAddrCast( - getOpLocation(Inst->getLoc()), dst, srcType); - } - } - recordClonedInstruction( - Inst, getBuilder().createCopyAddr(getOpLocation(Inst->getLoc()), src, - dst, Inst->isTakeOfSrc(), - Inst->isInitializationOfDest())); - } - - SILValue remapResultType(SILLocation loc, SILValue val) { - auto specializedTy = remapType(val->getType()); - if (val->getType() == specializedTy) - return val; - return createCast(loc, val, specializedTy); - } - - void visitThinToThickFunctionInst(ThinToThickFunctionInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto loc = getOpLocation(Inst->getLoc()); - auto opd = remapResultType(loc, getOpValue(Inst->getOperand())); - recordClonedInstruction(Inst, getBuilder().createThinToThickFunction( - loc, opd, getOpType(Inst->getType()))); - } - - void visitStoreInst(StoreInst *Inst) { - auto src = getOpValue(Inst->getSrc()); - auto dst = getOpValue(Inst->getDest()); - auto srcType = src->getType(); - auto destType = dst->getType(); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - // If the types mismatch cast the operands to the non opaque archetype. - if (destType.getASTType() != srcType.getASTType()) { - if (srcType.getASTType()->hasOpaqueArchetype()) { - assert(!srcType.isAddress()); - src = createCast(getOpLocation(Inst->getLoc()), src, - destType.getObjectType()); - } else if (destType.getASTType()->hasOpaqueArchetype()) { - dst = getBuilder().createUncheckedAddrCast( - getOpLocation(Inst->getLoc()), dst, srcType.getAddressType()); - } - } - - if (!getBuilder().hasOwnership()) { - switch (Inst->getOwnershipQualifier()) { - case StoreOwnershipQualifier::Assign: { - auto *li = getBuilder().createLoad(getOpLocation(Inst->getLoc()), dst, - LoadOwnershipQualifier::Unqualified); - auto *si = getBuilder().createStore( - getOpLocation(Inst->getLoc()), src, getOpValue(Inst->getDest()), - StoreOwnershipQualifier::Unqualified); - getBuilder().emitDestroyValueOperation(getOpLocation(Inst->getLoc()), - li); - return recordClonedInstruction(Inst, si); - } - case StoreOwnershipQualifier::Init: - case StoreOwnershipQualifier::Trivial: - case StoreOwnershipQualifier::Unqualified: - break; - } - - return recordClonedInstruction( - Inst, - getBuilder().createStore(getOpLocation(Inst->getLoc()), src, dst, - StoreOwnershipQualifier::Unqualified)); - } - - recordClonedInstruction( - Inst, getBuilder().createStore(getOpLocation(Inst->getLoc()), src, dst, - Inst->getOwnershipQualifier())); - } - -protected: - - SILType remapType(SILType Ty) { - SILType &Sty = TypeCache[Ty]; - if (Sty) - return Sty; - auto *dc = getDeclContextIfInCurrentModule(Original); - - // Apply the opaque types substitution. - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - dc, Original.getResilienceExpansion()); - Sty = Ty.subst(Original.getModule(), replacer, replacer, - CanGenericSignature(), true); - return Sty; - } - - CanType remapASTType(CanType ty) { - // Apply the opaque types substitution. - return substOpaqueTypesWithUnderlyingTypes(ty, &Original) - ->getCanonicalType(); - } - - ProtocolConformanceRef remapConformance(Type type, - ProtocolConformanceRef conf) { - auto *dc = getDeclContextIfInCurrentModule(Original); - // Apply the opaque types substitution. - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - dc, Original.getResilienceExpansion()); - return conf.subst(type, replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes); - } - - SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { - // Apply the opaque types substitution. - return substOpaqueTypesWithUnderlyingTypes(Subs, &Original); - } - - SILValue createCast(SILLocation loc, SILValue opd, SILType type) { - auto &CurFn = getBuilder().getFunction(); - if (opd->getType().isAddress()) { - return getBuilder().createUncheckedAddrCast(loc, opd, type); - } else if (opd->getType().is()) { - return getBuilder().createConvertFunction( - loc, opd, type, /*withoutActuallyEscaping*/ false); - } else if (opd->getType().isTrivial(CurFn)) { - return getBuilder().createUncheckedTrivialBitCast(loc, opd, type); - } else if (opd->getType().canRefCast(opd->getType(), type, - CurFn.getModule())) { - return getBuilder().createUncheckedRefCast(loc, opd, type); - } else { - // This could be improved upon by recursively recomposing the type. - auto *stackLoc = getBuilder().createAllocStack(loc, type); - auto *addr = getBuilder().createUncheckedAddrCast( - loc, stackLoc, opd->getType().getAddressType()); - getBuilder().createTrivialStoreOr(loc, opd, addr, - StoreOwnershipQualifier::Init, true); - SILValue res = getBuilder().createTrivialLoadOr( - loc, stackLoc, LoadOwnershipQualifier::Take, true); - getBuilder().createDeallocStack(loc, stackLoc); - return res; - } - } - - void replaceBlockArgumentType(SILLocation loc, SILBasicBlock *destBlock, - SILType withType) { - assert(destBlock->getArguments().size() == 1); - - auto origType = (*destBlock->args_begin())->getType(); - auto origPhi = destBlock->getPhiArguments()[0]; - SILValue undef = SILUndef::get(origType, getBuilder().getFunction()); - SmallVector useList(origPhi->use_begin(), origPhi->use_end()); - for (auto *use : useList) { - use->set(undef); - } - - auto *newPhi = - destBlock->replacePhiArgument(0, withType, origPhi->getOwnershipKind()); - - getBuilder().setInsertionPoint(destBlock->begin()); - auto cast = createCast(loc, newPhi, origType); - for (auto *use : useList) { - use->set(cast); - } - } - - void fixUp(SILFunction *) { - auto &clonedFunction = getBuilder().getFunction(); - for (auto &BB : clonedFunction) { - for (auto &cloned : BB) { - // Fix up the type of try_apply successor block arguments. - if (auto *tryApply = dyn_cast(&cloned)) { - auto normalBB = tryApply->getNormalBB(); - SILFunctionConventions calleeConv( - tryApply->getSubstCalleeType(), - tryApply->getFunction()->getModule()); - auto normalBBType = (*normalBB->args_begin())->getType(); - auto applyResultType = calleeConv.getSILResultType(); - if (normalBBType != calleeConv.getSILResultType()) { - replaceBlockArgumentType(tryApply->getLoc(), normalBB, applyResultType); - } - } - // Fix up the type of switch_enum successor block arguments. - if (auto *switchEnum = dyn_cast(&cloned)) { - SILType enumTy = switchEnum->getOperand()->getType(); - for (unsigned i = 0, e = switchEnum->getNumCases(); i < e; ++i) { - EnumElementDecl *elt; - SILBasicBlock *dest; - std::tie(elt, dest) = switchEnum->getCase(i); - - if (elt->hasAssociatedValues() && - dest->getArguments().size() == 1) { - SILType eltArgTy = - enumTy.getEnumElementType(elt, clonedFunction.getModule()); - SILType bbArgTy = dest->getArguments()[0]->getType(); - if (eltArgTy != bbArgTy) - replaceBlockArgumentType(switchEnum->getLoc(), dest, eltArgTy); - - } - } - } - } - } - } -}; -} // namespace - -void OpaqueSpecializerCloner::clone() { - for (auto arg: entryBlock->getArguments()) - recordFoldedValue(arg, arg); - cloneReachableBlocks(cloneFromBlock, {}, entryBlock, - true /*havePrepopulatedFunctionArgs*/); - getBuilder().setInsertionPoint(entryBlock); - getBuilder().createBranch(RegularLocation::getAutoGeneratedLocation(), - getOpBasicBlock(cloneFromBlock)); -} - -/// Update address uses of the opaque type archetype with the concrete type. -/// This is neccessary for apply instructions. -void OpaqueSpecializerCloner::insertOpaqueToConcreteAddressCasts( - SILInstruction *orig, SILInstruction *cloned) { - - // Replace apply operands. - if (auto apply = ApplySite::isa(cloned)) { - SavedInsertionPointRAII restore(getBuilder()); - getBuilder().setInsertionPoint(apply.getInstruction()); - auto substConv = apply.getSubstCalleeConv(); - unsigned idx = 0; - for (auto &opd : apply.getArgumentOperands()) { - auto argIdx = apply.getCalleeArgIndex(opd); - auto argType = substConv.getSILArgumentType(argIdx); - if (argType.getASTType() != opd.get()->getType().getASTType()) { - opd.set(createCast(apply.getLoc(), opd.get(), argType)); - } - ++idx; - } - } -} - -namespace { -class OpaqueArchetypeSpecializer : public SILFunctionTransform { - void run() override { - if (!EnableOpaqueArchetypeSpecializer) - return; - - auto *context = getFunction(); - - if (!context->shouldOptimize()) - return; - - auto opaqueArchetypeWouldChange = [=](CanType ty) -> bool { - if (!ty->hasOpaqueArchetype()) - return false; - - return ty.findIf([=](Type type) -> bool { - if (auto opaqueTy = type->getAs()) { - auto opaque = opaqueTy->getDecl(); - OpaqueSubstitutionKind subKind = - ReplaceOpaqueTypesWithUnderlyingTypes:: - shouldPerformSubstitution(opaque, - context->getModule().getSwiftModule(), - context->getResilienceExpansion()); - return subKind != OpaqueSubstitutionKind::DontSubstitute; - } - return false; - }); - }; - - // Look for opaque type archetypes. - bool foundOpaqueArchetype = false; - for (auto &BB : *getFunction()) { - for (auto &inst : BB) { - auto hasOpaqueOperand = [&] (SILInstruction &inst) -> bool { - // Check the operands for opaque types. - for (auto &opd : inst.getAllOperands()) - if (opaqueArchetypeWouldChange(opd.get()->getType().getASTType())) - return true; - return false; - }; - if ((foundOpaqueArchetype = hasOpaqueOperand(inst))) - break; - auto hasOpaqueResult = [&](SILInstruction &inst) -> bool { - // Check the results for opaque types. - for (const auto &res : inst.getResults()) - if (opaqueArchetypeWouldChange(res->getType().getASTType())) - return true; - return false; - }; - if ((foundOpaqueArchetype = hasOpaqueResult(inst))) - break; - } - if (foundOpaqueArchetype) - break; - } - - if (foundOpaqueArchetype) { - OpaqueSpecializerCloner s(*getFunction()); - s.clone(); - removeUnreachableBlocks(*getFunction()); - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - } - } -}; -} // end anonymous namespace - -SILTransform *swift::createOpaqueArchetypeSpecializer() { - return new OpaqueArchetypeSpecializer(); -} diff --git a/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp b/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp index 9c5ac3a6e8e42..e30cbc9cce79f 100644 --- a/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp +++ b/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp @@ -78,12 +78,14 @@ class LSLocationPrinter : public SILModuleTransform { SILValue V = LI->getOperand(); // This is an address type, take it object type. SILType Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else if (auto *SI = dyn_cast(&II)) { SILValue V = SI->getDest(); // This is an address type, take it object type. SILType Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else { // Not interested in these instructions yet. continue; @@ -91,7 +93,7 @@ class LSLocationPrinter : public SILModuleTransform { llvm::outs() << "#" << Counter++ << II; for (auto &T : PPList) { - T.getValue().print(llvm::outs(), *M); + T.getValue().print(llvm::outs(), *M, TypeExpansionContext(Fn)); } PPList.clear(); } @@ -111,12 +113,14 @@ class LSLocationPrinter : public SILModuleTransform { V = LI->getOperand(); // This is an address type, take it object type. Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else if (auto *SI = dyn_cast(&II)) { V = SI->getDest(); // This is an address type, take it object type. Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else { // Not interested in these instructions yet. continue; @@ -124,7 +128,7 @@ class LSLocationPrinter : public SILModuleTransform { llvm::outs() << "#" << Counter++ << II; for (auto &T : PPList) { - T.getValue().print(llvm::outs(), *M); + T.getValue().print(llvm::outs(), *M, TypeExpansionContext(Fn)); } PPList.clear(); } @@ -150,14 +154,16 @@ class LSLocationPrinter : public SILModuleTransform { L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else if (auto *SI = dyn_cast(&II)) { SILValue Mem = SI->getDest(); SILValue UO = getUnderlyingObject(Mem); L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else { // Not interested in these instructions yet. continue; @@ -165,7 +171,7 @@ class LSLocationPrinter : public SILModuleTransform { llvm::outs() << "#" << Counter++ << II; for (auto &Loc : Locs) { - Loc.print(llvm::outs(), &Fn.getModule()); + Loc.print(llvm::outs(), &Fn.getModule(), TypeExpansionContext(Fn)); } Locs.clear(); } @@ -194,14 +200,16 @@ class LSLocationPrinter : public SILModuleTransform { L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else if (auto *SI = dyn_cast(&II)) { SILValue Mem = SI->getDest(); SILValue UO = getUnderlyingObject(Mem); L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else { // Not interested in these instructions yet. continue; @@ -216,10 +224,10 @@ class LSLocationPrinter : public SILModuleTransform { } // This should get the original (unexpanded) location back. - LSLocation::reduce(L, &Fn.getModule(), SLocs); + LSLocation::reduce(L, &Fn.getModule(), TypeExpansionContext(Fn), SLocs); llvm::outs() << "#" << Counter++ << II; for (auto &Loc : SLocs) { - Loc.print(llvm::outs(), &Fn.getModule()); + Loc.print(llvm::outs(), &Fn.getModule(), TypeExpansionContext(Fn)); } L.reset(); Locs.clear(); diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 31703885545fa..75645fa97a748 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -12,11 +12,382 @@ #define DEBUG_TYPE "serialize-sil" #include "swift/Strings.h" +#include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILCloner.h" +#include "swift/SIL/SILFunction.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" using namespace swift; +namespace { +/// In place map opaque archetypes to their underlying type in a function. +/// This needs to happen when a function changes from serializable to not +/// serializable. +class MapOpaqueArchetypes : public SILCloner { + using SuperTy = SILCloner; + + SILBasicBlock *origEntryBlock; + SILBasicBlock *clonedEntryBlock; +public: + friend class SILCloner; + friend class SILCloner; + friend class SILInstructionVisitor; + + MapOpaqueArchetypes(SILFunction &fun) : SuperTy(fun) { + origEntryBlock = fun.getEntryBlock(); + clonedEntryBlock = fun.createBasicBlock(); + } + + SILType remapType(SILType Ty) { + if (!Ty.getASTType()->hasOpaqueArchetype() || + !getBuilder() + .getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return Ty; + + return getBuilder().getTypeLowering(Ty).getLoweredType().getCategoryType( + Ty.getCategory()); + } + + CanType remapASTType(CanType ty) { + if (!ty->hasOpaqueArchetype() || + !getBuilder() + .getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return ty; + // Remap types containing opaque result types in the current context. + return getBuilder() + .getTypeLowering(SILType::getPrimitiveObjectType(ty)) + .getLoweredType() + .getASTType(); + } + + ProtocolConformanceRef remapConformance(Type ty, + ProtocolConformanceRef conf) { + auto context = getBuilder().getTypeExpansionContext(); + auto conformance = conf; + if (ty->hasOpaqueArchetype() && + context.shouldLookThroughOpaqueTypeArchetypes()) { + conformance = + substOpaqueTypesWithUnderlyingTypes(conformance, ty, context); + } + return conformance; + } + + void replace(); +}; +} // namespace + +void MapOpaqueArchetypes::replace() { + // Map the function arguments. + SmallVector entryArgs; + entryArgs.reserve(origEntryBlock->getArguments().size()); + for (auto &origArg : origEntryBlock->getArguments()) { + SILType mappedType = remapType(origArg->getType()); + auto *NewArg = clonedEntryBlock->createFunctionArgument( + mappedType, origArg->getDecl(), true); + entryArgs.push_back(NewArg); + } + + getBuilder().setInsertionPoint(clonedEntryBlock); + auto &fn = getBuilder().getFunction(); + cloneFunctionBody(&fn, clonedEntryBlock, entryArgs, + true /*replaceOriginalFunctionInPlace*/); + // Insert the new entry block at the beginning. + fn.getBlocks().splice(fn.getBlocks().begin(), fn.getBlocks(), + clonedEntryBlock); + removeUnreachableBlocks(fn); +} + +static bool opaqueArchetypeWouldChange(TypeExpansionContext context, + CanType ty) { + if (!ty->hasOpaqueArchetype()) + return false; + + return ty.findIf([=](Type type) -> bool { + if (auto opaqueTy = type->getAs()) { + auto opaque = opaqueTy->getDecl(); + auto module = context.getContext()->getParentModule(); + OpaqueSubstitutionKind subKind = + ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( + opaque, module, context.getResilienceExpansion()); + return subKind != OpaqueSubstitutionKind::DontSubstitute; + } + return false; + }); +} + +static bool hasOpaqueArchetypeOperand(TypeExpansionContext context, + SILInstruction &inst) { + // Check the operands for opaque types. + for (auto &opd : inst.getAllOperands()) + if (opaqueArchetypeWouldChange(context, opd.get()->getType().getASTType())) + return true; + return false; +} + +static bool hasOpaqueArchetypeResult(TypeExpansionContext context, + SILInstruction &inst) { + // Check the results for opaque types. + for (const auto &res : inst.getResults()) + if (opaqueArchetypeWouldChange(context, res->getType().getASTType())) + return true; + return false; +} + +static bool hasOpaqueArchetype(TypeExpansionContext context, + SILInstruction &inst) { + // Check operands and results. + if (hasOpaqueArchetypeOperand(context, inst)) + return true; + if (hasOpaqueArchetypeResult(context, inst)) + return true; + + // Check substitution maps. + switch (inst.getKind()) { + case SILInstructionKind::AllocStackInst: + case SILInstructionKind::AllocRefInst: + case SILInstructionKind::AllocRefDynamicInst: + case SILInstructionKind::AllocValueBufferInst: + case SILInstructionKind::AllocBoxInst: + case SILInstructionKind::AllocExistentialBoxInst: + case SILInstructionKind::IndexAddrInst: + case SILInstructionKind::TailAddrInst: + case SILInstructionKind::IndexRawPointerInst: + case SILInstructionKind::FunctionRefInst: + case SILInstructionKind::DynamicFunctionRefInst: + case SILInstructionKind::PreviousDynamicFunctionRefInst: + case SILInstructionKind::GlobalAddrInst: + case SILInstructionKind::GlobalValueInst: + case SILInstructionKind::IntegerLiteralInst: + case SILInstructionKind::FloatLiteralInst: + case SILInstructionKind::StringLiteralInst: + case SILInstructionKind::ClassMethodInst: + case SILInstructionKind::SuperMethodInst: + case SILInstructionKind::ObjCMethodInst: + case SILInstructionKind::ObjCSuperMethodInst: + case SILInstructionKind::WitnessMethodInst: + case SILInstructionKind::UpcastInst: + case SILInstructionKind::AddressToPointerInst: + case SILInstructionKind::PointerToAddressInst: + case SILInstructionKind::UncheckedRefCastInst: + case SILInstructionKind::UncheckedAddrCastInst: + case SILInstructionKind::UncheckedTrivialBitCastInst: + case SILInstructionKind::UncheckedBitwiseCastInst: + case SILInstructionKind::RefToRawPointerInst: + case SILInstructionKind::RawPointerToRefInst: +#define LOADABLE_REF_STORAGE(Name, ...) \ + case SILInstructionKind::RefTo##Name##Inst: \ + case SILInstructionKind::Name##ToRefInst: +#include "swift/AST/ReferenceStorage.def" +#undef LOADABLE_REF_STORAGE_HELPER + case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::ConvertEscapeToNoEscapeInst: + case SILInstructionKind::ThinFunctionToPointerInst: + case SILInstructionKind::PointerToThinFunctionInst: + case SILInstructionKind::RefToBridgeObjectInst: + case SILInstructionKind::BridgeObjectToRefInst: + case SILInstructionKind::BridgeObjectToWordInst: + case SILInstructionKind::ThinToThickFunctionInst: + case SILInstructionKind::ThickToObjCMetatypeInst: + case SILInstructionKind::ObjCToThickMetatypeInst: + case SILInstructionKind::ObjCMetatypeToObjectInst: + case SILInstructionKind::ObjCExistentialMetatypeToObjectInst: + case SILInstructionKind::UnconditionalCheckedCastValueInst: + case SILInstructionKind::UnconditionalCheckedCastInst: + case SILInstructionKind::ClassifyBridgeObjectInst: + case SILInstructionKind::ValueToBridgeObjectInst: + case SILInstructionKind::MarkDependenceInst: + case SILInstructionKind::CopyBlockInst: + case SILInstructionKind::CopyBlockWithoutEscapingInst: + case SILInstructionKind::CopyValueInst: +#define UNCHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongCopy##Name##ValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongCopy##Name##ValueInst: +#include "swift/AST/ReferenceStorage.def" +#undef UNCHECKED_REF_STORAGE +#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE + case SILInstructionKind::UncheckedOwnershipConversionInst: + case SILInstructionKind::IsUniqueInst: + case SILInstructionKind::IsEscapingClosureInst: + case SILInstructionKind::LoadInst: + case SILInstructionKind::LoadBorrowInst: + case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::StoreBorrowInst: + case SILInstructionKind::BeginAccessInst: +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ + case SILInstructionKind::Load##Name##Inst: +#include "swift/AST/ReferenceStorage.def" +#undef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE + case SILInstructionKind::MarkUninitializedInst: + case SILInstructionKind::ProjectValueBufferInst: + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::ProjectExistentialBoxInst: + case SILInstructionKind::BuiltinInst: + case SILInstructionKind::MetatypeInst: + case SILInstructionKind::ValueMetatypeInst: + case SILInstructionKind::ExistentialMetatypeInst: + case SILInstructionKind::ObjCProtocolInst: + case SILInstructionKind::ObjectInst: + case SILInstructionKind::TupleInst: + case SILInstructionKind::TupleExtractInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::StructInst: + case SILInstructionKind::StructExtractInst: + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::EnumInst: + case SILInstructionKind::UncheckedEnumDataInst: + case SILInstructionKind::InitEnumDataAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::SelectEnumInst: + case SILInstructionKind::SelectEnumAddrInst: + case SILInstructionKind::SelectValueInst: + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InitExistentialValueInst: + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::InitExistentialRefInst: + case SILInstructionKind::OpenExistentialRefInst: + case SILInstructionKind::InitExistentialMetatypeInst: + case SILInstructionKind::OpenExistentialMetatypeInst: + case SILInstructionKind::OpenExistentialBoxInst: + case SILInstructionKind::OpenExistentialValueInst: + case SILInstructionKind::OpenExistentialBoxValueInst: + case SILInstructionKind::ProjectBlockStorageInst: + case SILInstructionKind::InitBlockStorageHeaderInst: + case SILInstructionKind::KeyPathInst: + case SILInstructionKind::UnreachableInst: + case SILInstructionKind::ReturnInst: + case SILInstructionKind::ThrowInst: + case SILInstructionKind::YieldInst: + case SILInstructionKind::UnwindInst: + case SILInstructionKind::BranchInst: + case SILInstructionKind::CondBranchInst: + case SILInstructionKind::SwitchValueInst: + case SILInstructionKind::SwitchEnumInst: + case SILInstructionKind::SwitchEnumAddrInst: + case SILInstructionKind::DynamicMethodBranchInst: + case SILInstructionKind::CheckedCastBranchInst: + case SILInstructionKind::CheckedCastAddrBranchInst: + case SILInstructionKind::CheckedCastValueBranchInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::DeallocRefInst: + case SILInstructionKind::DeallocPartialRefInst: + case SILInstructionKind::DeallocValueBufferInst: + case SILInstructionKind::DeallocBoxInst: + case SILInstructionKind::DeallocExistentialBoxInst: + case SILInstructionKind::StrongRetainInst: + case SILInstructionKind::StrongReleaseInst: + case SILInstructionKind::UnmanagedRetainValueInst: + case SILInstructionKind::UnmanagedReleaseValueInst: + case SILInstructionKind::UnmanagedAutoreleaseValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongRetain##Name##Inst: \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::Name##ReleaseInst: +#include "swift/AST/ReferenceStorage.def" +#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE + case SILInstructionKind::RetainValueInst: + case SILInstructionKind::RetainValueAddrInst: + case SILInstructionKind::ReleaseValueInst: + case SILInstructionKind::ReleaseValueAddrInst: + case SILInstructionKind::SetDeallocatingInst: + case SILInstructionKind::AutoreleaseValueInst: + case SILInstructionKind::BindMemoryInst: + case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::DestroyValueInst: + case SILInstructionKind::EndBorrowInst: + case SILInstructionKind::EndAccessInst: + case SILInstructionKind::BeginUnpairedAccessInst: + case SILInstructionKind::EndUnpairedAccessInst: + case SILInstructionKind::StoreInst: + case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::MarkFunctionEscapeInst: + case SILInstructionKind::DebugValueInst: + case SILInstructionKind::DebugValueAddrInst: +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Store##Name##Inst: +#include "swift/AST/ReferenceStorage.def" + case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::EndLifetimeInst: + case SILInstructionKind::InjectEnumAddrInst: + case SILInstructionKind::DeinitExistentialAddrInst: + case SILInstructionKind::DeinitExistentialValueInst: + case SILInstructionKind::UnconditionalCheckedCastAddrInst: + case SILInstructionKind::UncheckedRefCastAddrInst: + case SILInstructionKind::AllocGlobalInst: + case SILInstructionKind::EndApplyInst: + case SILInstructionKind::AbortApplyInst: + case SILInstructionKind::CondFailInst: + case SILInstructionKind::DestructureStructInst: + case SILInstructionKind::DestructureTupleInst: + // Handle by operand and result check. + break; + + case SILInstructionKind::ApplyInst: + case SILInstructionKind::PartialApplyInst: + case SILInstructionKind::TryApplyInst: + case SILInstructionKind::BeginApplyInst: + // Check substitution map. + auto apply = ApplySite(&inst); + auto subs = apply.getSubstitutionMap(); + for (auto ty: subs.getReplacementTypes()) { + if (opaqueArchetypeWouldChange(context, ty->getCanonicalType())) + return true; + } + break; + } + + return false; +} + +static bool hasOpaqueArchetypeArgument(TypeExpansionContext context, SILBasicBlock &BB) { + for (auto *arg : BB.getArguments()) { + if (opaqueArchetypeWouldChange(context, arg->getType().getASTType())) + return true; + } + return false; +} + +static bool hasAnyOpaqueArchetype(SILFunction &F) { + bool foundOpaqueArchetype = false; + auto context = F.getTypeExpansionContext(); + for (auto &BB : F) { + // Check basic block argument types. + if (hasOpaqueArchetypeArgument(context, BB)) { + foundOpaqueArchetype = true; + break; + } + + // Check instruction results and operands. + for (auto &inst : BB) { + if (hasOpaqueArchetype(context, inst)) { + foundOpaqueArchetype = true; + break; + } + } + + if (foundOpaqueArchetype) + break; + } + + return foundOpaqueArchetype; +} + +void updateOpaqueArchetypes(SILFunction &F) { + // Only map if there are opaque archetypes that could change. + if (!hasAnyOpaqueArchetype(F)) + return; + + MapOpaqueArchetypes(F).replace(); +} + /// A utility pass to serialize a SILModule at any place inside the optimization /// pipeline. class SerializeSILPass : public SILModuleTransform { @@ -24,7 +395,16 @@ class SerializeSILPass : public SILModuleTransform { /// optimizations and for a better dead function elimination. void removeSerializedFlagFromAllFunctions(SILModule &M) { for (auto &F : M) { + bool wasSerialized = F.isSerialized() != IsNotSerialized; F.setSerialized(IsNotSerialized); + + // We are removing [serialized] from the function. This will change how + // opaque archetypes are lowered in SIL - they might lower to their + // underlying type. Update the function's opaque archetypes. + if (wasSerialized && F.isDefinition()) { + updateOpaqueArchetypes(F); + invalidateAnalysis(&F, SILAnalysis::InvalidationKind::Everything); + } } for (auto &WT : M.getWitnessTables()) { diff --git a/lib/SILOptimizer/Utils/CastOptimizer.cpp b/lib/SILOptimizer/Utils/CastOptimizer.cpp index 1ff70ac1ea143..f5e3a5fe77ad8 100644 --- a/lib/SILOptimizer/Utils/CastOptimizer.cpp +++ b/lib/SILOptimizer/Utils/CastOptimizer.cpp @@ -628,7 +628,8 @@ CastOptimizer::optimizeBridgedSwiftToObjCCast(SILDynamicCastInst dynamicCast) { std::tie(bridgedFunc, subMap) = result.getValue(); } - SILType SubstFnTy = bridgedFunc->getLoweredType().substGenericArgs(M, subMap); + SILType SubstFnTy = bridgedFunc->getLoweredType().substGenericArgs( + M, subMap, TypeExpansionContext(*F)); SILFunctionConventions substConv(SubstFnTy.castTo(), M); // Check that this is a case that the authors of this code thought it could diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp index ff881c8fb690c..e7d6e9e0f2714 100644 --- a/lib/SILOptimizer/Utils/Devirtualize.cpp +++ b/lib/SILOptimizer/Utils/Devirtualize.cpp @@ -743,14 +743,16 @@ FullApplySite swift::devirtualizeClassMethod(FullApplySite applySite, auto *f = getTargetClassMethod(module, cd, mi); - CanSILFunctionType genCalleeType = f->getLoweredFunctionType(); + CanSILFunctionType genCalleeType = f->getLoweredFunctionTypeInContext( + TypeExpansionContext(*applySite.getFunction())); SubstitutionMap subs = getSubstitutionsForCallee( module, genCalleeType, classOrMetatype->getType().getASTType(), applySite); CanSILFunctionType substCalleeType = genCalleeType; if (genCalleeType->isPolymorphic()) - substCalleeType = genCalleeType->substGenericArgs(module, subs); + substCalleeType = genCalleeType->substGenericArgs( + module, subs, TypeExpansionContext(*applySite.getFunction())); SILFunctionConventions substConv(substCalleeType, module); SILBuilderWithScope builder(applySite.getInstruction()); @@ -937,7 +939,8 @@ SubstitutionMap swift::getWitnessMethodSubstitutions(SILModule &module, ApplySite applySite, SILFunction *f, ProtocolConformanceRef cRef) { - auto witnessFnTy = f->getLoweredFunctionType(); + auto witnessFnTy = f->getLoweredFunctionTypeInContext( + TypeExpansionContext(*applySite.getFunction())); assert(witnessFnTy->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); @@ -975,8 +978,10 @@ static ApplySite devirtualizeWitnessMethod(ApplySite applySite, SILFunction *f, // Figure out the exact bound type of the function to be called by // applying all substitutions. - auto calleeCanType = f->getLoweredFunctionType(); - auto substCalleeCanType = calleeCanType->substGenericArgs(module, subMap); + auto calleeCanType = f->getLoweredFunctionTypeInContext( + TypeExpansionContext(*applySite.getFunction())); + auto substCalleeCanType = calleeCanType->substGenericArgs( + module, subMap, TypeExpansionContext(*applySite.getFunction())); // Collect arguments from the apply instruction. SmallVector arguments; diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index d9ddecae47bb1..51375e857424e 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -537,12 +537,12 @@ bool ReabstractionInfo::canBeSpecialized(ApplySite Apply, SILFunction *Callee, return ReInfo.prepareAndCheck(Apply, Callee, ParamSubs); } -ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, - SubstitutionMap ParamSubs, - IsSerialized_t Serialized, - bool ConvertIndirectToDirect, - OptRemark::Emitter *ORE) +ReabstractionInfo::ReabstractionInfo( + ModuleDecl *targetModule, bool isWholeModule, ApplySite Apply, + SILFunction *Callee, SubstitutionMap ParamSubs, IsSerialized_t Serialized, + bool ConvertIndirectToDirect, OptRemark::Emitter *ORE) : ConvertIndirectToDirect(ConvertIndirectToDirect), + TargetModule(targetModule), isWholeModule(isWholeModule), Serialized(Serialized) { if (!prepareAndCheck(Apply, Callee, ParamSubs, ORE)) return; @@ -576,14 +576,16 @@ ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, auto CalleeFnTy = Callee->getLoweredFunctionType(); assert(CalleeFnTy->isPolymorphic()); auto CalleeSubstFnTy = CalleeFnTy->substGenericArgs( - Callee->getModule(), getCalleeParamSubstitutionMap()); + Callee->getModule(), getCalleeParamSubstitutionMap(), + getResilienceExpansion()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && "Substituted callee type should not have type parameters"); SpecializedSubstFnTy = SpecializedFnTy->substGenericArgs( - Callee->getModule(), getCallerParamSubstitutionMap()); + Callee->getModule(), getCallerParamSubstitutionMap(), + getResilienceExpansion()); assert(!SpecializedSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); @@ -756,7 +758,8 @@ ReabstractionInfo::createSubstitutedType(SILFunction *OrigF, { Lowering::GenericContextScope GenericScope(M.Types, CanSpecializedGenericSig); - FnTy = OrigF->getLoweredFunctionType()->substGenericArgs(M, SubstMap); + FnTy = OrigF->getLoweredFunctionType()->substGenericArgs( + M, SubstMap, getResilienceExpansion()); // FIXME: Some of the added new requirements may not have been taken into // account by the substGenericArgs. So, canonicalize in the context of the // specialized signature. @@ -886,7 +889,7 @@ void ReabstractionInfo::performFullSpecializationPreparation( ClonerParamSubMap = ParamSubs; SubstitutedType = Callee->getLoweredFunctionType()->substGenericArgs( - M, ClonerParamSubMap); + M, ClonerParamSubMap, getResilienceExpansion()); CallerParamSubMap = {}; createSubstitutedAndSpecializedTypes(); } @@ -1755,8 +1758,10 @@ void ReabstractionInfo::finishPartialSpecializationPreparation( } /// This constructor is used when processing @_specialize. -ReabstractionInfo::ReabstractionInfo(SILFunction *Callee, - GenericSignature SpecializedSig) { +ReabstractionInfo::ReabstractionInfo(ModuleDecl *targetModule, + bool isWholeModule, SILFunction *Callee, + GenericSignature SpecializedSig) + : TargetModule(targetModule), isWholeModule(isWholeModule) { Serialized = Callee->isSerialized(); if (shouldNotSpecialize(Callee, nullptr)) @@ -1932,10 +1937,11 @@ static void prepareCallArguments(ApplySite AI, SILBuilder &Builder, /// Return a substituted callee function type. static CanSILFunctionType -getCalleeSubstFunctionType(SILValue Callee, SubstitutionMap Subs) { +getCalleeSubstFunctionType(SILValue Callee, SubstitutionMap Subs, + TypeExpansionContext context) { // Create a substituted callee type. auto CanFnTy = Callee->getType().castTo(); - return CanFnTy->substGenericArgs(*Callee->getModule(), Subs); + return CanFnTy->substGenericArgs(*Callee->getModule(), Subs, context); } /// Create a new apply based on an old one, but with a different @@ -1956,7 +1962,8 @@ static ApplySite replaceWithSpecializedCallee(ApplySite AI, Subs = ReInfo.getCallerParamSubstitutionMap(); } - auto CalleeSubstFnTy = getCalleeSubstFunctionType(Callee, Subs); + auto CalleeSubstFnTy = + getCalleeSubstFunctionType(Callee, Subs, ReInfo.getResilienceExpansion()); auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy); SILFunctionConventions substConv(CalleeSubstFnTy, Builder.getModule()); @@ -2261,9 +2268,10 @@ static bool createPrespecialized(StringRef UnspecializedName, if (!UnspecFunc || !UnspecFunc->isDefinition()) return false; - ReabstractionInfo ReInfo(ApplySite(), UnspecFunc, Apply.getSubstitutionMap(), - IsNotSerialized, /*ConvertIndirectToDirect=*/true, - nullptr); + ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), ApplySite(), + UnspecFunc, Apply.getSubstitutionMap(), + IsNotSerialized, + /*ConvertIndirectToDirect=*/true, nullptr); if (!ReInfo.canBeSpecialized()) return false; @@ -2362,9 +2370,10 @@ void swift::trySpecializeApplyOfGeneric( Serialized = IsNotSerialized; } - ReabstractionInfo ReInfo(Apply, RefF, Apply.getSubstitutionMap(), - Serialized, /*ConvertIndirectToDirect=*/true, - &ORE); + ReabstractionInfo ReInfo(FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), Apply, RefF, + Apply.getSubstitutionMap(), Serialized, + /*ConvertIndirectToDirect=*/true, &ORE); if (!ReInfo.canBeSpecialized()) return; diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index e952b85f57dbc..fa87ac8eb62cd 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -1224,7 +1224,7 @@ bool swift::simplifyUsers(SingleValueInstruction *inst) { /// True if a type can be expanded without a significant increase to code size. bool swift::shouldExpand(SILModule &module, SILType ty) { // FIXME: Expansion - auto expansion = ResilienceExpansion::Minimal; + auto expansion = TypeExpansionContext::minimal(); if (module.Types.getTypeLowering(ty, expansion).isAddressOnly()) { return false; diff --git a/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp b/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp index 93dcad0c477b6..80992cb15ad58 100644 --- a/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp +++ b/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp @@ -31,10 +31,9 @@ removeLSLocations(LSLocationValueMap &Values, LSLocationList &NextLevel) { //===----------------------------------------------------------------------===// // LSValue //===----------------------------------------------------------------------===// -void -LSValue::expand(SILValue Base, SILModule *M, LSValueList &Vals, - TypeExpansionAnalysis *TE) { - for (const auto &P : TE->getTypeExpansion((*Base).getType(), M)) { +void LSValue::expand(SILValue Base, SILModule *M, TypeExpansionContext context, + LSValueList &Vals, TypeExpansionAnalysis *TE) { + for (const auto &P : TE->getTypeExpansion((*Base).getType(), M, context)) { Vals.push_back(LSValue(Base, P.getValue())); } } @@ -42,18 +41,20 @@ LSValue::expand(SILValue Base, SILModule *M, LSValueList &Vals, void LSValue::reduceInner(LSLocation &Base, SILModule *M, LSLocationValueMap &Values, SILInstruction *InsertPt) { + TypeExpansionContext context(*InsertPt->getFunction()); + // If this is a class reference type, we have reached end of the type tree. - if (Base.getType(M).getClassOrBoundGenericClass()) + if (Base.getType(M, context).getClassOrBoundGenericClass()) return; // This a don't expand node. - if (!shouldExpand(*M, Base.getType(M))) { + if (!shouldExpand(*M, Base.getType(M, context))) { return; } // This is a leaf node, we must have a value for it. LSLocationList NextLevel; - Base.getNextLevelLSLocations(NextLevel, M); + Base.getNextLevelLSLocations(NextLevel, M, context); if (NextLevel.empty()) return; @@ -117,11 +118,10 @@ LSValue::reduceInner(LSLocation &Base, SILModule *M, LSLocationValueMap &Values, NullablePtr AI = Projection::createAggFromFirstLevelProjections( Builder, RegularLocation::getAutoGeneratedLocation(), - Base.getType(M).getObjectType(), - Vals); + Base.getType(M, context).getObjectType(), Vals); // This is the Value for the current base. - ProjectionPath P(Base.getType(M)); + ProjectionPath P(Base.getType(M, context)); Values[Base] = LSValue(SILValue(AI.get()), P); removeLSLocations(Values, NextLevel); } @@ -163,11 +163,11 @@ LSLocation::isMayAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA) { return true; } -void -LSLocation::getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod) { - SILType Ty = getType(Mod); +void LSLocation::getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod, + TypeExpansionContext context) { + SILType Ty = getType(Mod, context); llvm::SmallVector Out; - Projection::getFirstLevelProjections(Ty, *Mod, Out); + Projection::getFirstLevelProjections(Ty, *Mod, context, Out); for (auto &X : Out) { ProjectionPath P((*Base).getType()); P.append(Path.getValue()); @@ -176,22 +176,23 @@ LSLocation::getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod) { } } -void -LSLocation::expand(LSLocation Base, SILModule *M, LSLocationList &Locs, - TypeExpansionAnalysis *TE) { +void LSLocation::expand(LSLocation Base, SILModule *M, + TypeExpansionContext context, LSLocationList &Locs, + TypeExpansionAnalysis *TE) { const ProjectionPath &BasePath = Base.getPath().getValue(); - for (const auto &P : TE->getTypeExpansion(Base.getType(M), M)) { + for (const auto &P : + TE->getTypeExpansion(Base.getType(M, context), M, context)) { Locs.push_back(LSLocation(Base.getBase(), BasePath, P.getValue())); } } /// Gets the sub-locations of \p Base in \p SubLocations. /// Returns false if this is not possible or too complex. -static bool -getSubLocations(LSLocationList &SubLocations, LSLocation Base, SILModule *M, - const LSLocationList &Locs) { +static bool getSubLocations(LSLocationList &SubLocations, LSLocation Base, + SILModule *M, TypeExpansionContext context, + const LSLocationList &Locs) { // If this is a class reference type, we have reached end of the type tree. - if (Base.getType(M).getClassOrBoundGenericClass()) + if (Base.getType(M, context).getClassOrBoundGenericClass()) return false; // Don't expand if it would be too complex. As Locs is a list (and not a set) @@ -199,27 +200,28 @@ getSubLocations(LSLocationList &SubLocations, LSLocation Base, SILModule *M, // Usually Locs is small anyway, because we limit expansion to 6 members. // But with deeply nested types we could run in a corner case where Locs is // large. - if (!shouldExpand(*M, Base.getType(M)) || Locs.size() >= 8) { + if (!shouldExpand(*M, Base.getType(M, context)) || Locs.size() >= 8) { return false; } // This is a leaf node. - Base.getNextLevelLSLocations(SubLocations, M); + Base.getNextLevelLSLocations(SubLocations, M, context); return !SubLocations.empty(); } /// Replaces \p SubLocations with \p Base in \p Locs if all sub-locations are /// alive, i.e. present in \p Locs. -static bool -replaceSubLocations(LSLocation Base, SILModule *M, LSLocationList &Locs, - const LSLocationList &SubLocations) { +static bool replaceSubLocations(LSLocation Base, SILModule *M, + TypeExpansionContext context, + LSLocationList &Locs, + const LSLocationList &SubLocations) { // Find whether all its children of Base are alive. bool Alive = true; for (auto &X : SubLocations) { // Recurse into the next level. LSLocationList NextInnerLevel; - if (getSubLocations(NextInnerLevel, X, M, Locs)) { - Alive &= replaceSubLocations(X, M, Locs, NextInnerLevel); + if (getSubLocations(NextInnerLevel, X, M, context, Locs)) { + Alive &= replaceSubLocations(X, M, context, Locs, NextInnerLevel); } else { Alive &= is_contained(Locs, X); } @@ -237,18 +239,19 @@ replaceSubLocations(LSLocation Base, SILModule *M, LSLocationList &Locs, return true; } -void LSLocation::reduce(LSLocation Base, SILModule *M, LSLocationList &Locs) { +void LSLocation::reduce(LSLocation Base, SILModule *M, + TypeExpansionContext context, LSLocationList &Locs) { LSLocationList SubLocations; - if (getSubLocations(SubLocations, Base, M, Locs)) - replaceSubLocations(Base, M, Locs, SubLocations); + if (getSubLocations(SubLocations, Base, M, context, Locs)) + replaceSubLocations(Base, M, context, Locs, SubLocations); } -void -LSLocation::enumerateLSLocation(SILModule *M, SILValue Mem, - std::vector &Locations, - LSLocationIndexMap &IndexMap, - LSLocationBaseMap &BaseMap, - TypeExpansionAnalysis *TypeCache) { +void LSLocation::enumerateLSLocation(TypeExpansionContext context, SILModule *M, + SILValue Mem, + std::vector &Locations, + LSLocationIndexMap &IndexMap, + LSLocationBaseMap &BaseMap, + TypeExpansionAnalysis *TypeCache) { // We have processed this SILValue before. if (BaseMap.find(Mem) != BaseMap.end()) return; @@ -272,7 +275,7 @@ LSLocation::enumerateLSLocation(SILModule *M, SILValue Mem, // Expand the given Mem into individual fields and add them to the // locationvault. LSLocationList Locs; - LSLocation::expand(L, M, Locs, TypeCache); + LSLocation::expand(L, M, context, Locs, TypeCache); for (auto &Loc : Locs) { if (IndexMap.find(Loc) != IndexMap.end()) continue; @@ -292,14 +295,16 @@ LSLocation::enumerateLSLocations(SILFunction &F, for (auto &B : F) { for (auto &I : B) { if (auto *LI = dyn_cast(&I)) { - enumerateLSLocation(&I.getModule(), LI->getOperand(), Locations, - IndexMap, BaseMap, TypeCache); + enumerateLSLocation(F.getTypeExpansionContext(), &I.getModule(), + LI->getOperand(), Locations, IndexMap, BaseMap, + TypeCache); ++LSCount.first; continue; } if (auto *SI = dyn_cast(&I)) { - enumerateLSLocation(&I.getModule(), SI->getDest(), Locations, - IndexMap, BaseMap, TypeCache); + enumerateLSLocation(F.getTypeExpansionContext(), &I.getModule(), + SI->getDest(), Locations, IndexMap, BaseMap, + TypeCache); ++LSCount.second; continue; } From 4cba76309f0640cd2acddf83902d0ff5c99e5b65 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 12:04:53 -0800 Subject: [PATCH 071/283] IRGen: Add TypeExpansionContext to IRGen --- lib/IRGen/GenCall.cpp | 46 ++++++++-- lib/IRGen/GenClass.cpp | 48 +++++------ lib/IRGen/GenClass.h | 25 +++--- lib/IRGen/GenEnum.cpp | 45 ++++++---- lib/IRGen/GenFunc.cpp | 7 +- lib/IRGen/GenHeap.cpp | 30 ++++--- lib/IRGen/GenKeyPath.cpp | 5 +- lib/IRGen/GenMeta.cpp | 9 +- lib/IRGen/GenObjC.cpp | 9 +- lib/IRGen/GenProto.cpp | 18 ++-- lib/IRGen/GenProto.h | 7 +- lib/IRGen/GenStruct.cpp | 15 ++-- lib/IRGen/GenThunk.cpp | 17 ++-- lib/IRGen/GenType.cpp | 26 +++--- lib/IRGen/IRGenFunction.h | 6 +- lib/IRGen/IRGenModule.cpp | 5 ++ lib/IRGen/IRGenModule.h | 5 +- lib/IRGen/IRGenSIL.cpp | 146 ++++++++++++++++++++++---------- lib/IRGen/LoadableByAddress.cpp | 4 +- lib/IRGen/MetadataRequest.cpp | 15 ++-- 20 files changed, 313 insertions(+), 175 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index bd2b25656cb41..96665c5bd7db4 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -3347,10 +3347,43 @@ Explosion NativeConventionSchema::mapIntoNative(IRGenModule &IGM, return nativeExplosion; } -void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result, +Explosion IRGenFunction::coerceValueTo(SILType fromTy, Explosion &from, + SILType toTy) { + if (fromTy == toTy) + return std::move(from); + + auto &fromTI = cast(IGM.getTypeInfo(fromTy)); + auto &toTI = cast(IGM.getTypeInfo(toTy)); + + Explosion result; + if (fromTI.getStorageType()->isPointerTy() && + toTI.getStorageType()->isPointerTy()) { + auto ptr = from.claimNext(); + ptr = Builder.CreateBitCast(ptr, toTI.getStorageType()); + result.add(ptr); + return result; + } + + auto temporary = toTI.allocateStack(*this, toTy, "coerce.temp"); + + auto addr = + Address(Builder.CreateBitCast(temporary.getAddressPointer(), + fromTI.getStorageType()->getPointerTo()), + temporary.getAlignment()); + fromTI.initialize(*this, from, addr, false); + + toTI.loadAsTake(*this, temporary.getAddress(), result); + toTI.deallocateStack(*this, temporary, toTy); + return result; +} + +void IRGenFunction::emitScalarReturn(SILType returnResultType, + SILType funcResultType, Explosion &result, bool isSwiftCCReturn, bool isOutlined) { if (result.empty()) { - assert(IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGM).empty() && + assert(IGM.getTypeInfo(returnResultType) + .nativeReturnValueSchema(IGM) + .empty() && "Empty explosion must match the native calling convention"); Builder.CreateRetVoid(); @@ -3359,12 +3392,13 @@ void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result, // In the native case no coercion is needed. if (isSwiftCCReturn) { + result = coerceValueTo(returnResultType, result, funcResultType); auto &nativeSchema = - IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGM); + IGM.getTypeInfo(funcResultType).nativeReturnValueSchema(IGM); assert(!nativeSchema.requiresIndirect()); - Explosion native = - nativeSchema.mapIntoNative(IGM, *this, result, resultType, isOutlined); + Explosion native = nativeSchema.mapIntoNative(IGM, *this, result, + funcResultType, isOutlined); if (native.size() == 1) { Builder.CreateRet(native.claimNext()); return; @@ -3391,7 +3425,7 @@ void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result, return; } - auto &resultTI = IGM.getTypeInfo(resultType); + auto &resultTI = IGM.getTypeInfo(returnResultType); auto schema = resultTI.getSchema(); auto *bodyType = schema.getScalarResultType(IGM); diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index e5a379f927d4a..9d168eb52649d 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -297,7 +297,8 @@ namespace { SILType classType, bool superclass) { for (VarDecl *var : theClass->getStoredProperties()) { - SILType type = classType.getFieldType(var, IGM.getSILModule()); + SILType type = classType.getFieldType(var, IGM.getSILModule(), + TypeExpansionContext::minimal()); // Lower the field type. auto *eltType = &IGM.getTypeInfo(type); @@ -500,23 +501,21 @@ Address IRGenFunction::emitByteOffsetGEP(llvm::Value *base, } /// Emit a field l-value by applying the given offset to the given base. -static OwnedAddress emitAddressAtOffset(IRGenFunction &IGF, - SILType baseType, - llvm::Value *base, - llvm::Value *offset, +static OwnedAddress emitAddressAtOffset(TypeExpansionContext context, + IRGenFunction &IGF, SILType baseType, + llvm::Value *base, llvm::Value *offset, VarDecl *field) { - auto &fieldTI = - IGF.getTypeInfo(baseType.getFieldType(field, IGF.getSILModule())); + auto &fieldTI = IGF.getTypeInfo( + baseType.getFieldType(field, IGF.getSILModule(), context)); auto addr = IGF.emitByteOffsetGEP(base, offset, fieldTI, base->getName() + "." + field->getName().str()); return OwnedAddress(addr, base); } -llvm::Constant * -irgen::tryEmitConstantClassFragilePhysicalMemberOffset(IRGenModule &IGM, - SILType baseType, - VarDecl *field) { - auto fieldType = baseType.getFieldType(field, IGM.getSILModule()); +llvm::Constant *irgen::tryEmitConstantClassFragilePhysicalMemberOffset( + TypeExpansionContext context, IRGenModule &IGM, SILType baseType, + VarDecl *field) { + auto fieldType = baseType.getFieldType(field, IGM.getSILModule(), context); // If the field is empty, its address doesn't matter. auto &fieldTI = IGM.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { @@ -572,11 +571,9 @@ irgen::getClassLayoutWithTailElems(IRGenModule &IGM, SILType classType, return ClassTI.createLayoutWithTailElems(IGM, classType, tailTypes); } -OwnedAddress irgen::projectPhysicalClassMemberAddress(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILType fieldType, - VarDecl *field) { +OwnedAddress irgen::projectPhysicalClassMemberAddress( + TypeExpansionContext context, IRGenFunction &IGF, llvm::Value *base, + SILType baseType, SILType fieldType, VarDecl *field) { // If the field is empty, its address doesn't matter. auto &fieldTI = IGF.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { @@ -606,13 +603,13 @@ OwnedAddress irgen::projectPhysicalClassMemberAddress(IRGenFunction &IGF, case FieldAccess::NonConstantDirect: { Address offsetA = IGF.IGM.getAddrOfFieldOffset(field, NotForDefinition); auto offset = IGF.Builder.CreateLoad(offsetA, "offset"); - return emitAddressAtOffset(IGF, baseType, base, offset, field); + return emitAddressAtOffset(context, IGF, baseType, base, offset, field); } case FieldAccess::ConstantIndirect: { auto metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType); auto offset = emitClassFieldOffset(IGF, baseClass, field, metadata); - return emitAddressAtOffset(IGF, baseType, base, offset, field); + return emitAddressAtOffset(context, IGF, baseType, base, offset, field); } } llvm_unreachable("bad field-access strategy"); @@ -2423,12 +2420,13 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, return FunctionPointer(fnPtr, signature); } -FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILDeclRef method, - CanSILFunctionType methodType, - bool useSuperVTable) { +FunctionPointer +irgen::emitVirtualMethodValue(IRGenFunction &IGF, + llvm::Value *base, + SILType baseType, + SILDeclRef method, + CanSILFunctionType methodType, + bool useSuperVTable) { // Find the metadata. llvm::Value *metadata; if (useSuperVTable) { diff --git a/lib/IRGen/GenClass.h b/lib/IRGen/GenClass.h index b7fdf7f758d87..dc800faaa519c 100644 --- a/lib/IRGen/GenClass.h +++ b/lib/IRGen/GenClass.h @@ -50,12 +50,10 @@ namespace irgen { enum class ClassDeallocationKind : unsigned char; enum class FieldAccess : uint8_t; - - OwnedAddress projectPhysicalClassMemberAddress(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILType fieldType, - VarDecl *field); + + OwnedAddress projectPhysicalClassMemberAddress( + TypeExpansionContext context, IRGenFunction &IGF, llvm::Value *base, + SILType baseType, SILType fieldType, VarDecl *field); /// Return a strategy for accessing the given stored class property. /// @@ -180,11 +178,10 @@ namespace irgen { /// Emit the constant fragile offset of the given property inside an instance /// of the class. - llvm::Constant * - tryEmitConstantClassFragilePhysicalMemberOffset(IRGenModule &IGM, - SILType baseType, - VarDecl *field); - + llvm::Constant *tryEmitConstantClassFragilePhysicalMemberOffset( + TypeExpansionContext context, IRGenModule &IGM, SILType baseType, + VarDecl *field); + FieldAccess getClassFieldAccess(IRGenModule &IGM, SILType baseType, VarDecl *field); @@ -208,10 +205,8 @@ namespace irgen { /// Given an instance pointer (or, for a static method, a class /// pointer), emit the callee for the given method. - FunctionPointer emitVirtualMethodValue(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILDeclRef method, + FunctionPointer emitVirtualMethodValue(IRGenFunction &IGF, llvm::Value *base, + SILType baseType, SILDeclRef method, CanSILFunctionType methodType, bool useSuperVTable); diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index 674a22ab0c052..c6e07d16b6c57 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -315,9 +315,10 @@ namespace { SILType getSingletonType(IRGenModule &IGM, SILType T) const { assert(!ElementsWithPayload.empty()); - + return T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } public: @@ -614,8 +615,9 @@ namespace { assert(ElementsWithPayload.size() == 1 && "empty singleton enum should not be dynamic!"); - auto payloadTy = T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + auto payloadTy = T.getEnumElementType( + ElementsWithPayload[0].decl, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); auto payloadLayout = emitTypeLayoutRef(IGF, payloadTy, collector); auto flags = emitEnumLayoutFlags(IGF.IGM, isVWTMutable); IGF.Builder.CreateCall( @@ -1567,7 +1569,8 @@ namespace { SILType getPayloadType(IRGenModule &IGM, SILType T) const { return T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } const TypeInfo &getPayloadTypeInfo() const { @@ -2957,8 +2960,9 @@ namespace { // Ask the runtime to do our layout using the payload metadata and number // of empty cases. - auto payloadTy = T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + auto payloadTy = + T.getEnumElementType(ElementsWithPayload[0].decl, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); auto payloadLayout = emitTypeLayoutRef(IGF, payloadTy, collector); auto emptyCasesVal = llvm::ConstantInt::get(IGM.Int32Ty, ElementsWithNoPayload.size()); @@ -4575,8 +4579,9 @@ namespace { unsigned tagIndex = 0; for (auto &payloadCasePair : ElementsWithPayload) { - SILType PayloadT = T.getEnumElementType(payloadCasePair.decl, - IGF.getSILModule()); + SILType PayloadT = + T.getEnumElementType(payloadCasePair.decl, IGF.getSILModule(), + IGF.IGM.getMaximalTypeExpansionContext()); auto &payloadTI = *payloadCasePair.ti; // Trivial and, in the case of a take, bitwise-takable payloads, // can all share the default path. @@ -4692,9 +4697,9 @@ namespace { } for (auto &payloadCasePair : ElementsWithPayload) { - SILType payloadT = - T.getEnumElementType(payloadCasePair.decl, - collector.IGF.getSILModule()); + SILType payloadT = T.getEnumElementType( + payloadCasePair.decl, collector.IGF.getSILModule(), + collector.IGF.IGM.getMaximalTypeExpansionContext()); auto &payloadTI = *payloadCasePair.ti; payloadTI.collectMetadataForOutlining(collector, payloadT); } @@ -4737,8 +4742,9 @@ namespace { // Destroy the data. Address dataAddr = IGF.Builder.CreateBitCast( addr, elt.ti->getStorageType()->getPointerTo()); - SILType payloadT = - T.getEnumElementType(elt.decl, IGF.getSILModule()); + SILType payloadT = T.getEnumElementType( + elt.decl, IGF.getSILModule(), + IGF.IGM.getMaximalTypeExpansionContext()); elt.ti->destroy(IGF, dataAddr, payloadT, true /*isOutlined*/); }); return; @@ -4977,9 +4983,11 @@ namespace { Address eltAddr = IGF.Builder.CreateStructGEP(metadataBuffer, i, IGM.getPointerSize() * i); if (i == 0) firstAddr = eltAddr.getAddress(); - - auto payloadTy = T.getEnumElementType(elt.decl, IGF.getSILModule()); - + + auto payloadTy = + T.getEnumElementType(elt.decl, IGF.getSILModule(), + IGF.IGM.getMaximalTypeExpansionContext()); + auto metadata = emitTypeLayoutRef(IGF, payloadTy, collector); IGF.Builder.CreateStore(metadata, eltAddr); @@ -5809,7 +5817,8 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) { // *Now* apply the substitutions and get the type info for the instance's // payload type, since we know this case carries an apparent payload in // the generic case. - SILType fieldTy = type.getEnumElementType(elt, TC.IGM.getSILModule()); + SILType fieldTy = type.getEnumElementType( + elt, TC.IGM.getSILModule(), TC.IGM.getMaximalTypeExpansionContext()); auto *substArgTI = &TC.IGM.getTypeInfo(fieldTy); elementsWithPayload.push_back({elt, substArgTI, origArgTI}); diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index 1a411ef85c5f6..c2b9f730bddf0 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -1138,8 +1138,11 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, FunctionPointer fnPtr = [&]() -> FunctionPointer { // If we found a function pointer statically, great. if (staticFnPtr) { - assert(staticFnPtr->getPointer()->getType() == fnTy && - "static function type mismatch?!"); + if (staticFnPtr->getPointer()->getType() != fnTy) { + auto fnPtr = staticFnPtr->getPointer(); + fnPtr = subIGF.Builder.CreateBitCast(fnPtr, fnTy); + return FunctionPointer(fnPtr, origSig); + } return *staticFnPtr; } diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index d3dc107dd68e5..9bb4ecbd0ccbb 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -1528,8 +1528,8 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) { // TODO: Multi-field boxes assert(T->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - auto &eltTI = IGM.getTypeInfoForLowered( - getSILBoxFieldLoweredType(T, IGM.getSILModule().Types, 0)); + auto &eltTI = IGM.getTypeInfoForLowered(getSILBoxFieldLoweredType( + IGM.getMaximalTypeExpansionContext(), T, IGM.getSILModule().Types, 0)); if (!eltTI.isFixedSize()) { if (!NonFixedBoxTI) NonFixedBoxTI = new NonFixedBoxTypeInfo(IGM); @@ -1577,8 +1577,9 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) { // Produce a tailored box metadata for the type. assert(T->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return new FixedBoxTypeInfo(IGM, - getSILBoxFieldType(T, IGM.getSILModule().Types, 0)); + return new FixedBoxTypeInfo( + IGM, getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(), + T, IGM.getSILModule().Types, 0)); } OwnedAddress @@ -1588,9 +1589,12 @@ irgen::emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType, auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); assert(boxType->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return boxTI.allocate(IGF, - getSILBoxFieldType(boxType, IGF.IGM.getSILModule().Types, 0), - env, name); + return boxTI.allocate( + IGF, + getSILBoxFieldType( + IGF.IGM.getMaximalTypeExpansionContext(), + boxType, IGF.IGM.getSILModule().Types, 0), + env, name); } void irgen::emitDeallocateBox(IRGenFunction &IGF, @@ -1599,8 +1603,10 @@ void irgen::emitDeallocateBox(IRGenFunction &IGF, auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); assert(boxType->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return boxTI.deallocate(IGF, box, - getSILBoxFieldType(boxType, IGF.IGM.getSILModule().Types, 0)); + return boxTI.deallocate( + IGF, box, + getSILBoxFieldType(IGF.IGM.getMaximalTypeExpansionContext(), boxType, + IGF.IGM.getSILModule().Types, 0)); } Address irgen::emitProjectBox(IRGenFunction &IGF, @@ -1609,8 +1615,10 @@ Address irgen::emitProjectBox(IRGenFunction &IGF, auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); assert(boxType->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return boxTI.project(IGF, box, - getSILBoxFieldType(boxType, IGF.IGM.getSILModule().Types, 0)); + return boxTI.project( + IGF, box, + getSILBoxFieldType(IGF.IGM.getMaximalTypeExpansionContext(), boxType, + IGF.IGM.getSILModule().Types, 0)); } Address irgen::emitAllocateExistentialBoxInBuffer( diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 51b30b5c00c11..5dda8dbbc0652 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -790,9 +790,8 @@ emitKeyPathComponent(IRGenModule &IGM, switch (getClassFieldAccess(IGM, loweredBaseContextTy, property)) { case FieldAccess::ConstantDirect: { // Known constant fixed offset. - auto offset = tryEmitConstantClassFragilePhysicalMemberOffset(IGM, - loweredClassTy, - property); + auto offset = tryEmitConstantClassFragilePhysicalMemberOffset( + TypeExpansionContext::minimal(), IGM, loweredClassTy, property); assert(offset && "no constant offset for ConstantDirect field?!"); addFixedOffset(/*struct*/ false, property->isLet(), offset); break; diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 2f1e1578f6059..c446dbe77570e 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1939,7 +1939,8 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF, unsigned index = 0; for (auto prop : storedProperties) { - auto propTy = T.getFieldType(prop, IGF.getSILModule()); + auto propTy = T.getFieldType(prop, IGF.getSILModule(), + TypeExpansionContext::minimal()); llvm::Value *metadata = emitTypeLayoutRef(IGF, propTy, collector); Address field = IGF.Builder.CreateConstArrayGEP(fields, index, IGM.getPointerSize()); @@ -3606,8 +3607,10 @@ namespace { B.add(offset); return; } - assert(IGM.getTypeInfo(Type.getFieldType(field, IGM.getSILModule())) - .isKnownEmpty(ResilienceExpansion::Maximal)); + assert(IGM.getTypeInfo( + Type.getFieldType(field, IGM.getSILModule(), + TypeExpansionContext::minimal())) + .isKnownEmpty(ResilienceExpansion::Maximal)); B.addInt32(0); } diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index e4b3d5f288241..bb65387cfb82e 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -858,8 +858,8 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, auto &callee = emission.getCallee(); auto resultType = callee.getOrigFunctionType()->getDirectFormalResultsType(IGM.getSILModule()); - subIGF.emitScalarReturn(resultType, result, true /*isSwiftCCReturn*/, - false); + subIGF.emitScalarReturn(resultType, resultType, result, + true /*isSwiftCCReturn*/, false); } return fwd; @@ -1018,8 +1018,9 @@ static SILDeclRef getObjCMethodRef(AbstractFunctionDecl *method) { } static CanSILFunctionType getObjCMethodType(IRGenModule &IGM, - AbstractFunctionDecl *method) { - return IGM.getSILTypes().getConstantFunctionType(getObjCMethodRef(method)); + AbstractFunctionDecl *method) { + return IGM.getSILTypes().getConstantFunctionType( + TypeExpansionContext::minimal(), getObjCMethodRef(method)); } static clang::CanQualType getObjCPropertyType(IRGenModule &IGM, diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 278ef516daa69..3f62597afd6be 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3186,7 +3186,8 @@ void irgen::expandTrailingWitnessSignature(IRGenModule &IGM, } FunctionPointer -irgen::emitWitnessMethodValue(IRGenFunction &IGF, +irgen::emitWitnessMethodValue(TypeExpansionContext expansionContext, + IRGenFunction &IGF, llvm::Value *wtable, SILDeclRef member) { auto *fn = cast(member.getDecl()); @@ -3201,7 +3202,8 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF, emitInvariantLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable()); - auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType(member); + auto fnType = + IGF.IGM.getSILTypes().getConstantFunctionType(expansionContext, member); Signature signature = IGF.IGM.getSignature(fnType); witnessFnPtr = IGF.Builder.CreateBitCast(witnessFnPtr, signature.getType()->getPointerTo()); @@ -3209,16 +3211,14 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF, return FunctionPointer(witnessFnPtr, signature); } -FunctionPointer -irgen::emitWitnessMethodValue(IRGenFunction &IGF, - CanType baseTy, - llvm::Value **baseMetadataCache, - SILDeclRef member, - ProtocolConformanceRef conformance) { +FunctionPointer irgen::emitWitnessMethodValue( + TypeExpansionContext expansionContext, IRGenFunction &IGF, CanType baseTy, + llvm::Value **baseMetadataCache, SILDeclRef member, + ProtocolConformanceRef conformance) { llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache, conformance); - return emitWitnessMethodValue(IGF, wtable, member); + return emitWitnessMethodValue(expansionContext, IGF, wtable, member); } llvm::Value *irgen::computeResilientWitnessTableIndex( diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index 7d602cc80b291..1b454091be05e 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -58,14 +58,15 @@ namespace irgen { /// Extract the method pointer from the given witness table /// as a function value. - FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, + FunctionPointer emitWitnessMethodValue(TypeExpansionContext context, + IRGenFunction &IGF, llvm::Value *wtable, SILDeclRef member); /// Extract the method pointer from an archetype's witness table /// as a function value. - FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, - CanType baseTy, + FunctionPointer emitWitnessMethodValue(TypeExpansionContext context, + IRGenFunction &IGF, CanType baseTy, llvm::Value **baseMetadataCache, SILDeclRef member, ProtocolConformanceRef conformance); diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index bd273beda33aa..c6eceff88c938 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -74,7 +74,8 @@ namespace { } SILType getType(IRGenModule &IGM, SILType T) const { - return T.getFieldType(Field, IGM.getSILModule()); + return T.getFieldType(Field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } }; @@ -95,7 +96,8 @@ namespace { SILType getType(IRGenModule &IGM, SILType T) const { if (Field) - return T.getFieldType(Field, IGM.getSILModule()); + return T.getFieldType(Field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); // The Swift-field-less cases use opaque storage, which is // guaranteed to ignore the type passed to it. @@ -592,7 +594,9 @@ namespace { SILType getType(VarDecl *field) { assert(field->getDeclContext() == TheStruct->getAnyNominal()); auto silType = SILType::getPrimitiveAddressType(TheStruct); - return silType.getFieldType(field, IGM.getSILModule()); + return silType.getFieldType( + field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } StructLayout performLayout(ArrayRef fieldTypes) { @@ -733,8 +737,9 @@ class ClangRecordLowering { // If we have a Swift import of this type, use our lowered information. if (swiftField) { - auto &fieldTI = cast( - IGM.getTypeInfo(SwiftType.getFieldType(swiftField, IGM.getSILModule()))); + auto &fieldTI = cast(IGM.getTypeInfo( + SwiftType.getFieldType(swiftField, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()))); addField(swiftField, offset, fieldTI); return; } diff --git a/lib/IRGen/GenThunk.cpp b/lib/IRGen/GenThunk.cpp index cdc9ffa7c8e84..5e2d7791b5fdb 100644 --- a/lib/IRGen/GenThunk.cpp +++ b/lib/IRGen/GenThunk.cpp @@ -36,7 +36,8 @@ using namespace irgen; /// Find the entry point for a method dispatch thunk. llvm::Function * -IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef, +IRGenModule::getAddrOfDispatchThunk(TypeExpansionContext context, + SILDeclRef declRef, ForDefinition_t forDefinition) { LinkEntity entity = LinkEntity::forDispatchThunk(declRef); @@ -46,7 +47,7 @@ IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef, return entry; } - auto fnType = getSILModule().Types.getConstantFunctionType(declRef); + auto fnType = getSILModule().Types.getConstantFunctionType(context, declRef); Signature signature = getSignature(fnType); LinkInfo link = LinkInfo::get(*this, entity, forDefinition); @@ -54,8 +55,8 @@ IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef, return entry; } -static FunctionPointer lookupMethod(IRGenFunction &IGF, - SILDeclRef declRef) { +static FunctionPointer lookupMethod(IRGenFunction &IGF, SILDeclRef declRef) { + auto expansionContext = TypeExpansionContext::minimal(); auto *decl = cast(declRef.getDecl()); // Protocol case. @@ -64,11 +65,12 @@ static FunctionPointer lookupMethod(IRGenFunction &IGF, llvm::Value *wtable = (IGF.CurFn->arg_end() - 1); // Find the witness we're interested in. - return emitWitnessMethodValue(IGF, wtable, declRef); + return emitWitnessMethodValue(expansionContext, IGF, wtable, declRef); } // Class case. - auto funcTy = IGF.IGM.getSILModule().Types.getConstantFunctionType(declRef); + auto funcTy = IGF.IGM.getSILModule().Types.getConstantFunctionType( + expansionContext, declRef); // Load the metadata, or use the 'self' value if we have a static method. llvm::Value *self; @@ -97,7 +99,8 @@ static FunctionPointer lookupMethod(IRGenFunction &IGF, } void IRGenModule::emitDispatchThunk(SILDeclRef declRef) { - auto *f = getAddrOfDispatchThunk(declRef, ForDefinition); + auto *f = getAddrOfDispatchThunk(TypeExpansionContext::minimal(), declRef, + ForDefinition); IRGenFunction IGF(*this, f); diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 4ba497af06556..6798b24bb23dc 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1466,25 +1466,25 @@ const TypeInfo &IRGenFunction::getTypeInfo(SILType T) { /// Return the SIL-lowering of the given type. SILType IRGenModule::getLoweredType(AbstractionPattern orig, Type subst) const { - return getSILTypes().getLoweredType(orig, subst, - ResilienceExpansion::Maximal); + return getSILTypes().getLoweredType( + orig, subst, TypeExpansionContext::maximalResilienceExpansionOnly()); } /// Return the SIL-lowering of the given type. SILType IRGenModule::getLoweredType(Type subst) const { - return getSILTypes().getLoweredType(subst, - ResilienceExpansion::Maximal); + return getSILTypes().getLoweredType( + subst, TypeExpansionContext::maximalResilienceExpansionOnly()); } /// Return the SIL-lowering of the given type. const Lowering::TypeLowering &IRGenModule::getTypeLowering(SILType type) const { - return getSILTypes().getTypeLowering(type, - ResilienceExpansion::Maximal); + return getSILTypes().getTypeLowering( + type, TypeExpansionContext::maximalResilienceExpansionOnly()); } bool IRGenModule::isTypeABIAccessible(SILType type) const { - return getSILModule().isTypeABIAccessible(type, - ResilienceExpansion::Maximal); + return getSILModule().isTypeABIAccessible( + type, TypeExpansionContext::maximalResilienceExpansionOnly()); } /// Get a pointer to the storage type for the given type. Note that, @@ -2307,7 +2307,10 @@ SILType irgen::getSingletonAggregateFieldType(IRGenModule &IGM, SILType t, auto allFields = structDecl->getStoredProperties(); if (allFields.size() == 1) { - auto fieldTy = t.getFieldType(allFields[0], IGM.getSILModule()); + auto fieldTy = t.getFieldType( + allFields[0], IGM.getSILModule(), + TypeExpansionContext(expansion, IGM.getSwiftModule(), + IGM.getSILModule().isWholeModule())); if (!IGM.isTypeABIAccessible(fieldTy)) return SILType(); return fieldTy; @@ -2327,7 +2330,10 @@ SILType irgen::getSingletonAggregateFieldType(IRGenModule &IGM, SILType t, auto theCase = allCases.begin(); if (!allCases.empty() && std::next(theCase) == allCases.end() && (*theCase)->hasAssociatedValues()) { - auto enumEltTy = t.getEnumElementType(*theCase, IGM.getSILModule()); + auto enumEltTy = t.getEnumElementType( + *theCase, IGM.getSILModule(), + TypeExpansionContext(expansion, IGM.getSwiftModule(), + IGM.getSILModule().isWholeModule())); if (!IGM.isTypeABIAccessible(enumEltTy)) return SILType(); return enumEltTy; diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 3ef32d7c19674..0a1affaf92acb 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -92,8 +92,9 @@ class IRGenFunction { //--- Function prologue and epilogue ------------------------------------------- public: Explosion collectParameters(); - void emitScalarReturn(SILType resultTy, Explosion &scalars, - bool isSwiftCCReturn, bool isOutlined); + void emitScalarReturn(SILType returnResultType, SILType funcResultType, + Explosion &scalars, bool isSwiftCCReturn, + bool isOutlined); void emitScalarReturn(llvm::Type *resultTy, Explosion &scalars); void emitBBForReturn(); @@ -280,6 +281,7 @@ class IRGenFunction { const SILDebugScope *getDebugScope() const { return DbgScope; } llvm::Value *coerceValue(llvm::Value *value, llvm::Type *toTy, const llvm::DataLayout &); + Explosion coerceValueTo(SILType fromTy, Explosion &from, SILType toTy); /// Mark a load as invariant. void setInvariantLoad(llvm::LoadInst *load); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 80af85aff9456..90037d14bbbfc 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1376,3 +1376,8 @@ const llvm::DataLayout &IRGenerator::getClangDataLayout() { ->getTargetInfo() .getDataLayout(); } + +TypeExpansionContext IRGenModule::getMaximalTypeExpansionContext() const { + return TypeExpansionContext::maximal(getSwiftModule(), + getSILModule().isWholeModule()); +} diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 17fd016ed8798..497c8416c4aae 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -837,6 +837,8 @@ class IRGenModule { ResilienceExpansion getResilienceExpansionForLayout(NominalTypeDecl *decl); ResilienceExpansion getResilienceExpansionForLayout(SILGlobalVariable *var); + TypeExpansionContext getMaximalTypeExpansionContext() const; + bool isResilientConformance(const NormalProtocolConformance *conformance); bool isResilientConformance(const RootProtocolConformance *root); bool isDependentConformance(const RootProtocolConformance *conformance); @@ -1254,7 +1256,8 @@ private: \ /// Cast the given constant to i8*. llvm::Constant *getOpaquePtr(llvm::Constant *pointer); - llvm::Function *getAddrOfDispatchThunk(SILDeclRef declRef, + llvm::Function *getAddrOfDispatchThunk(TypeExpansionContext context, + SILDeclRef declRef, ForDefinition_t forDefinition); void emitDispatchThunk(SILDeclRef declRef); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 119a0d9202cf0..b69d8be804e1a 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -622,6 +622,16 @@ class IRGenSILFunction : return foundBB->second; } + TypeExpansionContext getExpansionContext() { + return TypeExpansionContext(*CurSILFn); + } + + SILType getLoweredTypeInContext(SILType ty) { + return CurSILFn->getModule() + .Types.getLoweredType(ty.getASTType(), getExpansionContext()) + .getCategoryType(ty.getCategory()); + } + StringRef getOrCreateAnonymousVarName(VarDecl *Decl) { llvm::SmallString<4> &Name = AnonymousVariables[Decl]; if (Name.empty()) { @@ -1343,41 +1353,72 @@ static ArrayRef emitEntryPointIndirectReturn( SILType directResultType = IGF.CurSILFn->mapTypeIntoContext(fnConv.getSILResultType()); if (requiresIndirectResult(directResultType)) { - auto &retTI = IGF.IGM.getTypeInfo(directResultType); - IGF.IndirectReturn = retTI.getAddressForPointer(params.claimNext()); + auto ¶mTI = IGF.IGM.getTypeInfo(directResultType); + auto &retTI = + IGF.IGM.getTypeInfo(IGF.getLoweredTypeInContext(directResultType)); + auto ptr = params.claimNext(); + if (paramTI.getStorageType() != retTI.getStorageType()) { + assert(directResultType.getASTType()->hasOpaqueArchetype()); + ptr = IGF.Builder.CreateBitCast(ptr, + retTI.getStorageType()->getPointerTo()); + } + IGF.IndirectReturn = retTI.getAddressForPointer(ptr); } auto bbargs = entry->getArguments(); // Map the indirect returns if present. unsigned numIndirectResults = fnConv.getNumIndirectSILResults(); - for (unsigned i = 0; i != numIndirectResults; ++i) { - SILArgument *ret = bbargs[i]; - auto &retTI = IGF.IGM.getTypeInfo(ret->getType()); - IGF.setLoweredAddress(ret, retTI.getAddressForPointer(params.claimNext())); - } + unsigned idx = 0; + for (auto indirectResultType : fnConv.getIndirectSILResultTypes()) { + SILArgument *ret = bbargs[idx]; + auto inContextResultType = + IGF.CurSILFn->mapTypeIntoContext(indirectResultType); + auto &retTI = IGF.IGM.getTypeInfo(ret->getType()); + // The parameter's type might be different due to looking through opaque + // archetypes. + auto ptr = params.claimNext(); + auto ¶mTI = IGF.IGM.getTypeInfo(inContextResultType); + if (paramTI.getStorageType() != retTI.getStorageType()) { + assert(inContextResultType.getASTType()->hasOpaqueArchetype()); + ptr = IGF.Builder.CreateBitCast(ptr, + retTI.getStorageType()->getPointerTo()); + } + auto addr = retTI.getAddressForPointer(ptr); + IGF.setLoweredAddress(ret, addr); + ++idx; + } + assert(numIndirectResults == idx); return bbargs.slice(numIndirectResults); } static void bindParameter(IRGenSILFunction &IGF, SILArgument *param, + SILType paramTy, Explosion &allParamValues) { // Pull out the parameter value and its formal type. - auto ¶mTI = IGF.getTypeInfo(param->getType()); + auto ¶mTI = IGF.getTypeInfo(IGF.CurSILFn->mapTypeIntoContext(paramTy)); + auto &argTI = IGF.getTypeInfo(param->getType()); // If the SIL parameter isn't passed indirectly, we need to map it // to an explosion. if (param->getType().isObject()) { Explosion paramValues; - auto &loadableTI = cast(paramTI); + auto &loadableParamTI = cast(paramTI); + auto &loadableArgTI = cast(paramTI); // If the explosion must be passed indirectly, load the value from the // indirect address. - auto &nativeSchema = paramTI.nativeParameterValueSchema(IGF.IGM); + auto &nativeSchema = argTI.nativeParameterValueSchema(IGF.IGM); if (nativeSchema.requiresIndirect()) { - Address paramAddr - = loadableTI.getAddressForPointer(allParamValues.claimNext()); - loadableTI.loadAsTake(IGF, paramAddr, paramValues); + Address paramAddr = + loadableParamTI.getAddressForPointer(allParamValues.claimNext()); + if (paramTI.getStorageType() != argTI.getStorageType()) + paramAddr = + loadableArgTI.getAddressForPointer(IGF.Builder.CreateBitCast( + paramAddr.getAddress(), + loadableArgTI.getStorageType()->getPointerTo())); + loadableArgTI.loadAsTake(IGF, paramAddr, paramValues); } else { if (!nativeSchema.empty()) { // Otherwise, we map from the native convention to the type's explosion @@ -1399,8 +1440,12 @@ static void bindParameter(IRGenSILFunction &IGF, // FIXME: that doesn't mean we should physically pass it // indirectly at this resilience expansion. An @in or @in_guaranteed parameter // could be passed by value in the right resilience domain. - Address paramAddr - = paramTI.getAddressForPointer(allParamValues.claimNext()); + auto ptr = allParamValues.claimNext(); + if (paramTI.getStorageType() != argTI.getStorageType()) { + ptr = + IGF.Builder.CreateBitCast(ptr, argTI.getStorageType()->getPointerTo()); + } + Address paramAddr = argTI.getAddressForPointer(ptr); IGF.setLoweredAddress(param, paramAddr); } @@ -1430,7 +1475,6 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, if (funcTy->hasErrorResult()) { IGF.setErrorResultSlot(allParamValues.takeLast()); } - // The coroutine context should be the first parameter. switch (funcTy->getCoroutineKind()) { case SILCoroutineKind::None: @@ -1443,6 +1487,8 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, break; } + SILFunctionConventions conv(funcTy, IGF.getSILModule()); + // The 'self' argument might be in the context position, which is // now the end of the parameter list. Bind it now. if (hasSelfContextParameter(funcTy)) { @@ -1451,10 +1497,12 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, Explosion selfTemp; selfTemp.add(allParamValues.takeLast()); - bindParameter(IGF, selfParam, selfTemp); + bindParameter(IGF, selfParam, + conv.getSILArgumentType(conv.getNumSILArguments() - 1), + selfTemp); - // Even if we don't have a 'self', if we have an error result, we - // should have a placeholder argument here. + // Even if we don't have a 'self', if we have an error result, we + // should have a placeholder argument here. } else if (funcTy->hasErrorResult() || funcTy->getRepresentation() == SILFunctionTypeRepresentation::Thick) { @@ -1463,8 +1511,11 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, } // Map the remaining SIL parameters to LLVM parameters. + unsigned i = 0; for (SILArgument *param : params) { - bindParameter(IGF, param, allParamValues); + auto argIdx = conv.getSILArgIndexOfFirstParam() + i; + bindParameter(IGF, param, conv.getSILArgumentType(argIdx), allParamValues); + ++i; } // Bind polymorphic arguments. This can only be done after binding @@ -1917,7 +1968,7 @@ void IRGenSILFunction::visitAllocGlobalInst(AllocGlobalInst *i) { void IRGenSILFunction::visitGlobalAddrInst(GlobalAddrInst *i) { SILGlobalVariable *var = i->getReferencedGlobal(); - SILType loweredTy = var->getLoweredType(); + SILType loweredTy = var->getLoweredTypeInContext(getExpansionContext()); assert(loweredTy == i->getType().getObjectType()); auto &ti = getTypeInfo(loweredTy); @@ -2643,7 +2694,12 @@ static void emitReturnInst(IRGenSILFunction &IGF, auto swiftCCReturn = funcLang == SILFunctionLanguage::Swift; assert(swiftCCReturn || funcLang == SILFunctionLanguage::C && "Need to handle all cases"); - IGF.emitScalarReturn(resultTy, result, swiftCCReturn, false); + SILFunctionConventions conv(IGF.CurSILFn->getLoweredFunctionType(), + IGF.getSILModule()); + auto funcResultType = + IGF.CurSILFn->mapTypeIntoContext(conv.getSILResultType()); + IGF.emitScalarReturn(resultTy, funcResultType, result, swiftCCReturn, + false); } } @@ -3567,12 +3623,10 @@ void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { llvm::Value *value = base.claimNext(); SILType baseTy = i->getOperand()->getType(); - Address field = projectPhysicalClassMemberAddress(*this, - value, - baseTy, - i->getType(), - i->getField()) - .getAddress(); + Address field = projectPhysicalClassMemberAddress( + TypeExpansionContext(*i->getFunction()), *this, value, + baseTy, i->getType(), i->getField()) + .getAddress(); setLoweredAddress(i, field); } @@ -4194,7 +4248,8 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { assert(i->getBoxType()->getLayout()->getFields().size() == 1 && "multi field boxes not implemented yet"); const TypeInfo &type = getTypeInfo( - getSILBoxFieldType(i->getBoxType(), IGM.getSILModule().Types, 0)); + getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(), i->getBoxType(), + IGM.getSILModule().Types, 0)); // Derive name from SIL location. bool IsAnonymous = false; @@ -4232,7 +4287,9 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { assert(i->getBoxType()->getLayout()->getFields().size() == 1 && "box for a local variable should only have one field"); - auto SILTy = getSILBoxFieldType(i->getBoxType(), IGM.getSILModule().Types, 0); + auto SILTy = getSILBoxFieldType( + IGM.getMaximalTypeExpansionContext(), + i->getBoxType(), IGM.getSILModule().Types, 0); auto RealType = SILTy.getASTType(); auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type); @@ -5411,8 +5468,10 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) { if (IGM.isResilient(conformance.getRequirement(), ResilienceExpansion::Maximal)) { - auto *fnPtr = IGM.getAddrOfDispatchThunk(member, NotForDefinition); - auto fnType = IGM.getSILTypes().getConstantFunctionType(member); + auto *fnPtr = IGM.getAddrOfDispatchThunk( + IGM.getMaximalTypeExpansionContext(), member, NotForDefinition); + auto fnType = IGM.getSILTypes().getConstantFunctionType( + IGM.getMaximalTypeExpansionContext(), member); auto sig = IGM.getSignature(fnType); auto fn = FunctionPointer::forDirect(fnPtr, sig); @@ -5423,9 +5482,10 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) { // It would be nice if this weren't discarded. llvm::Value *baseMetadataCache = nullptr; - auto fn = emitWitnessMethodValue(*this, baseTy, &baseMetadataCache, - member, conformance); - + auto fn = + emitWitnessMethodValue(TypeExpansionContext(*i->getFunction()), *this, + baseTy, &baseMetadataCache, member, conformance); + setLoweredFunctionPointer(i, fn); } @@ -5582,9 +5642,9 @@ void IRGenSILFunction::visitSuperMethodInst(swift::SuperMethodInst *i) { // Non-resilient case. - auto fn = emitVirtualMethodValue(*this, baseValue, baseType, - method, methodType, - /*useSuperVTable*/ true); + auto fn = + emitVirtualMethodValue(*this, baseValue, baseType, method, methodType, + /*useSuperVTable*/ true); setLoweredFunctionPointer(i, fn); } @@ -5608,7 +5668,8 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { auto *classDecl = cast(method.getDecl()->getDeclContext()); if (IGM.hasResilientMetadata(classDecl, ResilienceExpansion::Maximal)) { - auto *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition); + auto *fnPtr = IGM.getAddrOfDispatchThunk(TypeExpansionContext(*CurSILFn), + method, NotForDefinition); auto sig = IGM.getSignature(methodType); FunctionPointer fn(fnPtr, sig); @@ -5618,10 +5679,9 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { // For Swift classes, get the method implementation from the vtable. // FIXME: better explosion kind, map as static. - FunctionPointer fn = emitVirtualMethodValue(*this, baseValue, - i->getOperand()->getType(), - method, methodType, - /*useSuperVTable*/ false); + FunctionPointer fn = emitVirtualMethodValue( + *this, baseValue, i->getOperand()->getType(), method, methodType, + /*useSuperVTable*/ false); setLoweredFunctionPointer(i, fn); } diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 73c451dc940fa..85698e126a4dd 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -2592,7 +2592,7 @@ bool LoadableByAddress::recreateUncheckedEnumDataInstr( GenericEnvironment *genEnv = F->getGenericEnvironment(); SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( - enumInstr->getElement(), F->getModule()); + enumInstr->getElement(), F->getModule(), TypeExpansionContext(*F)); SingleValueInstruction *newInstr = nullptr; if (newType.isAddress()) { newType = newType.getObjectType(); @@ -2625,7 +2625,7 @@ bool LoadableByAddress::recreateUncheckedTakeEnumDataAddrInst( GenericEnvironment *genEnv = F->getGenericEnvironment(); SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( - enumInstr->getElement(), F->getModule()); + enumInstr->getElement(), F->getModule(), TypeExpansionContext(*F)); SingleValueInstruction *newInstr = nullptr; if (caseTy != origType.getObjectType()) { auto *takeEnum = enumBuilder.createUncheckedTakeEnumDataAddr( diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index ed9c5bdfff275..e2adea5d371d0 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -516,8 +516,9 @@ CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) { // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(getSwiftModule(), - ResilienceExpansion::Maximal); + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + getSwiftModule(), ResilienceExpansion::Maximal, + getSILModule().isWholeModule()); auto underlyingTy = type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes) ->getCanonicalType(); @@ -532,8 +533,9 @@ SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes( // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type.getASTType()->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(getSwiftModule(), - ResilienceExpansion::Maximal); + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + getSwiftModule(), ResilienceExpansion::Maximal, + getSILModule().isWholeModule()); auto underlyingTy = type.subst(getSILModule(), replacer, replacer, genericSig, /*substitute opaque*/ true); @@ -549,8 +551,9 @@ IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type, // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(getSwiftModule(), - ResilienceExpansion::Maximal); + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + getSwiftModule(), ResilienceExpansion::Maximal, + getSILModule().isWholeModule()); auto substConformance = conformance.subst( type, replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); auto underlyingTy = From 4f300bbfee215d690bf50699257c5597e033a836 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Mon, 4 Nov 2019 12:05:10 -0800 Subject: [PATCH 072/283] Update tests --- test/IRGen/lazy_opaque_result_type.swift | 2 +- test/IRGen/opaque_result_type_debug.swift | 4 +- .../opaque_result_type_substitution.swift | 11 +- test/IRGen/partial_apply.sil | 9 +- .../opaque_return_type_serialize.sil | 4 +- test/SILGen/opaque_result_type.swift | 133 ++++++++++++++++-- test/SILOptimizer/cast_folding.swift | 3 +- .../specialize_opaque_type_archetypes.swift | 54 +++---- ...ize_opaque_type_archetypes_multifile.swift | 2 +- 9 files changed, 164 insertions(+), 58 deletions(-) diff --git a/test/IRGen/lazy_opaque_result_type.swift b/test/IRGen/lazy_opaque_result_type.swift index 382ad927ec039..d526623ebff0b 100644 --- a/test/IRGen/lazy_opaque_result_type.swift +++ b/test/IRGen/lazy_opaque_result_type.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-implicit-dynamic -disable-availability-checking -Xllvm -sil-disable-pass=OpaqueArchetypeSpecializer -parse-as-library -module-name=test -O -primary-file %s -emit-ir > %t.ll +// RUN: %target-swift-frontend -enable-implicit-dynamic -disable-availability-checking -parse-as-library -module-name=test -O -primary-file %s -emit-ir > %t.ll // RUN: %FileCheck %s < %t.ll protocol P { } diff --git a/test/IRGen/opaque_result_type_debug.swift b/test/IRGen/opaque_result_type_debug.swift index c4e26d4d201db..a5b0de69c28f4 100644 --- a/test/IRGen/opaque_result_type_debug.swift +++ b/test/IRGen/opaque_result_type_debug.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-availability-checking -g -emit-ir -enable-anonymous-context-mangled-names %s | %FileCheck %s +// RUN: %target-swift-frontend -enable-library-evolution -disable-availability-checking -g -emit-ir -enable-anonymous-context-mangled-names %s | %FileCheck %s public protocol P {} extension Int: P {} @@ -24,6 +24,7 @@ public var prop: some P { // CHECK: @"$s24opaque_result_type_debug3FooVQrycipQOMQ" = {{.*}}constant{{.*}} @"$s24opaque_result_type_debug3FooVQrycipMXX" public struct Foo { + public init() {} public subscript() -> some P { return 0 } @@ -33,6 +34,7 @@ public struct Foo { @_silgen_name("use") public func use(_: T) +@inlinable public func bar(genericValue: T) { use(genericValue) diff --git a/test/IRGen/opaque_result_type_substitution.swift b/test/IRGen/opaque_result_type_substitution.swift index e5d80c020f113..5fbe15e35217e 100644 --- a/test/IRGen/opaque_result_type_substitution.swift +++ b/test/IRGen/opaque_result_type_substitution.swift @@ -1,21 +1,22 @@ -// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -primary-file %s | %FileCheck %s +// RUN: %target-swift-frontend -enable-library-evolution -disable-availability-checking -emit-ir -primary-file %s | %FileCheck %s -protocol E {} +public protocol E {} -struct Pair : E { +public struct Pair : E { var fst : T var snd : V - init(_ f: T, _ s: V) { + public init(_ f: T, _ s: V) { self.fst = f self.snd = s } - func foobar() -> some E { + public func foobar() -> some E { return self } } +@inlinable public func usePair(_ t: T, _ v: V) { var x = Pair(t, v) let q = x.foobar() diff --git a/test/IRGen/partial_apply.sil b/test/IRGen/partial_apply.sil index 87974e3d3c67c..79c5d9d10e354 100644 --- a/test/IRGen/partial_apply.sil +++ b/test/IRGen/partial_apply.sil @@ -527,7 +527,14 @@ enum GenericEnum2 { sil public_external @generic_indirect_return2 : $@convention(thin) (Int) -> @owned GenericEnum2 // CHECK-LABEL: define{{.*}} @partial_apply_generic_indirect_return2 -// CHECK: insertvalue {{.*}}$s24generic_indirect_return2TA +// CHECK: [[CTX:%.]] = call noalias %swift.refcounted* @swift_allocObject +// CHECK: store {{.*}}$s24generic_indirect_return2TA +// CHECK: store %swift.refcounted* [[CTX]] +// CHECK: [[FN:%.*]] = load i8* +// CHECK: [[CTX2:%.*]] = load %swift.refcounted* +// CHECK: [[R1:%.]] = insertvalue { i8*, %swift.refcounted* } undef, i8* [[FN]], 0 +// CHECK: [[R2:%.*]] = insertvalue { i8*, %swift.refcounted* } [[R1]], %swift.refcounted* [[CTX2]], 1 +// CHECK: ret { i8*, %swift.refcounted* } [[R2]] // CHECK-LABEL: define internal swiftcc void @"$s24generic_indirect_return2TA"(%T13partial_apply12GenericEnum2OySiG* noalias nocapture sret, %swift.refcounted* swiftself) // CHECK: [[CASTED_ADDR:%.*]] = bitcast %T13partial_apply12GenericEnum2OySiG* %0 to %T13partial_apply12GenericEnum2O* diff --git a/test/SIL/Serialization/opaque_return_type_serialize.sil b/test/SIL/Serialization/opaque_return_type_serialize.sil index 2a42e8740a058..37c589eebe77e 100644 --- a/test/SIL/Serialization/opaque_return_type_serialize.sil +++ b/test/SIL/Serialization/opaque_return_type_serialize.sil @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -disable-availability-checking -emit-module -emit-module-path %t/OpaqueReturnTypeExporter.swiftmodule -module-name OpaqueReturnTypeExporter %S/Inputs/OpaqueReturnTypeExporter.swift +// RUN: %target-swift-frontend -disable-availability-checking -enable-library-evolution -emit-module -emit-module-path %t/OpaqueReturnTypeExporter.swiftmodule -module-name OpaqueReturnTypeExporter %S/Inputs/OpaqueReturnTypeExporter.swift // RUN: %target-sil-opt -I %t %s -emit-sib -module-name test -o %t/test.sib // RUN: %target-swift-frontend -disable-availability-checking -I %t -emit-ir %t/test.sib @@ -12,7 +12,7 @@ typealias SomeButt2 = @_opaqueReturnTypeOf("$sSi24OpaqueReturnTypeExporterE8some sil @$s24OpaqueReturnTypeExporter07exportsaB0QryF : $@convention(thin) () -> @out SomeButt sil @$sSi24OpaqueReturnTypeExporterE8someButtQryF : $@convention(thin) (Int) -> @out SomeButt2 -sil @use_opaque_type : $@convention(thin) (Int) -> () { +sil [serialized] @use_opaque_type : $@convention(thin) (Int) -> () { entry(%a : $Int): %f = function_ref @$s24OpaqueReturnTypeExporter07exportsaB0QryF : $@convention(thin) () -> @out SomeButt %x = alloc_stack $SomeButt diff --git a/test/SILGen/opaque_result_type.swift b/test/SILGen/opaque_result_type.swift index 50ae18fc37105..eacbd6204ab5f 100644 --- a/test/SILGen/opaque_result_type.swift +++ b/test/SILGen/opaque_result_type.swift @@ -1,4 +1,8 @@ -// RUN: %target-swift-frontend -disable-availability-checking -emit-silgen %s | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -I %t -disable-availability-checking -emit-silgen %s | %FileCheck %s + +import resilient_struct protocol P {} protocol Q: AnyObject {} @@ -10,39 +14,138 @@ class C: Q {} // CHECK-LABEL: sil hidden {{.*}}11valueToAddr1xQr func valueToAddr(x: String) -> some P { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 - // CHECK: [[VALUE_COPY:%.*]] = copy_value %1 - // CHECK: store [[VALUE_COPY]] to [init] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*String, [[ARG1:%.*]] : @guaranteed $String): + // CHECK: [[VALUE_COPY:%.*]] = copy_value [[ARG1]] + // CHECK: store [[VALUE_COPY]] to [init] [[ARG0]] return x } // CHECK-LABEL: sil hidden {{.*}}10addrToAddr1xQr func addrToAddr(x: AddrOnly) -> some P { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 - // CHECK: copy_addr %1 to [initialization] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*AddrOnly, [[ARG1:%.*]] : $*AddrOnly): + // CHECK: copy_addr [[ARG1]] to [initialization] [[ARG0]] return x } // CHECK-LABEL: sil hidden {{.*}}13genericAddrToE01xQr func genericAddrToAddr(x: T) -> some P { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 - // CHECK: copy_addr %1 to [initialization] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T): + // CHECK: copy_addr [[ARG1]] to [initialization] [[ARG0]] return x } // CHECK-LABEL: sil hidden {{.*}}12valueToValue1xQr func valueToValue(x: C) -> some Q { - // CHECK: [[VALUE_COPY:%.*]] = copy_value %0 - // CHECK: [[CAST_TO_OPAQUE:%.*]] = unchecked_ref_cast [[VALUE_COPY]] - // CHECK: return [[CAST_TO_OPAQUE]] + // CHECK: bb0([[ARG:%.*]] : @guaranteed $C): + // CHECK: [[VALUE_COPY:%.*]] = copy_value [[ARG]] + // CHECK: return [[VALUE_COPY]] return x } // CHECK-LABEL: sil hidden {{.*}}13reabstraction1xQr func reabstraction(x: @escaping () -> ()) -> some Any { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 : ${{.*}} to $*@callee_guaranteed () -> @out () - // CHECK: [[VALUE_COPY:%.*]] = copy_value %1 - // CHECK: [[VALUE_REABSTRACT:%.*]] = partial_apply [callee_guaranteed] {{%.*}}([[VALUE_COPY]]) - // CHECK: store [[VALUE_REABSTRACT]] to [init] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*@callee_guaranteed () -> @out (), [[ARG1:%.*]] : @guaranteed $@callee_guaranteed () -> ()): + // CHECK: [[VALUE_COPY:%.*]] = copy_value [[ARG1]] + // CHECK: [[REABSTRACT:%.*]] = function_ref @$sIeg_ytIegr_TR + // CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]([[VALUE_COPY]]) + // CHECK: store [[THUNK]] to [init] [[ARG0]] return x } + +protocol X { + associatedtype A + func foo() -> A +} + +extension Int : P {} + +extension ResilientInt : P {} + +class K : P {} + +func useClosure2(_ cl: () -> ()) {} + +func useClosure(_ cl: @escaping () -> ()) { + cl() +} + +struct S : X { + + func foo() -> some P { + return returnTrivial() + } + + func returnTrivial() -> some P { + return 1 + } + + func returnClass() -> some P { + return K() + } + + func returnResilient() -> some P { + return ResilientInt(i: 1) + } + + func testCapture() { + var someP = returnTrivial() + var someK = returnClass() + var someR = returnResilient() + useClosure { + someP = self.returnTrivial() + someK = self.returnClass() + someR = self.returnResilient() + } + print(someP) + print(someK) + print(someR) + } + + func testCapture2() { + var someP = returnTrivial() + var someK = returnClass() + var someR = returnResilient() + useClosure2 { + someP = self.returnTrivial() + someK = self.returnClass() + someR = self.returnResilient() + } + print(someP) + print(someK) + print(someR) + } + + func testCapture3() { + let someP = returnTrivial() + let someK = returnClass() + let someR = returnResilient() + useClosure { + print(someP) + print(someK) + print(someR) + } + } + + func testCapture4() { + let someP = returnTrivial() + let someK = returnClass() + let someR = returnResilient() + useClosure { + print(someP) + print(someK) + print(someR) + } + } +} + +extension Optional : P { } + +struct S2 : X { + func foo() -> some P { + let x : Optional = 1 + return x + } + func returnFunctionType() -> () -> A { + return foo + } +} diff --git a/test/SILOptimizer/cast_folding.swift b/test/SILOptimizer/cast_folding.swift index 156b6a2d54780..1394bbc7b10d0 100644 --- a/test/SILOptimizer/cast_folding.swift +++ b/test/SILOptimizer/cast_folding.swift @@ -1072,7 +1072,7 @@ public func testCastToPForOptionalFailure() -> Bool { struct Underlying : P { } -func returnOpaque() -> some P { +public func returnOpaque() -> some P { return Underlying() } @@ -1083,6 +1083,7 @@ func returnOpaque() -> some P { // MANDATORY: [[U:%.*]] = alloc_stack $Underlying // MANDATORY: unconditional_checked_cast_addr @_opaqueReturnTypeOf{{.*}}in [[O]] : $*@_opaqueReturnTypeOf{{.*}}to Underlying in [[U]] : $*Underlying // MANDATORY: load [[U]] : $*Underlying +@inlinable public func testCastOpaqueArchetype() { let o = returnOpaque() as! Underlying } diff --git a/test/SILOptimizer/specialize_opaque_type_archetypes.swift b/test/SILOptimizer/specialize_opaque_type_archetypes.swift index 50dee3429f0c3..48cf8c8799248 100644 --- a/test/SILOptimizer/specialize_opaque_type_archetypes.swift +++ b/test/SILOptimizer/specialize_opaque_type_archetypes.swift @@ -49,12 +49,12 @@ func identity(_ t: T) -> T { // CHECK-LABEL: sil @$s1A10testFooBaryyxAA1PRzlF : $@convention(thin) (@in_guaranteed T) -> () { // CHECK: bb3([[FOOS_INT:%.*]] : $Builtin.Int64): -// CHECK: [[ID:%.*]] = function_ref @$s1A8identityyxxlFs5Int64V_Tg5 : $@convention(thin) (Int64) -> Int64 // CHECK: [[FOO_RES:%.*]] = struct $Int64 ([[FOOS_INT]] : $Builtin.Int64) +// CHECK: [[ID:%.*]] = function_ref @$s1A8identityyxxlFs5Int64V_Tg5 : $@convention(thin) (Int64) -> Int64 // CHECK: [[ID_RES:%.*]] = apply [[ID]]([[FOO_RES]]) : $@convention(thin) (Int64) -> Int64 // CHECK: [[USEP:%.*]] = function_ref @$s1A4usePyyxAA1PRzlFs5Int64V_Tg5 : $@convention(thin) (Int64) -> () -// CHECK: %27 = apply [[USEP]]([[ID_RES]]) : $@convention(thin) (Int64) -> () -// CHECK: %29 = apply [[USEP]]([[FOO_RES]]) : $@convention(thin) (Int64) -> () +// CHECK: apply [[USEP]]([[ID_RES]]) : $@convention(thin) (Int64) -> () +// CHECK: apply [[USEP]]([[FOO_RES]]) : $@convention(thin) (Int64) -> () public func testFooBar(_ t : T) { let x = foo(getInt()) @@ -99,9 +99,8 @@ public func returnC() -> some CP { } // CHECK-LABEL: sil @$s1A4useCyyF -// CHECK: [[INT:%.*]] = struct $Int64 ( -// CHECK: // function_ref specialized useP(_:) // CHECK: [[FUN:%.*]] = function_ref @$s1A4usePyyxAA1PRzlFs5Int64V_Tg5 +// CHECK: [[INT:%.*]] = struct $Int64 ( // CHECK: = apply [[FUN]]([[INT]]) public func useC() { let c = returnC() @@ -166,6 +165,7 @@ struct Container2 { } class Container3 { + @inline(__always) init(member : S.T) { self.member = member } @@ -229,8 +229,8 @@ func nonResilient() -> some ExternalP2 { // CHECK-LABEL: sil @$s1A019usePairResilientNonC0yyF : $@convention(thin) () -> () // CHECK: alloc_stack $Pair: P3 { // Don't assert. // CHECK-LABEL: sil {{.*}} @$s1A7AdapterVyxGAA2P3A2aEP3foo2ATQzyFTW // CHECK: [[F:%.*]] = function_ref @$s1A7AdapterV3fooQryF -// CHECK: apply [[F]]<τ_0_0>(%0, %1) : $@convention(method) <τ_0_0 where τ_0_0 : P3> (@in_guaranteed Adapter<τ_0_0>) -> @out @_opaqueReturnTypeOf("$s1A7AdapterV3fooQryF", 0) +// CHECK: apply [[F]]<τ_0_0>(%0, %1) : $@convention(method) <τ_0_0 where τ_0_0 : P3> (@in_guaranteed Adapter<τ_0_0>) -> extension P3 { public func foo() -> some P3 { return Adapter(inner: self) @@ -270,9 +270,8 @@ extension P3 { // CHECK-LABEL: sil @$s1A21useExternalResilient2yyF : $@convention(thin) () -> () // CHECK: [[RES:%.*]] = alloc_stack $Int64 -// CHECK: [[FUN:%.*]] = function_ref @$s9External226inlinableExternalResilientQryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External226inlinableExternalResilientQryF", 0) -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External226inlinableExternalResilientQryF", 0) -// CHECK: apply [[FUN]]([[RES2]]) +// CHECK: [[FUN:%.*]] = function_ref @$s9External226inlinableExternalResilientQryF : $@convention(thin) () -> @out Int64 +// CHECK: apply [[FUN]]([[RES]]) // CHECK: return public func useExternalResilient2() { let e = inlinableExternalResilient() @@ -282,9 +281,8 @@ public func useExternalResilient2() { // In this case we should only 'peel' one layer of opaque archetypes. // CHECK-LABEL: sil @$s1A21useExternalResilient3yyF // CHECK: [[RES:%.*]] = alloc_stack $@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) -// CHECK: [[FUN:%.*]] = function_ref @$s9External3031inlinableExternalResilientCallsD0QryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External3031inlinableExternalResilientCallsD0QryF", 0) -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0){{.*}}to $*@_opaqueReturnTypeOf("$s9External3031inlinableExternalResilientCallsD0QryF", 0) -// CHECK: apply [[FUN]]([[RES2]]) +// CHECK: [[FUN:%.*]] = function_ref @$s9External3031inlinableExternalResilientCallsD0QryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) +// CHECK: apply [[FUN]]([[RES]]) public func useExternalResilient3() { let e = inlinableExternalResilientCallsResilient() useP(e.myValue3()) @@ -293,9 +291,8 @@ public func useExternalResilient3() { // Check that we can look throught two layers of inlinable resilient functions. // CHECK-LABEL: sil @$s1A21useExternalResilient4yyF // CHECK: [[RES:%.*]] = alloc_stack $Int64 -// CHECK: [[FUN:%.*]] = function_ref @$s9External3040inlinableExternalResilientCallsInlinablecD0QryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External3040inlinableExternalResilientCallsInlinablecD0QryF", 0) -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External3040inlinableExternalResilientCallsInlinablecD0QryF", 0) -// CHECK: apply [[FUN]]([[RES2]]) +// CHECK: [[FUN:%.*]] = function_ref @$s9External3040inlinableExternalResilientCallsInlinablecD0QryF : $@convention(thin) () -> @out Int64 +// CHECK: apply [[FUN]]([[RES]]) public func useExternalResilient4() { let e = inlinableExternalResilientCallsInlinableExternalResilient() useP(e.myValue3()) @@ -306,8 +303,7 @@ public func useExternalResilient4() { // CHECK: [[CONTAINER:%.*]] = apply [[CONTAINER_INIT_FUN]] // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[COMPUTED_PROP:%.*]] = function_ref @$s8External0A9ContainerV16computedPropertyQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s8External0A9ContainerV16computedPropertyQrvp", 0) -// CHECK: apply [[COMPUTED_PROP]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[COMPUTED_PROP]]([[RES]], [[CONTAINER]]) // CHECK: [[MYVALUE:%.*]] = function_ref @$ss5Int64V8ExternalE8myValue2AByF : $@convention(method) (Int64) -> Int64 // CHECK: apply [[MYVALUE]] public func testStoredProperty() { @@ -329,8 +325,7 @@ public func testResilientProperty() { // CHECK: [[CONTAINER:%.*]] = alloc_stack $ResilientContainer // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[FUN:%.*]] = function_ref @$s9External218ResilientContainerV18inlineablePropertyQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External218ResilientContainerV18inlineablePropertyQrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINER]]) public func testResilientInlinableProperty() { let r = ResilientContainer() useP(r.inlineableProperty.myValue3()) @@ -340,8 +335,7 @@ public func testResilientInlinableProperty() { // CHECK: [[CONTAINER:%.*]] = alloc_stack $ResilientContainer // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[FUN:%.*]] = function_ref @$s9External218ResilientContainerV19inlineableProperty2Qrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External218ResilientContainerV19inlineableProperty2Qrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINER]]) public func testResilientInlinableProperty3() { let r = ResilientContainer() useP(r.inlineableProperty2.myValue3()) @@ -362,8 +356,7 @@ public func testResilientProperty2() { // CHECK: [[CONTAINER:%.*]] = alloc_stack $ResilientContainer2 // CHECK: [[RES:%.*]] = alloc_stack $@_opaqueReturnTypeOf("$s9External218ResilientContainerV16computedPropertyQrvp", 0) // CHECK: [[FUN:%.*]] = function_ref @$s9External319ResilientContainer2V18inlineablePropertyQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*@_opaqueReturnTypeOf("$s9External218ResilientContainerV16computedPropertyQrvp", 0){{.*}}to $*@_opaqueReturnTypeOf("$s9External319ResilientContainer2V18inlineablePropertyQrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINER]]) public func testResilientInlinableProperty2() { let r = ResilientContainer2() useP(r.inlineableProperty.myValue3()) @@ -373,8 +366,7 @@ public func testResilientInlinableProperty2() { // CHECK: [[CONTAINTER:%.*]] = alloc_stack $ResilientContainer2 // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[FUN:%.*]] = function_ref @$s9External319ResilientContainer2V023inlineablePropertyCallsB10InlineableQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External319ResilientContainer2V023inlineablePropertyCallsB10InlineableQrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINTER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINTER]]) public func testResilientInlinablePropertyCallsResilientInlinable() { let r = ResilientContainer2() useP(r.inlineablePropertyCallsResilientInlineable.myValue3()) @@ -420,9 +412,9 @@ func testIt(cl: (Int64) throws -> T) { // CHECK-LABEL: sil shared [noinline] @$s1A16testPartialApplyyyxAA2P4RzlFAA2PAV_Tg5 // CHECK: [[PA:%.*]] = alloc_stack $PA // CHECK: store %0 to [[PA]] : $*PA -// CHECK: [[F:%.*]] = function_ref @$s1A2PAVAA2P4A2aDP3fooy2ATQzs5Int64VFTW : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out @_opaqueReturnTypeOf("$s1A2PAV3fooyQrs5Int64VF", 0) -// CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[F]]([[PA]]) : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out @_opaqueReturnTypeOf("$s1A2PAV3fooyQrs5Int64VF", 0) -// CHECK: convert_function [[C]] : $@callee_guaranteed (Int64) -> @out @_opaqueReturnTypeOf("$s1A2PAV3fooyQrs5Int64VF", {{.*}} to $@callee_guaranteed (Int64) -> (@out Int64, @error Error) +// CHECK: [[F:%.*]] = function_ref @$s1A2PAVAA2P4A2aDP3fooy2ATQzs5Int64VFTW : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out Int64 +// CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[F]]([[PA]]) : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out Int64 +// CHECK: convert_function [[C]] : $@callee_guaranteed (Int64) -> @out Int64 to $@callee_guaranteed (Int64) -> (@out Int64, @error Error) @inline(never) func testPartialApply(_ t: T) { let fun = t.foo @@ -563,7 +555,7 @@ public func rdar56410009_inlinedInner() { // CHECK-LABEL: sil @$s1A25rdar56410009_inlinedOuteryyF public func rdar56410009_inlinedOuter() { // CHECK: [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER:%.+]] = function_ref @$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFAA08externalD0QryFQOyQo__Tg5 - // CHECK: = apply [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) (@in_guaranteed @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) 🦸) -> @out @_opaqueReturnTypeOf("$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlF", 0) 🦸<@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) 🦸> + // CHECK: = apply [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) (@in_guaranteed @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) 🦸) -> @out WrapperP2<@_opaqueReturnTypeOf("$s9External217externalResilientQryF" _ = inlinableExternalResilientWrapper(externalResilient()) } // CHECK: end sil function '$s1A25rdar56410009_inlinedOuteryyF' @@ -582,6 +574,6 @@ public func rdar56410009_inlinedOuter() { // CHECK-LABEL: sil @$s1A24rdar56410009_inlinedBothyyF public func rdar56410009_inlinedBoth() { // CHECK: [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER:%.+]] = function_ref @$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFs5Int64V_Tg5 - // CHECK: = apply [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) (Int64) -> @out @_opaqueReturnTypeOf("$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlF", 0) 🦸 + // CHECK: = apply [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) (Int64) -> @out WrapperP2 _ = inlinableExternalResilientWrapper(inlinableExternalResilient()) } // CHECK: end sil function '$s1A24rdar56410009_inlinedBothyyF' diff --git a/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift b/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift index 9e70b35bb6466..f858632090311 100644 --- a/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift +++ b/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -O -Xllvm -enable-opaque-archetype-specializer -disable-availability-checking -primary-file %s %S/Inputs/specialize_opaque_type_archetypes_multifile_A.swift -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -O -disable-availability-checking -primary-file %s %S/Inputs/specialize_opaque_type_archetypes_multifile_A.swift -emit-sil | %FileCheck %s protocol P {} From 74d14785d5c2d2573b415681b8d85c23990a4016 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 7 Nov 2019 08:16:22 -0800 Subject: [PATCH 073/283] Address review comment TypeBase::hasOpaqueArchetypePropertiesOrCases --- include/swift/SIL/TypeLowering.h | 2 +- lib/AST/Type.cpp | 11 +++++------ lib/SIL/TypeLowering.cpp | 10 ++++------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 04d9fdca569c5..a820df43f4baa 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -862,7 +862,7 @@ class TypeConverter { CanType getLoweredTypeOfGlobal(VarDecl *var); - bool hasOpaqueArchetypePropertiesOrCases(CanType ty); + bool hasOpaqueArchetypeOrPropertiesOrCases(CanType ty); /// Return the SILFunctionType for a native function value of the /// given type. diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index f3f486b12ca63..fb2863e97d4fb 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4641,13 +4641,11 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { } bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { - if (hasOpaqueArchetype()) - return true; - if (auto *structDecl = getStructOrBoundGenericStruct()) { for (auto *field : structDecl->getStoredProperties()) { - auto fieldTy = field->getInterfaceType(); - if (fieldTy->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) + auto fieldTy = field->getInterfaceType()->getCanonicalType(); + if (fieldTy->hasOpaqueArchetype() || + fieldTy->hasOpaqueArchetypePropertiesOrCases()) return true; } } @@ -4655,7 +4653,8 @@ bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { if (auto *enumDecl = getEnumOrBoundGenericEnum()) { for (auto *elt : enumDecl->getAllElements()) { auto eltType = elt->getInterfaceType(); - if (eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) + if (eltType->hasOpaqueArchetype() || + eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) return true; } } diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 18c07ce960026..f124d37c7c4bc 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -1522,7 +1522,7 @@ TypeConverter::getSILFunctionType(TypeExpansionContext context, getLoweredRValueType(context, origType, substType)); } -bool TypeConverter::hasOpaqueArchetypePropertiesOrCases(CanType ty) { +bool TypeConverter::hasOpaqueArchetypeOrPropertiesOrCases(CanType ty) { if (ty->hasOpaqueArchetype()) return true; @@ -1541,14 +1541,13 @@ TypeConverter::getTypeLowering(AbstractionPattern origType, TypeExpansionContext forExpansion) { CanType substType = origSubstType->getCanonicalType(); auto origHadOpaqueTypeArchetype = - origSubstType->hasOpaqueArchetype() || - hasOpaqueArchetypePropertiesOrCases(origSubstType->getCanonicalType()); + hasOpaqueArchetypeOrPropertiesOrCases(origSubstType->getCanonicalType()); auto key = getTypeKey(origType, substType, forExpansion); assert((!key.isDependent() || getCurGenericContext()) && "dependent type outside of generic context?!"); assert(!substType->is()); - auto *candidateLowering = find(key); + auto *candidateLowering = find(key.getKeyForMinimalExpansion()); auto *lowering = getTypeLoweringForExpansion( key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); if (lowering != nullptr) @@ -1741,8 +1740,7 @@ TypeConverter::getTypeLowering(SILType type, loweredType, forExpansion); auto origHadOpaqueTypeArchetype = - loweredType->hasOpaqueArchetype() || - hasOpaqueArchetypePropertiesOrCases(loweredType); + hasOpaqueArchetypeOrPropertiesOrCases(loweredType); return getTypeLoweringForLoweredType(key, forExpansion, origHadOpaqueTypeArchetype); From 2ea1c5cc413d0cd862c6d4263d1d12c4ad86cd3d Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 7 Nov 2019 09:06:44 -0800 Subject: [PATCH 074/283] Use IGM.getMaximalTypeExpansionContext() in more places --- lib/IRGen/GenClass.cpp | 25 +++++++++++++------------ lib/IRGen/GenClass.h | 5 ++--- lib/IRGen/GenKeyPath.cpp | 2 +- lib/IRGen/GenProto.cpp | 19 ++++++++----------- lib/IRGen/GenProto.h | 6 ++---- lib/IRGen/GenThunk.cpp | 13 ++++++------- lib/IRGen/IRGenModule.h | 3 +-- lib/IRGen/IRGenSIL.cpp | 16 ++++++---------- 8 files changed, 39 insertions(+), 50 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 9d168eb52649d..f120032ca8901 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -501,21 +501,20 @@ Address IRGenFunction::emitByteOffsetGEP(llvm::Value *base, } /// Emit a field l-value by applying the given offset to the given base. -static OwnedAddress emitAddressAtOffset(TypeExpansionContext context, - IRGenFunction &IGF, SILType baseType, +static OwnedAddress emitAddressAtOffset(IRGenFunction &IGF, SILType baseType, llvm::Value *base, llvm::Value *offset, VarDecl *field) { - auto &fieldTI = IGF.getTypeInfo( - baseType.getFieldType(field, IGF.getSILModule(), context)); + auto &fieldTI = IGF.getTypeInfo(baseType.getFieldType( + field, IGF.getSILModule(), IGF.IGM.getMaximalTypeExpansionContext())); auto addr = IGF.emitByteOffsetGEP(base, offset, fieldTI, base->getName() + "." + field->getName().str()); return OwnedAddress(addr, base); } llvm::Constant *irgen::tryEmitConstantClassFragilePhysicalMemberOffset( - TypeExpansionContext context, IRGenModule &IGM, SILType baseType, - VarDecl *field) { - auto fieldType = baseType.getFieldType(field, IGM.getSILModule(), context); + IRGenModule &IGM, SILType baseType, VarDecl *field) { + auto fieldType = baseType.getFieldType(field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); // If the field is empty, its address doesn't matter. auto &fieldTI = IGM.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { @@ -571,9 +570,11 @@ irgen::getClassLayoutWithTailElems(IRGenModule &IGM, SILType classType, return ClassTI.createLayoutWithTailElems(IGM, classType, tailTypes); } -OwnedAddress irgen::projectPhysicalClassMemberAddress( - TypeExpansionContext context, IRGenFunction &IGF, llvm::Value *base, - SILType baseType, SILType fieldType, VarDecl *field) { +OwnedAddress irgen::projectPhysicalClassMemberAddress(IRGenFunction &IGF, + llvm::Value *base, + SILType baseType, + SILType fieldType, + VarDecl *field) { // If the field is empty, its address doesn't matter. auto &fieldTI = IGF.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { @@ -603,13 +604,13 @@ OwnedAddress irgen::projectPhysicalClassMemberAddress( case FieldAccess::NonConstantDirect: { Address offsetA = IGF.IGM.getAddrOfFieldOffset(field, NotForDefinition); auto offset = IGF.Builder.CreateLoad(offsetA, "offset"); - return emitAddressAtOffset(context, IGF, baseType, base, offset, field); + return emitAddressAtOffset(IGF, baseType, base, offset, field); } case FieldAccess::ConstantIndirect: { auto metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType); auto offset = emitClassFieldOffset(IGF, baseClass, field, metadata); - return emitAddressAtOffset(context, IGF, baseType, base, offset, field); + return emitAddressAtOffset(IGF, baseType, base, offset, field); } } llvm_unreachable("bad field-access strategy"); diff --git a/lib/IRGen/GenClass.h b/lib/IRGen/GenClass.h index dc800faaa519c..66018fbfb7b8d 100644 --- a/lib/IRGen/GenClass.h +++ b/lib/IRGen/GenClass.h @@ -52,7 +52,7 @@ namespace irgen { enum class FieldAccess : uint8_t; OwnedAddress projectPhysicalClassMemberAddress( - TypeExpansionContext context, IRGenFunction &IGF, llvm::Value *base, + IRGenFunction &IGF, llvm::Value *base, SILType baseType, SILType fieldType, VarDecl *field); /// Return a strategy for accessing the given stored class property. @@ -179,8 +179,7 @@ namespace irgen { /// Emit the constant fragile offset of the given property inside an instance /// of the class. llvm::Constant *tryEmitConstantClassFragilePhysicalMemberOffset( - TypeExpansionContext context, IRGenModule &IGM, SILType baseType, - VarDecl *field); + IRGenModule &IGM, SILType baseType, VarDecl *field); FieldAccess getClassFieldAccess(IRGenModule &IGM, SILType baseType, diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 5dda8dbbc0652..ca4a41ccb25d5 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -791,7 +791,7 @@ emitKeyPathComponent(IRGenModule &IGM, case FieldAccess::ConstantDirect: { // Known constant fixed offset. auto offset = tryEmitConstantClassFragilePhysicalMemberOffset( - TypeExpansionContext::minimal(), IGM, loweredClassTy, property); + IGM, loweredClassTy, property); assert(offset && "no constant offset for ConstantDirect field?!"); addFixedOffset(/*struct*/ false, property->isLet(), offset); break; diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 3f62597afd6be..e61ad1c1674e4 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3185,11 +3185,9 @@ void irgen::expandTrailingWitnessSignature(IRGenModule &IGM, out.push_back(IGM.WitnessTablePtrTy); } -FunctionPointer -irgen::emitWitnessMethodValue(TypeExpansionContext expansionContext, - IRGenFunction &IGF, - llvm::Value *wtable, - SILDeclRef member) { +FunctionPointer irgen::emitWitnessMethodValue(IRGenFunction &IGF, + llvm::Value *wtable, + SILDeclRef member) { auto *fn = cast(member.getDecl()); auto proto = cast(fn->getDeclContext()); @@ -3202,8 +3200,8 @@ irgen::emitWitnessMethodValue(TypeExpansionContext expansionContext, emitInvariantLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable()); - auto fnType = - IGF.IGM.getSILTypes().getConstantFunctionType(expansionContext, member); + auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType( + IGF.IGM.getMaximalTypeExpansionContext(), member); Signature signature = IGF.IGM.getSignature(fnType); witnessFnPtr = IGF.Builder.CreateBitCast(witnessFnPtr, signature.getType()->getPointerTo()); @@ -3212,13 +3210,12 @@ irgen::emitWitnessMethodValue(TypeExpansionContext expansionContext, } FunctionPointer irgen::emitWitnessMethodValue( - TypeExpansionContext expansionContext, IRGenFunction &IGF, CanType baseTy, - llvm::Value **baseMetadataCache, SILDeclRef member, - ProtocolConformanceRef conformance) { + IRGenFunction &IGF, CanType baseTy, llvm::Value **baseMetadataCache, + SILDeclRef member, ProtocolConformanceRef conformance) { llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache, conformance); - return emitWitnessMethodValue(expansionContext, IGF, wtable, member); + return emitWitnessMethodValue(IGF, wtable, member); } llvm::Value *irgen::computeResilientWitnessTableIndex( diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index 1b454091be05e..f91b802841c85 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -58,15 +58,13 @@ namespace irgen { /// Extract the method pointer from the given witness table /// as a function value. - FunctionPointer emitWitnessMethodValue(TypeExpansionContext context, - IRGenFunction &IGF, + FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, llvm::Value *wtable, SILDeclRef member); /// Extract the method pointer from an archetype's witness table /// as a function value. - FunctionPointer emitWitnessMethodValue(TypeExpansionContext context, - IRGenFunction &IGF, CanType baseTy, + FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, CanType baseTy, llvm::Value **baseMetadataCache, SILDeclRef member, ProtocolConformanceRef conformance); diff --git a/lib/IRGen/GenThunk.cpp b/lib/IRGen/GenThunk.cpp index 5e2d7791b5fdb..db4d67356ac81 100644 --- a/lib/IRGen/GenThunk.cpp +++ b/lib/IRGen/GenThunk.cpp @@ -36,8 +36,7 @@ using namespace irgen; /// Find the entry point for a method dispatch thunk. llvm::Function * -IRGenModule::getAddrOfDispatchThunk(TypeExpansionContext context, - SILDeclRef declRef, +IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef, ForDefinition_t forDefinition) { LinkEntity entity = LinkEntity::forDispatchThunk(declRef); @@ -47,7 +46,8 @@ IRGenModule::getAddrOfDispatchThunk(TypeExpansionContext context, return entry; } - auto fnType = getSILModule().Types.getConstantFunctionType(context, declRef); + auto fnType = getSILModule().Types.getConstantFunctionType( + getMaximalTypeExpansionContext(), declRef); Signature signature = getSignature(fnType); LinkInfo link = LinkInfo::get(*this, entity, forDefinition); @@ -56,7 +56,7 @@ IRGenModule::getAddrOfDispatchThunk(TypeExpansionContext context, } static FunctionPointer lookupMethod(IRGenFunction &IGF, SILDeclRef declRef) { - auto expansionContext = TypeExpansionContext::minimal(); + auto expansionContext = IGF.IGM.getMaximalTypeExpansionContext(); auto *decl = cast(declRef.getDecl()); // Protocol case. @@ -65,7 +65,7 @@ static FunctionPointer lookupMethod(IRGenFunction &IGF, SILDeclRef declRef) { llvm::Value *wtable = (IGF.CurFn->arg_end() - 1); // Find the witness we're interested in. - return emitWitnessMethodValue(expansionContext, IGF, wtable, declRef); + return emitWitnessMethodValue(IGF, wtable, declRef); } // Class case. @@ -99,8 +99,7 @@ static FunctionPointer lookupMethod(IRGenFunction &IGF, SILDeclRef declRef) { } void IRGenModule::emitDispatchThunk(SILDeclRef declRef) { - auto *f = getAddrOfDispatchThunk(TypeExpansionContext::minimal(), declRef, - ForDefinition); + auto *f = getAddrOfDispatchThunk(declRef, ForDefinition); IRGenFunction IGF(*this, f); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 497c8416c4aae..933b463e93dcb 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1256,8 +1256,7 @@ private: \ /// Cast the given constant to i8*. llvm::Constant *getOpaquePtr(llvm::Constant *pointer); - llvm::Function *getAddrOfDispatchThunk(TypeExpansionContext context, - SILDeclRef declRef, + llvm::Function *getAddrOfDispatchThunk(SILDeclRef declRef, ForDefinition_t forDefinition); void emitDispatchThunk(SILDeclRef declRef); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index b69d8be804e1a..2fa991e2e02e8 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3623,9 +3623,8 @@ void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { llvm::Value *value = base.claimNext(); SILType baseTy = i->getOperand()->getType(); - Address field = projectPhysicalClassMemberAddress( - TypeExpansionContext(*i->getFunction()), *this, value, - baseTy, i->getType(), i->getField()) + Address field = projectPhysicalClassMemberAddress(*this, value, baseTy, + i->getType(), i->getField()) .getAddress(); setLoweredAddress(i, field); } @@ -5468,8 +5467,7 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) { if (IGM.isResilient(conformance.getRequirement(), ResilienceExpansion::Maximal)) { - auto *fnPtr = IGM.getAddrOfDispatchThunk( - IGM.getMaximalTypeExpansionContext(), member, NotForDefinition); + auto *fnPtr = IGM.getAddrOfDispatchThunk(member, NotForDefinition); auto fnType = IGM.getSILTypes().getConstantFunctionType( IGM.getMaximalTypeExpansionContext(), member); auto sig = IGM.getSignature(fnType); @@ -5482,9 +5480,8 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) { // It would be nice if this weren't discarded. llvm::Value *baseMetadataCache = nullptr; - auto fn = - emitWitnessMethodValue(TypeExpansionContext(*i->getFunction()), *this, - baseTy, &baseMetadataCache, member, conformance); + auto fn = emitWitnessMethodValue(*this, baseTy, &baseMetadataCache, member, + conformance); setLoweredFunctionPointer(i, fn); } @@ -5668,8 +5665,7 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { auto *classDecl = cast(method.getDecl()->getDeclContext()); if (IGM.hasResilientMetadata(classDecl, ResilienceExpansion::Maximal)) { - auto *fnPtr = IGM.getAddrOfDispatchThunk(TypeExpansionContext(*CurSILFn), - method, NotForDefinition); + auto *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition); auto sig = IGM.getSignature(methodType); FunctionPointer fn(fnPtr, sig); From 8a4d61fb1d406cb3f843e2dfe20f393d55f59603 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 7 Nov 2019 13:52:01 -0800 Subject: [PATCH 075/283] Remove FIXME in TypeExpansionAnalysis: It's cache needed to be keyed by the type expansion context --- include/swift/AST/TypeExpansionContext.h | 31 +++++++++++++++++++ include/swift/SIL/TypeLowering.h | 29 ----------------- .../Analysis/TypeExpansionAnalysis.h | 5 ++- .../Analysis/TypeExpansionAnalysis.cpp | 13 ++++---- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/include/swift/AST/TypeExpansionContext.h b/include/swift/AST/TypeExpansionContext.h index 7c266e63d73ef..761fa53217bd5 100644 --- a/include/swift/AST/TypeExpansionContext.h +++ b/include/swift/AST/TypeExpansionContext.h @@ -18,9 +18,11 @@ #define SWIFT_TYPEEXPANSIONCONTEXT_H #include "swift/AST/ResilienceExpansion.h" +#include "llvm/ADT/DenseMap.h" namespace swift { class DeclContext; + class SILFunction; /// Describes the context in which SIL types should eventually be expanded. /// Required for lowering resilient types and deciding whether to look through @@ -111,4 +113,33 @@ class TypeExpansionContext { } // namespace swift +namespace llvm { +template <> struct DenseMapInfo { + using TypeExpansionContext = swift::TypeExpansionContext; + + static TypeExpansionContext getEmptyKey() { + return TypeExpansionContext( + swift::ResilienceExpansion::Minimal, + reinterpret_cast( + DenseMapInfo::getEmptyKey()), + false); + } + static TypeExpansionContext getTombstoneKey() { + return TypeExpansionContext( + swift::ResilienceExpansion::Minimal, + reinterpret_cast( + DenseMapInfo::getTombstoneKey()), + false); + } + + static unsigned getHashValue(TypeExpansionContext val) { + return DenseMapInfo::getHashValue(val.getHashKey()); + } + + static bool isEqual(TypeExpansionContext LHS, TypeExpansionContext RHS) { + return LHS == RHS; + } +}; +} // namespace llvm + #endif diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index a820df43f4baa..fa3d4adedf953 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -30,35 +30,6 @@ namespace clang { class Type; } -namespace llvm { -template <> struct DenseMapInfo { - using TypeExpansionContext = swift::TypeExpansionContext; - - static TypeExpansionContext getEmptyKey() { - return TypeExpansionContext( - swift::ResilienceExpansion::Minimal, - reinterpret_cast( - DenseMapInfo::getEmptyKey()), - false); - } - static TypeExpansionContext getTombstoneKey() { - return TypeExpansionContext( - swift::ResilienceExpansion::Minimal, - reinterpret_cast( - DenseMapInfo::getTombstoneKey()), - false); - } - - static unsigned getHashValue(TypeExpansionContext val) { - return DenseMapInfo::getHashValue(val.getHashKey()); - } - - static bool isEqual(TypeExpansionContext LHS, TypeExpansionContext RHS) { - return LHS == RHS; - } -}; -} - namespace swift { class AnyFunctionRef; enum class Bridgeability : unsigned; diff --git a/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h b/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h index 46be7f8af2bd0..69c0b7718e1cc 100644 --- a/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h @@ -12,6 +12,7 @@ #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_TYPEEXPANSIONANALYSIS_H #define SWIFT_SILOPTIMIZER_ANALYSIS_TYPEEXPANSIONANALYSIS_H +#include "swift/AST/TypeExpansionContext.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILType.h" #include "swift/SIL/SILValue.h" @@ -22,7 +23,9 @@ namespace swift { /// This analysis determines memory effects during destruction. class TypeExpansionAnalysis : public SILAnalysis { - llvm::DenseMap ExpansionCache; + llvm::DenseMap, ProjectionPathList> + ExpansionCache; + public: TypeExpansionAnalysis(SILModule *M) : SILAnalysis(SILAnalysisKind::TypeExpansion) {} diff --git a/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp b/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp index 657022a286b3d..43fa3d930e048 100644 --- a/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp @@ -28,9 +28,8 @@ const ProjectionPathList & TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod, TypeExpansionContext context) { // Check whether we have the type expansion. - auto Iter = ExpansionCache.find(B); - // TODO: FIXME: ExpansionCache.find((B,context)) - // + auto key = std::make_pair(B, context); + auto Iter = ExpansionCache.find(key); // if (Iter != ExpansionCache.end()) { return Iter->second; @@ -40,8 +39,8 @@ TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod, if (!shouldExpand(*Mod, B)) { // Push the empty projection path. ProjectionPath P(B); - ExpansionCache[B].push_back(P); - return ExpansionCache[B]; + ExpansionCache[key].push_back(P); + return ExpansionCache[key]; } // Flush the cache if the size of the cache is too large. @@ -51,8 +50,8 @@ TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod, // Build the type expansion for the leaf nodes. ProjectionPath::expandTypeIntoLeafProjectionPaths(B, Mod, context, - ExpansionCache[B]); - return ExpansionCache[B]; + ExpansionCache[key]); + return ExpansionCache[key]; } SILAnalysis *swift::createTypeExpansionAnalysis(SILModule *M) { From f3a5d69672c02436800c710ed229cfaa9b2a6221 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 8 Nov 2019 07:35:40 -0800 Subject: [PATCH 076/283] Adjust to recent change --- lib/SILOptimizer/IPO/GlobalOpt.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 0152388b7d9f7..e0cc724ab1a1b 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -748,7 +748,9 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF, if (hasPublicVisibility(SILG->getLinkage())) expansion = ResilienceExpansion::Minimal; - auto &tl = Module->Types.getTypeLowering(SILG->getLoweredType(), expansion); + auto &tl = Module->Types.getTypeLowering( + SILG->getLoweredType(), + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion)); if (!tl.isLoadable()) return; From 3c45041b1725c318257184b493be98b609167ef9 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 5 Nov 2019 15:44:36 -0800 Subject: [PATCH 077/283] Add driver modes to emit and dump Clang precompiled modules. --- .../swift/AST/DiagnosticsClangImporter.def | 6 + include/swift/ClangImporter/ClangImporter.h | 24 +++ .../ClangImporter/ClangImporterOptions.h | 5 +- include/swift/Frontend/FrontendOptions.h | 3 + include/swift/Option/Options.td | 7 + lib/ClangImporter/ClangImporter.cpp | 165 +++++++++++++++--- lib/Driver/Driver.cpp | 15 ++ lib/Driver/ToolChains.cpp | 5 +- .../ArgsToFrontendOptionsConverter.cpp | 4 + lib/Frontend/CompilerInvocation.cpp | 3 + lib/Frontend/FrontendOptions.cpp | 28 +++ lib/FrontendTool/FrontendTool.cpp | 29 ++- test/ClangImporter/pcm-emit-and-import.swift | 15 ++ test/lit.cfg | 14 ++ 14 files changed, 291 insertions(+), 32 deletions(-) create mode 100644 test/ClangImporter/pcm-emit-and-import.swift diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index 4da06a218fb5c..d74022dbb66d4 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -64,6 +64,12 @@ ERROR(bridging_header_pch_error,Fatal, "failed to emit precompiled header '%0' for bridging header '%1'", (StringRef, StringRef)) +ERROR(emit_pcm_error,Fatal, + "failed to emit precompiled module '%0' for module map '%1'", + (StringRef, StringRef)) +ERROR(dump_pcm_error,Fatal, + "failed to dump precompiled module '%0'", (StringRef)) + WARNING(invalid_swift_name_method,none, "too %select{few|many}0 parameters in swift_name attribute (expected %1; " "got %2)", (bool, unsigned, unsigned)) diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 61f3225a379e6..4a2766d5a5264 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -112,6 +112,17 @@ class ClangImporter final : public ClangModuleLoader { DependencyTracker *tracker, DWARFImporterDelegate *dwarfImporterDelegate); + /// Creates a clone of Clang importer's compiler instance that has been + /// configured for operations on precompiled outputs (either emitting a + /// precompiled header, emitting a precompiled module, or dumping a + /// precompiled module). + /// + /// The caller of this method should set any action-specific invocation + /// options (like FrontendOptions::ProgramAction, input files, and output + /// paths), then create the appropriate FrontendAction and execute it. + std::unique_ptr + cloneCompilerInstanceForPrecompiling(); + public: /// Create a new Clang importer that can import a suitable Clang /// module into the given ASTContext. @@ -333,6 +344,19 @@ class ClangImporter final : public ClangModuleLoader { /// if we need to persist a PCH for later reuse. bool canReadPCH(StringRef PCHFilename); + /// Makes a temporary replica of the ClangImporter's CompilerInstance, reads a + /// module map into the replica and emits a PCM file for one of the modules it + /// declares. Delegates to clang for everything except construction of the + /// replica. + bool emitPrecompiledModule(StringRef moduleMapPath, StringRef moduleName, + StringRef outputPath); + + /// Makes a temporary replica of the ClangImporter's CompilerInstance and + /// dumps information about a PCM file (assumed to be generated by -emit-pcm + /// or in the Swift module cache). Delegates to clang for everything except + /// construction of the replica. + bool dumpPrecompiledModule(StringRef modulePath, StringRef outputPath); + const clang::Module *getClangOwningModule(ClangNode Node) const; bool hasTypedef(const clang::Decl *typeDecl) const; diff --git a/include/swift/ClangImporter/ClangImporterOptions.h b/include/swift/ClangImporter/ClangImporterOptions.h index 3cfa1ba678cfa..4bf47c4d3575f 100644 --- a/include/swift/ClangImporter/ClangImporterOptions.h +++ b/include/swift/ClangImporter/ClangImporterOptions.h @@ -60,7 +60,10 @@ class ClangImporterOptions { /// Swift code. Normal, /// Set up Clang for backend compilation only. - EmbedBitcode + EmbedBitcode, + /// Set up Clang to emit a precompiled module from a C/Objective-C module + /// map or dump debugging info about a precompiled module. + PrecompiledModule }; /// Controls how Clang is initially set up. diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index c663d99d79155..fd668d73a69a3 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -148,6 +148,9 @@ class FrontendOptions { EmitObject, ///< Emit object file DumpTypeInfo, ///< Dump IRGen type info + + EmitPCM, ///< Emit precompiled Clang module from a module map + DumpPCM, ///< Dump information about a precompiled Clang module }; /// Indicates the action the user requested that the frontend perform. diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 166730ec17174..21b68e312e9d9 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -748,6 +748,9 @@ def emit_sibgen : Flag<["-"], "emit-sibgen">, def emit_imported_modules : Flag<["-"], "emit-imported-modules">, HelpText<"Emit a list of the imported modules">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def emit_pcm : Flag<["-"], "emit-pcm">, + HelpText<"Emit a precompiled Clang module from a module map">, ModeOpt, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; def c : Flag<["-"], "c">, Alias, Flags<[FrontendOption, NoInteractiveOption]>, ModeOpt; @@ -792,6 +795,10 @@ def print_ast : Flag<["-"], "print-ast">, HelpText<"Parse and type-check input file(s) and pretty print AST(s)">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def dump_pcm : Flag<["-"], "dump-pcm">, + HelpText<"Dump debugging information about a precompiled Clang module">, + ModeOpt, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; // Other Modes def repl : Flag<["-"], "repl">, diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 75746d617ad80..21ff9e67be946 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -902,6 +902,7 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, invocationArgStrs.push_back("clang"); switch (importerOpts.Mode) { case ClangImporterOptions::Modes::Normal: + case ClangImporterOptions::Modes::PrecompiledModule: getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts); break; case ClangImporterOptions::Modes::EmbedBitcode: @@ -1062,7 +1063,17 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, if (importerOpts.Mode == ClangImporterOptions::Modes::EmbedBitcode) return importer; + // ClangImporter always sets this in Normal mode, so we need to make sure to + // set it before bailing out early when configuring ClangImporter for + // precompiled modules. This is not a benign langopt, so forgetting this (for + // example, if we combined the early exit below with the one above) would make + // the compiler instance used to emit PCMs incompatible with the one used to + // read them later. instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; + + if (importerOpts.Mode == ClangImporterOptions::Modes::PrecompiledModule) + return importer; + bool canBegin = action->BeginSourceFile(instance, instance.getFrontendOpts().Inputs[0]); if (!canBegin) @@ -1446,43 +1457,82 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath, return result; } -bool -ClangImporter::emitBridgingPCH(StringRef headerPath, - StringRef outputPCHPath) { +/// Returns the appropriate source input language based on language options. +static clang::Language getLanguageFromOptions( + const clang::LangOptions *LangOpts) { + if (LangOpts->OpenCL) + return clang::Language::OpenCL; + if (LangOpts->CUDA) + return clang::Language::CUDA; + if (LangOpts->ObjC) + return LangOpts->CPlusPlus ? + clang::Language::ObjCXX : clang::Language::ObjC; + return LangOpts->CPlusPlus ? clang::Language::CXX : clang::Language::C; +} + +/// Wraps the given frontend action in an index data recording action if the +/// frontend options have an index store path specified. +static +std::unique_ptr wrapActionForIndexingIfEnabled( + const clang::FrontendOptions &FrontendOpts, + std::unique_ptr action) { + if (!FrontendOpts.IndexStorePath.empty()) { + return clang::index::createIndexDataRecordingAction( + FrontendOpts, std::move(action)); + } + return action; +} + +std::unique_ptr +ClangImporter::cloneCompilerInstanceForPrecompiling() { auto invocation = std::make_shared(*Impl.Invocation); - invocation->getFrontendOpts().DisableFree = false; - invocation->getFrontendOpts().Inputs.clear(); - invocation->getFrontendOpts().Inputs.push_back( - clang::FrontendInputFile(headerPath, clang::Language::ObjC)); - invocation->getFrontendOpts().OutputFile = outputPCHPath; - invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; - invocation->getPreprocessorOpts().resetNonModularOptions(); - invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true; - invocation->getLangOpts()->CacheGeneratedPCH = true; - clang::CompilerInstance emitInstance( + auto &PPOpts = invocation->getPreprocessorOpts(); + PPOpts.resetNonModularOptions(); + + auto &FrontendOpts = invocation->getFrontendOpts(); + FrontendOpts.DisableFree = false; + FrontendOpts.Inputs.clear(); + + auto clonedInstance = llvm::make_unique( Impl.Instance->getPCHContainerOperations(), &Impl.Instance->getModuleCache()); - emitInstance.setInvocation(std::move(invocation)); - emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(), - /*ShouldOwnClient=*/false); + clonedInstance->setInvocation(std::move(invocation)); + clonedInstance->createDiagnostics(&Impl.Instance->getDiagnosticClient(), + /*ShouldOwnClient=*/false); clang::FileManager &fileManager = Impl.Instance->getFileManager(); - emitInstance.setFileManager(&fileManager); - emitInstance.createSourceManager(fileManager); - emitInstance.setTarget(&Impl.Instance->getTarget()); + clonedInstance->setFileManager(&fileManager); + clonedInstance->createSourceManager(fileManager); + clonedInstance->setTarget(&Impl.Instance->getTarget()); - std::unique_ptr action; - action.reset(new clang::GeneratePCHAction()); - if (!emitInstance.getFrontendOpts().IndexStorePath.empty()) { - action = clang::index:: - createIndexDataRecordingAction(emitInstance.getFrontendOpts(), - std::move(action)); - } - emitInstance.ExecuteAction(*action); + return clonedInstance; +} + +bool +ClangImporter::emitBridgingPCH(StringRef headerPath, + StringRef outputPCHPath) { + auto emitInstance = cloneCompilerInstanceForPrecompiling(); + auto &invocation = emitInstance->getInvocation(); + + auto LangOpts = invocation.getLangOpts(); + LangOpts->NeededByPCHOrCompilationUsesPCH = true; + LangOpts->CacheGeneratedPCH = true; + + auto language = getLanguageFromOptions(LangOpts); + auto inputFile = clang::FrontendInputFile(headerPath, language); + + auto &FrontendOpts = invocation.getFrontendOpts(); + FrontendOpts.Inputs = {inputFile}; + FrontendOpts.OutputFile = outputPCHPath; + FrontendOpts.ProgramAction = clang::frontend::GeneratePCH; - if (emitInstance.getDiagnostics().hasErrorOccurred()) { + auto action = wrapActionForIndexingIfEnabled( + FrontendOpts, llvm::make_unique()); + emitInstance->ExecuteAction(*action); + + if (emitInstance->getDiagnostics().hasErrorOccurred()) { Impl.SwiftContext.Diags.diagnose({}, diag::bridging_header_pch_error, outputPCHPath, headerPath); @@ -1491,6 +1541,65 @@ ClangImporter::emitBridgingPCH(StringRef headerPath, return false; } +bool ClangImporter::emitPrecompiledModule(StringRef moduleMapPath, + StringRef moduleName, + StringRef outputPath) { + auto emitInstance = cloneCompilerInstanceForPrecompiling(); + auto &invocation = emitInstance->getInvocation(); + + auto LangOpts = invocation.getLangOpts(); + LangOpts->setCompilingModule(clang::LangOptions::CMK_ModuleMap); + LangOpts->ModuleName = moduleName; + LangOpts->CurrentModule = LangOpts->ModuleName; + + auto language = getLanguageFromOptions(LangOpts); + auto inputFile = clang::FrontendInputFile( + moduleMapPath, clang::InputKind( + language, clang::InputKind::ModuleMap, false)); + + auto &FrontendOpts = invocation.getFrontendOpts(); + FrontendOpts.Inputs = {inputFile}; + FrontendOpts.OriginalModuleMap = moduleMapPath; + FrontendOpts.OutputFile = outputPath; + FrontendOpts.ProgramAction = clang::frontend::GenerateModule; + + auto action = wrapActionForIndexingIfEnabled( + FrontendOpts, + llvm::make_unique()); + emitInstance->ExecuteAction(*action); + + if (emitInstance->getDiagnostics().hasErrorOccurred()) { + Impl.SwiftContext.Diags.diagnose({}, + diag::emit_pcm_error, + outputPath, moduleMapPath); + return true; + } + return false; +} + +bool ClangImporter::dumpPrecompiledModule(StringRef modulePath, + StringRef outputPath) { + auto dumpInstance = cloneCompilerInstanceForPrecompiling(); + auto &invocation = dumpInstance->getInvocation(); + + auto inputFile = clang::FrontendInputFile( + modulePath, clang::InputKind( + clang::Language::Unknown, clang::InputKind::Precompiled, false)); + + auto &FrontendOpts = invocation.getFrontendOpts(); + FrontendOpts.Inputs = {inputFile}; + FrontendOpts.OutputFile = outputPath; + + auto action = llvm::make_unique(); + dumpInstance->ExecuteAction(*action); + + if (dumpInstance->getDiagnostics().hasErrorOccurred()) { + Impl.SwiftContext.Diags.diagnose({}, diag::dump_pcm_error, modulePath); + return true; + } + return false; +} + void ClangImporter::collectVisibleTopLevelModuleNames( SmallVectorImpl &names) const { SmallVector Modules; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index c696ac204bf93..9622b4c052a9a 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1410,6 +1410,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, OI.CompilerOutputType = file_types::TY_PCH; break; + case options::OPT_emit_pcm: + OI.CompilerMode = OutputInfo::Mode::SingleCompile; + OI.CompilerOutputType = file_types::TY_ClangModuleFile; + break; + case options::OPT_emit_imported_modules: OI.CompilerOutputType = file_types::TY_ImportedModules; // We want the imported modules from the module as a whole, not individual @@ -1442,6 +1447,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, OI.CompilerOutputType = file_types::TY_Nothing; break; + case options::OPT_dump_pcm: + OI.CompilerMode = OutputInfo::Mode::SingleCompile; + OI.CompilerOutputType = file_types::TY_Nothing; + break; + case options::OPT_i: // Keep the default output/mode; this flag was removed and should already // have been diagnosed above. @@ -1842,6 +1852,11 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, } case OutputInfo::Mode::SingleCompile: { if (Inputs.empty()) break; + if (Args.hasArg(options::OPT_emit_pcm) && Inputs.size() != 1) { + // -emit-pcm mode requires exactly one input (the module map). + Diags.diagnose(SourceLoc(), diag::error_mode_requires_one_input_file); + return; + } if (Args.hasArg(options::OPT_embed_bitcode)) { // Make sure we can handle the inputs. bool HandledHere = true; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 1dd32b67bd156..26e52ce79b790 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -499,6 +499,8 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { return "-emit-ir"; case file_types::TY_LLVM_BC: return "-emit-bc"; + case file_types::TY_ClangModuleFile: + return "-emit-pcm"; case file_types::TY_Assembly: return "-S"; case file_types::TY_SwiftModuleFile: @@ -522,7 +524,6 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { case file_types::TY_AutolinkFile: case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: - case file_types::TY_ClangModuleFile: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: case file_types::TY_Image: @@ -758,6 +759,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_SIL: case file_types::TY_SIB: case file_types::TY_PCH: + case file_types::TY_ClangModuleFile: case file_types::TY_IndexData: llvm_unreachable("Cannot be output from backend job"); case file_types::TY_Swift: @@ -765,7 +767,6 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_AutolinkFile: case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: - case file_types::TY_ClangModuleFile: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: case file_types::TY_Image: diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 96a36172c22c0..ca5a2b5cdee2e 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -393,6 +393,10 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) { return FrontendOptions::ActionType::DumpTypeInfo; if (Opt.matches(OPT_print_ast)) return FrontendOptions::ActionType::PrintAST; + if (Opt.matches(OPT_emit_pcm)) + return FrontendOptions::ActionType::EmitPCM; + if (Opt.matches(OPT_dump_pcm)) + return FrontendOptions::ActionType::DumpPCM; if (Opt.matches(OPT_repl) || Opt.matches(OPT_deprecated_integrated_repl)) return FrontendOptions::ActionType::REPL; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 32c0d0da0c256..66dd41b2fdbd0 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -596,6 +596,9 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, if (Args.hasArg(OPT_embed_bitcode)) Opts.Mode = ClangImporterOptions::Modes::EmbedBitcode; + else if (Args.hasArg(OPT_emit_pcm) || Args.hasArg(OPT_dump_pcm)) + Opts.Mode = ClangImporterOptions::Modes::PrecompiledModule; + if (auto *A = Args.getLastArg(OPT_import_objc_header)) Opts.BridgingHeader = A->getValue(); Opts.DisableSwiftBridgeAttr |= Args.hasArg(OPT_disable_swift_bridge_attr); diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 46e60edfb27c6..dd21f74b00dfb 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -40,6 +40,7 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { case ActionType::PrintAST: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: + case ActionType::DumpPCM: return false; case ActionType::EmitPCH: case ActionType::EmitSILGen: @@ -59,6 +60,7 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { case ActionType::EmitObject: case ActionType::EmitImportedModules: case ActionType::DumpTypeInfo: + case ActionType::EmitPCM: return true; } llvm_unreachable("Unknown ActionType"); @@ -95,6 +97,8 @@ bool FrontendOptions::isActionImmediate(ActionType action) { case ActionType::EmitObject: case ActionType::EmitImportedModules: case ActionType::DumpTypeInfo: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; } llvm_unreachable("Unknown ActionType"); @@ -154,6 +158,7 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) { case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: + case ActionType::DumpPCM: return TY_Nothing; case ActionType::EmitPCH: @@ -195,6 +200,9 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) { case ActionType::EmitImportedModules: return TY_ImportedModules; + + case ActionType::EmitPCM: + return TY_ClangModuleFile; } llvm_unreachable("unhandled action"); } @@ -214,6 +222,7 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::DumpPCM: return false; case ActionType::ResolveImports: case ActionType::Typecheck: @@ -229,6 +238,7 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) { case ActionType::EmitAssembly: case ActionType::EmitObject: case ActionType::EmitImportedModules: + case ActionType::EmitPCM: return true; } llvm_unreachable("unhandled action"); @@ -250,6 +260,8 @@ bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Typecheck: case ActionType::MergeModules: @@ -286,6 +298,8 @@ bool FrontendOptions::canActionEmitObjCHeader(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Typecheck: case ActionType::MergeModules: @@ -319,6 +333,8 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::ResolveImports: case ActionType::Typecheck: @@ -358,6 +374,8 @@ bool FrontendOptions::canActionEmitModule(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::MergeModules: case ActionType::EmitModuleOnly: @@ -398,6 +416,8 @@ bool FrontendOptions::canActionEmitInterface(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Typecheck: case ActionType::MergeModules: @@ -439,6 +459,8 @@ bool FrontendOptions::doesActionProduceOutput(ActionType action) { case ActionType::MergeModules: case ActionType::CompileModuleFromInterface: case ActionType::DumpTypeInfo: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return true; case ActionType::NoneAction: @@ -462,6 +484,7 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) { case ActionType::EmitObject: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: return false; case ActionType::Parse: @@ -480,6 +503,7 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) { case ActionType::EmitAssembly: case ActionType::EmitIR: case ActionType::DumpTypeInfo: + case ActionType::DumpPCM: return true; } llvm_unreachable("unhandled action"); @@ -501,6 +525,8 @@ bool FrontendOptions::doesActionGenerateSIL(ActionType action) { case ActionType::EmitImportedModules: case ActionType::EmitPCH: case ActionType::CompileModuleFromInterface: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::EmitSILGen: case ActionType::EmitSIBGen: @@ -543,6 +569,8 @@ bool FrontendOptions::doesActionGenerateIR(ActionType action) { case ActionType::EmitSIBGen: case ActionType::EmitSIB: case ActionType::EmitImportedModules: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Immediate: case ActionType::REPL: diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 7cb57df324d3a..44629ad48987c 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -764,6 +764,29 @@ static bool precompileBridgingHeader(CompilerInvocation &Invocation, .InputsAndOutputs.getSingleOutputFilename()); } +static bool precompileClangModule(CompilerInvocation &Invocation, + CompilerInstance &Instance) { + auto clangImporter = static_cast( + Instance.getASTContext().getClangModuleLoader()); + return clangImporter->emitPrecompiledModule( + Invocation.getFrontendOptions() + .InputsAndOutputs.getFilenameOfFirstInput(), + Invocation.getFrontendOptions().ModuleName, + Invocation.getFrontendOptions() + .InputsAndOutputs.getSingleOutputFilename()); +} + +static bool dumpPrecompiledClangModule(CompilerInvocation &Invocation, + CompilerInstance &Instance) { + auto clangImporter = static_cast( + Instance.getASTContext().getClangModuleLoader()); + return clangImporter->dumpPrecompiledModule( + Invocation.getFrontendOptions() + .InputsAndOutputs.getFilenameOfFirstInput(), + Invocation.getFrontendOptions() + .InputsAndOutputs.getSingleOutputFilename()); +} + static bool buildModuleFromInterface(CompilerInvocation &Invocation, CompilerInstance &Instance) { const FrontendOptions &FEOpts = Invocation.getFrontendOptions(); @@ -1133,10 +1156,14 @@ static bool performCompile(CompilerInstance &Instance, Instance.getASTContext().LangOpts.VerifySyntaxTree = true; } - // We've been asked to precompile a bridging header; we want to + // We've been asked to precompile a bridging header or module; we want to // avoid touching any other inputs and just parse, emit and exit. if (Action == FrontendOptions::ActionType::EmitPCH) return precompileBridgingHeader(Invocation, Instance); + if (Action == FrontendOptions::ActionType::EmitPCM) + return precompileClangModule(Invocation, Instance); + if (Action == FrontendOptions::ActionType::DumpPCM) + return dumpPrecompiledClangModule(Invocation, Instance); if (Action == FrontendOptions::ActionType::CompileModuleFromInterface) return buildModuleFromInterface(Invocation, Instance); diff --git a/test/ClangImporter/pcm-emit-and-import.swift b/test/ClangImporter/pcm-emit-and-import.swift new file mode 100644 index 0000000000000..d2845aeccc5bb --- /dev/null +++ b/test/ClangImporter/pcm-emit-and-import.swift @@ -0,0 +1,15 @@ +// Emit the explicit module. +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-pcm -module-name script -o %t/script.pcm %S/Inputs/custom-modules/module.map + +// Verify some of the output of the -dump-pcm flag. +// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-DUMP +// CHECK-DUMP: Information for module file '{{.*}}/script.pcm': +// CHECK-DUMP: Module name: script +// CHECK-DUMP: Module map file: {{.*}}/Inputs/custom-modules/module.map + +// Compile a source file that imports the explicit module. +// RUN: %target-swift-frontend -typecheck -verify -Xcc -fmodule-file=%t/script.pcm %s + +import script +var _ : ScriptTy diff --git a/test/lit.cfg b/test/lit.cfg index f274c7403f88f..8986acdb8d188 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -402,6 +402,7 @@ config.substitutions.append( ('%sil-passpipeline-dumper', "%r" % (config.sil_pas config.substitutions.append( ('%lldb-moduleimport-test', "%r %s" % (config.lldb_moduleimport_test, mcp_opt)) ) config.substitutions.append( ('%lldb-moduleimport-test-with-sdk', '%s -sdk %r' % (config.lldb_moduleimport_test, config.variant_sdk)) ) +config.substitutions.append( ('%swift-dump-pcm', "%r -dump-pcm" % config.swiftc) ) config.substitutions.append( ('%swift-ide-test_plain', config.swift_ide_test) ) config.substitutions.append( ('%swift-ide-test', "%r %s %s -swift-version %s" % (config.swift_ide_test, mcp_opt, ccp_opt, swift_version)) ) config.substitutions.append( ('%swift-syntax-test', config.swift_syntax_test) ) @@ -895,6 +896,9 @@ if run_vendor == 'apple': config.target_swift_modulewrap = ( '%s -modulewrap -target %s' % (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = ( + '%s -emit-pcm -target %s' % + (config.swiftc, config.variant_triple)) subst_target_swift_frontend_mock_sdk_after = \ target_options_for_mock_sdk_after config.target_sil_opt = ( @@ -984,6 +988,8 @@ elif run_os in ['windows-msvc']: resource_dir_opt, mcp_opt)) config.target_swift_modulewrap = \ ('%r -modulewrap -target %s' % (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = \ + ('%r -emit-pcm -target %s' % (config.swiftc, config.variant_triple)) elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'windows-gnu'] or @@ -1071,6 +1077,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_swift_modulewrap = ( '%s -modulewrap -target %s' % (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = ( + '%s -emit-pcm -target %s' % + (config.swiftc, config.variant_triple)) config.target_clang = ( "clang++ -target %s %s -fobjc-runtime=ios-5.0" % (config.variant_triple, clang_mcp_opt)) @@ -1191,6 +1200,9 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': config.target_swift_modulewrap = ' '.join([ config.swiftc, '-modulewrap', '-target', config.variant_triple]) + config.target_swift_emit_pcm = ' '.join([ + config.swiftc, '-emit-pcm', + '-target', config.variant_triple]) config.target_clang = ' '.join([ 'clang++', '-target', config.variant_triple, @@ -1617,6 +1629,8 @@ if hasattr(config, 'target_swift_autolink_extract'): config.substitutions.append(('%target-swift-modulewrap', config.target_swift_modulewrap)) +config.substitutions.append(('%target-swift-emit-pcm', + config.target_swift_emit_pcm)) config.substitutions.insert(0, ('%platform-module-dir', platform_module_dir)) config.substitutions.insert(0, ('%platform-sdk-overlay-dir', platform_sdk_overlay_dir)) From 2c7b518460c08fe0b3b5dcee6c9048e2bf863d4a Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 5 Nov 2019 16:03:29 -0800 Subject: [PATCH 078/283] Consolidate code that computes resource dir relative to swift executable. --- include/swift/Frontend/Frontend.h | 6 ++++++ lib/Frontend/CompilerInvocation.cpp | 14 ++++++++++---- tools/driver/modulewrap_main.cpp | 10 +++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index c779245b70358..c616bdfb75d65 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -202,6 +202,12 @@ class CompilerInvocation { void setRuntimeResourcePath(StringRef Path); + /// Computes the runtime resource path relative to the given Swift + /// executable. + static void computeRuntimeResourcePathFromExecutablePath( + StringRef mainExecutablePath, + llvm::SmallString<128> &runtimeResourcePath); + void setSDKPath(const std::string &Path); StringRef getSDKPath() const { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 66dd41b2fdbd0..a35df4961bf80 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -38,11 +38,17 @@ swift::CompilerInvocation::CompilerInvocation() { setTargetTriple(llvm::sys::getDefaultTargetTriple()); } +void CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( + StringRef mainExecutablePath, llvm::SmallString<128> &runtimeResourcePath) { + runtimeResourcePath.assign(mainExecutablePath); + llvm::sys::path::remove_filename(runtimeResourcePath); // Remove /swift + llvm::sys::path::remove_filename(runtimeResourcePath); // Remove /bin + llvm::sys::path::append(runtimeResourcePath, "lib", "swift"); +} + void CompilerInvocation::setMainExecutablePath(StringRef Path) { - llvm::SmallString<128> LibPath(Path); - llvm::sys::path::remove_filename(LibPath); // Remove /swift - llvm::sys::path::remove_filename(LibPath); // Remove /bin - llvm::sys::path::append(LibPath, "lib", "swift"); + llvm::SmallString<128> LibPath; + computeRuntimeResourcePathFromExecutablePath(Path, LibPath); setRuntimeResourcePath(LibPath.str()); llvm::SmallString<128> DiagnosticDocsPath(Path); diff --git a/tools/driver/modulewrap_main.cpp b/tools/driver/modulewrap_main.cpp index 886d7a62fba45..7c5e30a4b7b51 100644 --- a/tools/driver/modulewrap_main.cpp +++ b/tools/driver/modulewrap_main.cpp @@ -156,13 +156,9 @@ int modulewrap_main(ArrayRef Args, const char *Argv0, // Wrap the bitstream in a module object file. To use the ClangImporter to // create the module loader, we need to properly set the runtime library path. SearchPathOptions SearchPathOpts; - // FIXME: This logic has been duplicated from - // CompilerInvocation::setMainExecutablePath. ModuleWrapInvocation - // should share its implementation. - SmallString<128> RuntimeResourcePath(MainExecutablePath); - llvm::sys::path::remove_filename(RuntimeResourcePath); // Remove /swift - llvm::sys::path::remove_filename(RuntimeResourcePath); // Remove /bin - llvm::sys::path::append(RuntimeResourcePath, "lib", "swift"); + SmallString<128> RuntimeResourcePath; + CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( + MainExecutablePath, RuntimeResourcePath); SearchPathOpts.RuntimeResourcePath = RuntimeResourcePath.str(); SourceManager SrcMgr; From 3745dcc3e4589972154a8b264f087a476df46678 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Mon, 11 Nov 2019 12:01:31 -0800 Subject: [PATCH 079/283] [TBDGen] Match TAPI's truncation behavior for dylib versions TAPI and the linker truncate individual version components to 255 if they overflow, because the linker packs 3 version components into a 32-bit int. Make sure we use the same parsing routines as TAPI. Fixes rdar://57043178 --- include/swift/AST/DiagnosticsFrontend.def | 9 ++++ include/swift/TBDGen/TBDGen.h | 8 +-- lib/Frontend/CompilerInvocation.cpp | 11 ++-- lib/TBDGen/TBDGen.cpp | 65 +++++++++++++++++------ test/TBD/dylib-version-truncation.swift | 27 ++++++++++ test/TBD/dylib-version.swift | 2 +- 6 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 test/TBD/dylib-version-truncation.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index b171c0f7dd92b..1cd364357fda6 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -232,6 +232,15 @@ ERROR(error_formatting_invalid_range,none, WARNING(stats_disabled,none, "compiler was not built with support for collecting statistics", ()) +WARNING(tbd_warn_truncating_version,none, + "truncating %select{current|compatibility}0 version '%1' in TBD file " + "to fit in 32-bit space used by old mach-o format", + (unsigned, StringRef)) + +ERROR(tbd_err_invalid_version,none, + "invalid dynamic library %select{current|compatibility}0 version '%1'", + (unsigned, StringRef)) + WARNING(tbd_only_supported_in_whole_module,none, "TBD generation is only supported when the whole module can be seen", ()) diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index 2d0f0f8867b82..c78803ff58e38 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -40,12 +40,12 @@ struct TBDGenOptions { std::string ModuleLinkName; /// The current project version to use in the generated TBD file. Defaults - /// to None. - llvm::Optional CurrentVersion = None; + /// to empty string if not provided. + std::string CurrentVersion; /// The dylib compatibility-version to use in the generated TBD file. Defaults - /// to None. - llvm::Optional CompatibilityVersion = None; + /// to empty string if not provided. + std::string CompatibilityVersion; }; void enumeratePublicSymbols(FileUnit *module, llvm::StringSet<> &symbols, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 32c0d0da0c256..58ba8278a636a 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -961,16 +961,11 @@ static bool ParseTBDGenArgs(TBDGenOptions &Opts, ArgList &Args, Opts.IsInstallAPI = Args.hasArg(OPT_tbd_is_installapi); if (const Arg *A = Args.getLastArg(OPT_tbd_compatibility_version)) { - if (auto vers = version::Version::parseVersionString( - A->getValue(), SourceLoc(), &Diags)) { - Opts.CompatibilityVersion = *vers; - } + Opts.CompatibilityVersion = A->getValue(); } + if (const Arg *A = Args.getLastArg(OPT_tbd_current_version)) { - if (auto vers = version::Version::parseVersionString( - A->getValue(), SourceLoc(), &Diags)) { - Opts.CurrentVersion = *vers; - } + Opts.CurrentVersion = A->getValue(); } return false; } diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 67e6fbf8c5cec..f9929708d3cda 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -19,6 +19,7 @@ #include "swift/AST/Availability.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" @@ -593,16 +594,44 @@ void TBDGenVisitor::addFirstFileSymbols() { } } -/// Converts a version tuple into a packed version, ignoring components beyond -/// major, minor, and subminor. -static llvm::MachO::PackedVersion -convertToPacked(const version::Version &version) { - // FIXME: Warn if version is greater than 3 components? - unsigned major = 0, minor = 0, subminor = 0; - if (version.size() > 0) major = version[0]; - if (version.size() > 1) minor = version[1]; - if (version.size() > 2) subminor = version[2]; - return llvm::MachO::PackedVersion(major, minor, subminor); +/// The kind of version being parsed, used for diagnostics. +/// Note: Must match the order in DiagnosticsFrontend.def +enum DylibVersionKind_t: unsigned { + CurrentVersion, + CompatibilityVersion +}; + +/// Converts a version string into a packed version, truncating each component +/// if necessary to fit all 3 into a 32-bit packed structure. +/// +/// For example, the version '1219.37.11' will be packed as +/// +/// Major (1,219) Minor (37) Patch (11) +/// ┌───────────────────┬──────────┬──────────┐ +/// │ 00001100 11000011 │ 00100101 │ 00001011 │ +/// └───────────────────┴──────────┴──────────┘ +/// +/// If an individual component is greater than the highest number that can be +/// represented in its alloted space, it will be truncated to the maximum value +/// that fits in the alloted space, which matches the behavior of the linker. +static Optional +parsePackedVersion(DylibVersionKind_t kind, StringRef versionString, + ASTContext &ctx) { + if (versionString.empty()) + return None; + + llvm::MachO::PackedVersion version; + auto result = version.parse64(versionString); + if (!result.first) { + ctx.Diags.diagnose(SourceLoc(), diag::tbd_err_invalid_version, + (unsigned)kind, versionString); + return None; + } + if (result.second) { + ctx.Diags.diagnose(SourceLoc(), diag::tbd_warn_truncating_version, + (unsigned)kind, versionString); + } + return version; } static bool isApplicationExtensionSafe(const LangOptions &LangOpts) { @@ -627,16 +656,20 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, file.setApplicationExtensionSafe( isApplicationExtensionSafe(M->getASTContext().LangOpts)); file.setInstallName(opts.InstallName); - if (auto currentVersion = opts.CurrentVersion) { - file.setCurrentVersion(convertToPacked(*currentVersion)); - } - if (auto compatibilityVersion = opts.CompatibilityVersion) { - file.setCompatibilityVersion(convertToPacked(*compatibilityVersion)); - } file.setTwoLevelNamespace(); file.setSwiftABIVersion(irgen::getSwiftABIVersion()); file.setInstallAPI(opts.IsInstallAPI); + if (auto packed = parsePackedVersion(CurrentVersion, + opts.CurrentVersion, ctx)) { + file.setCurrentVersion(*packed); + } + + if (auto packed = parsePackedVersion(CompatibilityVersion, + opts.CompatibilityVersion, ctx)) { + file.setCompatibilityVersion(*packed); + } + llvm::MachO::Target target(triple); file.addTarget(target); diff --git a/test/TBD/dylib-version-truncation.swift b/test/TBD/dylib-version-truncation.swift new file mode 100644 index 0000000000000..bc9bb431f6892 --- /dev/null +++ b/test/TBD/dylib-version-truncation.swift @@ -0,0 +1,27 @@ +// REQUIRES: VENDOR=apple +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 2.0 | %FileCheck %s --check-prefix TWOPOINTZERO +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 2 | %FileCheck %s --check-prefix TWOPOINTZERO +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 20.10 | %FileCheck %s --check-prefix TWENTYPOINTTEN + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 2.0 | %FileCheck %s --check-prefix TWOPOINTZEROCOMPAT +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 2 | %FileCheck %s --check-prefix TWOPOINTZEROCOMPAT +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 20.10 | %FileCheck %s --check-prefix TWENTYPOINTTENCOMPAT + +// Make sure we correctly truncate a value over 255 + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 20.300 2>&1 | %FileCheck %s --check-prefix TWENTYPOINTTHREEHUNDRED +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 20.300 2>&1 | %FileCheck %s --check-prefix TWENTYPOINTTHREEHUNDREDCOMPAT + +// TWOPOINTZERO: current-version: 2 +// TWENTYPOINTTEN: current-version: 20.10 + +// TWOPOINTZEROCOMPAT: compatibility-version: 2 +// TWENTYPOINTTENCOMPAT: compatibility-version: 20.10 + +// TWENTYPOINTTHREEHUNDRED: warning: truncating current version '20.300' in TBD file to fit in 32-bit space used by old mach-o format +// TWENTYPOINTTHREEHUNDRED: current-version: 20.255 + +// TWENTYPOINTTHREEHUNDREDCOMPAT: warning: truncating compatibility version '20.300' in TBD file to fit in 32-bit space used by old mach-o format +// TWENTYPOINTTHREEHUNDREDCOMPAT: compatibility-version: 20.255 diff --git a/test/TBD/dylib-version.swift b/test/TBD/dylib-version.swift index 242cf2fc7322b..7edb907acdb85 100644 --- a/test/TBD/dylib-version.swift +++ b/test/TBD/dylib-version.swift @@ -14,4 +14,4 @@ // CURRENT: current-version: 2 // COMPAT: compatibility-version: 2 -// BOGUS: version component contains non-numeric characters +// BOGUS: invalid dynamic library compatibility version 'not_a_version_string' From 93ce8a068f3a01f01f5890260d11a95744a21f67 Mon Sep 17 00:00:00 2001 From: Agisilaos Tsaraboulidis Date: Tue, 12 Nov 2019 01:17:19 +0200 Subject: [PATCH 080/283] Improve README: Add a notice to cd into swift directory before building Swift (#16227) * Improve doc: Add notice to cd into swift directory Added another "step" before someone tries to build swift so I can make the process easier for newbies. * fix typo * adress harlan feedback --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 755b98e4e71bc..805b7714bfe54 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,10 @@ There are two primary build systems to use: Xcode and Ninja. The Xcode build system allows you to work in Xcode, but Ninja is a bit faster and supports more environments. +First, make sure that you're in the swift directory: + + cd swift + To build using Ninja, run: swift/utils/build-script --release-debuginfo @@ -326,8 +330,8 @@ expressed today. ## Build Dependencies ### CMake -[CMake](https://cmake.org) is the core infrastructure used to configure builds of -Swift and its companion projects; at least version 3.4.3 is required. +[CMake](https://cmake.org) is the core infrastructure used to configure builds of +Swift and its companion projects; at least version 3.4.3 is required. On macOS, you can download the [CMake Binary Distribution](https://cmake.org/download), bundled as an application, copy it to `/Applications`, and add the embedded From 854ffdd424d5eacea1856746bf60242466347c67 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 11 Nov 2019 15:21:05 -0800 Subject: [PATCH 081/283] [NFC] Drop typeCheckExternalDefinitions --- include/swift/Subsystems.h | 3 --- lib/Sema/TypeChecker.cpp | 6 ------ 2 files changed, 9 deletions(-) diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 2e91d7248fa7c..615f0ed4a1156 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -232,9 +232,6 @@ namespace swift { /// of declarations in the module. void checkInconsistentImplementationOnlyImports(ModuleDecl *M); - /// Incrementally type-check only added external definitions. - void typeCheckExternalDefinitions(SourceFile &SF); - /// Recursively validate the specified type. /// /// This is used when dealing with partial source files (e.g. SIL parsing, diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index d06c5c7ea2de4..b35eaff8f8688 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -334,12 +334,6 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) TC.definedFunctions.clear(); } -void swift::typeCheckExternalDefinitions(SourceFile &SF) { - assert(SF.ASTStage == SourceFile::TypeChecked); - auto &Ctx = SF.getASTContext(); - typeCheckFunctionsAndExternalDecls(SF, createTypeChecker(Ctx)); -} - void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, OptionSet Options, unsigned StartElem, From e385439e6efd254bae2f4295d5c80efad0613b5d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 11 Nov 2019 16:10:48 -0800 Subject: [PATCH 082/283] Use ArgEmitter --- lib/SILGen/SILGenApply.cpp | 29 +++++++++++++++++++++++++++++ lib/SILGen/SILGenExpr.cpp | 22 ++++++++++++++-------- lib/SILGen/SILGenFunction.h | 3 +++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 00be4b4e930fa..a29bd3c2c51f9 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6067,6 +6067,35 @@ RValue SILGenFunction::emitDynamicSubscriptExpr(DynamicSubscriptExpr *e, return RValue(*this, e, emitManagedRValueWithCleanup(optResult, optTL)); } +void SILGenFunction::emitKeyPathSubscriptOperands(CanSILFunctionType fnType, + Expr *indexExpr) { + SmallVector argValues; + SmallVector delayedArgs; +// auto fnType = F.getLoweredFunctionType(); + ArgEmitter emitter(*this, fnType.getPointer()->getRepresentation(), /*yield*/ true, + /*isForCoroutine*/ false, + ClaimedParamsRef(fnType, fnType.getPointer()->getParameters()), + argValues, delayedArgs, + /*foreign error*/ None, ImportAsMemberStatus()); + + SmallVector indexExprs; + if (auto paren = dyn_cast(indexExpr)) { + indexExprs.push_back(indexExpr); + } else if (auto tupleExpr = dyn_cast(indexExpr)) { + for (auto *tupleElement : tupleExpr->getElements()) { + indexExprs.push_back(tupleElement); + } + } + + for (auto *expr : indexExprs) { + emitter.emitSingleArg(ArgumentSource(expr), + AbstractionPattern(expr->getType())); + } + + if (!delayedArgs.empty()) + emitDelayedArguments(*this, delayedArgs, argValues); +} + ManagedValue ArgumentScope::popPreservingValue(ManagedValue mv) { formalEvalScope.pop(); return normalScope.popPreservingValue(mv); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 322d17c2bf9c8..919457ed78cbf 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3626,15 +3626,21 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { component.getSubscriptIndexHashableConformances(), baseTy, /*for descriptor*/ false)); - if (kind == KeyPathExpr::Component::Kind::Subscript) { - lowerSubscriptOperands(component); - } else { - lowerOperands(component); - } - - assert(numOperands == operands.size() - && "operand count out of sync"); +// if (kind == KeyPathExpr::Component::Kind::Subscript) { +// lowerSubscriptOperands(component); +// } else { +// lowerOperands(component); +// } + +// assert(numOperands == operands.size() +// && "operand count out of sync"); baseTy = loweredComponents.back().getComponentType(); + baseTy.dump(); +// loweredComponents.back().g + auto subscriptFn = loweredComponents.back().getComputedPropertyGetter(); + SGF.prepareSubscriptIndices(<#SubscriptDecl *subscript#>, <#SubstitutionMap subs#>, <#AccessStrategy strategy#>, <#Expr *indices#>) + SGF.emitKeyPathSubscriptOperands(SGF.F.getLoweredFunctionType(), // subscriptFn->getLoweredFunctionType(), + component.getIndexExpr()); break; } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 0887461ea7992..ca6931e5313d7 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -665,6 +665,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction IsFreeFunctionWitness_t isFree, bool isSelfConformance); + void emitKeyPathSubscriptOperands(CanSILFunctionType fnType, + Expr *indexExpr); + /// Convert a block to a native function with a thunk. ManagedValue emitBlockToFunc(SILLocation loc, ManagedValue block, From 44bde5493872a2f792b39c59e6abb3c1c3f40b3b Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 11 Nov 2019 16:24:46 -0800 Subject: [PATCH 083/283] Update WindowsBuild.md Update dispatch parameters for Foundation --- docs/WindowsBuild.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index 9f3a4002d7b89..e42e015c69037 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -157,15 +157,14 @@ cd "S:\b\foundation cmake -G Ninja^ -DCMAKE_BUILD_TYPE=RelWithDebInfo^ -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_SWIFT_COMPILER=S:\b\toolchain\bin\swiftc.exe^ + -DCMAKE_Swift_COMPILER=S:\b\toolchain\bin\swiftc.exe^ -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib"^ -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include"^ -DENABLE_TESTING=YES^ -DICU_ROOT="S:/Library/icu-64"^ -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2.lib"^ -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include"^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=S:\b\libdispatch^ + -Ddispatch_DIR=S:\b\libdispatch\cmake\modules^ -DFOUNDATION_PATH_TO_XCTEST_BUILD=S:\b\xctest^ S:\swift-corelibs-foundation ninja From 9d08d4f48cc71bbd38b9b9db22b92390e87baf00 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Mon, 11 Nov 2019 17:25:35 -0800 Subject: [PATCH 084/283] [ModuleInterface] Don't print @_staticInitializeObjCMetadata This attribute is computed during the build and is rejected by the parser. Make sure not to print it. Fixes rdar://56923079 --- lib/AST/ASTPrinter.cpp | 8 ++++++- ...static-initialize-objc-metadata-attr.swift | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/ModuleInterface/static-initialize-objc-metadata-attr.swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 76ab23270673d..af61b5e16c02b 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -196,7 +196,13 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { // the default to 'public' and mark the 'internal' things. result.PrintAccess = true; - result.ExcludeAttrList = {DAK_AccessControl, DAK_SetterAccess, DAK_Lazy}; + result.ExcludeAttrList = { + DAK_AccessControl, + DAK_SetterAccess, + DAK_Lazy, + DAK_StaticInitializeObjCMetadata, + DAK_RestatedObjCConformance + }; return result; } diff --git a/test/ModuleInterface/static-initialize-objc-metadata-attr.swift b/test/ModuleInterface/static-initialize-objc-metadata-attr.swift new file mode 100644 index 0000000000000..f2ed55450f8e2 --- /dev/null +++ b/test/ModuleInterface/static-initialize-objc-metadata-attr.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -target %target-pre-stable-abi-triple -module-name Module | %FileCheck %s +// REQUIRES: objc_interop + +import Foundation + +// To infer @_staticInitializeObjCMetadata, the following needs to be true +// Our class needs to be: +// - A subclass of a generic Objective-C class +// - That inherits a conformance to a protocol +// - Declared in a module with a deployment target before the stable ABI + +public class Super: NSObject, NSCoding { + required public init(coder: NSCoder) {} + public func encode(with: NSCoder) {} +} + +// CHECK-NOT: @_staticInitializeObjCMetadata +// CHECK: public class Sub : Module.Super +public class Sub: Super { + required public init(coder: NSCoder) {} + override public func encode(with: NSCoder) {} +} From 51d7d6aec1a6be72d28bafc184164eb6a4d27ad1 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 11 Nov 2019 18:17:52 -0800 Subject: [PATCH 085/283] [Docs] Minor edits to differentiable programming manifesto. (#28200) Polish "zero tangent vector initialization" doc comments. --- docs/DifferentiableProgramming.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index 935f7a94572a0..78b68eb48d732 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -971,21 +971,20 @@ public protocol Differentiable { /// A closure that produces a zero tangent vector and does not capture `self`. /// - /// A zero tangent vector of `self` is equal to `TangentVector.zero` - /// sometimes. In some cases, the zero tangent vector dependes on - /// information on `self`, such as shape. For differentiable programming, it - /// is more memory-efficient to define a custom - /// `zeroTangentVectorInitializer` to return a closure that captures and - /// uses such information to create a zero tangent vector. For example: + /// In some cases, the zero tangent vector of `self` is equal to + /// `TangentVector.zero`. In other cases, the zero tangent vector depends on + /// information in `self`, such as shape for an n-dimensional array type. + /// For differentiable programming, it is more memory-efficient to define a + /// custom `zeroTangentVectorInitializer` property which returns a closure + /// that captures and uses only the necessary information to create a zero + /// tangent vector. For example: /// /// ```swift /// struct Vector { - /// var scalars: [Float] - /// var count: Int { scalars.count } - /// init(repeating repeatedElement: Float, count: Int) { ... } + /// var scalars: [Float] + /// var count: Int { scalars.count } + /// init(repeating repeatedElement: Float, count: Int) { ... } /// } - /// - /// ... /// /// extension Vector: Differentiable { /// typealias TangentVector = Vector From 51dc7c85357010a11b42afa9e69f5c4ad01977e0 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 11 Nov 2019 19:35:47 -0800 Subject: [PATCH 086/283] Create emitKeyPathSubscriptOperands to lower all the args properly (including the default ones) --- lib/SILGen/SILGenApply.cpp | 48 ++++++++++++---------- lib/SILGen/SILGenExpr.cpp | 81 ++++++------------------------------- lib/SILGen/SILGenFunction.h | 9 +++-- 3 files changed, 44 insertions(+), 94 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index a29bd3c2c51f9..18d1ef321aba5 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6067,33 +6067,37 @@ RValue SILGenFunction::emitDynamicSubscriptExpr(DynamicSubscriptExpr *e, return RValue(*this, e, emitManagedRValueWithCleanup(optResult, optTL)); } -void SILGenFunction::emitKeyPathSubscriptOperands(CanSILFunctionType fnType, - Expr *indexExpr) { +SmallVector SILGenFunction::emitKeyPathSubscriptOperands( + SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr, + CanSILFunctionType fnType) { + Type interfaceType = subscript->getInterfaceType(); SmallVector argValues; SmallVector delayedArgs; -// auto fnType = F.getLoweredFunctionType(); - ArgEmitter emitter(*this, fnType.getPointer()->getRepresentation(), /*yield*/ true, - /*isForCoroutine*/ false, - ClaimedParamsRef(fnType, fnType.getPointer()->getParameters()), - argValues, delayedArgs, - /*foreign error*/ None, ImportAsMemberStatus()); - - SmallVector indexExprs; - if (auto paren = dyn_cast(indexExpr)) { - indexExprs.push_back(indexExpr); - } else if (auto tupleExpr = dyn_cast(indexExpr)) { - for (auto *tupleElement : tupleExpr->getElements()) { - indexExprs.push_back(tupleElement); - } - } - - for (auto *expr : indexExprs) { - emitter.emitSingleArg(ArgumentSource(expr), - AbstractionPattern(expr->getType())); - } + ArgEmitter emitter( + *this, fnType.getPointer()->getRepresentation(), + /*yield*/ true, + /*isForCoroutine*/ false, + ClaimedParamsRef(fnType, fnType.getPointer()->getParameters()), argValues, + delayedArgs, + /*foreign error*/ None, ImportAsMemberStatus()); + + CanFunctionType substFnType = + subs ? cast(interfaceType->castTo() + ->substGenericArgs(subs) + ->getCanonicalType()) + : cast(interfaceType->getCanonicalType()); + + AbstractionPattern origFnType(substFnType); + auto prepared = + prepareSubscriptIndices(subscript, subs, + // Strategy doesn't matter + AccessStrategy::getStorage(), indexExpr); + emitter.emitPreparedArgs(std::move(prepared), origFnType); if (!delayedArgs.empty()) emitDelayedArguments(*this, delayedArgs, argValues); + + return argValues; } ManagedValue ArgumentScope::popPreservingValue(ManagedValue mv) { diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 919457ed78cbf..cf39e1cf832b5 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3554,60 +3554,6 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto baseTy = rootTy; SmallVector operands; - auto lowerSubscriptOperands = [this, &operands, - E](const KeyPathExpr::Component &component) { - if (!component.getIndexExpr()) - return; - - auto decl = dyn_cast(component.getDeclRef().getDecl()); - assert(decl && - "lowerSubscriptOperands must be called with a subscript decl"); - - // Evaluate the index arguments. - SmallVector indexValues; - RValue indexResult; - if (auto paren = dyn_cast(component.getIndexExpr())) { - auto param = decl->getIndices()->get(0); - if (param->isDefaultArgument()) - indexResult = visit(param->getDefaultValue(), SGFContext()); - else - indexResult = visit(paren, SGFContext()); - indexValues.push_back(std::move(indexResult)); - } else if (auto tupleExpr = dyn_cast(component.getIndexExpr())) { - for (size_t index = 0; index < tupleExpr->getNumElements(); ++index) { - auto param = decl->getIndices()->get(index); - if (param->isDefaultArgument()) - indexResult = visit(param->getDefaultValue(), SGFContext()); - else - indexResult = visit(tupleExpr->getElement(index), SGFContext()); - indexValues.push_back(std::move(indexResult)); - } - } - - for (auto &rv : indexValues) { - operands.push_back(std::move(rv).forwardAsSingleValue(SGF, E)); - } - }; - - auto lowerOperands = [this, &operands, - E](const KeyPathExpr::Component &component) { - if (!component.getIndexExpr()) - return; - - // Evaluate the index arguments. - SmallVector indexValues; - auto indexResult = visit(component.getIndexExpr(), SGFContext()); - if (isa(indexResult.getType())) { - std::move(indexResult).extractElements(indexValues); - } else { - indexValues.push_back(std::move(indexResult)); - } - - for (auto &rv : indexValues) { - operands.push_back(std::move(rv).forwardAsSingleValue(SGF, E)); - } - }; - for (auto &component : E->getComponents()) { switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Property: @@ -3626,21 +3572,20 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { component.getSubscriptIndexHashableConformances(), baseTy, /*for descriptor*/ false)); -// if (kind == KeyPathExpr::Component::Kind::Subscript) { -// lowerSubscriptOperands(component); -// } else { -// lowerOperands(component); -// } - -// assert(numOperands == operands.size() -// && "operand count out of sync"); baseTy = loweredComponents.back().getComponentType(); - baseTy.dump(); -// loweredComponents.back().g - auto subscriptFn = loweredComponents.back().getComputedPropertyGetter(); - SGF.prepareSubscriptIndices(<#SubscriptDecl *subscript#>, <#SubstitutionMap subs#>, <#AccessStrategy strategy#>, <#Expr *indices#>) - SGF.emitKeyPathSubscriptOperands(SGF.F.getLoweredFunctionType(), // subscriptFn->getLoweredFunctionType(), - component.getIndexExpr()); + if (kind == KeyPathExpr::Component::Kind::Property) + break; + + auto subscriptFn = + loweredComponents.back().getComputedPropertyId().getFunction(); + auto subscript = cast(decl); + auto loweredArgs = SGF.emitKeyPathSubscriptOperands( + subscript, component.getDeclRef().getSubstitutions(), + component.getIndexExpr(), subscriptFn->getLoweredFunctionType()); + + for (auto &arg : loweredArgs) { + operands.push_back(arg.forward(SGF)); + } break; } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index ca6931e5313d7..480d69cfc59be 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -664,10 +664,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree, bool isSelfConformance); - - void emitKeyPathSubscriptOperands(CanSILFunctionType fnType, - Expr *indexExpr); - + + SmallVector + emitKeyPathSubscriptOperands(SubscriptDecl *subscript, SubstitutionMap subs, + Expr *indexExpr, CanSILFunctionType fnType); + /// Convert a block to a native function with a thunk. ManagedValue emitBlockToFunc(SILLocation loc, ManagedValue block, From 27ffff100e1cc0381b871450f338015fcc326c01 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 11 Nov 2019 19:37:16 -0800 Subject: [PATCH 087/283] Use range-based for-loop --- lib/Sema/CSApply.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 52f4f73b43e79..24d33e1599be7 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4599,10 +4599,8 @@ namespace { cs.getASTContext().getProtocol(KnownProtocolKind::Hashable); auto fnType = overload.openedType->castTo(); - auto params = fnType->getParams(); SmallVector newLabels; - for (size_t index = 0; index < fnType->getNumParams(); ++index) { - auto param = params[index]; + for (auto ¶m : fnType->getParams()) { newLabels.push_back(param.getLabel()); auto indexType = simplifyType(param.getPlainType()); From 9203080c0e1dce13de665fe2749f8e9f70a6cc67 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 30 Oct 2019 16:50:34 -0700 Subject: [PATCH 088/283] [CodeCompletion] Fix several completion failure in multi-file scenario - Default memberwise initializer for struct was not suggested - Dynamic member lookup didn't work - `init(rawValue:)` was not suggested for `RawRepresentable` types - '$name' was not suggested for `@propertyWrapper`ed value rdar://problem/56391233 --- lib/Sema/LookupVisibleDecls.cpp | 51 +++++++++++++++++ test/IDE/complete_multifile.swift | 93 +++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 test/IDE/complete_multifile.swift diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index e8fa6e0f47fe1..515a7cfe9c6fc 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -15,6 +15,7 @@ // //===----------------------------------------------------------------------===// +#include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/GenericSignatureBuilder.h" @@ -24,6 +25,7 @@ #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/AST/SourceFile.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/STLExtras.h" @@ -474,6 +476,53 @@ static void lookupTypeMembers(BaseTy, PT, Consumer, CurrDC, LS, Reason); } +/// Trigger synthesizing implicit member declarations to make them "visible". +static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, + const DeclContext *DC) { + // Synthesize the memberwise initializer for structs or default initializer + // for classes. + if (!NTD->hasInterfaceType()) + TypeChecker::addImplicitConstructors(NTD); + + // Check all conformances to trigger the synthesized decl generation. + // e.g. init(rawValue:) for RawRepresentable. + for (auto Conformance : NTD->getAllConformances()) { + auto Proto = Conformance->getProtocol(); + if (!Proto->isAccessibleFrom(DC)) + continue; + auto NormalConformance = dyn_cast( + Conformance->getRootConformance()); + if (!NormalConformance) + continue; + for (auto Member : Proto->getMembers()) { + auto *VD = dyn_cast(Member); + if (!VD || !VD->isProtocolRequirement()) + continue; + if (auto *ATD = dyn_cast(Member)) + (void)NormalConformance->getTypeWitnessAndDecl(ATD); + else + (void)NormalConformance->getWitness(VD); + } + } + + // Generate '$' and '_' prefixed variables that have attached property + // wrappers. + auto synthesizePropertyWrappers = [](IterableDeclContext *IDC) { + for (auto Member : IDC->getMembers()) { + if (auto var = dyn_cast(Member)) { + if (var->hasAttachedPropertyWrapper()) { + auto sourceFile = var->getDeclContext()->getParentSourceFile(); + if (sourceFile && sourceFile->Kind != SourceFileKind::Interface) + (void)var->getPropertyWrapperBackingPropertyInfo(); + } + } + } + }; + synthesizePropertyWrappers(NTD); + for (auto ED : NTD->getExtensions()) + synthesizePropertyWrappers(ED); +} + static void lookupVisibleMemberDeclsImpl( Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC, LookupState LS, DeclVisibilityKind Reason, GenericSignatureBuilder *GSB, @@ -583,6 +632,8 @@ static void lookupVisibleMemberDeclsImpl( if (!CurNominal) break; + synthesizeMemberDeclsForLookup(CurNominal, CurrDC); + // Look in for members of a nominal type. lookupTypeMembers(BaseTy, BaseTy, Consumer, CurrDC, LS, Reason); lookupDeclsFromProtocolsBeingConformedTo(BaseTy, Consumer, LS, CurrDC, diff --git a/test/IDE/complete_multifile.swift b/test/IDE/complete_multifile.swift new file mode 100644 index 0000000000000..3b73ee7abea2d --- /dev/null +++ b/test/IDE/complete_multifile.swift @@ -0,0 +1,93 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=POINT_PAREN | %FileCheck --check-prefix=POINT_PAREN %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=POINT_DOT | %FileCheck --check-prefix=POINT_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=LENS_DOT | %FileCheck --check-prefix=LENS_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=MYENUM_DOT | %FileCheck --check-prefix=MYENUM_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=HASWRAPPED_DOT| %FileCheck --check-prefix=HASWRAPPED_DOT %s + +// BEGIN Library.swift + +public struct Point { + var x: Int + var y: Int +} + +@dynamicMemberLookup +struct Lens { + var obj: T + init(_ obj: T) { self.obj = obj } + + subscript(dynamicMember member: WritableKeyPath) -> Lens { + get { return Lens(obj[keyPath: member]) } + set { obj[keyPath: member] = newValue.obj } + } +} + +enum MyEnum: String { + case foo = "foo" + case bar = "bar" +} + +@propertyWrapper +struct Wrap { + var wrappedValue: T + + public var projectedValue: Self { + get { self } + set { self = newValue } + } +} + +struct HasWrapped { + @Wrap + var wrapped: Int = 1 +} + +// BEGIN Main.swift + +func testStructDefaultInit() { + Point(#^POINT_PAREN^# +// POINT_PAREN: Begin completions, 1 items +// POINT_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#x: Int#}, {#y: Int#}[')'][#Point#]; +// POINT_PAREN: End completions + func sync() {} + Point.#^POINT_DOT^# +// POINT_DOT: Begin completions, 3 items +// POINT_DOT-DAG: Keyword[self]/CurrNominal: self[#Point.Type#]; +// POINT_DOT-DAG: Keyword/CurrNominal: Type[#Point.Type#]; +// POINT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#}, {#y: Int#})[#Point#]; +// POINT_DOT: End completions +} + +func testDynamicMemberLookup(lens: Lens) { + _ = lens.#^LENS_DOT^# +// LENS_DOT: Begin completions, 4 items +// LENS_DOT-DAG: Keyword[self]/CurrNominal: self[#Lens#]; +// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: x[#Lens#]; +// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: y[#Lens#]; +// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: obj[#Point#]; +// LENS_DOT: End completions +} +func testRawRepresentable() { + MyEnum.#^MYENUM_DOT^# +// MYENUM_DOT: Begin completions, 7 items +// MYENUM_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum.Type#]; +// MYENUM_DOT-DAG: Keyword/CurrNominal: Type[#MyEnum.Type#]; +// MYENUM_DOT-DAG: Decl[EnumElement]/CurrNominal: foo[#MyEnum#]; +// MYENUM_DOT-DAG: Decl[EnumElement]/CurrNominal: bar[#MyEnum#]; +// MYENUM_DOT-DAG: Decl[TypeAlias]/CurrNominal: RawValue[#String#]; +// MYENUM_DOT-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: String#})[#MyEnum?#]; +// MYENUM_DOT-DAG: Decl[InstanceMethod]/Super: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#]; +// MYENUM_DOT: End completions +} + +func testHasWrappedValue(value: HasWrapped) { + value.#^HASWRAPPED_DOT^# +// HASWRAPPED_DOT: Begin completions, 3 items +// HASWRAPPED_DOT: Keyword[self]/CurrNominal: self[#HasWrapped#]; +// HASWRAPPED_DOT: Decl[InstanceVar]/CurrNominal: wrapped[#Int#]; +// HASWRAPPED_DOT: Decl[InstanceVar]/CurrNominal: $wrapped[#Wrap#]; +// HASWRAPPED_DOT: End completions +} From 2564a6e494f052ea28b19f66f9c1b11a9b4f05ad Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 30 Oct 2019 17:13:16 -0700 Subject: [PATCH 089/283] [CodeCompletion] Avoid typechecking all toplevel decls in the current file - Use `performParseAndResolveImportsOnly()` to invoke the frontend - Do `bindExtensions()` in `ide::typeCheckContextUntil()` - Typecheck preceding `TopLevelCodeDecl`s only if the compleiton is in a `TopLevelCodeDecl` - Other related tweaks rdar://problem/56636747 --- include/swift/Sema/IDETypeChecking.h | 2 + lib/Frontend/Frontend.cpp | 9 +-- lib/IDE/CodeCompletion.cpp | 69 +++++++++++++++---- lib/IDE/ExprContextAnalysis.cpp | 38 +++++++--- lib/Sema/TypeChecker.cpp | 6 +- test/IDE/complete_after_super.swift | 4 +- test/IDE/complete_expr_postfix_begin.swift | 4 +- .../CodeComplete/complete_member.swift | 2 +- .../CompileNotifications/diagnostics.swift | 2 +- .../lib/SwiftLang/SwiftCompletion.cpp | 2 +- .../SwiftLang/SwiftConformingMethodList.cpp | 2 +- .../lib/SwiftLang/SwiftTypeContextInfo.cpp | 2 +- tools/swift-ide-test/swift-ide-test.cpp | 6 +- 13 files changed, 109 insertions(+), 39 deletions(-) diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 2ab4540edfde3..1689f4398de47 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -46,6 +46,8 @@ namespace swift { class ValueDecl; struct PrintOptions; + void bindExtensions(SourceFile &SF); + /// Typecheck binding initializer at \p bindingIndex. void typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned bindingIndex); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index fac2d1af8ad73..aaae5387d7762 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -845,8 +845,13 @@ void CompilerInstance::parseAndCheckTypesUpTo( // If the limiting AST stage is name binding, we're done. if (limitStage <= SourceFile::NameBound) { + if (Invocation.isCodeCompletion()) { + performCodeCompletionSecondPass(*PersistentState.get(), + *Invocation.getCodeCompletionFactory()); + } return; } + assert(!Invocation.isCodeCompletion()); const auto &options = Invocation.getFrontendOptions(); forEachFileToTypeCheck([&](SourceFile &SF) { @@ -870,10 +875,6 @@ void CompilerInstance::parseAndCheckTypesUpTo( } }); - if (Invocation.isCodeCompletion()) { - performCodeCompletionSecondPass(*PersistentState.get(), - *Invocation.getCodeCompletionFactory()); - } finishTypeChecking(TypeCheckOptions); } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index bec4a42b54254..0ae9a2454de3c 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4138,10 +4138,47 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { void addAccessControl(const ValueDecl *VD, CodeCompletionResultBuilder &Builder) { - assert(CurrDeclContext->getSelfNominalTypeDecl()); - auto AccessOfContext = - CurrDeclContext->getSelfNominalTypeDecl()->getFormalAccess(); - auto Access = std::min(VD->getFormalAccess(), AccessOfContext); + auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl(); + assert(CurrentNominal); + + auto AccessOfContext = CurrentNominal->getFormalAccess(); + if (AccessOfContext < AccessLevel::Public) + return; + + auto Access = VD->getFormalAccess(); + // Use the greater access between the protocol requirement and the witness. + // In case of: + // + // public protocol P { func foo() } + // public class B { func foo() {} } + // public class C: B, P { + // + // } + // + // 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding + // declaration, the user needs to write both 'public' and 'override': + // + // public class C: B { + // public override func foo() {} + // } + if (Access < AccessLevel::Public && + !isa(VD->getDeclContext())) { + for (auto Conformance : CurrentNominal->getAllConformances()) { + auto Proto = Conformance->getProtocol(); + for (auto Member : Proto->getMembers()) { + auto Requirement = dyn_cast(Member); + if (!Requirement || !Requirement->isProtocolRequirement() || + isa(Requirement)) + continue; + + auto Witness = Conformance->getWitnessDecl(Requirement); + if (Witness == VD) + Access = std::max(Access, Requirement->getFormalAccess()); + } + } + } + + Access = std::min(Access, AccessOfContext); // Only emit 'public', not needed otherwise. if (Access >= AccessLevel::Public) Builder.addAccessControlKeyword(Access); @@ -4364,9 +4401,11 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { if (D->shouldHideFromEditor()) return; - if (D->isFinal() || - // A 'class' member with an initial value cannot be overriden either. - (D->isStatic() && D->getAttrs().hasAttribute())) + if (D->isFinal()) + return; + + // A 'class' member with an initial value cannot be overriden either. + if (D->isStatic() && isa(D) && cast(D)->hasInitialValue()) return; bool hasIntroducer = hasFuncIntroducer || @@ -4955,13 +4994,16 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::TypeIdentifierWithoutDot: break; - case CompletionKind::TypeDeclResultBeginning: - if (!isa(CurDeclContext)) - if (CurDeclContext->isTypeContext() || - (ParsedDecl && isa(ParsedDecl))) + case CompletionKind::TypeDeclResultBeginning: { + auto DC = CurDeclContext; + if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl()) + DC = ParsedDecl->getDeclContext(); + if (!isa(DC)) + if (DC->isTypeContext() || (ParsedDecl && isa(ParsedDecl))) addOpaqueTypeKeyword(Sink); LLVM_FALLTHROUGH; + } case CompletionKind::TypeSimpleBeginning: addAnyTypeKeyword(Sink); break; @@ -5130,8 +5172,6 @@ void CodeCompletionCallbacksImpl::doneParsing() { CD->getContextKind() == DeclContextKind::TopLevelCodeDecl) MaybeFuncBody = false; } - // Add keywords even if type checking fails completely. - addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); if (auto *DC = dyn_cast_or_null(ParsedDecl)) { if (DC->isChildContextOf(CurDeclContext)) @@ -5142,6 +5182,9 @@ void CodeCompletionCallbacksImpl::doneParsing() { CurDeclContext, CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc()); + // Add keywords even if type checking fails completely. + addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); + Optional ExprType; ConcreteDeclRef ReferencedDecl = nullptr; if (ParsedExpr) { diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 5bafb15f05d4e..4a78c31775263 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" @@ -61,10 +62,9 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { if (auto *patternInit = dyn_cast(DC)) { if (auto *PBD = patternInit->getBinding()) { auto i = patternInit->getBindingIndex(); + PBD->getPattern(i)->forEachVariable( + [](VarDecl *VD) { (void)VD->getInterfaceType(); }); if (PBD->getInit(i)) { - PBD->getPattern(i)->forEachVariable([](VarDecl *VD) { - (void) VD->getInterfaceType(); - }); if (!PBD->isInitializerChecked(i)) typeCheckPatternBinding(PBD, i); } @@ -91,15 +91,35 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { } // anonymous namespace void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) { - // The only time we have to explicitly check a TopLevelCodeDecl - // is when we're directly inside of one. In this case, - // performTypeChecking() did not type check it for us. + // Lookup the swift module. This ensures that we record all known + // protocols in the AST. + (void) DC->getASTContext().getStdlibModule(); + + bindExtensions(*DC->getParentSourceFile()); + while (isa(DC)) DC = DC->getParent(); - if (auto *TLCD = dyn_cast(DC)) - typeCheckTopLevelCodeDecl(TLCD); - else + + if (auto *TLCD = dyn_cast(DC)) { + // Typecheck all 'TopLevelCodeDecl's up to the target one. + // In theory, this is not needed, but it fails to resolve the type of + // 'guard'ed variable. e.g. + // + // guard value = something() else { fatalError() } + // + // Here, 'value' is '' unless we explicitly typecheck the + // 'guard' statement. + SourceFile *SF = DC->getParentSourceFile(); + for (auto *D : SF->Decls) { + if (auto Code = dyn_cast(D)) { + typeCheckTopLevelCodeDecl(Code); + if (Code == TLCD) + break; + } + } + } else { typeCheckContextImpl(DC, Loc); + } } //===----------------------------------------------------------------------===// diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 61ca40efc3abf..efa19c64bc8c4 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -398,7 +398,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, // Resolve extensions. This has to occur first during type checking, // because the extensions need to be wired into the AST for name lookup // to work. - bindExtensions(SF); + ::bindExtensions(SF); // Type check the top-level elements of the source file. for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { @@ -739,3 +739,7 @@ TypeChecker::getDeclTypeCheckingSemantics(ValueDecl *decl) { } return DeclTypeCheckingSemantics::Normal; } + +void swift::bindExtensions(SourceFile &SF) { + ::bindExtensions(SF); +} diff --git a/test/IDE/complete_after_super.swift b/test/IDE/complete_after_super.swift index 01c2353d49270..5727dd9892051 100644 --- a/test/IDE/complete_after_super.swift +++ b/test/IDE/complete_after_super.swift @@ -491,7 +491,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: End completions } - func instanceFunc1() { + override func instanceFunc1() { #^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3: Begin completions // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}} @@ -504,7 +504,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: End completions } - func instanceFunc1(_ a: Int) { + override func instanceFunc1(_ a: Int) { super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5: Begin completions // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}} diff --git a/test/IDE/complete_expr_postfix_begin.swift b/test/IDE/complete_expr_postfix_begin.swift index 407ddfee8b7a8..5d8029ea6fc74 100644 --- a/test/IDE/complete_expr_postfix_begin.swift +++ b/test/IDE/complete_expr_postfix_begin.swift @@ -75,8 +75,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_1 | %FileCheck %s -check-prefix=IN_TUPLE_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_2 | %FileCheck %s -check-prefix=IN_TUPLE_2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2 +// RUN-FIXME(rdar56755598): %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1 +// RUN-FIXME(rdar56755598): %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_3 | %FileCheck %s -check-prefix=OWN_INIT_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_4 | %FileCheck %s -check-prefix=OWN_INIT_4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_5 | %FileCheck %s -check-prefix=OWN_INIT_5 diff --git a/test/SourceKit/CodeComplete/complete_member.swift b/test/SourceKit/CodeComplete/complete_member.swift index 1851daee22ee6..3816b82331f44 100644 --- a/test/SourceKit/CodeComplete/complete_member.swift +++ b/test/SourceKit/CodeComplete/complete_member.swift @@ -32,7 +32,7 @@ class Base { } class Derived: Base { - func foo() {} + override func foo() {} } func testOverrideUSR() { diff --git a/test/SourceKit/CompileNotifications/diagnostics.swift b/test/SourceKit/CompileNotifications/diagnostics.swift index 4a6d0894e5956..e2a2f6a09649d 100644 --- a/test/SourceKit/CompileNotifications/diagnostics.swift +++ b/test/SourceKit/CompileNotifications/diagnostics.swift @@ -63,7 +63,7 @@ // Note: we're missing the "compiler is in code completion mode" diagnostic, // which is probably just as well. // RUN: %sourcekitd-test -req=track-compiles == -req=complete -offset=0 %s -- %s | %FileCheck %s -check-prefix=NODIAGS -// RUN: %sourcekitd-test -req=track-compiles == -req=complete -pos=2:1 %S/Inputs/sema-error.swift -- %S/Inputs/sema-error.swift | %FileCheck %s -check-prefix=SEMA +// RUN: %sourcekitd-test -req=track-compiles == -req=complete -pos=2:1 %S/Inputs/sema-error.swift -- %S/Inputs/sema-error.swift | %FileCheck %s -check-prefix=NODIAGS // FIXME: invalid arguments cause us to early-exit and not send the notifications // RUN_DISABLED: %sourcekitd-test -req=track-compiles == -req=sema %s -- %s -invalid-arg | %FileCheck %s -check-prefix=INVALID_ARG diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 4ab794506e2ff..5dc4aed03c39e 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -212,7 +212,7 @@ static bool swiftCodeCompleteImpl( SwiftConsumer.setContext(&CI.getASTContext(), &Invocation, &CompletionContext); registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); + CI.performParseAndResolveImportsOnly(); SwiftConsumer.clearContext(); return true; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 2876879da1d5e..06f9bfd76e274 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -82,7 +82,7 @@ static bool swiftConformingMethodListImpl( return true; } registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); + CI.performParseAndResolveImportsOnly(); return true; } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index af7edc90b91bc..3109a68689b08 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -81,7 +81,7 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, return true; } registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); + CI.performParseAndResolveImportsOnly(); return true; } diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 64829bb8cd6ce..a8243cb9fb326 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -766,7 +766,7 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, if (CI.setup(Invocation)) return 1; registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); + CI.performParseAndResolveImportsOnly(); return 0; } @@ -831,7 +831,7 @@ doConformingMethodList(const CompilerInvocation &InitInvok, if (CI.setup(Invocation)) return 1; registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); + CI.performParseAndResolveImportsOnly(); return 0; } @@ -908,7 +908,7 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, if (CI.setup(Invocation)) return 1; registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); + CI.performParseAndResolveImportsOnly(); return 0; } From 32e68c74ff7eb969b77bcb697b68ca1707a81aea Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 5 Nov 2019 18:00:07 -0800 Subject: [PATCH 090/283] Factor out synthesizePropertyWrapperStorageWrapperProperties() --- lib/Sema/LookupVisibleDecls.cpp | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index 515a7cfe9c6fc..4d01dd8ab2fbb 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -205,6 +205,9 @@ static void collectVisibleMemberDecls(const DeclContext *CurrDC, LookupState LS, } } +static void +synthesizePropertyWrapperStorageWrapperProperties(IterableDeclContext *IDC); + /// Lookup members in extensions of \p LookupType, using \p BaseType as the /// underlying type when checking any constraints on the extensions. static void doGlobalExtensionLookup(Type BaseType, @@ -222,6 +225,8 @@ static void doGlobalExtensionLookup(Type BaseType, extension)), false)) continue; + synthesizePropertyWrapperStorageWrapperProperties(extension); + collectVisibleMemberDecls(CurrDC, LS, BaseType, extension, FoundDecls); } @@ -476,6 +481,20 @@ static void lookupTypeMembers(BaseTy, PT, Consumer, CurrDC, LS, Reason); } +// Generate '$' and '_' prefixed variables that have attached property +// wrappers. +static void +synthesizePropertyWrapperStorageWrapperProperties(IterableDeclContext *IDC) { + auto SF = IDC->getDecl()->getDeclContext()->getParentSourceFile(); + if (!SF || SF->Kind == SourceFileKind::Interface) + return; + + for (auto Member : IDC->getMembers()) + if (auto var = dyn_cast(Member)) + if (var->hasAttachedPropertyWrapper()) + (void)var->getPropertyWrapperBackingPropertyInfo(); +} + /// Trigger synthesizing implicit member declarations to make them "visible". static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, const DeclContext *DC) { @@ -505,22 +524,7 @@ static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, } } - // Generate '$' and '_' prefixed variables that have attached property - // wrappers. - auto synthesizePropertyWrappers = [](IterableDeclContext *IDC) { - for (auto Member : IDC->getMembers()) { - if (auto var = dyn_cast(Member)) { - if (var->hasAttachedPropertyWrapper()) { - auto sourceFile = var->getDeclContext()->getParentSourceFile(); - if (sourceFile && sourceFile->Kind != SourceFileKind::Interface) - (void)var->getPropertyWrapperBackingPropertyInfo(); - } - } - } - }; - synthesizePropertyWrappers(NTD); - for (auto ED : NTD->getExtensions()) - synthesizePropertyWrappers(ED); + synthesizePropertyWrapperStorageWrapperProperties(NTD); } static void lookupVisibleMemberDeclsImpl( From 7bebb00673df7883dd724f4dc8a300f8e0d35ac6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 11 Nov 2019 19:04:00 -0500 Subject: [PATCH 091/283] AST: Small cleanup for ASTContext::getOverrideGenericSignature() --- lib/AST/ASTContext.cpp | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index cc890454af87d..ffe364e43cea5 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4354,42 +4354,28 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, auto derivedGenericCtx = derived->getAsGenericContext(); auto &ctx = base->getASTContext(); - if (!baseGenericCtx) { + if (!baseGenericCtx || !derivedGenericCtx) return nullptr; - } auto baseClass = base->getDeclContext()->getSelfClassDecl(); - - if (!baseClass) { + if (!baseClass) return nullptr; - } auto derivedClass = derived->getDeclContext()->getSelfClassDecl(); - auto baseClassSig = baseClass->getGenericSignature(); - - if (!derivedClass) { + if (!derivedClass) return nullptr; - } - if (derivedClass->getSuperclass().isNull()) { + if (derivedClass->getSuperclass().isNull()) return nullptr; - } - if (!derivedGenericCtx || !derivedGenericCtx->isGeneric()) { + if (baseGenericCtx->getGenericSignature().isNull() || + derivedGenericCtx->getGenericSignature().isNull()) return nullptr; - } - - if (derivedClass->getGenericSignature().isNull() && - !baseGenericCtx->isGeneric()) { - return nullptr; - } + auto baseClassSig = baseClass->getGenericSignature(); auto subMap = derivedClass->getSuperclass()->getContextSubstitutionMap( derivedClass->getModuleContext(), baseClass); - if (baseGenericCtx->getGenericSignature().isNull()) { - return nullptr; - } unsigned derivedDepth = 0; auto key = OverrideSignatureKey(baseGenericCtx->getGenericSignature(), @@ -4405,9 +4391,11 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, derivedDepth = derivedSig->getGenericParams().back()->getDepth() + 1; SmallVector addedGenericParams; - for (auto gp : *derivedGenericCtx->getGenericParams()) { - addedGenericParams.push_back( - gp->getDeclaredInterfaceType()->castTo()); + if (auto *gpList = derivedGenericCtx->getGenericParams()) { + for (auto gp : *gpList) { + addedGenericParams.push_back( + gp->getDeclaredInterfaceType()->castTo()); + } } unsigned baseDepth = 0; From 317c0317d445243153dd8bee2710c87e06b0b72b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 11 Nov 2019 23:04:09 -0500 Subject: [PATCH 092/283] AST: Cope with invalid inputs better in ExtensionDecl::isConstrainedExtension() --- lib/AST/Decl.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a59f780d6945e..a21908bb3830e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1221,16 +1221,21 @@ bool ExtensionDecl::hasValidParent() const { } bool ExtensionDecl::isConstrainedExtension() const { - // Non-generic extension. - if (!getGenericSignature()) + auto nominal = getExtendedNominal(); + if (!nominal) return false; - auto nominal = getExtendedNominal(); - assert(nominal); + auto typeSig = nominal->getGenericSignature(); + if (!typeSig) + return false; + + auto extSig = getGenericSignature(); + if (!extSig) + return false; // If the generic signature differs from that of the nominal type, it's a // constrained extension. - return !getGenericSignature()->isEqual(nominal->getGenericSignature()); + return !typeSig->isEqual(extSig); } bool ExtensionDecl::isEquivalentToExtendedContext() const { From 736343f068d38558d05b43ed4492154bb14e61b8 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 11 Nov 2019 23:04:14 -0500 Subject: [PATCH 093/283] Sema: Skip override checking for members of constrained extensions When a method in an extension of a class looks like an override of a method from the base class, we emit a diagnostic. However due to a bug we used to skip this diagnostic for certain members of constrained extensions. Indeed it feels like we should not be doing this check at all for members of constrained extensions, so lets explicitly skip it, fixing a source compatibility problem that was introduced when the unrelated bug was fixed. Fixes , . --- lib/Sema/TypeCheckDeclOverride.cpp | 10 ++++++++++ test/attr/attr_override.swift | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 157a8ccf5891d..0b3a5fd4d58b9 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -234,6 +234,11 @@ static bool areOverrideCompatibleSimple(ValueDecl *decl, return false; } + // Ignore declarations that are defined inside constrained extensions. + if (auto *ext = dyn_cast(parentDecl->getDeclContext())) + if (ext->isConstrainedExtension()) + return false; + // The declarations must be of the same kind. if (decl->getKind() != parentDecl->getKind()) return false; @@ -1169,6 +1174,11 @@ bool swift::checkOverrides(ValueDecl *decl) { // Otherwise, we have more checking to do. } + // Members of constrained extensions are not considered to be overrides. + if (auto *ext = dyn_cast(decl->getDeclContext())) + if (ext->isConstrainedExtension()) + return false; + // Accessor methods get overrides through their storage declaration, and // all checking can be performed via that mechanism. if (isa(decl)) { diff --git a/test/attr/attr_override.swift b/test/attr/attr_override.swift index bfe6f6df68e90..4c74cfed12d8d 100644 --- a/test/attr/attr_override.swift +++ b/test/attr/attr_override.swift @@ -626,3 +626,25 @@ class SR_10198_Derived_1: SR_10198_Base_1 { init(_ arg1: Int) { super.init(SR_10198_Base_S()) } // okay, doesn't crash } +// SR-11740 + +public class SR_11740_Base {} + +public class SR_11740_Derived + : SR_11740_Base, A>, + SR_11740_Q {} + +public protocol SR_11740_P {} + +public protocol SR_11740_Q: SR_11740_P { + associatedtype A +} + +public extension SR_11740_Base where F: SR_11740_Q { + static func foo(_: F.A) {} +} + +extension SR_11740_Derived where F: SR_11740_P { + public static func foo(_: A) {} +} + From f6a76f51a0e456c8f67633d9be2506abb7cb1bc1 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 6 Nov 2019 11:52:47 -0800 Subject: [PATCH 094/283] Utilize forEachTypeWitness()/forEachValueWitness() --- lib/IDE/CodeCompletion.cpp | 17 ++++++----------- lib/Sema/LookupVisibleDecls.cpp | 14 +++++--------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 0ae9a2454de3c..c2df1990168d1 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4164,17 +4164,12 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { if (Access < AccessLevel::Public && !isa(VD->getDeclContext())) { for (auto Conformance : CurrentNominal->getAllConformances()) { - auto Proto = Conformance->getProtocol(); - for (auto Member : Proto->getMembers()) { - auto Requirement = dyn_cast(Member); - if (!Requirement || !Requirement->isProtocolRequirement() || - isa(Requirement)) - continue; - - auto Witness = Conformance->getWitnessDecl(Requirement); - if (Witness == VD) - Access = std::max(Access, Requirement->getFormalAccess()); - } + Conformance->getRootConformance()->forEachValueWitness( + [&](ValueDecl *req, Witness witness) { + if (witness.getDecl() == VD) + Access = std::max( + Access, Conformance->getProtocol()->getFormalAccess()); + }); } } diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index 4d01dd8ab2fbb..154b2d502bee0 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -513,15 +513,11 @@ static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, Conformance->getRootConformance()); if (!NormalConformance) continue; - for (auto Member : Proto->getMembers()) { - auto *VD = dyn_cast(Member); - if (!VD || !VD->isProtocolRequirement()) - continue; - if (auto *ATD = dyn_cast(Member)) - (void)NormalConformance->getTypeWitnessAndDecl(ATD); - else - (void)NormalConformance->getWitness(VD); - } + NormalConformance->forEachTypeWitness( + [](AssociatedTypeDecl *, Type, TypeDecl *) { return false; }, + /*useResolver=*/true); + NormalConformance->forEachValueWitness([](ValueDecl *, Witness) {}, + /*useResolver=*/true); } synthesizePropertyWrapperStorageWrapperProperties(NTD); From d51d8447f03a40fdd63ee9573d6ef1f8c244cf9f Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 6 Nov 2019 12:50:41 -0800 Subject: [PATCH 095/283] Do 'bindExtensions()' as a part of 'performParseAndNameBindingOnly()' --- include/swift/Sema/IDETypeChecking.h | 2 -- include/swift/Subsystems.h | 3 +++ lib/Frontend/Frontend.cpp | 27 +++++++++++++++++---------- lib/IDE/ExprContextAnalysis.cpp | 6 ------ lib/Sema/TypeChecker.cpp | 4 ---- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 1689f4398de47..2ab4540edfde3 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -46,8 +46,6 @@ namespace swift { class ValueDecl; struct PrintOptions; - void bindExtensions(SourceFile &SF); - /// Typecheck binding initializer at \p bindingIndex. void typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned bindingIndex); diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 2e91d7248fa7c..755509fbc676d 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -199,6 +199,9 @@ namespace swift { /// \returns a reference to the type checker instance. TypeChecker &createTypeChecker(ASTContext &Ctx); + /// Bind all 'extension' visible from \p SF to the extended nominal. + void bindExtensions(SourceFile &SF); + /// Once parsing and name-binding are complete, this walks the AST to resolve /// types and diagnose problems therein. /// diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index aaae5387d7762..40e2f5149b429 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -843,18 +843,13 @@ void CompilerInstance::parseAndCheckTypesUpTo( }) && "some files have not yet had their imports resolved"); MainModule->setHasResolvedImports(); - // If the limiting AST stage is name binding, we're done. - if (limitStage <= SourceFile::NameBound) { - if (Invocation.isCodeCompletion()) { - performCodeCompletionSecondPass(*PersistentState.get(), - *Invocation.getCodeCompletionFactory()); - } - return; - } - assert(!Invocation.isCodeCompletion()); - const auto &options = Invocation.getFrontendOptions(); forEachFileToTypeCheck([&](SourceFile &SF) { + if (limitStage == SourceFile::NameBound) { + bindExtensions(SF); + return; + } + performTypeChecking(SF, PersistentState->getTopLevelContext(), TypeCheckOptions, /*curElem*/ 0, options.WarnLongFunctionBodies, @@ -875,6 +870,17 @@ void CompilerInstance::parseAndCheckTypesUpTo( } }); + if (Invocation.isCodeCompletion()) { + assert(limitStage == SourceFile::NameBound); + performCodeCompletionSecondPass(*PersistentState.get(), + *Invocation.getCodeCompletionFactory()); + } + + // If the limiting AST stage is name binding, we're done. + if (limitStage <= SourceFile::NameBound) { + return; + } + finishTypeChecking(TypeCheckOptions); } @@ -986,6 +992,7 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( llvm_unreachable("invalid limit stage"); case SourceFile::NameBound: performNameBinding(MainFile, CurTUElem); + bindExtensions(MainFile); break; case SourceFile::TypeChecked: const auto &options = Invocation.getFrontendOptions(); diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 4a78c31775263..44320e0517381 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -91,12 +91,6 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { } // anonymous namespace void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) { - // Lookup the swift module. This ensures that we record all known - // protocols in the AST. - (void) DC->getASTContext().getStdlibModule(); - - bindExtensions(*DC->getParentSourceFile()); - while (isa(DC)) DC = DC->getParent(); diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index efa19c64bc8c4..0fae2e69f3e87 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -385,10 +385,6 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, if (!SF.getParentModule()->isOnoneSupportModule()) TC.setSkipNonInlinableBodies(true); - // Lookup the swift module. This ensures that we record all known - // protocols in the AST. - (void) TC.getStdlibModule(&SF); - if (!Ctx.LangOpts.DisableAvailabilityChecking) { // Build the type refinement hierarchy for the primary // file before type checking. From 044790d3e94583fa383250c03913c2ad368dd4cb Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 12 Nov 2019 13:29:53 +0900 Subject: [PATCH 096/283] Workaround cyclic dependency issue in memberwise initializer synthesis --- lib/Sema/LookupVisibleDecls.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index 154b2d502bee0..056995ff4b923 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -500,7 +500,8 @@ static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, const DeclContext *DC) { // Synthesize the memberwise initializer for structs or default initializer // for classes. - if (!NTD->hasInterfaceType()) + if (!NTD->getASTContext().evaluator.hasActiveRequest( + SynthesizeMemberwiseInitRequest{NTD})) TypeChecker::addImplicitConstructors(NTD); // Check all conformances to trigger the synthesized decl generation. From eab9be8f2fb8f6d6b19ee9ee25172a21693d01f4 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 11 Nov 2019 17:56:13 -0800 Subject: [PATCH 097/283] [pmo] Fix load_borrow for strong control equivalence issues. This entailed untangling a few issues. I have purposefully only fixed this for now for load_borrow to make the change in tree smaller (the reason for my splitting the code paths in a previous group of commits). I am going to follow up with a load copy version of the patch. I think that the code should be the same. The interesting part about this is that all of these code conditions are caught by the ownership verifier, suggesting to me that the code in the stdlib/overlays is simple enough that we didn't hit these code patterns. Anyways, the main change here is that we now eliminate control equivalence issues arising from @owned values that are not control equivalent being forwarded into aggregates and phis. This would cause our outer value to be destroyed early since we would be destroying it once for every time iteration in a loop. The way that this is done is that (noting that this is only for borrows today): * The AvailableValueAggregator always copies values at available value points to lifetime extend the values to the load block. In the load block, the aggregator will take the incoming values and borrow the value to form tuples, structs as needed. This new guaranteed aggregate is then copied. If we do not need to form an aggregate, we just copy. Since the copy is in our load block, we know that we can use it for our load_borrow. Importantly note how we no longer allow for these aggregates to forward owned ownership preventing the control equivalence problem above. * Since the aggregator may use the SSA updater if it finds multiple available values, we need to also make sure that any phis are strongly control equivalent on all of its incoming values. So we insert copies along those edges and then lifetime extend the phi as appropriate. * Since in all of the above all copy_value inserted by the available value aggregator are never consumed, we can just add destroy_values in the appropriate places and everything works. --- .../Mandatory/PredictableMemOpt.cpp | 468 ++++++++++++--- .../predictable_memaccess_opts.sil | 555 +++++++++++++++++- 2 files changed, 914 insertions(+), 109 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 50700bd41e8a2..7e5d0045f5f1f 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -19,8 +19,10 @@ #include "swift/SIL/SILBuilder.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILSSAUpdater.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" @@ -410,6 +412,8 @@ class AvailableValueAggregator { /// take. SmallVector insertedInsts; + SmallVector insertedPhiNodes; + public: AvailableValueAggregator(SILInstruction *Inst, MutableArrayRef AvailableValueList, @@ -431,12 +435,6 @@ class AvailableValueAggregator { bool isTopLevel = true); bool canTake(SILType loadTy, unsigned firstElt) const; - /// If as a result of us copying values, we may have unconsumed destroys, find - /// the appropriate location and place the values there. Only used when - /// ownership is enabled. - SingleValueInstruction *addMissingDestroysForCopiedValues(LoadBorrowInst *li, - SILValue newVal); - SingleValueInstruction *addMissingDestroysForCopiedValues(LoadInst *li, SILValue newVal); @@ -455,6 +453,19 @@ class AvailableValueAggregator { return expectedOwnership == AvailableValueExpectedOwnership::Copy; } + /// Given a load_borrow that we have aggregated a new value for, fixup the + /// reference counts of the intermediate copies and phis to ensure that all + /// forwarding operations in the CFG are strongly control equivalent (i.e. run + /// the same number of times). + void fixupOwnership(LoadBorrowInst *lbi, SILValue newVal) { + // Sort inserted insts so we can bisect upon it and mark copy_value as needing + // to be skipped. + sort(insertedInsts); + SmallBitVector instsToSkip(insertedInsts.size()); + addHandOffCopyDestroysForPhis(lbi, newVal, instsToSkip); + addMissingDestroysForCopiedValues(lbi, newVal, instsToSkip); + } + private: SILValue aggregateFullyAvailableValue(SILType loadTy, unsigned firstElt); SILValue aggregateTupleSubElts(TupleType *tt, SILType loadTy, @@ -464,6 +475,15 @@ class AvailableValueAggregator { SILValue handlePrimitiveValue(SILType loadTy, SILValue address, unsigned firstElt); bool isFullyAvailable(SILType loadTy, unsigned firstElt) const; + + + /// If as a result of us copying values, we may have unconsumed destroys, find + /// the appropriate location and place the values there. Only used when + /// ownership is enabled. + void addMissingDestroysForCopiedValues(LoadBorrowInst *li, SILValue newVal, + const SmallBitVector &instsToSkip); + void addHandOffCopyDestroysForPhis(LoadBorrowInst *li, SILValue newVal, + SmallBitVector &instsToSkipOut); }; } // end anonymous namespace @@ -553,18 +573,53 @@ SILValue AvailableValueAggregator::aggregateValues(SILType LoadTy, // Check to see if the requested value is fully available, as an aggregate. // This is a super-common case for single-element structs, but is also a // general answer for arbitrary structs and tuples as well. - if (SILValue Result = aggregateFullyAvailableValue(LoadTy, FirstElt)) + if (SILValue Result = aggregateFullyAvailableValue(LoadTy, FirstElt)) { return Result; + } // If we have a tuple type, then aggregate the tuple's elements into a full // tuple value. - if (TupleType *TT = LoadTy.getAs()) - return aggregateTupleSubElts(TT, LoadTy, Address, FirstElt); + if (TupleType *tupleType = LoadTy.getAs()) { + SILValue result = + aggregateTupleSubElts(tupleType, LoadTy, Address, FirstElt); + if (isTopLevel && + result.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { + SILValue borrowedResult = result; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + result = builder.emitCopyValueOperation(Loc, borrowedResult); + SmallVector introducers; + bool foundIntroducers = + getUnderlyingBorrowIntroducingValues(borrowedResult, introducers); + (void)foundIntroducers; + assert(foundIntroducers); + for (auto value : introducers) { + builder.emitEndBorrowOperation(Loc, value.value); + } + } + return result; + } // If we have a struct type, then aggregate the struct's elements into a full // struct value. - if (auto *SD = getFullyReferenceableStruct(LoadTy)) - return aggregateStructSubElts(SD, LoadTy, Address, FirstElt); + if (auto *structDecl = getFullyReferenceableStruct(LoadTy)) { + SILValue result = + aggregateStructSubElts(structDecl, LoadTy, Address, FirstElt); + if (isTopLevel && + result.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { + SILValue borrowedResult = result; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + result = builder.emitCopyValueOperation(Loc, borrowedResult); + SmallVector introducers; + bool foundIntroducers = + getUnderlyingBorrowIntroducingValues(borrowedResult, introducers); + (void)foundIntroducers; + assert(foundIntroducers); + for (auto value : introducers) { + builder.emitEndBorrowOperation(Loc, value.value); + } + } + return result; + } // Otherwise, we have a non-aggregate primitive. Load or extract the value. // @@ -610,7 +665,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // SSA updater to get a value. The reason why this is safe is that we can only // have multiple insertion points if we are storing exactly the same value // implying that we can just copy firstVal at each insertion point. - SILSSAUpdater updater; + SILSSAUpdater updater(&insertedPhiNodes); updater.Initialize(loadTy); Optional singularValue; @@ -653,6 +708,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // Finally, grab the value from the SSA updater. SILValue result = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); + assert(result.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); return result; } @@ -681,6 +737,15 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, FirstElt += NumSubElt; } + // If we are going to use this to promote a borrowed value, insert borrow + // operations. Eventually I am going to do this for everything, but this + // should make it easier to bring up. + if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { + for (unsigned i : indices(ResultElts)) { + ResultElts[i] = B.emitBeginBorrowOperation(Loc, ResultElts[i]); + } + } + return B.createTuple(Loc, LoadTy, ResultElts); } @@ -708,6 +773,12 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, firstElt += numSubElt; } + if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { + for (unsigned i : indices(resultElts)) { + resultElts[i] = B.emitBeginBorrowOperation(Loc, resultElts[i]); + } + } + return B.createStruct(Loc, loadTy, resultElts); } @@ -725,8 +796,9 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, if (!val) { LoadInst *load = ([&]() { if (B.hasOwnership()) { - return B.createTrivialLoadOr(Loc, address, - LoadOwnershipQualifier::Copy); + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.createTrivialLoadOr(Loc, address, + LoadOwnershipQualifier::Copy); } return B.createLoad(Loc, address, LoadOwnershipQualifier::Unqualified); }()); @@ -756,7 +828,7 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, // inserted copies at each of these insertion points, we know that we will // never have the same value along all paths unless we have a trivial value // meaning the SSA updater given a non-trivial value must /always/ be used. - SILSSAUpdater updater; + SILSSAUpdater updater(&insertedPhiNodes); updater.Initialize(loadTy); Optional singularValue; @@ -871,24 +943,247 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li, return nullptr; } -SingleValueInstruction * -AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadBorrowInst *lbi, - SILValue newVal) { +void AvailableValueAggregator::addHandOffCopyDestroysForPhis(LoadBorrowInst *lbi, SILValue newVal, + SmallBitVector &instsToSkip) { + ValueLifetimeAnalysis::Frontier lifetimeFrontier; + SmallPtrSet visitedBlocks; + SmallVector leakingBlocks; + SmallVector, 8> incomingValues; + auto loc = RegularLocation::getAutoGeneratedLocation(); + + LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); +#ifndef NDEBUG + for (auto *phi : insertedPhiNodes) { + LLVM_DEBUG(llvm::dbgs() << "Phi: " << *phi); + } +#endif + + // Before we begin, identify the offset for all phis that are intermediate + // phis inserted by the SSA updater. + SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); + for (unsigned i : indices(insertedPhiNodes)) { + if (insertedPhiNodes[i]->getSingleUserOfType()) { + intermediatePhiOffsets.set(i); + } + } + + // First go through all of our phi nodes doing the following: + // + // 1. If any of the phi node have a copy_value as an operand, we know that the + // copy_value does not dominate our final definition. In such a case since + // we may not have that the copy_value is post-dominated by the phi, we + // need to insert a copy_value at the phi to allow for post-domination and + // then use the ValueLifetimeChecker to determine the rest of the frontier + // for the value. + // + // 2. If our phi node is used by another phi node, we run into a similar + // problem where we could have that our original phi node does not dominate + // our final definition and may not be strongly control dependent on our + // phi. To work around this problem, we insert at the phi a copy_value to + // allow for the phi to post_dominate its copy and then extend the lifetime + // of the phied value over that copy. + for (unsigned i : indices(insertedPhiNodes)) { + auto *phiArg = insertedPhiNodes[i]; + + // If our phiArg is not owned, continue. No fixes are needed. + if (phiArg->getOwnershipKind() != ValueOwnershipKind::Owned) + continue; + + LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phiArg); + // Otherwise, we have a copy_value that may not be strongly control + // equivalent with our phi node. In such a case, we need to use + // ValueLifetimeAnalysis to lifetime extend the copy such that we can + // produce a new copy_value at the phi. We insert destroys along the + // frontier. + visitedBlocks.clear(); + leakingBlocks.clear(); + incomingValues.clear(); + + phiArg->getIncomingPhiValues(incomingValues); + unsigned phiIndex = phiArg->getIndex(); + for (auto pair : incomingValues) { + SILValue value = pair.second; + + // If we had a non-trivial type with non-owned ownership, we will not see + // a copy_value, so skip them here. + if (value.getOwnershipKind() != ValueOwnershipKind::Owned) + continue; + + // Otherwise, value should be from a copy_value or a phi node. + assert(isa(value) || isa(value)); + + // If we have a copy_value Set a bit for it in instsToSkip so that when we + // start processing insertedInstrs we know that we handled it here + // already. + if (auto *cvi = dyn_cast(value)) { + auto iter = lower_bound(insertedInsts, cvi); + assert(iter != insertedInsts.end() && *iter == cvi); + instsToSkip[std::distance(insertedInsts.begin(), iter)] = true; + + // Then check if our termInst is in the same block as our copy_value. In + // such a case, we can just use the copy_value as our phi's value + // without needing to worry about any issues around control equivalence. + if (pair.first == cvi->getParent()) + continue; + } else { + assert(isa(value)); + } + + // Otherwise, insert a copy_value instruction right before the phi. We use + // that for our actual phi. + auto *termInst = pair.first->getTerminator(); + SILBuilderWithScope builder(termInst); + auto *phiCopy = builder.createCopyValue(loc, value); + termInst->setOperand(phiIndex, phiCopy); + + // Normalize on our base now that we have inserted the copy_value into the + // terminator block. If we have a copy_value, just use it directly as our + // base. We know it isn't in the block of our phiCopy due to a check + // above. + SILInstruction *base = nullptr; + if (auto *cvi = dyn_cast(value)) { + assert(cvi->getParent() != phiCopy->getParent() && + "Just to check invariant from above"); + base = cvi; + } else { + assert(isa(value)); + // If we have a phi argument and our incoming value block is the same as + // our phi block, we know that the copy_value we inserted will only be + // used by the phi. So insert a destroy_value in the incoming value + // block after the copy_value that we inserted and then continue. + if (pair.first == value->getParentBlock()) { + builder.createDestroyValue(loc, value); + continue; + } + + // Otherwise, our copy_value may not be post-dominated by our phi. To + // work around that, we need to insert destroys along the other + // paths. So set base to the first instruction in our argument's block, + // so we can insert destroys for our base. + base = &*value->getParentBlock()->begin(); + } + assert(base && "Should have been assigned"); + + // Then lifetime extend our base over the copy_value. + assert(lifetimeFrontier.empty()); + ValueLifetimeAnalysis analysis(base, phiCopy); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, + &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createDestroyValue(loc, value); + } + + visitedBlocks.clear(); + leakingBlocks.clear(); + } + + // Then see if our phi is an intermediate phi. If it is an intermediate phi, + // we know that this is not the phi node that is post-dominated by the + // load_borrow and that we will lifetime extend it via the child + // phi. Instead, we need to just ensure that our phi arg does not leak onto + // its set of post-dominating paths, subtracting from that set the path + // through our terminator use. + if (intermediatePhiOffsets[i]) { + continue; + } + + // If we reach this point, then we know that we are a phi node that actually + // dominates our user so we need to lifetime extend it over the + // load_borrow. Thus insert copy_value along the incoming edges and then + // lifetime extend the phi node over the load_borrow. + // + // The linear lifetime checker doesn't care if the passed in load is + // actually a user of our copy_value. What we care about is that the load is + // guaranteed to be in the block where we have reformed the tuple in a + // consuming manner. This means if we add it as the consuming use of the + // copy, we can find the leaking places if any exist. + // + // Then perform the linear lifetime check. If we succeed, continue. We have + // no further work to do. + auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; + LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); + auto error = checker.checkValue( + phiArg, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, + errorKind, &leakingBlocks); + + if (!error.getFoundError()) { + // If we did not find an error, then our copy_value must be strongly + // control equivalent as our load_borrow. So just insert a destroy_value + // for the copy_value. + auto next = std::next(lbi->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), phiArg); + continue; + } + + // Ok, we found some leaking blocks and potentially a loop. If we do not + // find a loop, insert the destroy_value after the load_borrow. We do not do + // this if we found a loop since our leaking blocks will lifetime extend the + // value over the loop. + if (!error.getFoundOverConsume()) { + auto next = std::next(lbi->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), phiArg); + } + + // Ok, we found some leaking blocks. Insert destroys at the beginning of + // these blocks for our copy_value. + for (auto *bb : leakingBlocks) { + SILBuilderWithScope b(bb->begin()); + b.emitDestroyValueOperation(loc, phiArg); + } + } + // Clear the phi node array now that we are done. + insertedPhiNodes.clear(); +} + +void AvailableValueAggregator::addMissingDestroysForCopiedValues( + LoadBorrowInst *lbi, SILValue newVal, + const SmallBitVector &instsToSkip) { assert(B.hasOwnership() && "We assume this is only called if we have ownership"); SmallPtrSet visitedBlocks; SmallVector leakingBlocks; - bool foundLoop = false; auto loc = RegularLocation::getAutoGeneratedLocation(); - while (!insertedInsts.empty()) { - auto *cvi = dyn_cast(insertedInsts.pop_back_val()); + + for (unsigned i : indices(insertedInsts)) { + // If we already handled this instruction above when handling phi nodes, + // just continue. + if (instsToSkip[i]) + continue; + + // Otherwise, see if this is a load [copy]. It if it a load [copy], then we + // know that the load [copy] must be in the load block meaing we can just + // put a destroy_value /after/ the load_borrow to ensure that the value + // lives long enough for us to copy_value it or a derived value for the + // begin_borrow. + if (auto *li = dyn_cast(insertedInsts[i])) { + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { + assert(li->getParent() == lbi->getParent()); + auto next = std::next(lbi->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), li); + continue; + } + } + + // Our copy_value may have been unset above if it was used by a phi + // (implying it does not dominate our final user). + auto *cvi = dyn_cast(insertedInsts[i]); if (!cvi) continue; // Clear our state. visitedBlocks.clear(); leakingBlocks.clear(); + // The linear lifetime checker doesn't care if the passed in load is // actually a user of our copy_value. What we care about is that the load is // guaranteed to be in the block where we have reformed the tuple in a @@ -902,51 +1197,34 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadBorrowInst *lbi, auto error = checker.checkValue( cvi, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, errorKind, &leakingBlocks); - if (!error.getFoundError()) + + if (!error.getFoundError()) { + // If we did not find an error, then our copy_value must be strongly + // control equivalent as our load_borrow. So just insert a destroy_value + // for the copy_value. + auto next = std::next(lbi->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), cvi); continue; + } - // Ok, we found some leaking blocks. Since we are using the linear lifetime - // checker with memory, we do not have any guarantees that the store is out - // side of a loop and a load is in a loop. In such a case, we want to - // replace the load with a copy_value. - foundLoop |= error.getFoundOverConsume(); + // Ok, we found some leaking blocks and potentially a loop. If we do not + // find a loop, insert the destroy_value after the load_borrow. We do not do + // this if we found a loop since our leaking blocks will lifetime extend the + // value over the loop. + if (!error.getFoundOverConsume()) { + auto next = std::next(lbi->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), cvi); + } - // Ok, we found some leaking blocks. Insert destroys at the - // beginning of these blocks for our copy_value. + // Ok, we found some leaking blocks. Insert destroys at the beginning of + // these blocks for our copy_value. for (auto *bb : leakingBlocks) { SILBuilderWithScope b(bb->begin()); b.emitDestroyValueOperation(loc, cvi); } } - - // If we didn't find a loop, we are done, just return svi to get RAUWed. - if (!foundLoop) { - // If we had a load_borrow, we have created an extra copy that we are going - // to borrow at the load point. This means we need to handle the destroying - // of the value along paths reachable from the load_borrow. Luckily that - // will exactly be after the end_borrows of the load_borrow. - for (auto *use : lbi->getUses()) { - if (auto *ebi = dyn_cast(use->getUser())) { - auto next = std::next(ebi->getIterator()); - SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(), - newVal); - } - } - return lbi; - } - - // If we found a loop, then we know that our leaking blocks are the exiting - // blocks of the loop and the value has been lifetime extended over the loop. - // If we have a load_borrow, we create a begin_borrow for the end_borrows in - // the loop. - newVal = SILBuilderWithScope(lbi).createBeginBorrow(lbi->getLoc(), newVal); - - lbi->replaceAllUsesWith(newVal); - SILValue addr = lbi->getOperand(); - lbi->eraseFromParent(); - if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); - return nullptr; } //===----------------------------------------------------------------------===// @@ -1704,8 +1982,8 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { AvailableValueExpectedOwnership::Copy); SILValue newVal = agg.aggregateValues(loadTy, li->getOperand(), firstElt); - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *li << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *li); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); ++NumLoadPromoted; // If we did not have ownership, we did not insert extra copies at our stores, @@ -1793,6 +2071,8 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { if (!result.hasValue()) return false; + ++NumLoadPromoted; + SILType loadTy = result->first; unsigned firstElt = result->second; @@ -1804,33 +2084,39 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { AvailableValueExpectedOwnership::Borrow); SILValue newVal = agg.aggregateValues(loadTy, lbi->getOperand(), firstElt); - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *lbi << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *lbi); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); - // If we inserted any copies, we created the copies at our stores. We know - // that in our load block, we will reform the aggregate as appropriate at the - // load implying that the value /must/ be fully consumed. If we promoted a +0 - // value, we created dominating destroys along those paths. Thus any leaking - // blocks that we may have can be found by performing a linear lifetime check - // over all copies that we found using the load as the "consuming uses" (just - // for the purposes of identifying the consuming block). - auto *oldLoad = agg.addMissingDestroysForCopiedValues(lbi, newVal); - - ++NumLoadPromoted; + // If we inserted any copies, we created the copies at our + // stores. We know that in our load block, we will reform the + // aggregate as appropriate, will borrow the value there and give us + // a whole pristine new value. Now in this routine, we go through + // all of the copies and phis that we inserted and ensure that: + // + // 1. Phis are always strongly control equivalent to the copies that + // produced their incoming values. + // + // 2. All intermediate copies are properly lifetime extended to the + // load block and all leaking blocks are filled in as appropriate + // with destroy_values. + agg.fixupOwnership(lbi, newVal); - // If we are returned the load, eliminate it. Otherwise, it was already - // handled for us... so return true. - if (!oldLoad) - return true; + // Now that we have fixed up the lifetimes of all of our incoming copies so + // that they are alive over the load point, copy, borrow newVal and insert + // destroy_value after the end_borrow and then RAUW. + SILBuilderWithScope builder(lbi); + SILValue copiedVal = builder.emitCopyValueOperation(lbi->getLoc(), newVal); + newVal = builder.createBeginBorrow(lbi->getLoc(), copiedVal); - // If our load was a +0 value, borrow the value and the RAUW. We reuse the - // end_borrows of our load_borrow. - newVal = - SILBuilderWithScope(oldLoad).createBeginBorrow(oldLoad->getLoc(), newVal); + for (auto *ebi : lbi->getUsersOfType()) { + auto next = std::next(ebi->getIterator()); + SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(), + copiedVal); + } - oldLoad->replaceAllUsesWith(newVal); - SILValue addr = oldLoad->getOperand(0); - oldLoad->eraseFromParent(); + lbi->replaceAllUsesWith(newVal); + SILValue addr = lbi->getOperand(); + lbi->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) recursivelyDeleteTriviallyDeadInstructions(addrI); return true; @@ -1917,8 +2203,8 @@ void AllocOptimize::promoteDestroyAddr( ++NumDestroyAddrPromoted; - LLVM_DEBUG(llvm::dbgs() << " *** Promoting destroy_addr: " << *dai << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting destroy_addr: " << *dai); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); SILBuilderWithScope(dai).emitDestroyValueOperation(dai->getLoc(), newVal); dai->eraseFromParent(); @@ -1945,8 +2231,8 @@ void AllocOptimize::promoteLoadTake( ++NumLoadTakePromoted; - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load_take: " << *li << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting load_take: " << *li); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); // Then perform the RAUW. li->replaceAllUsesWith(newVal); @@ -2223,8 +2509,8 @@ static bool optimizeMemoryAccesses(SILFunction &fn) { continue; } - LLVM_DEBUG(llvm::dbgs() << "*** PMO Optimize Memory Accesses looking at: " - << *alloc << "\n"); + LLVM_DEBUG(llvm::dbgs() + << "*** PMO Optimize Memory Accesses looking at: " << *alloc); PMOMemoryObjectInfo memInfo(alloc); // Set up the datastructure used to collect the uses of the allocation. @@ -2266,8 +2552,8 @@ static bool eliminateDeadAllocations(SILFunction &fn) { } LLVM_DEBUG(llvm::dbgs() - << "*** PMO Dead Allocation Elimination looking at: " << *alloc - << "\n"); + << "*** PMO Dead Allocation Elimination looking at: " + << *alloc); PMOMemoryObjectInfo memInfo(alloc); // Set up the datastructure used to collect the uses of the allocation. diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index cf92559a25b39..192723cfbd731 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -21,9 +21,12 @@ struct IntPair { var y: Builtin.Int32 } -sil @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +sil @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () sil @intpair_user : $@convention(thin) (IntPair) -> () +sil @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () sil @inout_int32_user : $@convention(thin) (@inout Builtin.Int32) -> () +sil @get_object : $@convention(thin) () -> @owned Builtin.NativeObject +sil @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () /// Needed to avoid tuple scalarization code in the use gatherer. struct NativeObjectAndTuple { @@ -545,13 +548,36 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK-LABEL: sil [ossa] @load_borrow_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { // CHECK: bb0([[ARG:%.*]] : +// Block where we have our store and do our lifetime extending copy_value. // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[BORROWED_ARG_COPY:%.*]] = begin_borrow [[ARG_COPY]] +// CHECK: br bb1 +// +// Our load block. Here, we insert our copy_value + begin_borrow that is +// associated with the load_borrow. We can not use the original copy since even +// though in this situation we know that our copy/borrow would be strongly +// control equivalent, this is not always true. To simplify the algorithm, we +// always insert the copy here. We insert a destroy_value to end the lifetime of +// ARG_COPY since we do not have a loop here. +// +// CHECK: bb1: +// CHECK: [[CONTROL_EQUIVALENT_ARG_COPY:%.*]] = copy_value [[ARG_COPY]] +// CHECK: [[BORROWED_ARG_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_ARG_COPY]] +// CHECK: destroy_value [[ARG_COPY]] +// CHECK: br bb2 +// +// The block where the load_borrow is actually used. We destroy the control +// equivalent arg copy here after the end_borrow. +// +// CHECK: bb2: // CHECK: [[RESULT:%.*]] = copy_value [[BORROWED_ARG_COPY]] // CHECK: end_borrow [[BORROWED_ARG_COPY]] -// CHECK: destroy_value [[ARG_COPY]] +// CHECK: destroy_value [[CONTROL_EQUIVALENT_ARG_COPY]] +// CHECK: br bb3 +// +// The block after the load_borrow is ever used. +// CHECK: bb3: // CHECK: destroy_addr [[STACK]] // CHECK: return [[RESULT]] // CHECK: } // end sil function 'load_borrow_promotion' @@ -559,9 +585,18 @@ sil [ossa] @load_borrow_promotion : $@convention(thin) (@owned Builtin.NativeObj bb0(%0 : @owned $Builtin.NativeObject): %1 = alloc_stack $Builtin.NativeObject store %0 to [init] %1 : $*Builtin.NativeObject + br bb1 + +bb1: %2 = load_borrow %1 : $*Builtin.NativeObject + br bb2 + +bb2: %3 = copy_value %2 : $Builtin.NativeObject end_borrow %2 : $Builtin.NativeObject + br bb3 + +bb3: destroy_addr %1 : $*Builtin.NativeObject dealloc_stack %1 : $*Builtin.NativeObject return %3 : $Builtin.NativeObject @@ -579,7 +614,7 @@ bb0(%0 : @owned $NativeObjectPair): bb2: %3 = load_borrow %2 : $*Builtin.NativeObject - %4 = function_ref @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () end_borrow %3 : $Builtin.NativeObject br bb2 @@ -597,7 +632,7 @@ bb0(%0 : @owned $NativeObjectPair): bb2: %3 = load_borrow %2 : $*Builtin.NativeObject - %4 = function_ref @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () end_borrow %3 : $Builtin.NativeObject cond_br undef, bb3, bb4 @@ -662,17 +697,21 @@ bb9: // CHECK: ([[TUP_0:%.*]], [[TUP_1:%.*]]) = destructure_tuple [[TUP]] // CHECK: [[TUP_0_COPY:%.*]] = copy_value [[TUP_0]] // CHECK: [[TUP_1_COPY:%.*]] = copy_value [[TUP_1]] -// CHECK: [[BORROWED_TUP_0_COPY:%.*]] = begin_borrow [[TUP_0_COPY]] -// CHECK: [[BORROWED_TUP_1_COPY:%.*]] = begin_borrow [[TUP_1_COPY]] +// CHECK: [[CONTROL_EQUIVALENT_TUP_0_COPY:%.*]] = copy_value [[TUP_0_COPY]] +// CHECK: [[BORROWED_TUP_0_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_TUP_0_COPY]] +// CHECK: destroy_value [[TUP_0_COPY]] +// CHECK: [[CONTROL_EQUIVALENT_TUP_1_COPY:%.*]] = copy_value [[TUP_1_COPY]] +// CHECK: [[BORROWED_TUP_1_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_TUP_1_COPY]] +// CHECK: destroy_value [[TUP_1_COPY]] // CHECK: [[BORROWED_TUP:%.*]] = tuple ([[BORROWED_TUP_0_COPY]] : ${{.*}}, [[BORROWED_TUP_1_COPY]] : // CHECK: [[TUP_EXT_1:%.*]] = tuple_extract [[BORROWED_TUP]] : // CHECK: [[TUP_EXT_2:%.*]] = tuple_extract [[BORROWED_TUP]] : // CHECK: apply {{%.*}}([[TUP_EXT_1]]) // CHECK: apply {{%.*}}([[TUP_EXT_2]]) // CHECK: end_borrow [[BORROWED_TUP_0_COPY]] -// CHECK: destroy_value [[TUP_0_COPY]] +// CHECK: destroy_value [[CONTROL_EQUIVALENT_TUP_0_COPY]] // CHECK: end_borrow [[BORROWED_TUP_1_COPY]] -// CHECK: destroy_value [[TUP_1_COPY]] +// CHECK: destroy_value [[CONTROL_EQUIVALENT_TUP_1_COPY]] // CHECK: } // end sil function 'load_borrow_tuple_scalarize' sil [canonical] [ossa] @load_borrow_tuple_scalarize : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -683,7 +722,7 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %4 = load_borrow %2 : $*(Builtin.NativeObject, Builtin.NativeObject) %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 0 %6 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 - %7 = function_ref @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %7 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %7(%5) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %7(%6) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () end_borrow %4 : $(Builtin.NativeObject, Builtin.NativeObject) @@ -694,12 +733,12 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): return %9999 : $() } -// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +// CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { // CHECK: bb0( // CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair // CHECK-NOT: bb{{[0-9][0-9]*}}( -// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_trivial' -sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial' +sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32): %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () %1 = alloc_stack $IntPair @@ -738,12 +777,12 @@ bb7: return %9999 : $() } -// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { // CHECK: bb0( // CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair // CHECK-NOT: bb{{[0-9][0-9]*}}( -// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_trivial_reload' -sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload' +sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () %1 = alloc_stack $IntPair @@ -782,7 +821,10 @@ bb7: return %9999 : $() } -sil [ossa] @multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK-NOT: load +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop' +sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () %1 = alloc_stack $IntPair @@ -820,4 +862,481 @@ bb7: dealloc_stack %1 : $*IntPair %9999 = tuple() return %9999 : $() -} \ No newline at end of file +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK: bb0( +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop' +sil [ossa] @multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_reload' +sil [ossa] @multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0c to [init] %1b : $*Builtin.NativeObject + destroy_value %0b : $Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + destroy_value %0c : $Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_store_in_loop' +sil [ossa] @multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + %0bhat = copy_value %0b : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_addr %1b : $*Builtin.NativeObject + %0bhat2 = copy_value %0bhat : $Builtin.NativeObject + store %0bhat2 to [init] %1b : $*Builtin.NativeObject + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_value %0bhat : $Builtin.NativeObject + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow' +sil [canonical] [ossa] @loop_carry_loadborrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load_borrow %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow_2' +sil [canonical] [ossa] @loop_carry_loadborrow_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load_borrow %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow_3' +sil [canonical] [ossa] @loop_carry_loadborrow_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + %1 = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) + %1a = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 0 + %1b = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 1 + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load_borrow %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + end_borrow %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + end_borrow %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + dealloc_stack %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow_4' +sil [canonical] [ossa] @loop_carry_loadborrow_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_load_borrow_phi_not_control_equivalent' +sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8 + +bb6: + br bb8 + +bb7: + br bbPreLoopHeader + +bb8: + br bbPreLoopHeader + +bbPreLoopHeader: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load_borrow %0 : $*Builtin.NativeObject + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// In this case, we will have that we need to separately lifetime extend our phi +// node's copy to prevent leaks along the edge skipping the loop. +// CHECK-LABEL: sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_load_borrow_phi_not_control_equivalent_2' +sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8a + +bb6: + br bb8a + +bb7: + br bbPreLoopHeader + +bb8a: + br bb8 + +bb8: + cond_br undef, bbPreLoopHeader1, bbSkipLoop + +bbPreLoopHeader: + br bbLoop + +bbPreLoopHeader1: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load_borrow %0 : $*Builtin.NativeObject + br bbLoop6 + +bbLoop6: + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbLoop5 + +bbLoop5: + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbEnd + +bbSkipLoop: + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + From 97b16ce5e35d5526a5ff314dc3609d49f9a6c244 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 12 Nov 2019 13:58:17 +0900 Subject: [PATCH 098/283] [CodeCompletion] Remove unsound check in override completion In override completion, we didn't use to emit 'class var' with the initial expression. However, having initial expression does not have any bearing on whether the declaration is overridable or not. --- lib/IDE/CodeCompletion.cpp | 4 ---- test/IDE/complete_override.swift | 21 ++++++++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index c2df1990168d1..a521c1413ff42 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4399,10 +4399,6 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { if (D->isFinal()) return; - // A 'class' member with an initial value cannot be overriden either. - if (D->isStatic() && isa(D) && cast(D)->hasInitialValue()) - return; - bool hasIntroducer = hasFuncIntroducer || hasVarIntroducer || hasTypealiasIntroducer; diff --git a/test/IDE/complete_override.swift b/test/IDE/complete_override.swift index 2970a0efc6980..27110af69ab8e 100644 --- a/test/IDE/complete_override.swift +++ b/test/IDE/complete_override.swift @@ -724,9 +724,10 @@ class Override26 : OverrideBase, OverrideP { // Same as MODIFIER24 } -// MODIFIER1: Begin completions, 9 items +// MODIFIER1: Begin completions, 10 items // MODIFIER1-DAG: Decl[Constructor]/Super: required init(p: Int) {|}; name=required init(p: Int) // MODIFIER1-DAG: Decl[StaticMethod]/Super: override class func classMethod() {|}; name=classMethod() +// MODIFIER1-DAG: Decl[StaticVar]/Super: override class var classVar: Int; name=classVar: Int // MODIFIER1-DAG: Decl[StaticVar]/Super: override class var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER1-DAG: Decl[InstanceMethod]/Super: override func defaultMethod() {|}; name=defaultMethod() // MODIFIER1-DAG: Decl[InstanceMethod]/Super: override func openMethod() {|}; name=openMethod() @@ -736,7 +737,8 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER1-DAG: Decl[AssociatedType]/Super: typealias Assoc = {#(Type)#}; name=Assoc = Type // MODIFIER1: End completions -// MODIFIER2: Begin completions, 5 items +// MODIFIER2: Begin completions, 6 items +// MODIFIER2-DAG: Decl[StaticVar]/Super: override class var classVar: Int; name=classVar: Int // MODIFIER2-DAG: Decl[StaticVar]/Super: override class var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER2-DAG: Decl[StaticMethod]/Super: override class func classMethod() {|}; name=classMethod() // MODIFIER2-DAG: Decl[InstanceMethod]/Super: override func defaultMethod() {|}; name=defaultMethod() @@ -760,7 +762,8 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER6-DAG: Decl[AssociatedType]/Super: Assoc = {#(Type)#}; name=Assoc = Type // MODIFIER6: End completions -// MODIFIER7: Begin completions, 7 items +// MODIFIER7: Begin completions, 8 items +// MODIFIER7-DAG: Decl[StaticVar]/Super: class var classVar: Int; name=classVar: Int // MODIFIER7-DAG: Decl[StaticVar]/Super: class var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER7-DAG: Decl[StaticMethod]/Super: class func classMethod() {|}; name=classMethod() // MODIFIER7-DAG: Decl[InstanceMethod]/Super: func defaultMethod() {|}; name=defaultMethod() @@ -785,11 +788,13 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER13-NOT: Begin completions -// MODIFIER15: Begin completions, 1 items +// MODIFIER15: Begin completions, 2 items +// MODIFIER15-DAG: Decl[StaticVar]/Super/Erase[4]: override var classVar: Int; name=classVar: Int // MODIFIER15-DAG: Decl[StaticVar]/Super/Erase[4]: override var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER15: End completions -// MODIFIER17: Begin completions, 1 items +// MODIFIER17: Begin completions, 2 items +// MODIFIER17-DAG: Decl[StaticVar]/Super: classVar: Int; name=classVar: Int // MODIFIER17-DAG: Decl[StaticVar]/Super: classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER17: End completions @@ -801,13 +806,15 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER22: Decl[StaticMethod]/Super/Erase[5]: override func classMethod() {|}; name=classMethod() // MODIFIER22: End completions -// MODIFIER23: Begin completions, 2 items +// MODIFIER23: Begin completions, 3 items // MODIFIER23-DAG: Decl[StaticMethod]/Super: override func classMethod() {|}; name=classMethod() +// MODIFIER23-DAG: Decl[StaticVar]/Super: override var classVar: Int; name=classVar: Int // MODIFIER23-DAG: Decl[StaticVar]/Super: override var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER23: End completions -// MODIFIER24: Begin completions, 2 items +// MODIFIER24: Begin completions, 3 items // MODIFIER24-DAG: Decl[StaticMethod]/Super: func classMethod() {|}; name=classMethod() +// MODIFIER24-DAG: Decl[StaticVar]/Super: var classVar: Int; name=classVar: Int // MODIFIER24-DAG: Decl[StaticVar]/Super: var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER24: End completions From ed77b86c241cf0e8f289d946e634d0fdb66da228 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 12 Nov 2019 07:23:12 -0800 Subject: [PATCH 099/283] getInterfaceType() always returns a type --- lib/AST/ASTPrinter.cpp | 75 +++++++++---------- lib/AST/Decl.cpp | 10 +-- lib/AST/USRGeneration.cpp | 2 - lib/IDE/ExprContextAnalysis.cpp | 3 - lib/Sema/CalleeCandidateInfo.cpp | 3 +- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/TypeCheckDecl.cpp | 5 +- lib/Sema/TypeCheckStmt.cpp | 3 +- lib/Serialization/Deserialization.cpp | 7 +- .../lib/SwiftLang/SwiftSourceDocInfo.cpp | 11 ++- 10 files changed, 46 insertions(+), 75 deletions(-) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index af61b5e16c02b..683bd769098ee 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2526,26 +2526,26 @@ void PrintAST::visitVarDecl(VarDecl *decl) { [&]{ Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl)); }); - if (auto type = decl->getInterfaceType()) { - Printer << ": "; - TypeLoc tyLoc; - if (auto *repr = decl->getTypeReprOrParentPatternTypeRepr()) - tyLoc = TypeLoc(repr, type); - else - tyLoc = TypeLoc::withoutLoc(type); - Printer.printDeclResultTypePre(decl, tyLoc); + auto type = decl->getInterfaceType(); + Printer << ": "; + TypeLoc tyLoc; + if (auto *repr = decl->getTypeReprOrParentPatternTypeRepr()) + tyLoc = TypeLoc(repr, type); + else + tyLoc = TypeLoc::withoutLoc(type); - // HACK: When printing result types for vars with opaque result types, - // always print them using the `some` keyword instead of printing - // the full stable reference. - llvm::SaveAndRestore - x(Options.OpaqueReturnTypePrinting, - PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword); + Printer.printDeclResultTypePre(decl, tyLoc); - printTypeLocForImplicitlyUnwrappedOptional( - tyLoc, decl->isImplicitlyUnwrappedOptional()); - } + // HACK: When printing result types for vars with opaque result types, + // always print them using the `some` keyword instead of printing + // the full stable reference. + llvm::SaveAndRestore + x(Options.OpaqueReturnTypePrinting, + PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword); + + printTypeLocForImplicitlyUnwrappedOptional( + tyLoc, decl->isImplicitlyUnwrappedOptional()); printAccessors(decl); } @@ -2674,18 +2674,13 @@ void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { auto curTy = AFD->getInterfaceType(); // Skip over the implicit 'self'. - if (AFD->hasImplicitSelfDecl()) { - if (curTy) - if (auto funTy = curTy->getAs()) - curTy = funTy->getResult(); - } + if (AFD->hasImplicitSelfDecl()) + if (auto funTy = curTy->getAs()) + curTy = funTy->getResult(); ArrayRef parameterListTypes; - if (curTy) { - if (auto funTy = curTy->getAs()) { - parameterListTypes = funTy->getParams(); - } - } + if (auto funTy = curTy->getAs()) + parameterListTypes = funTy->getParams(); printParameterList(BodyParams, parameterListTypes, AFD->argumentNameIsAPIByDefault()); @@ -2878,15 +2873,14 @@ void PrintAST::printEnumElement(EnumElementDecl *elt) { auto params = ArrayRef(); - if (auto type = elt->getInterfaceType()) { - if (!elt->isInvalid()) { - // Walk to the params of the associated values. - // (EnumMetaType) -> (AssocValues) -> Enum - params = type->castTo() - ->getResult() - ->castTo() - ->getParams(); - } + if (!elt->isInvalid()) { + // Walk to the params of the associated values. + // (EnumMetaType) -> (AssocValues) -> Enum + auto type = elt->getInterfaceType(); + params = type->castTo() + ->getResult() + ->castTo() + ->getParams(); } // @escaping is not valid in enum element position, even though the @@ -2977,11 +2971,10 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { }, [&] { // Parameters printGenericDeclGenericParams(decl); auto params = ArrayRef(); - if (auto type = decl->getInterfaceType()) { - if (!decl->isInvalid()) { - // Walk to the params of the subscript's indices. - params = type->castTo()->getParams(); - } + if (!decl->isInvalid()) { + // Walk to the params of the subscript's indices. + auto type = decl->getInterfaceType(); + params = type->castTo()->getParams(); } printParameterList(decl->getIndices(), params, /*isAPINameByDefault*/false); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a59f780d6945e..40519d24b6659 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3426,11 +3426,7 @@ Type TypeDecl::getDeclaredInterfaceType() const { selfTy, const_cast(ATD)); } - Type interfaceType = getInterfaceType(); - if (!interfaceType) - return ErrorType::get(getASTContext()); - - return interfaceType->getMetatypeInstanceType(); + return getInterfaceType()->getMetatypeInstanceType(); } int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) { @@ -4771,10 +4767,6 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, auto type = value->getInterfaceType(); - // FIXME: Deal with broken recursion. - if (!type) - return SelfReferenceKind::None(); - // Skip invalid declarations. if (type->hasError()) return SelfReferenceKind::None(); diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index cf9d7895f71b9..db298ab702686 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -242,8 +242,6 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, } auto declIFaceTy = D->getInterfaceType(); - if (!declIFaceTy) - return std::string(); // Invalid code. if (declIFaceTy.findIf([](Type t) -> bool { diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 44320e0517381..f5ee2b6db3377 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -288,9 +288,6 @@ static void collectPossibleCalleesByQualifiedLookup( if (!isMemberDeclApplied(&DC, baseTy->getMetatypeInstanceType(), VD)) continue; Type declaredMemberType = VD->getInterfaceType(); - if (!declaredMemberType) { - continue; - } if (!declaredMemberType->is()) continue; if (VD->getDeclContext()->isTypeContext()) { diff --git a/lib/Sema/CalleeCandidateInfo.cpp b/lib/Sema/CalleeCandidateInfo.cpp index bfd548925d3dd..67b5127efac82 100644 --- a/lib/Sema/CalleeCandidateInfo.cpp +++ b/lib/Sema/CalleeCandidateInfo.cpp @@ -602,8 +602,7 @@ void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn, auto ctors = TypeChecker::lookupConstructors( CS.DC, instanceType, NameLookupFlags::IgnoreAccessControl); for (auto ctor : ctors) { - if (ctor.getValueDecl()->getInterfaceType()) - candidates.push_back({ ctor.getValueDecl(), 1 }); + candidates.push_back({ ctor.getValueDecl(), 1 }); } } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index eadd085b89378..a9dca5bcfae22 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1470,7 +1470,7 @@ Type ConstraintSystem::getEffectiveOverloadType(const OverloadChoice &overload, // Retrieve the interface type. auto type = decl->getInterfaceType(); - if (!type || type->hasError()) { + if (type->hasError()) { return Type(); } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 213f1c4a3fc63..9a230feec7380 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -3939,11 +3939,8 @@ bool swift::isMemberOperator(FuncDecl *decl, Type type) { // Check the parameters for a reference to 'Self'. bool isProtocol = selfNominal && isa(selfNominal); for (auto param : *decl->getParameters()) { - auto paramType = param->getInterfaceType(); - if (!paramType) break; - // Look through a metatype reference, if there is one. - paramType = paramType->getMetatypeInstanceType(); + auto paramType = param->getInterfaceType()->getMetatypeInstanceType(); auto nominal = paramType->getAnyNominal(); if (type.isNull()) { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index da0a60faca8d9..bcf05ecba04c4 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1166,8 +1166,7 @@ class StmtChecker : public StmtVisitor { } assert(isa(initialCaseVarDecl->getParentPatternStmt())); - if (vd->getInterfaceType() && initialCaseVarDecl->getType() && - !initialCaseVarDecl->isInvalid() && + if (initialCaseVarDecl->getType() && !initialCaseVarDecl->isInvalid() && !vd->getType()->isEqual(initialCaseVarDecl->getType())) { getASTContext().Diags.diagnose(vd->getLoc(), diag::type_mismatch_multiple_pattern_list, vd->getType(), initialCaseVarDecl->getType()); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 8e71fccdd34c3..d7f6d8d6e91f4 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1122,11 +1122,8 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule, return true; // If we're expecting a type, make sure this decl has the expected type. - if (canTy) { - auto ifaceTy = value->getInterfaceType(); - if (!ifaceTy || !ifaceTy->isEqual(canTy)) - return true; - } + if (canTy && !value->getInterfaceType()->isEqual(canTy)) + return true; if (value->isStatic() != isStatic) return true; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 74e8ee8c05978..4fb215165d9c5 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -778,12 +778,11 @@ static bool passCursorInfoForDecl(SourceFile* SF, unsigned USREnd = SS.size(); unsigned TypenameBegin = SS.size(); - if (auto vdType = VD->getInterfaceType()) { - llvm::raw_svector_ostream OS(SS); - PrintOptions Options; - Options.PrintTypeAliasUnderlyingType = true; - vdType.print(OS, Options); - } + llvm::raw_svector_ostream OS(SS); + PrintOptions Options; + Options.PrintTypeAliasUnderlyingType = true; + VD->getInterfaceType().print(OS, Options); + unsigned TypenameEnd = SS.size(); unsigned MangledTypeStart = SS.size(); From 2a75179849ae85c8d8e4a6f12eed5b290595fa6f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 12 Nov 2019 07:23:52 -0800 Subject: [PATCH 100/283] Remove a couple of unnecessary hasInterfaceType() checks We call isInvalid() a few lines above, so the interface type gets computed anyway. --- lib/Sema/CSSimplify.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index d40a5a05ad1b6..85bd58b9d617c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5274,10 +5274,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, return; } - // FIXME: Deal with broken recursion - if (!decl->hasInterfaceType()) - return; - // Dig out the instance type and figure out what members of the instance type // we are going to see. auto baseTy = candidate.getBaseType(); @@ -5649,10 +5645,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, return result; } - // FIXME: Deal with broken recursion - if (!cand->hasInterfaceType()) - continue; - result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false, /*isUnwrappedOptional=*/false), MemberLookupResult::UR_Inaccessible); From a7fb2d32f36e97ac2c51f8635eae83e28912099f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 12 Nov 2019 08:09:58 -0800 Subject: [PATCH 101/283] VarDecl::getType always returns a type --- lib/Sema/TypeCheckDeclObjC.cpp | 10 ++++------ lib/Sema/TypeCheckDeclOverride.cpp | 3 --- lib/Sema/TypeCheckPattern.cpp | 5 +---- lib/Sema/TypeCheckPropertyWrapper.cpp | 2 +- lib/Sema/TypeCheckStmt.cpp | 2 +- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 8d769cd58e648..59eccfb70903b 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -225,12 +225,10 @@ static void diagnoseFunctionParamNotRepresentable( AFD->diagnose(diag::objc_invalid_on_func_param_type, ParamIndex + 1, getObjCDiagnosticAttrKind(Reason)); } - if (Type ParamTy = P->getType()) { - SourceRange SR; - if (auto typeRepr = P->getTypeRepr()) - SR = typeRepr->getSourceRange(); - diagnoseTypeNotRepresentableInObjC(AFD, ParamTy, SR); - } + SourceRange SR; + if (auto typeRepr = P->getTypeRepr()) + SR = typeRepr->getSourceRange(); + diagnoseTypeNotRepresentableInObjC(AFD, P->getType(), SR); describeObjCReason(AFD, Reason); } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 157a8ccf5891d..631aa8f859989 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -282,9 +282,6 @@ diagnoseMismatchedOptionals(const ValueDecl *member, Type paramTy = decl->getType(); Type parentParamTy = parentDecl->getType(); - if (!paramTy || !parentParamTy) - return; - auto *repr = decl->getTypeRepr(); if (!repr) return; diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 521f5edec0953..7740c3db2b7d3 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1488,10 +1488,7 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, if (param->isInvalid()) return true; - if (auto type = param->getType()) - return !isValidType(type); - - return true; + return !isValidType(param->getType()); }; auto handleParameter = [&](ParamDecl *param, Type ty, bool forceMutable) { diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 4f0644c980370..e59c97d2117c2 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -546,7 +546,7 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( // Compute the type of the property to plug in to the wrapper type. Type propertyType = var->getType(); - if (!propertyType || propertyType->hasError()) + if (propertyType->hasError()) return Type(); using namespace constraints; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index bcf05ecba04c4..b2d7608258e54 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1166,7 +1166,7 @@ class StmtChecker : public StmtVisitor { } assert(isa(initialCaseVarDecl->getParentPatternStmt())); - if (initialCaseVarDecl->getType() && !initialCaseVarDecl->isInvalid() && + if (!initialCaseVarDecl->isInvalid() && !vd->getType()->isEqual(initialCaseVarDecl->getType())) { getASTContext().Diags.diagnose(vd->getLoc(), diag::type_mismatch_multiple_pattern_list, vd->getType(), initialCaseVarDecl->getType()); From 631305f64994c96872d6283afce68a5acce01579 Mon Sep 17 00:00:00 2001 From: Julian Lettner Date: Tue, 12 Nov 2019 08:19:06 -0800 Subject: [PATCH 102/283] Reactivate libdispatch tests on Linux (#27940) * Reactivate libdispatch tests on Linux The swift-corelibs-libdispatch project had its build directory layout changed [1] which silently deactivated the libdispatch tests on Linux, because we use hard-coded paths to look for the required libdispatch artifacts. This change adapts the paths to the new layout. Thanks to Jordan Rose for noticing and diagnosing this [2]. SR-11568 [2] https://bugs.swift.org/browse/SR-11568 [1] https://github.com/apple/swift-corelibs-libdispatch/pull/515 * Fix libdispatch Swift module directory --- test/lit.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/lit.cfg b/test/lit.cfg index f274c7403f88f..ed6a853df0fe7 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1024,16 +1024,16 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_runtime = "native" config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract") - libdispatch_artifact_dir = make_path(config.libdispatch_build_path, 'src') + libdispatch_artifact_dir = config.libdispatch_build_path + libdispatch_swift_module_dir = make_path(libdispatch_artifact_dir, 'src', 'swift', 'swift') libdispatch_artifacts = [ make_path(libdispatch_artifact_dir, 'libdispatch.so'), make_path(libdispatch_artifact_dir, 'libswiftDispatch.so'), - make_path(libdispatch_artifact_dir, 'swift', 'Dispatch.swiftmodule')] + make_path(libdispatch_swift_module_dir, 'Dispatch.swiftmodule')] if (all(os.path.exists(p) for p in libdispatch_artifacts)): config.available_features.add('libdispatch') config.libdispatch_artifact_dir = libdispatch_artifact_dir libdispatch_source_dir = make_path(config.swift_src_root, os.pardir, 'swift-corelibs-libdispatch') - libdispatch_swift_module_dir = make_path(libdispatch_artifact_dir, 'swift') config.import_libdispatch = ('-I %s -I %s -L %s' % (libdispatch_source_dir, libdispatch_swift_module_dir, libdispatch_artifact_dir)) From cd17339f82ffbe3b4481c9bcbcc5e1276944650b Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 12 Nov 2019 09:28:31 -0800 Subject: [PATCH 103/283] [AutoDiff upstream] Update `@differentiable` syntax. Clean up unused syntax components and fix description strings. --- utils/gyb_syntax_support/AttributeNodes.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index 1967ae33805e2..e81c2d8b781a5 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -209,7 +209,7 @@ # generic-where-clause? Node('DifferentiableAttributeArguments', kind='Syntax', description=''' - The arguments for the `@differentiable` attribute: an optional \ + The arguments for the `@differentiable` attribute: an optional differentiation parameter list and associated functions. ''', children=[ @@ -219,10 +219,6 @@ The comma following the differentiation parameters clause, if it exists. ''', is_optional=True), - Child('MaybePrimal', kind='DifferentiableAttributeFuncSpecifier', - is_optional=True), - Child('MaybeAdjoint', kind='DifferentiableAttributeFuncSpecifier', - is_optional=True), Child('MaybeJVP', kind='DifferentiableAttributeFuncSpecifier', is_optional=True), Child('MaybeVJP', kind='DifferentiableAttributeFuncSpecifier', @@ -266,7 +262,7 @@ # differentiation-param -> ('self' | identifer) ','? Node('DifferentiationParam', kind='Syntax', description=''' - A differentiation parameter: either the "self" identifier or a \ + A differentiation parameter: either the "self" identifier or a function parameter name. ''', traits=['WithTrailingComma'], @@ -283,7 +279,7 @@ # ('jvp' | 'vjp') ':' func-decl-name ','? Node('DifferentiableAttributeFuncSpecifier', kind='Syntax', description=''' - A function specifier, consisting of an identifier, colon, and a \ + A function specifier, consisting of an identifier, colon, and a function declaration name (e.g. `vjp: foo(_:_:)`). ''', traits=['WithTrailingComma'], @@ -313,7 +309,7 @@ ]), Child('Arguments', kind='DeclNameArguments', is_optional=True, description=''' - The argument labels of the referenced function, optionally \ + The argument labels of the referenced function, optionally specified. '''), ]), From 253d6ac6c606e13ce40fca6b376813c0d3827b58 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 11 Nov 2019 13:48:52 +0900 Subject: [PATCH 104/283] [Parse] Remove unused bit from PersistentParserState FunctionBodyState is no longer used. --- include/swift/Parse/PersistentParserState.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index d15e3b72d7d21..f7c18181b1083 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -25,7 +25,6 @@ #include "llvm/ADT/DenseMap.h" namespace swift { - class AbstractFunctionDecl; /// Parser state persistent across multiple parses. class PersistentParserState { @@ -37,22 +36,6 @@ class PersistentParserState { bool isValid() const { return Loc.isValid(); } }; - class FunctionBodyState { - ParserPos BodyPos; - SavedScope Scope; - friend class Parser; - - SavedScope takeScope() { - return std::move(Scope); - } - - public: - FunctionBodyState(SourceRange BodyRange, SourceLoc PreviousLoc, - SavedScope &&Scope) - : BodyPos{BodyRange.Start, PreviousLoc}, Scope(std::move(Scope)) - {} - }; - enum class DelayedDeclKind { TopLevelCodeDecl, Decl, @@ -89,10 +72,6 @@ class PersistentParserState { bool PerformConditionEvaluation = true; private: ScopeInfo ScopeInfo; - using DelayedFunctionBodiesTy = - llvm::DenseMap>; - DelayedFunctionBodiesTy DelayedFunctionBodies; /// Parser sets this if it stopped parsing before the buffer ended. ParserPosition MarkedPos; From 4e50237f5476b2facade8daf0abf59dd7f27221d Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 11 Nov 2019 14:06:09 +0900 Subject: [PATCH 105/283] [Parse/CodeCompletion] Cleanup code completion facilities in Parse - delayParseFromBeginningToHere is useless. Use delayDecl() instead - Inline PersistentParserState::delayTopLevel() - Cleanup completion handling in parseDecl() --- include/swift/Parse/Parser.h | 2 - include/swift/Parse/PersistentParserState.h | 3 -- lib/Parse/ParseDecl.cpp | 42 ++++++--------------- lib/Parse/ParseStmt.cpp | 5 ++- lib/Parse/PersistentParserState.cpp | 7 ---- 5 files changed, 15 insertions(+), 44 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 0723b9184b485..481a69969a774 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -895,8 +895,6 @@ class Parser { /// Options that control the parsing of declarations. using ParseDeclOptions = OptionSet; - void delayParseFromBeginningToHere(ParserPosition BeginParserPosition, - ParseDeclOptions Flags); void consumeDecl(ParserPosition BeginParserPosition, ParseDeclOptions Flags, bool IsTopLevel); diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index f7c18181b1083..1ffb058e77c7c 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -95,9 +95,6 @@ class PersistentParserState { void delayDeclList(IterableDeclContext *D); - void delayTopLevel(TopLevelCodeDecl *TLCD, SourceRange BodyRange, - SourceLoc PreviousLoc); - bool hasDelayedDecl() { return CodeCompletionDelayedDeclState.get() != nullptr; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b379b60ace174..3a4dd2ffa4c34 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3045,21 +3045,6 @@ void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { } } -void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition, - ParseDeclOptions Flags) { - auto CurLoc = Tok.getLoc(); - backtrackToPosition(BeginParserPosition); - SourceLoc BeginLoc = Tok.getLoc(); - SourceLoc EndLoc = CurLoc; - State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, - Flags.toRaw(), - CurDeclContext, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); - - while (Tok.isNot(tok::eof)) - consumeToken(); -} - /// Parse a single syntactic declaration and return a list of decl /// ASTs. This can return multiple results for var decls that bind to multiple /// values, structs that define a struct decl and a constructor, etc. @@ -3429,26 +3414,23 @@ Parser::parseDecl(ParseDeclOptions Flags, consumeToken(tok::code_complete); } - if (AttrStatus.hasCodeCompletion()) { - if (CodeCompletion) { + if (AttrStatus.hasCodeCompletion() || DeclResult.hasCodeCompletion()) { + if (isCodeCompletionFirstPass() && + !CurDeclContext->isModuleScopeContext() && + !isa(CurDeclContext) && + !isa(CurDeclContext)) { + // Only consume non-toplevel decls. + consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false); + + return makeParserError(); + } + if (AttrStatus.hasCodeCompletion() && CodeCompletion) { Optional DK; if (DeclResult.isNonNull()) DK = DeclResult.get()->getKind(); CodeCompletion->setAttrTargetDeclKind(DK); - } else { - delayParseFromBeginningToHere(BeginParserPosition, Flags); - return makeParserError(); } - } - - if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass() && - !CurDeclContext->isModuleScopeContext() && - !isa(CurDeclContext) && - !isa(CurDeclContext)) { - // Only consume non-toplevel decls. - consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false); - - return makeParserError(); + DeclResult.setHasCodeCompletion(); } if (auto SF = CurDeclContext->getParentSourceFile()) { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 9923d1c9cdc69..6638d44b8bf17 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -256,8 +256,9 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, SourceLoc EndLoc = PreviousLoc; backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); - State->delayTopLevel(TLCD, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); + State->delayDecl(PersistentParserState::DelayedDeclKind::TopLevelCodeDecl, + PD_Default, TLCD, {BeginLoc, EndLoc}, + BeginParserPosition.PreviousLoc); // Skip the rest of the file to prevent the parser from constructing the AST // for it. Forward references are not allowed at the top level. diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 89d700847d2b6..02523698cb9c6 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -45,10 +45,3 @@ void PersistentParserState::parseAllDelayedDeclLists() { for (auto IDC : DelayedDeclLists) IDC->loadAllMembers(); } - -void PersistentParserState::delayTopLevel(TopLevelCodeDecl *TLCD, - SourceRange BodyRange, - SourceLoc PreviousLoc) { - delayDecl(DelayedDeclKind::TopLevelCodeDecl, 0U, TLCD, BodyRange, - PreviousLoc); -} From 097726717e09561fe42b78cec1e1eeaf4315cd35 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 8 Nov 2019 16:31:25 -0800 Subject: [PATCH 106/283] Absorb the TypeCheckerFlags into the Frontend De-duplicate TypeCheckingFlags, TypeChecker's Options, and the TypeChecker-Oriented FrontendOptions into a dedicated TypeCheckerOptions type. This moves a bunch of configuration state out of the type checker and into the ASTContext where it belongs. --- include/swift/Basic/LangOptions.h | 50 ++++++++- include/swift/Frontend/FrontendOptions.h | 24 ----- include/swift/Subsystems.h | 27 +---- lib/Frontend/Frontend.cpp | 21 ---- lib/Sema/TypeChecker.h | 125 ----------------------- 5 files changed, 51 insertions(+), 196 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 100df539d0ff7..b2190db83029d 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -56,7 +56,7 @@ namespace swift { /// A collection of options that affect the language dialect and /// provide compiler debugging facilities. - class LangOptions { + class LangOptions final { public: /// The target we are building for. @@ -456,6 +456,54 @@ namespace swift { PlatformConditionValues; llvm::SmallVector CustomConditionalCompilationFlags; }; + + class TypeCheckerOptions final { + public: + /// If non-zero, warn when a function body takes longer than this many + /// milliseconds to type-check. + /// + /// Intended for debugging purposes only. + unsigned WarnLongFunctionBodies = 0; + + /// If non-zero, warn when type-checking an expression takes longer + /// than this many milliseconds. + /// + /// Intended for debugging purposes only. + unsigned WarnLongExpressionTypeChecking = 0; + + /// If non-zero, abort the expression type checker if it takes more + /// than this many seconds. + unsigned ExpressionTimeoutThreshold = 600; + + /// If non-zero, abort the switch statement exhaustiveness checker if + /// the Space::minus function is called more than this many times. + /// + /// Why this number? Times out in about a second on a 2017 iMac, Retina 5K, + /// 4.2 GHz Intel Core i7. + /// (It's arbitrary, but will keep the compiler from taking too much time.) + unsigned SwitchCheckingInvocationThreshold = 200000; + + /// Whether to delay checking that benefits from having the entire + /// module parsed, e.g., Objective-C method override checking. + bool DelayWholeModuleChecking = false; + + /// If true, the time it takes to type-check each function will be dumped + /// to llvm::errs(). + bool DebugTimeFunctionBodies = false; + + /// If true, the time it takes to type-check each expression will be + /// dumped to llvm::errs(). + bool DebugTimeExpressions = false; + + /// Indicate that the type checker is checking code that will be + /// immediately executed. This will suppress certain warnings + /// when executing scripts. + bool InImmediateMode = false; + + /// Indicate that the type checker should skip type-checking non-inlinable + /// function bodies. + bool SkipNonInlinableFunctionBodies = false; + }; } // end namespace swift #endif // SWIFT_BASIC_LANGOPTIONS_H diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index c663d99d79155..77190a34aa6e1 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -82,28 +82,6 @@ class FrontendOptions { /// Emit index data for imported serialized swift system modules. bool IndexSystemModules = false; - /// If non-zero, warn when a function body takes longer than this many - /// milliseconds to type-check. - /// - /// Intended for debugging purposes only. - unsigned WarnLongFunctionBodies = 0; - - /// If non-zero, warn when type-checking an expression takes longer - /// than this many milliseconds. - /// - /// Intended for debugging purposes only. - unsigned WarnLongExpressionTypeChecking = 0; - - /// If non-zero, overrides the default threshold for how long we let - /// the expression type checker run before we consider an expression - /// too complex. - unsigned SolverExpressionTimeThreshold = 0; - - /// If non-zero, overrides the default threshold for how many times - /// the Space::minus function is called before we consider switch statement - /// exhaustiveness checking to be too complex. - unsigned SwitchCheckingInvocationThreshold = 0; - /// The module for which we should verify all of the generic signatures. std::string VerifyGenericSignaturesInModule; @@ -183,8 +161,6 @@ class FrontendOptions { /// \sa swift::SharedTimer bool DebugTimeCompilation = false; - bool SkipNonInlinableFunctionBodies = false; - /// The path to which we should output statistics files. std::string StatsOutputDir; diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 755509fbc676d..16374a42ddfb6 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -168,30 +168,6 @@ namespace swift { /// to add calls to externally provided functions that simulate /// "program counter"-like debugging events. void performPCMacro(SourceFile &SF, TopLevelContext &TLC); - - /// Flags used to control type checking. - enum class TypeCheckingFlags : unsigned { - /// Whether to delay checking that benefits from having the entire - /// module parsed, e.g., Objective-C method override checking. - DelayWholeModuleChecking = 1 << 0, - - /// If set, dumps wall time taken to check each function body to - /// llvm::errs(). - DebugTimeFunctionBodies = 1 << 1, - - /// Indicates that the type checker is checking code that will be - /// immediately executed. - ForImmediateMode = 1 << 2, - - /// If set, dumps wall time taken to type check each expression to - /// llvm::errs(). - DebugTimeExpressions = 1 << 3, - - /// If set, the typechecker will skip typechecking non-inlinable function - /// bodies. Set this if you're trying to quickly emit a module or module - /// interface without a full compilation. - SkipNonInlinableFunctionBodies = 1 << 4, - }; /// Creates a type checker instance on the given AST context, if it /// doesn't already have one. @@ -372,7 +348,8 @@ namespace swift { class ParserUnit { public: ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, - const LangOptions &LangOpts, StringRef ModuleName, + const LangOptions &LangOpts, const TypeCheckerOptions &TyOpts, + StringRef ModuleName, std::shared_ptr spActions = nullptr, SyntaxParsingCache *SyntaxCache = nullptr); ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 40e2f5149b429..9260aa615d13a 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -912,27 +912,6 @@ void CompilerInstance::parseLibraryFile( performNameBinding(*NextInput); } -OptionSet CompilerInstance::computeTypeCheckingOptions() { - OptionSet TypeCheckOptions; - if (isWholeModuleCompilation()) { - TypeCheckOptions |= TypeCheckingFlags::DelayWholeModuleChecking; - } - const auto &options = Invocation.getFrontendOptions(); - if (options.DebugTimeFunctionBodies) { - TypeCheckOptions |= TypeCheckingFlags::DebugTimeFunctionBodies; - } - if (FrontendOptions::isActionImmediate(options.RequestedAction)) { - TypeCheckOptions |= TypeCheckingFlags::ForImmediateMode; - } - if (options.DebugTimeExpressionTypeChecking) { - TypeCheckOptions |= TypeCheckingFlags::DebugTimeExpressions; - } - if (options.SkipNonInlinableFunctionBodies) { - TypeCheckOptions |= TypeCheckingFlags::SkipNonInlinableFunctionBodies; - } - return TypeCheckOptions; -} - bool CompilerInstance::parsePartialModulesAndLibraryFiles( const ImplicitImports &implicitImports) { FrontendStatsTracer tracer(Context->Stats, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 884fd1f1172df..a517de886fd19 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -544,47 +544,6 @@ class TypeChecker final { private: ASTContext &Context; - /// If non-zero, warn when a function body takes longer than this many - /// milliseconds to type-check. - /// - /// Intended for debugging purposes only. - unsigned WarnLongFunctionBodies = 0; - - /// If non-zero, warn when type-checking an expression takes longer - /// than this many milliseconds. - /// - /// Intended for debugging purposes only. - unsigned WarnLongExpressionTypeChecking = 0; - - /// If non-zero, abort the expression type checker if it takes more - /// than this many seconds. - unsigned ExpressionTimeoutThreshold = 600; - - /// If non-zero, abort the switch statement exhaustiveness checker if - /// the Space::minus function is called more than this many times. - /// - /// Why this number? Times out in about a second on a 2017 iMac, Retina 5K, - // 4.2 GHz Intel Core i7. - // (It's arbitrary, but will keep the compiler from taking too much time.) - unsigned SwitchCheckingInvocationThreshold = 200000; - - /// If true, the time it takes to type-check each function will be dumped - /// to llvm::errs(). - bool DebugTimeFunctionBodies = false; - - /// If true, the time it takes to type-check each expression will be - /// dumped to llvm::errs(). - bool DebugTimeExpressions = false; - - /// Indicate that the type checker is checking code that will be - /// immediately executed. This will suppress certain warnings - /// when executing scripts. - bool InImmediateMode = false; - - /// Indicate that the type checker should skip type-checking non-inlinable - /// function bodies. - bool SkipNonInlinableFunctionBodies = false; - TypeChecker(ASTContext &Ctx); friend class ASTContext; friend class constraints::ConstraintSystem; @@ -604,90 +563,6 @@ class TypeChecker final { LangOptions &getLangOpts() const { return Context.LangOpts; } - /// Dump the time it takes to type-check each function to llvm::errs(). - void enableDebugTimeFunctionBodies() { - DebugTimeFunctionBodies = true; - } - - /// Dump the time it takes to type-check each function to llvm::errs(). - void enableDebugTimeExpressions() { - DebugTimeExpressions = true; - } - - bool getDebugTimeExpressions() { - return DebugTimeExpressions; - } - - /// If \p timeInMS is non-zero, warn when a function body takes longer than - /// this many milliseconds to type-check. - /// - /// Intended for debugging purposes only. - void setWarnLongFunctionBodies(unsigned timeInMS) { - WarnLongFunctionBodies = timeInMS; - } - - /// If \p timeInMS is non-zero, warn when type-checking an expression - /// takes longer than this many milliseconds. - /// - /// Intended for debugging purposes only. - void setWarnLongExpressionTypeChecking(unsigned timeInMS) { - WarnLongExpressionTypeChecking = timeInMS; - } - - /// Return the current setting for the number of milliseconds - /// threshold we use to determine whether to warn about an - /// expression taking a long time. - unsigned getWarnLongExpressionTypeChecking() { - return WarnLongExpressionTypeChecking; - } - - /// Set the threshold that determines the upper bound for the number - /// of seconds we'll let the expression type checker run before - /// considering an expression "too complex". - void setExpressionTimeoutThreshold(unsigned timeInSeconds) { - ExpressionTimeoutThreshold = timeInSeconds; - } - - /// Return the current setting for the threshold that determines - /// the upper bound for the number of seconds we'll let the - /// expression type checker run before considering an expression - /// "too complex". - /// If zero, do not limit the checking. - unsigned getExpressionTimeoutThresholdInSeconds() { - return ExpressionTimeoutThreshold; - } - - /// Get the threshold that determines the upper bound for the number - /// of times we'll let the Space::minus routine run before - /// considering a switch statement "too complex". - /// If zero, do not limit the checking. - unsigned getSwitchCheckingInvocationThreshold() const { - return SwitchCheckingInvocationThreshold; - } - - /// Set the threshold that determines the upper bound for the number - /// of times we'll let the Space::minus routine run before - /// considering a switch statement "too complex". - void setSwitchCheckingInvocationThreshold(unsigned invocationCount) { - SwitchCheckingInvocationThreshold = invocationCount; - } - - void setSkipNonInlinableBodies(bool skip) { - SkipNonInlinableFunctionBodies = skip; - } - - bool canSkipNonInlinableBodies() const { - return SkipNonInlinableFunctionBodies; - } - - bool getInImmediateMode() { - return InImmediateMode; - } - - void setInImmediateMode(bool InImmediateMode) { - this->InImmediateMode = InImmediateMode; - } - static Type getArraySliceType(SourceLoc loc, Type elementType); static Type getDictionaryType(SourceLoc loc, Type keyType, Type valueType); static Type getOptionalType(SourceLoc loc, Type elementType); From 422bb372d39d537a1ef6d81bad4920c4084ac77c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 8 Nov 2019 16:33:25 -0800 Subject: [PATCH 107/283] Teach the frontend how to parse TypeCheckerOptions --- include/swift/Frontend/Frontend.h | 13 +++--- .../ArgsToFrontendOptionsConverter.cpp | 34 +------------- lib/Frontend/ArgsToFrontendOptionsConverter.h | 3 -- lib/Frontend/CompilerInvocation.cpp | 44 +++++++++++++++++++ 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index c779245b70358..01e59d2da77f9 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -83,6 +83,7 @@ struct ModuleBuffers { /// which manages the actual compiler execution. class CompilerInvocation { LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; FrontendOptions FrontendOpts; ClangImporterOptions ClangImporterOpts; SearchPathOptions SearchPathOpts; @@ -215,6 +216,11 @@ class CompilerInvocation { return LangOpts; } + TypeCheckerOptions &getTypeCheckerOptions() { return TypeCheckerOpts; } + const TypeCheckerOptions &getTypeCheckerOptions() const { + return TypeCheckerOpts; + } + FrontendOptions &getFrontendOptions() { return FrontendOpts; } const FrontendOptions &getFrontendOptions() const { return FrontendOpts; } @@ -648,14 +654,11 @@ class CompilerInstance { bool parsePartialModulesAndLibraryFiles(const ImplicitImports &implicitImports); - OptionSet computeTypeCheckingOptions(); - void forEachFileToTypeCheck(llvm::function_ref fn); - void parseAndTypeCheckMainFileUpTo(SourceFile::ASTStage_t LimitStage, - OptionSet TypeCheckOptions); + void parseAndTypeCheckMainFileUpTo(SourceFile::ASTStage_t LimitStage); - void finishTypeChecking(OptionSet TypeCheckOptions); + void finishTypeChecking(); public: const PrimarySpecificPaths & diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 96a36172c22c0..d4852be02deb3 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -90,15 +90,6 @@ bool ArgsToFrontendOptionsConverter::convert( computeDebugTimeOptions(); computeTBDOptions(); - setUnsignedIntegerArgument(OPT_warn_long_function_bodies, 10, - Opts.WarnLongFunctionBodies); - setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, 10, - Opts.WarnLongExpressionTypeChecking); - setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, 10, - Opts.SolverExpressionTimeThreshold); - setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, 10, - Opts.SwitchCheckingInvocationThreshold); - Opts.CheckOnoneSupportCompleteness = Args.hasArg(OPT_check_onone_completeness); Opts.DebuggerTestingTransform = Args.hasArg(OPT_debugger_testing_transform); @@ -163,7 +154,7 @@ bool ArgsToFrontendOptionsConverter::convert( return true; if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction) - && Opts.SkipNonInlinableFunctionBodies) { + && Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies)) { Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_skipping_function_bodies); return true; } @@ -222,17 +213,7 @@ void ArgsToFrontendOptionsConverter::computePrintStatsOptions() { void ArgsToFrontendOptionsConverter::computeDebugTimeOptions() { using namespace options; - Opts.DebugTimeFunctionBodies |= Args.hasArg(OPT_debug_time_function_bodies); - Opts.DebugTimeExpressionTypeChecking |= - Args.hasArg(OPT_debug_time_expression_type_checking); Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation); - Opts.SkipNonInlinableFunctionBodies |= - Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies); - - // If asked to perform InstallAPI, go ahead and enable non-inlinable function - // body skipping. - Opts.SkipNonInlinableFunctionBodies |= - Args.hasArg(OPT_tbd_is_installapi); if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) { Opts.StatsOutputDir = A->getValue(); @@ -266,19 +247,6 @@ void ArgsToFrontendOptionsConverter::computeTBDOptions() { } } -void ArgsToFrontendOptionsConverter::setUnsignedIntegerArgument( - options::ID optionID, unsigned radix, unsigned &valueToSet) { - if (const Arg *A = Args.getLastArg(optionID)) { - unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(radix, attempt)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - } else { - valueToSet = attempt; - } - } -} - void ArgsToFrontendOptionsConverter::computePlaygroundOptions() { using namespace options; Opts.PlaygroundTransform |= Args.hasArg(OPT_playground); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.h b/lib/Frontend/ArgsToFrontendOptionsConverter.h index ebf09b6d3153e..8431ac4c0090b 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.h +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.h @@ -47,9 +47,6 @@ class ArgsToFrontendOptionsConverter { void computePrintStatsOptions(); void computeTBDOptions(); - void setUnsignedIntegerArgument(options::ID optionID, unsigned radix, - unsigned &valueToSet); - bool setUpInputKindAndImmediateArgs(); bool checkUnusedSupplementaryOutputPaths() const; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 58ba8278a636a..1326da917850d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -552,6 +552,46 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, return HadError || UnsupportedOS || UnsupportedArch; } +static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, + DiagnosticEngine &Diags, + const FrontendOptions &FrontendOpts) { + using namespace options; + + auto setUnsignedIntegerArgument = [&Args, &Diags]( + options::ID optionID, unsigned radix, unsigned &valueToSet) { + if (const Arg *A = Args.getLastArg(optionID)) { + unsigned attempt; + if (StringRef(A->getValue()).getAsInteger(radix, attempt)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + } else { + valueToSet = attempt; + } + } + }; + + setUnsignedIntegerArgument(OPT_warn_long_function_bodies, 10, + Opts.WarnLongFunctionBodies); + setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, 10, + Opts.WarnLongExpressionTypeChecking); + setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, 10, + Opts.ExpressionTimeoutThreshold); + setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, 10, + Opts.SwitchCheckingInvocationThreshold); + + Opts.DebugTimeFunctionBodies |= Args.hasArg(OPT_debug_time_function_bodies); + Opts.DebugTimeExpressions |= + Args.hasArg(OPT_debug_time_expression_type_checking); + Opts.SkipNonInlinableFunctionBodies |= + Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies); + + // If asked to perform InstallAPI, go ahead and enable non-inlinable function + // body skipping. + Opts.SkipNonInlinableFunctionBodies |= Args.hasArg(OPT_tbd_is_installapi); + + return false; +} + static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args, DiagnosticEngine &Diags, @@ -1384,6 +1424,10 @@ bool CompilerInvocation::parseArgs( return true; } + if (ParseTypeCheckerArgs(TypeCheckerOpts, ParsedArgs, Diags, FrontendOpts)) { + return true; + } + if (ParseClangImporterArgs(ClangImporterOpts, ParsedArgs, Diags, workingDirectory)) { return true; From 48805b1d447e11dc69f9321b6d86b4ecad1cc0e6 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 8 Nov 2019 16:34:28 -0800 Subject: [PATCH 108/283] Give ASTContext TypeCheckerOptions Strip TypeChecker of all of this state. --- include/swift/AST/ASTContext.h | 13 ++-- include/swift/Subsystems.h | 11 +-- lib/AST/ASTContext.cpp | 12 ++-- lib/Frontend/Frontend.cpp | 68 ++++++++----------- lib/IDE/REPLCodeCompletion.cpp | 3 +- lib/Immediate/REPL.cpp | 4 +- lib/Parse/Parser.cpp | 37 +++++----- lib/Sema/ConstraintSystem.cpp | 5 +- lib/Sema/ConstraintSystem.h | 6 +- lib/Sema/TypeCheckDecl.cpp | 2 +- lib/Sema/TypeChecker.cpp | 40 +++-------- tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp | 1 + tools/driver/modulewrap_main.cpp | 5 +- tools/driver/swift_indent_main.cpp | 1 + .../libSwiftSyntaxParser.cpp | 3 +- unittests/AST/TestContext.cpp | 3 +- unittests/AST/TestContext.h | 1 + .../ClangImporter/ClangImporterTests.cpp | 3 +- unittests/FrontendTool/ModuleLoadingTests.cpp | 4 +- unittests/Parse/TokenizerTests.cpp | 3 +- 20 files changed, 101 insertions(+), 124 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index ac9ea106e580f..e45eb30803d44 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -201,8 +201,9 @@ class ASTContext final { ASTContext(const ASTContext&) = delete; void operator=(const ASTContext&) = delete; - ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, - SourceManager &SourceMgr, DiagnosticEngine &Diags); + ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, + SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, + DiagnosticEngine &Diags); public: // Members that should only be used by ASTContext.cpp. @@ -213,10 +214,9 @@ class ASTContext final { void operator delete(void *Data) throw(); - static ASTContext *get(LangOptions &langOpts, + static ASTContext *get(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, - SourceManager &SourceMgr, - DiagnosticEngine &Diags); + SourceManager &SourceMgr, DiagnosticEngine &Diags); ~ASTContext(); /// Optional table of counters to report, nullptr when not collecting. @@ -228,6 +228,9 @@ class ASTContext final { /// The language options used for translation. LangOptions &LangOpts; + /// The type checker options. + TypeCheckerOptions &TypeCheckerOpts; + /// The search path options used by this AST context. SearchPathOptions &SearchPathOpts; diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 16374a42ddfb6..1c874e407a171 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -66,6 +66,7 @@ namespace swift { class Token; class TopLevelContext; class TypeChecker; + class TypeCheckerOptions; struct TypeLoc; class UnifiedStatsReporter; enum class SourceFileKind; @@ -183,16 +184,8 @@ namespace swift { /// /// \param StartElem Where to start for incremental type-checking in the main /// source file. - /// - /// \param WarnLongFunctionBodies If non-zero, warn when a function body takes - /// longer than this many milliseconds to type-check void performTypeChecking(SourceFile &SF, TopLevelContext &TLC, - OptionSet Options, - unsigned StartElem = 0, - unsigned WarnLongFunctionBodies = 0, - unsigned WarnLongExpressionTypeChecking = 0, - unsigned ExpressionTimeoutThreshold = 0, - unsigned SwitchCheckingInvocationThreshold = 0); + unsigned StartElem = 0); /// Now that we have type-checked an entire module, perform any type /// checking that requires the full module, e.g., Objective-C method diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index cc890454af87d..2edfbd31824b4 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -504,6 +504,7 @@ void ASTContext::operator delete(void *Data) throw() { } ASTContext *ASTContext::get(LangOptions &langOpts, + TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) { @@ -516,15 +517,16 @@ ASTContext *ASTContext::get(LangOptions &langOpts, auto impl = reinterpret_cast((char*)mem + sizeof(ASTContext)); impl = reinterpret_cast(llvm::alignAddr(impl,alignof(Implementation))); new (impl) Implementation(); - return new (mem) ASTContext(langOpts, SearchPathOpts, SourceMgr, Diags); + return new (mem) + ASTContext(langOpts, typeckOpts, SearchPathOpts, SourceMgr, Diags); } -ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, +ASTContext::ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, + SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) : LangOpts(langOpts), - SearchPathOpts(SearchPathOpts), - SourceMgr(SourceMgr), - Diags(Diags), + TypeCheckerOpts(typeckOpts), + SearchPathOpts(SearchPathOpts), SourceMgr(SourceMgr), Diags(Diags), evaluator(Diags, langOpts.DebugDumpCycles), TheBuiltinModule(createBuiltinModule(*this)), StdlibModuleName(getIdentifier(STDLIB_NAME)), diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 9260aa615d13a..1ce19d9e519b7 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -190,9 +190,9 @@ bool CompilerInstance::setUpASTContextIfNeeded() { return false; } - Context.reset(ASTContext::get(Invocation.getLangOptions(), - Invocation.getSearchPathOptions(), SourceMgr, - Diagnostics)); + Context.reset(ASTContext::get( + Invocation.getLangOptions(), Invocation.getTypeCheckerOptions(), + Invocation.getSearchPathOptions(), SourceMgr, Diagnostics)); registerParseRequestFunctions(Context->evaluator); registerTypeCheckerRequestFunctions(Context->evaluator); @@ -222,17 +222,28 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok) { setUpLLVMArguments(); setUpDiagnosticOptions(); + const auto &frontendOpts = Invocation.getFrontendOptions(); + // If we are asked to emit a module documentation file, configure lexing and // parsing to remember comments. - if (Invocation.getFrontendOptions().InputsAndOutputs.hasModuleDocOutputPath()) + if (frontendOpts.InputsAndOutputs.hasModuleDocOutputPath()) Invocation.getLangOptions().AttachCommentsToDecls = true; // If we are doing index-while-building, configure lexing and parsing to // remember comments. - if (!Invocation.getFrontendOptions().IndexStorePath.empty()) { + if (!frontendOpts.IndexStorePath.empty()) { Invocation.getLangOptions().AttachCommentsToDecls = true; } + // Set up the type checker options. + auto &typeCkOpts = Invocation.getTypeCheckerOptions(); + if (isWholeModuleCompilation()) { + typeCkOpts.DelayWholeModuleChecking = true; + } + if (FrontendOptions::isActionImmediate(frontendOpts.RequestedAction)) { + typeCkOpts.InImmediateMode = true; + } + assert(Lexer::isIdentifier(Invocation.getModuleName())); if (isInSILMode()) @@ -825,14 +836,12 @@ void CompilerInstance::parseAndCheckTypesUpTo( if (hadLoadError) return; - OptionSet TypeCheckOptions = computeTypeCheckingOptions(); - // Type-check main file after parsing all other files so that // it can use declarations from other files. // In addition, the main file has parsing and type-checking // interwined. if (MainBufferID != NO_SUCH_BUFFER) { - parseAndTypeCheckMainFileUpTo(limitStage, TypeCheckOptions); + parseAndTypeCheckMainFileUpTo(limitStage); } assert(llvm::all_of(MainModule->getFiles(), [](const FileUnit *File) -> bool { @@ -843,19 +852,13 @@ void CompilerInstance::parseAndCheckTypesUpTo( }) && "some files have not yet had their imports resolved"); MainModule->setHasResolvedImports(); - const auto &options = Invocation.getFrontendOptions(); - forEachFileToTypeCheck([&](SourceFile &SF) { - if (limitStage == SourceFile::NameBound) { - bindExtensions(SF); - return; - } + // If the limiting AST stage is name binding, we're done. + if (limitStage <= SourceFile::NameBound) { + return; + } - performTypeChecking(SF, PersistentState->getTopLevelContext(), - TypeCheckOptions, /*curElem*/ 0, - options.WarnLongFunctionBodies, - options.WarnLongExpressionTypeChecking, - options.SolverExpressionTimeThreshold, - options.SwitchCheckingInvocationThreshold); + forEachFileToTypeCheck([&](SourceFile &SF) { + performTypeChecking(SF, PersistentState->getTopLevelContext()); if (!Context->hadError() && Invocation.getFrontendOptions().PCMacro) { performPCMacro(SF, PersistentState->getTopLevelContext()); @@ -871,17 +874,10 @@ void CompilerInstance::parseAndCheckTypesUpTo( }); if (Invocation.isCodeCompletion()) { - assert(limitStage == SourceFile::NameBound); performCodeCompletionSecondPass(*PersistentState.get(), *Invocation.getCodeCompletionFactory()); } - - // If the limiting AST stage is name binding, we're done. - if (limitStage <= SourceFile::NameBound) { - return; - } - - finishTypeChecking(TypeCheckOptions); + finishTypeChecking(); } void CompilerInstance::parseLibraryFile( @@ -937,8 +933,7 @@ bool CompilerInstance::parsePartialModulesAndLibraryFiles( } void CompilerInstance::parseAndTypeCheckMainFileUpTo( - SourceFile::ASTStage_t LimitStage, - OptionSet TypeCheckOptions) { + SourceFile::ASTStage_t LimitStage) { FrontendStatsTracer tracer(Context->Stats, "parse-and-typecheck-main-file"); bool mainIsPrimary = @@ -971,16 +966,10 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( llvm_unreachable("invalid limit stage"); case SourceFile::NameBound: performNameBinding(MainFile, CurTUElem); - bindExtensions(MainFile); break; case SourceFile::TypeChecked: - const auto &options = Invocation.getFrontendOptions(); performTypeChecking(MainFile, PersistentState->getTopLevelContext(), - TypeCheckOptions, CurTUElem, - options.WarnLongFunctionBodies, - options.WarnLongExpressionTypeChecking, - options.SolverExpressionTimeThreshold, - options.SwitchCheckingInvocationThreshold); + CurTUElem); break; } } @@ -1020,9 +1009,8 @@ void CompilerInstance::forEachFileToTypeCheck( } } -void CompilerInstance::finishTypeChecking( - OptionSet TypeCheckOptions) { - if (TypeCheckOptions & TypeCheckingFlags::DelayWholeModuleChecking) { +void CompilerInstance::finishTypeChecking() { + if (getASTContext().TypeCheckerOpts.DelayWholeModuleChecking) { forEachSourceFileIn(MainModule, [&](SourceFile &SF) { performWholeModuleTypeChecking(SF); }); diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index c63b76c02dbd4..b5256bc556c86 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -212,7 +212,8 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, do { parseIntoSourceFile(SF, *BufferID, &Done, nullptr, &PersistentState); } while (!Done); - performTypeChecking(SF, PersistentState.getTopLevelContext(), None, + llvm::SaveAndRestore clearTyOpts(Ctx.TypeCheckerOpts, {}); + performTypeChecking(SF, PersistentState.getTopLevelContext(), OriginalDeclCount); performCodeCompletionSecondPass(PersistentState, *CompletionCallbacksFactory); diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 5d55c9f633c25..a57aab42f2d21 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -193,8 +193,8 @@ typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, &PersistentState); } while (!Done); - performTypeChecking(REPLInputFile, PersistentState.getTopLevelContext(), - /*Options*/None); + llvm::SaveAndRestore clearTyOpts(Ctx.TypeCheckerOpts, {}); + performTypeChecking(REPLInputFile, PersistentState.getTopLevelContext()); return REPLModule; } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 2f422f7a88347..261a356e6ab44 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1087,6 +1087,7 @@ Parser::getStringLiteralIfNotInterpolated(SourceLoc Loc, struct ParserUnit::Implementation { std::shared_ptr SPActions; LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; DiagnosticEngine Diags; ASTContext &Ctx; @@ -1094,19 +1095,16 @@ struct ParserUnit::Implementation { std::unique_ptr TheParser; Implementation(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, - const LangOptions &Opts, StringRef ModuleName, + const LangOptions &Opts, const TypeCheckerOptions &TyOpts, + StringRef ModuleName, std::shared_ptr spActions) - : SPActions(std::move(spActions)), - LangOpts(Opts), - Diags(SM), - Ctx(*ASTContext::get(LangOpts, SearchPathOpts, SM, Diags)), - SF(new (Ctx) SourceFile( - *ModuleDecl::create(Ctx.getIdentifier(ModuleName), Ctx), - SFKind, BufferID, - SourceFile::ImplicitModuleImportKind::None, - Opts.CollectParsedToken, - Opts.BuildSyntaxTree)) { - } + : SPActions(std::move(spActions)), + LangOpts(Opts), TypeCheckerOpts(TyOpts), Diags(SM), + Ctx(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, SM, Diags)), + SF(new (Ctx) SourceFile( + *ModuleDecl::create(Ctx.getIdentifier(ModuleName), Ctx), SFKind, + BufferID, SourceFile::ImplicitModuleImportKind::None, + Opts.CollectParsedToken, Opts.BuildSyntaxTree)) {} ~Implementation() { // We need to delete the parser before the context so that it can finalize @@ -1117,15 +1115,18 @@ struct ParserUnit::Implementation { }; ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID) - : ParserUnit(SM, SFKind, BufferID, LangOptions(), "input") { + : ParserUnit(SM, SFKind, BufferID, + LangOptions(), TypeCheckerOptions(), "input") { } ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, - const LangOptions &LangOpts, StringRef ModuleName, + const LangOptions &LangOpts, + const TypeCheckerOptions &TypeCheckOpts, + StringRef ModuleName, std::shared_ptr spActions, SyntaxParsingCache *SyntaxCache) - : Impl(*new Implementation(SM, SFKind, BufferID, LangOpts, ModuleName, - std::move(spActions))) { + : Impl(*new Implementation(SM, SFKind, BufferID, LangOpts, TypeCheckOpts, + ModuleName, std::move(spActions))) { Impl.SF->SyntaxParsingCache = SyntaxCache; Impl.TheParser.reset(new Parser(BufferID, *Impl.SF, /*SIL=*/nullptr, @@ -1135,8 +1136,8 @@ ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned Buffer ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, unsigned Offset, unsigned EndOffset) - : Impl(*new Implementation(SM, SFKind, BufferID, LangOptions(), "input", - nullptr)) { + : Impl(*new Implementation(SM, SFKind, BufferID, LangOptions(), + TypeCheckerOptions(), "input", nullptr)) { std::unique_ptr Lex; Lex.reset(new Lexer(Impl.LangOpts, SM, diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index eadd085b89378..1b622a9e37d99 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -35,10 +35,10 @@ using namespace constraints; #define DEBUG_TYPE "ConstraintSystem" ExpressionTimer::ExpressionTimer(Expr *E, ConstraintSystem &CS) - : E(E), WarnLimit(CS.getTypeChecker().getWarnLongExpressionTypeChecking()), + : E(E), Context(CS.getASTContext()), StartTime(llvm::TimeRecord::getCurrentTime()), - PrintDebugTiming(CS.getTypeChecker().getDebugTimeExpressions()), + PrintDebugTiming(CS.getASTContext().TypeCheckerOpts.DebugTimeExpressions), PrintWarning(true) { if (auto *baseCS = CS.baseCS) { // If we already have a timer in the base constraint @@ -66,6 +66,7 @@ ExpressionTimer::~ExpressionTimer() { if (!PrintWarning) return; + const auto WarnLimit = getWarnLimit(); if (WarnLimit != 0 && elapsedMS >= WarnLimit && E->getLoc().isValid()) Context.Diags.diagnose(E->getLoc(), diag::debug_long_expression, elapsedMS, WarnLimit) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index b04e69f4a2d05..a6186e97bbcbe 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -124,7 +124,6 @@ struct RestrictionOrFix { class ExpressionTimer { Expr* E; - unsigned WarnLimit; ASTContext &Context; llvm::TimeRecord StartTime; @@ -136,6 +135,9 @@ class ExpressionTimer { ~ExpressionTimer(); + unsigned getWarnLimit() const { + return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking; + } llvm::TimeRecord startedAt() const { return StartTime; } /// Return the elapsed process time (including fractional seconds) @@ -3723,7 +3725,7 @@ class ConstraintSystem { } const auto timeoutThresholdInMillis = - getTypeChecker().getExpressionTimeoutThresholdInSeconds(); + getASTContext().TypeCheckerOpts.ExpressionTimeoutThreshold; if (Timer && Timer->isExpired(timeoutThresholdInMillis)) { // Disable warnings about expressions that go over the warning // threshold since we're arbitrarily ending evaluation and diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 213f1c4a3fc63..afa323ab59e41 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -3184,7 +3184,7 @@ class DeclChecker : public DeclVisitor { auto &TC = *Ctx.getLegacyGlobalTypeChecker(); // Make sure we're in the mode that's skipping function bodies. - if (!TC.canSkipNonInlinableBodies()) + if (!getASTContext().TypeCheckerOpts.SkipNonInlinableFunctionBodies) return false; // Make sure there even _is_ a body that we can skip. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 0fae2e69f3e87..20001f7cbe471 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -327,12 +327,7 @@ void swift::typeCheckExternalDefinitions(SourceFile &SF) { } void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, - OptionSet Options, - unsigned StartElem, - unsigned WarnLongFunctionBodies, - unsigned WarnLongExpressionTypeChecking, - unsigned ExpressionTimeoutThreshold, - unsigned SwitchCheckingInvocationThreshold) { + unsigned StartElem) { if (SF.ASTStage == SourceFile::TypeChecked) return; @@ -340,12 +335,11 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, // because type-checking expressions mutates the AST and that throws off the // scope-based lookups. Only the top-level scopes because extensions have not // been bound yet. - if (SF.getASTContext().LangOpts.EnableASTScopeLookup && - SF.isSuitableForASTScopes()) + auto &Ctx = SF.getASTContext(); + if (Ctx.LangOpts.EnableASTScopeLookup && SF.isSuitableForASTScopes()) SF.getScope() .buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); - auto &Ctx = SF.getASTContext(); BufferIndirectlyCausingDiagnosticRAII cpr(SF); // Make sure we have a type checker. @@ -360,30 +354,12 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, { SharedTimer timer("Type checking and Semantic analysis"); - TC.setWarnLongFunctionBodies(WarnLongFunctionBodies); - TC.setWarnLongExpressionTypeChecking(WarnLongExpressionTypeChecking); - if (ExpressionTimeoutThreshold != 0) - TC.setExpressionTimeoutThreshold(ExpressionTimeoutThreshold); - - if (SwitchCheckingInvocationThreshold != 0) - TC.setSwitchCheckingInvocationThreshold( - SwitchCheckingInvocationThreshold); - - if (Options.contains(TypeCheckingFlags::DebugTimeFunctionBodies)) - TC.enableDebugTimeFunctionBodies(); - - if (Options.contains(TypeCheckingFlags::DebugTimeExpressions)) - TC.enableDebugTimeExpressions(); - - if (Options.contains(TypeCheckingFlags::ForImmediateMode)) - TC.setInImmediateMode(true); - - if (Options.contains(TypeCheckingFlags::SkipNonInlinableFunctionBodies)) + if (Ctx.TypeCheckerOpts.SkipNonInlinableFunctionBodies) // Disable this optimization if we're compiling SwiftOnoneSupport, because // we _definitely_ need to look inside every declaration to figure out // what gets prespecialized. - if (!SF.getParentModule()->isOnoneSupportModule()) - TC.setSkipNonInlinableBodies(true); + if (SF.getParentModule()->isOnoneSupportModule()) + Ctx.TypeCheckerOpts.SkipNonInlinableFunctionBodies = false; if (!Ctx.LangOpts.DisableAvailabilityChecking) { // Build the type refinement hierarchy for the primary @@ -416,7 +392,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, } // Checking that benefits from having the whole module available. - if (!(Options & TypeCheckingFlags::DelayWholeModuleChecking)) { + if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking) { performWholeModuleTypeChecking(SF); } @@ -439,7 +415,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, // been cached, it will never be added to the ASTContext. The solution is to // skip verification and avoid caching it. #ifndef NDEBUG - if (!(Options & TypeCheckingFlags::DelayWholeModuleChecking) && + if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking && SF.Kind != SourceFileKind::REPL && SF.Kind != SourceFileKind::SIL && !Ctx.LangOpts.DebuggerSupport) { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index a911eab4f072a..922c8e54ebd3c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -700,6 +700,7 @@ class SwiftDocumentSyntaxInfo { Parser.reset( new ParserUnit(SM, SourceFileKind::Main, BufferID, CompInv.getLangOptions(), + CompInv.getTypeCheckerOptions(), CompInv.getModuleName(), SynTreeCreator, CompInv.getMainFileSyntaxParsingCache()) diff --git a/tools/driver/modulewrap_main.cpp b/tools/driver/modulewrap_main.cpp index 886d7a62fba45..c2b835a110070 100644 --- a/tools/driver/modulewrap_main.cpp +++ b/tools/driver/modulewrap_main.cpp @@ -166,10 +166,11 @@ int modulewrap_main(ArrayRef Args, const char *Argv0, SearchPathOpts.RuntimeResourcePath = RuntimeResourcePath.str(); SourceManager SrcMgr; + TypeCheckerOptions TypeCheckOpts; LangOptions LangOpts; LangOpts.Target = Invocation.getTargetTriple(); - ASTContext &ASTCtx = *ASTContext::get(LangOpts, SearchPathOpts, SrcMgr, - Instance.getDiags()); + ASTContext &ASTCtx = *ASTContext::get(LangOpts, TypeCheckOpts, SearchPathOpts, + SrcMgr, Instance.getDiags()); registerParseRequestFunctions(ASTCtx.evaluator); registerTypeCheckerRequestFunctions(ASTCtx.evaluator); diff --git a/tools/driver/swift_indent_main.cpp b/tools/driver/swift_indent_main.cpp index 2c57d7d95aedd..bb44ea8a085b4 100644 --- a/tools/driver/swift_indent_main.cpp +++ b/tools/driver/swift_indent_main.cpp @@ -61,6 +61,7 @@ class FormatterDocument { BufferID = SM.addNewSourceBuffer(std::move(Buffer)); Parser.reset(new ParserUnit(SM, SourceFileKind::Main, BufferID, CompInv.getLangOptions(), + CompInv.getTypeCheckerOptions(), CompInv.getModuleName())); Parser->getDiagnosticEngine().addConsumer(DiagConsumer); Parser->parse(); diff --git a/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp b/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp index 177b2ed07c969..006ca9fa6e67b 100644 --- a/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp +++ b/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp @@ -274,6 +274,7 @@ swiftparse_client_node_t SynParser::parse(const char *source) { SourceManager SM; unsigned bufID = SM.addNewSourceBuffer( llvm::MemoryBuffer::getMemBuffer(source, "syntax_parse_source")); + TypeCheckerOptions tyckOpts; LangOptions langOpts; langOpts.BuildSyntaxTree = true; langOpts.CollectParsedToken = false; @@ -285,7 +286,7 @@ swiftparse_client_node_t SynParser::parse(const char *source) { std::make_shared(*this, SM, bufID); // We have to use SourceFileKind::Main to avoid diagnostics like // illegal_top_level_expr - ParserUnit PU(SM, SourceFileKind::Main, bufID, langOpts, + ParserUnit PU(SM, SourceFileKind::Main, bufID, langOpts, tyckOpts, "syntax_parse_module", std::move(parseActions), /*SyntaxCache=*/nullptr); // Evaluating pound conditions may lead to unknown syntax. diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index bf7865245b1d6..d246d8a0fce33 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -34,7 +34,8 @@ static void declareOptionalType(ASTContext &ctx, SourceFile *fileForLookups, } TestContext::TestContext(ShouldDeclareOptionalTypes optionals) - : Ctx(*ASTContext::get(LangOpts, SearchPathOpts, SourceMgr, Diags)) { + : Ctx(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, SourceMgr, + Diags)) { registerParseRequestFunctions(Ctx.evaluator); registerTypeCheckerRequestFunctions(Ctx.evaluator); auto stdlibID = Ctx.getIdentifier(STDLIB_NAME); diff --git a/unittests/AST/TestContext.h b/unittests/AST/TestContext.h index 428a933b0cd01..46aa8d985b62b 100644 --- a/unittests/AST/TestContext.h +++ b/unittests/AST/TestContext.h @@ -27,6 +27,7 @@ namespace unittest { class TestContextBase { public: LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; SourceManager SourceMgr; DiagnosticEngine Diags; diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index 2c922f88e6284..94d021452a1c1 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -69,12 +69,13 @@ TEST(ClangImporterTest, emitPCHInMemory) { // Set up the importer and emit a bridging PCH. swift::LangOptions langOpts; langOpts.Target = llvm::Triple("x86_64", "apple", "darwin"); + swift::TypeCheckerOptions typeckOpts; INITIALIZE_LLVM(); swift::SearchPathOptions searchPathOpts; swift::SourceManager sourceMgr; swift::DiagnosticEngine diags(sourceMgr); std::unique_ptr context( - ASTContext::get(langOpts, searchPathOpts, sourceMgr, diags)); + ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags)); auto importer = ClangImporter::create(*context, options); std::string PCH = createFilename(cache, "bridging.h.pch"); diff --git a/unittests/FrontendTool/ModuleLoadingTests.cpp b/unittests/FrontendTool/ModuleLoadingTests.cpp index 0dda275ddaace..2015d27f624d7 100644 --- a/unittests/FrontendTool/ModuleLoadingTests.cpp +++ b/unittests/FrontendTool/ModuleLoadingTests.cpp @@ -94,10 +94,12 @@ class ModuleInterfaceLoaderTest : public testing::Test { PrintingDiagnosticConsumer printingConsumer; DiagnosticEngine diags(sourceMgr); diags.addConsumer(printingConsumer); + TypeCheckerOptions typeckOpts; LangOptions langOpts; langOpts.Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); SearchPathOptions searchPathOpts; - auto ctx = ASTContext::get(langOpts, searchPathOpts, sourceMgr, diags); + auto ctx = + ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags); auto loader = ModuleInterfaceLoader::create( *ctx, cacheDir, prebuiltCacheDir, diff --git a/unittests/Parse/TokenizerTests.cpp b/unittests/Parse/TokenizerTests.cpp index 2ae73865f403b..36faa9634c010 100644 --- a/unittests/Parse/TokenizerTests.cpp +++ b/unittests/Parse/TokenizerTests.cpp @@ -82,7 +82,8 @@ class TokenizerTest : public ::testing::Test { } std::vector parseAndGetSplitTokens(unsigned BufID) { - swift::ParserUnit PU(SM, SourceFileKind::Main, BufID, LangOpts, "unknown"); + swift::ParserUnit PU(SM, SourceFileKind::Main, BufID, + LangOpts, TypeCheckerOptions(), "unknown"); bool Done = false; while (!Done) { From 54ff860e9be1e37957e98f7398ff0faf192bef8b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 8 Nov 2019 16:34:50 -0800 Subject: [PATCH 109/283] Use TypeCheckerOptions to make TypeCheckSwitchStmt a utility --- lib/Sema/TypeCheckSwitchStmt.cpp | 12 +++++------- lib/Sema/TypeChecker.h | 5 +++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index 5f1b51f9ee806..0f09fc359c97b 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -885,16 +885,14 @@ namespace { }; ASTContext &Context; - unsigned CheckingThreshold; const SwitchStmt *Switch; const DeclContext *DC; APIntMap IntLiteralCache; llvm::DenseMap FloatLiteralCache; llvm::DenseMap StringLiteralCache; - SpaceEngine(ASTContext &C, unsigned Threashold, - const SwitchStmt *SS, const DeclContext *DC) - : Context(C), CheckingThreshold(Threashold), Switch(SS), DC(DC) {} + SpaceEngine(ASTContext &C, const SwitchStmt *SS, const DeclContext *DC) + : Context(C), Switch(SS), DC(DC) {} bool checkRedundantLiteral(const Pattern *Pat, Expr *&PrevPattern) { if (Pat->getKind() != PatternKind::Expr) { @@ -1010,7 +1008,8 @@ namespace { Space totalSpace = Space::forType(subjectType, Identifier()); Space coveredSpace = Space::forDisjunct(spaces); - unsigned minusCount = CheckingThreshold; + unsigned minusCount + = Context.TypeCheckerOpts.SwitchCheckingInvocationThreshold; auto diff = totalSpace.minus(coveredSpace, DC, &minusCount); if (!diff) { diagnoseMissingCases(RequiresDefault::SpaceTooLarge, Space(), @@ -1519,8 +1518,7 @@ namespace { void TypeChecker::checkSwitchExhaustiveness(const SwitchStmt *stmt, const DeclContext *DC, bool limited) { - SpaceEngine(Context, getSwitchCheckingInvocationThreshold(), stmt, DC) - .checkExhaustiveness(limited); + SpaceEngine(DC->getASTContext(), stmt, DC).checkExhaustiveness(limited); } void SpaceEngine::Space::dump() const { diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index a517de886fd19..a84765d67a4eb 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1059,8 +1059,9 @@ class TypeChecker final { /// \param limitChecking The checking process relies on the switch statement /// being well-formed. If it is not, pass true to this flag to run a limited /// form of analysis. - void checkSwitchExhaustiveness(const SwitchStmt *stmt, const DeclContext *DC, - bool limitChecking); + static void checkSwitchExhaustiveness(const SwitchStmt *stmt, + const DeclContext *DC, + bool limitChecking); /// Type check the given expression as a condition, which converts /// it to a logic value. From b8dc4f0a3fc3f4e076ee80a30b15d3c672bb3c2e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 8 Nov 2019 16:36:20 -0800 Subject: [PATCH 110/283] Use TypeCheckerOptions to simplify FunctionBodyTimer --- lib/Sema/TypeCheckStmt.cpp | 25 ++++++++++------------- test/IRGen/objc_enum_multi_file.swift | 2 +- test/IRGen/objc_protocol_multi_file.swift | 2 +- test/decl/enum/objc_enum_multi_file.swift | 12 +++++------ 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index da0a60faca8d9..4136a8e5cabeb 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -178,13 +178,9 @@ namespace { class FunctionBodyTimer { AnyFunctionRef Function; llvm::TimeRecord StartTime = llvm::TimeRecord::getCurrentTime(); - unsigned WarnLimit; - bool ShouldDump; public: - FunctionBodyTimer(AnyFunctionRef Fn, bool shouldDump, - unsigned warnLimit) - : Function(Fn), WarnLimit(warnLimit), ShouldDump(shouldDump) {} + FunctionBodyTimer(AnyFunctionRef Fn) : Function(Fn) {} ~FunctionBodyTimer() { llvm::TimeRecord endTime = llvm::TimeRecord::getCurrentTime(false); @@ -195,7 +191,7 @@ namespace { ASTContext &ctx = Function.getAsDeclContext()->getASTContext(); auto *AFD = Function.getAbstractFunctionDecl(); - if (ShouldDump) { + if (ctx.TypeCheckerOpts.WarnLongFunctionBodies) { // Round up to the nearest 100th of a millisecond. llvm::errs() << llvm::format("%0.2f", ceil(elapsed * 100000) / 100) << "ms\t"; Function.getLoc().print(llvm::errs(), ctx.SourceMgr); @@ -210,6 +206,7 @@ namespace { llvm::errs() << "\n"; } + const auto WarnLimit = ctx.TypeCheckerOpts.DebugTimeFunctionBodies; if (WarnLimit != 0 && elapsedMS >= WarnLimit) { if (AFD) { ctx.Diags.diagnose(AFD, diag::debug_long_function_body, @@ -1424,7 +1421,8 @@ class StmtChecker : public StmtVisitor { } if (!switchStmt->isImplicit()) { - TC.checkSwitchExhaustiveness(switchStmt, DC, limitExhaustivityChecks); + TypeChecker::checkSwitchExhaustiveness(switchStmt, DC, + limitExhaustivityChecks); } return switchStmt; @@ -2110,9 +2108,9 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, ctx.Stats->getFrontendCounters().NumFunctionsTypechecked++; Optional timer; - TypeChecker &tc = *ctx.getLegacyGlobalTypeChecker(); - if (tc.DebugTimeFunctionBodies || tc.WarnLongFunctionBodies) - timer.emplace(AFD, tc.DebugTimeFunctionBodies, tc.WarnLongFunctionBodies); + const auto &tyOpts = ctx.TypeCheckerOpts; + if (tyOpts.DebugTimeFunctionBodies || tyOpts.WarnLongFunctionBodies) + timer.emplace(AFD); BraceStmt *body = AFD->getBody(); if (!body || AFD->isBodyTypeChecked()) @@ -2204,11 +2202,10 @@ bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { BraceStmt *body = closure->getBody(); - auto *TC = closure->getASTContext().getLegacyGlobalTypeChecker(); Optional timer; - if (TC->DebugTimeFunctionBodies || TC->WarnLongFunctionBodies) - timer.emplace(closure, TC->DebugTimeFunctionBodies, - TC->WarnLongFunctionBodies); + const auto &tyOpts = closure->getASTContext().TypeCheckerOpts; + if (tyOpts.DebugTimeFunctionBodies || tyOpts.WarnLongFunctionBodies) + timer.emplace(closure); bool HadError = StmtChecker(closure).typeCheckBody(body); if (body) { diff --git a/test/IRGen/objc_enum_multi_file.swift b/test/IRGen/objc_enum_multi_file.swift index 2f70b371933c1..1ef5a4e160f6a 100644 --- a/test/IRGen/objc_enum_multi_file.swift +++ b/test/IRGen/objc_enum_multi_file.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -module-name main -primary-file %s %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main -primary-file %s %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir | %FileCheck %s // RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -emit-module %S/Inputs/objc_enum_multi_file_helper.swift -o %t // RUN: %target-swift-frontend -module-name main -primary-file %s -I %t -DIMPORT -emit-ir | %FileCheck %s diff --git a/test/IRGen/objc_protocol_multi_file.swift b/test/IRGen/objc_protocol_multi_file.swift index 03affcc33d975..21c739982baa7 100644 --- a/test/IRGen/objc_protocol_multi_file.swift +++ b/test/IRGen/objc_protocol_multi_file.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -primary-file %s %S/Inputs/objc_protocol_multi_file_helper.swift -g -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -primary-file %s %S/Inputs/objc_protocol_multi_file_helper.swift -g -emit-ir | %FileCheck %s // This used to crash . // To tickle the crash, SubProto must not be used elsewhere in this file. diff --git a/test/decl/enum/objc_enum_multi_file.swift b/test/decl/enum/objc_enum_multi_file.swift index 8cd12f876a8f1..6e4451ad7f560 100644 --- a/test/decl/enum/objc_enum_multi_file.swift +++ b/test/decl/enum/objc_enum_multi_file.swift @@ -1,9 +1,9 @@ -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NO_RAW_TYPE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D BAD_RAW_TYPE 2>&1 | %FileCheck -check-prefix=BAD_RAW_TYPE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_TYPE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_VALUE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_VALUE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_CASES 2>&1 | %FileCheck -check-prefix=NO_CASES %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D DUPLICATE_CASES 2>&1 | %FileCheck -check-prefix=DUPLICATE_CASES %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NO_RAW_TYPE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D BAD_RAW_TYPE 2>&1 | %FileCheck -check-prefix=BAD_RAW_TYPE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_TYPE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_VALUE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_VALUE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_CASES 2>&1 | %FileCheck -check-prefix=NO_CASES %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D DUPLICATE_CASES 2>&1 | %FileCheck -check-prefix=DUPLICATE_CASES %s // Note that the *other* file is the primary file in this test! #if NO_RAW_TYPE From a02fca16e250d937209d0ece6d4012a40fe712c7 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 12 Nov 2019 10:12:11 -0800 Subject: [PATCH 111/283] [ownership] Add a frontend option -disable-ossa-opts to disable ossa based opts for benchmarking purposes. --- include/swift/AST/SILOptions.h | 4 ++++ include/swift/Option/FrontendOptions.td | 2 ++ lib/Frontend/CompilerInvocation.cpp | 1 + lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp | 4 ++++ lib/SILOptimizer/Transforms/DestroyHoisting.cpp | 4 ++++ 5 files changed, 15 insertions(+) diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index 736f141f0bd03..e19c2f3ca12b9 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -53,6 +53,10 @@ class SILOptions { /// Controls whether the SIL ARC optimizations are run. bool EnableARCOptimizations = true; + /// Controls whether specific OSSA optimizations are run. For benchmarking + /// purposes. + bool EnableOSSAOptimizations = true; + /// Should we run any SIL performance optimizations /// /// Useful when you want to enable -O LLVM opts but not -O SIL opts. diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index a30267f2d89c3..563aa63e8fdaf 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -254,6 +254,8 @@ def debugger_support : Flag<["-"], "debugger-support">, def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; +def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">, + HelpText<"Don't run SIL OSSA optimization passes.">; def disable_sil_partial_apply : Flag<["-"], "disable-sil-partial-apply">, HelpText<"Disable use of partial_apply in SIL generation">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 58ba8278a636a..a91375574da1f 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -844,6 +844,7 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, Opts.RemoveRuntimeAsserts |= Args.hasArg(OPT_RemoveRuntimeAsserts); Opts.EnableARCOptimizations |= !Args.hasArg(OPT_disable_arc_opts); + Opts.EnableOSSAOptimizations &= !Args.hasArg(OPT_disable_ossa_opts); Opts.DisableSILPerfOptimizations |= Args.hasArg(OPT_disable_sil_perf_optzns); Opts.VerifyAll |= Args.hasArg(OPT_sil_verify_all); Opts.DebugSerialization |= Args.hasArg(OPT_sil_debug_serialization); diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 72fa8368a7fba..8aec01ed072f1 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -772,6 +772,10 @@ struct SemanticARCOpts : SILFunctionTransform { void run() override { SILFunction &f = *getFunction(); + // Return early if we are not performing OSSA optimizations. + if (!f.getModule().getOptions().EnableOSSAOptimizations) + return; + // Make sure we are running with ownership verification enabled. assert(f.getModule().getOptions().VerifySILOwnership && "Can not perform semantic arc optimization unless ownership " diff --git a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp index 3498f716a7110..0a42f6160a685 100644 --- a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp @@ -716,6 +716,10 @@ class DestroyHoistingPass : public SILFunctionTransform { if (!F->hasOwnership()) return; + // If we are not supposed to perform ossa optimizations, bail. + if (!F->getModule().getOptions().EnableOSSAOptimizations) + return; + LLVM_DEBUG(llvm::dbgs() << "*** DestroyHoisting on function: " << F->getName() << " ***\n"); From cd8ebe49bbb614f0f3459b993e92065d43edd9c9 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 11 Nov 2019 18:21:24 +0900 Subject: [PATCH 112/283] [Parse/CodeCompletion] Cleanup code completion facilities in Parse - Rename code completion related names in 'PersistentParserState' so it's clear when you are using. - Refactor 'performCodeCompletionSecondPass()': Inline and consolidate 'parse*Delayed()' because they used to share many code. rdar://problem/56926367 --- include/swift/Parse/Parser.h | 16 ++- include/swift/Parse/PersistentParserState.h | 59 ++++++----- lib/Parse/ParseDecl.cpp | 69 ++----------- lib/Parse/ParseStmt.cpp | 53 +--------- lib/Parse/Parser.cpp | 103 +++++++++++++++++--- lib/Parse/PersistentParserState.cpp | 12 +-- 6 files changed, 144 insertions(+), 168 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 481a69969a774..754fac8d45bd2 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -43,6 +43,7 @@ namespace llvm { namespace swift { class CodeCompletionCallbacks; + class CodeCompletionCallbacksFactory; class DefaultArgumentInitializer; class DiagnosticEngine; class Expr; @@ -867,8 +868,6 @@ class Parser { BraceItemListKind::Brace); ParserResult parseBraceItemList(Diag<> ID); - void parseTopLevelCodeDeclDelayed(); - //===--------------------------------------------------------------------===// // Decl Parsing @@ -919,8 +918,6 @@ class Parser { bool IsAtStartOfLineOrPreviousHadSemi, llvm::function_ref Handler); - void parseDeclDelayed(); - std::vector parseDeclListDelayed(IterableDeclContext *IDC); bool parseMemberDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, @@ -1089,7 +1086,6 @@ class Parser { bool HasFuncKeyword = true); void parseAbstractFunctionBody(AbstractFunctionDecl *AFD); BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD); - void parseAbstractFunctionBodyDelayed(); ParserResult parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes); @@ -1583,6 +1579,16 @@ class Parser { parsePlatformVersionConstraintSpec(); ParserResult parsePlatformAgnosticVersionConstraintSpec(); + + //===--------------------------------------------------------------------===// + // Code completion second pass. + + static void + performCodeCompletionSecondPass(PersistentParserState &ParserState, + CodeCompletionCallbacksFactory &Factory); + + void performCodeCompletionSecondPassImpl( + PersistentParserState::CodeCompletionDelayedDeclState &info); }; /// Describes a parsed declaration name. diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 1ffb058e77c7c..44f53983c6ca6 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -17,7 +17,6 @@ #ifndef SWIFT_PARSE_PERSISTENTPARSERSTATE_H #define SWIFT_PARSE_PERSISTENTPARSERSTATE_H -#include "swift/AST/LazyResolver.h" #include "swift/Basic/SourceLoc.h" #include "swift/Parse/LocalContext.h" #include "swift/Parse/ParserPosition.h" @@ -26,6 +25,10 @@ namespace swift { +class SourceFile; +class DeclContext; +class IterableDeclContext; + /// Parser state persistent across multiple parses. class PersistentParserState { public: @@ -36,16 +39,16 @@ class PersistentParserState { bool isValid() const { return Loc.isValid(); } }; - enum class DelayedDeclKind { + enum class CodeCompletionDelayedDeclKind { TopLevelCodeDecl, Decl, FunctionBody, }; - class DelayedDeclState { + class CodeCompletionDelayedDeclState { friend class PersistentParserState; friend class Parser; - DelayedDeclKind Kind; + CodeCompletionDelayedDeclKind Kind; unsigned Flags; DeclContext *ParentContext; ParserPos BodyPos; @@ -57,13 +60,13 @@ class PersistentParserState { } public: - DelayedDeclState(DelayedDeclKind Kind, unsigned Flags, - DeclContext *ParentContext, SourceRange BodyRange, - SourceLoc PreviousLoc, SavedScope &&Scope) - : Kind(Kind), Flags(Flags), ParentContext(ParentContext), - BodyPos{BodyRange.Start, PreviousLoc}, - BodyEnd(BodyRange.End), Scope(std::move(Scope)) - {} + CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, + SourceRange BodyRange, SourceLoc PreviousLoc, + SavedScope &&Scope) + : Kind(Kind), Flags(Flags), + ParentContext(ParentContext), BodyPos{BodyRange.Start, PreviousLoc}, + BodyEnd(BodyRange.End), Scope(std::move(Scope)) {} }; bool InPoundLineEnvironment = false; @@ -76,7 +79,7 @@ class PersistentParserState { /// Parser sets this if it stopped parsing before the buffer ended. ParserPosition MarkedPos; - std::unique_ptr CodeCompletionDelayedDeclState; + std::unique_ptr CodeCompletionDelayedDeclStat; std::vector DelayedDeclLists; @@ -89,28 +92,24 @@ class PersistentParserState { PersistentParserState(ASTContext &ctx) : PersistentParserState() { } ~PersistentParserState(); - void delayDecl(DelayedDeclKind Kind, unsigned Flags, - DeclContext *ParentContext, - SourceRange BodyRange, SourceLoc PreviousLoc); - - void delayDeclList(IterableDeclContext *D); + void setCodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + unsigned Flags, + DeclContext *ParentContext, + SourceRange BodyRange, + SourceLoc PreviousLoc); - bool hasDelayedDecl() { - return CodeCompletionDelayedDeclState.get() != nullptr; - } - DelayedDeclKind getDelayedDeclKind() { - return CodeCompletionDelayedDeclState->Kind; - } - SourceLoc getDelayedDeclLoc() { - return CodeCompletionDelayedDeclState->BodyPos.Loc; + bool hasCodeCompletionDelayedDeclState() { + return CodeCompletionDelayedDeclStat.get() != nullptr; } - DeclContext *getDelayedDeclContext() { - return CodeCompletionDelayedDeclState->ParentContext; - } - std::unique_ptr takeDelayedDeclState() { - return std::move(CodeCompletionDelayedDeclState); + + std::unique_ptr + takeCodeCompletionDelayedDeclState() { + assert(hasCodeCompletionDelayedDeclState()); + return std::move(CodeCompletionDelayedDeclStat); } + void delayDeclList(IterableDeclContext *D); + void parseAllDelayedDeclLists(); TopLevelContext &getTopLevelContext() { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3a4dd2ffa4c34..12aad047d86e3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3008,9 +3008,10 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition, backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); - State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, Flags.toRaw(), - CurDeclContext, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); + State->setCodeCompletionDelayedDeclState( + PersistentParserState::CodeCompletionDelayedDeclKind::Decl, + Flags.toRaw(), CurDeclContext, {BeginLoc, EndLoc}, + BeginParserPosition.PreviousLoc); while (SourceMgr.isBeforeInBuffer(Tok.getLoc(), CurrentLoc)) consumeToken(); @@ -3575,48 +3576,6 @@ std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { return parseDeclList(LBLoc, RBLoc, Id, Options, IDC, hadError); } -void Parser::parseDeclDelayed() { - auto DelayedState = State->takeDelayedDeclState(); - assert(DelayedState.get() && "should have delayed state"); - - auto BeginParserPosition = getParserPosition(DelayedState->BodyPos); - auto EndLexerState = L->getStateForEndOfTokenLoc(DelayedState->BodyEnd); - - // ParserPositionRAII needs a primed parser to restore to. - if (Tok.is(tok::NUM_TOKENS)) - consumeTokenWithoutFeedingReceiver(); - - // Ensure that we restore the parser state at exit. - ParserPositionRAII PPR(*this); - - // Create a lexer that cannot go past the end state. - Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); - - // Temporarily swap out the parser's current lexer with our new one. - llvm::SaveAndRestore T(L, &LocalLex); - - // Rewind to the beginning of the decl. - restoreParserPosition(BeginParserPosition); - - // Re-enter the lexical scope. - Scope S(this, DelayedState->takeScope()); - ContextChange CC(*this, DelayedState->ParentContext); - - parseDecl(ParseDeclOptions(DelayedState->Flags), - /*IsAtStartOfLineOrPreviousHadSemi=*/true, - [&](Decl *D) { - if (auto *parent = DelayedState->ParentContext) { - if (auto *NTD = dyn_cast(parent)) { - NTD->addMember(D); - } else if (auto *ED = dyn_cast(parent)) { - ED->addMember(D); - } else if (auto *SF = dyn_cast(parent)) { - SF->Decls.push_back(D); - } - } - }); -} - /// Parse an 'import' declaration, doing no token skipping on error. /// /// \verbatim @@ -5640,9 +5599,9 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, if (isCodeCompletionFirstPass()) { if (SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) { - State->delayDecl(PersistentParserState::DelayedDeclKind::FunctionBody, - PD_Default, AFD, BodyRange, - BeginParserPosition.PreviousLoc); + State->setCodeCompletionDelayedDeclState( + PersistentParserState::CodeCompletionDelayedDeclKind::FunctionBody, + PD_Default, AFD, BodyRange, BeginParserPosition.PreviousLoc); } else { AFD->setBodySkipped(BodyRange); } @@ -5957,20 +5916,6 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { return parseBraceItemList(diag::func_decl_without_brace).getPtrOrNull(); } -/// Parse a delayed function body from the 'PersistentParserState'. -void Parser::parseAbstractFunctionBodyDelayed() { - auto DelayedState = State->takeDelayedDeclState(); - assert(DelayedState.get() && "should have delayed state"); - auto CD = DelayedState->ParentContext->getAsDecl(); - auto AFD = cast(CD); - - // Eagarly parse local decls or nested function bodies inside the body. - llvm::SaveAndRestore DisableDelayedBody(DelayBodyParsing, false); - - auto body = parseAbstractFunctionBodyDelayed(AFD); - AFD->setBodyParsed(body); -} - /// Parse a 'enum' declaration, returning true (and doing no token /// skipping) on error. /// diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 6638d44b8bf17..ff625a4326f79 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -256,9 +256,9 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, SourceLoc EndLoc = PreviousLoc; backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); - State->delayDecl(PersistentParserState::DelayedDeclKind::TopLevelCodeDecl, - PD_Default, TLCD, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); + State->setCodeCompletionDelayedDeclState( + PersistentParserState::CodeCompletionDelayedDeclKind::TopLevelCodeDecl, + PD_Default, TLCD, {BeginLoc, EndLoc}, BeginParserPosition.PreviousLoc); // Skip the rest of the file to prevent the parser from constructing the AST // for it. Forward references are not allowed at the top level. @@ -520,53 +520,6 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, return BraceItemsStatus; } -void Parser::parseTopLevelCodeDeclDelayed() { - auto DelayedState = State->takeDelayedDeclState(); - assert(DelayedState.get() && "should have delayed state"); - - auto BeginParserPosition = getParserPosition(DelayedState->BodyPos); - auto EndLexerState = L->getStateForEndOfTokenLoc(DelayedState->BodyEnd); - - // ParserPositionRAII needs a primed parser to restore to. - if (Tok.is(tok::NUM_TOKENS)) - consumeTokenWithoutFeedingReceiver(); - - // Ensure that we restore the parser state at exit. - ParserPositionRAII PPR(*this); - - // Create a lexer that cannot go past the end state. - Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); - - // Temporarily swap out the parser's current lexer with our new one. - llvm::SaveAndRestore T(L, &LocalLex); - - // Rewind to the beginning of the top-level code. - restoreParserPosition(BeginParserPosition); - - // Re-enter the lexical scope. - Scope S(this, DelayedState->takeScope()); - - // Re-enter the top-level decl context. - // FIXME: this can issue discriminators out-of-order? - auto *TLCD = cast(DelayedState->ParentContext); - ContextChange CC(*this, TLCD, &State->getTopLevelContext()); - - SourceLoc StartLoc = Tok.getLoc(); - ASTNode Result; - - // Expressions can't begin with a closure literal at statement position. This - // prevents potential ambiguities with trailing closure syntax. - if (Tok.is(tok::l_brace)) { - diagnose(Tok, diag::statement_begins_with_closure); - } - - parseExprOrStmt(Result); - if (!Result.isNull()) { - auto Brace = BraceStmt::create(Context, StartLoc, Result, Tok.getLoc()); - TLCD->setBody(Brace); - } -} - /// Recover from a 'case' or 'default' outside of a 'switch' by consuming up to /// the next ':'. static ParserResult recoverFromInvalidCase(Parser &P) { diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 2f422f7a88347..8c06993c6950b 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -113,37 +113,112 @@ void SILParserTUStateBase::anchor() { } void swift::performCodeCompletionSecondPass( PersistentParserState &ParserState, CodeCompletionCallbacksFactory &Factory) { + Parser::performCodeCompletionSecondPass(ParserState, Factory); +} + +void Parser::performCodeCompletionSecondPass( + PersistentParserState &ParserState, + CodeCompletionCallbacksFactory &Factory) { SharedTimer timer("CodeCompletionSecondPass"); - if (!ParserState.hasDelayedDecl()) + if (!ParserState.hasCodeCompletionDelayedDeclState()) return; - auto &SF = *ParserState.getDelayedDeclContext()->getParentSourceFile(); + auto state = ParserState.takeCodeCompletionDelayedDeclState(); + + auto &SF = *state->ParentContext->getParentSourceFile(); auto &SM = SF.getASTContext().SourceMgr; - auto BufferID = SM.findBufferContainingLoc(ParserState.getDelayedDeclLoc()); + auto BufferID = SM.findBufferContainingLoc(state->BodyPos.Loc); Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr); - // Disable libSyntax creation in the delayed parsing. - TheParser.SyntaxContext->disable(); - std::unique_ptr CodeCompletion( Factory.createCodeCompletionCallbacks(TheParser)); TheParser.setCodeCompletionCallbacks(CodeCompletion.get()); - switch (ParserState.getDelayedDeclKind()) { - case PersistentParserState::DelayedDeclKind::TopLevelCodeDecl: - TheParser.parseTopLevelCodeDeclDelayed(); + TheParser.performCodeCompletionSecondPassImpl(*state); +} + +void Parser::performCodeCompletionSecondPassImpl( + PersistentParserState::CodeCompletionDelayedDeclState &info) { + // Disable libSyntax creation in the delayed parsing. + SyntaxContext->disable(); + + auto BeginParserPosition = getParserPosition(info.BodyPos); + auto EndLexerState = L->getStateForEndOfTokenLoc(info.BodyEnd); + + // ParserPositionRAII needs a primed parser to restore to. + if (Tok.is(tok::NUM_TOKENS)) + consumeTokenWithoutFeedingReceiver(); + + // Ensure that we restore the parser state at exit. + ParserPositionRAII PPR(*this); + + // Create a lexer that cannot go past the end state. + Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); + + // Temporarily swap out the parser's current lexer with our new one. + llvm::SaveAndRestore T(L, &LocalLex); + + // Rewind to the beginning of the top-level code. + restoreParserPosition(BeginParserPosition); + + // Do not delay parsing in the second pass. + llvm::SaveAndRestore DisableDelayedBody(DelayBodyParsing, false); + + // Re-enter the lexical scope. + Scope S(this, info.takeScope()); + + DeclContext *DC = info.ParentContext; + + switch (info.Kind) { + case PersistentParserState::CodeCompletionDelayedDeclKind::TopLevelCodeDecl: { + // Re-enter the top-level code decl context. + // FIXME: this can issue discriminators out-of-order? + auto *TLCD = cast(DC); + ContextChange CC(*this, TLCD, &State->getTopLevelContext()); + + SourceLoc StartLoc = Tok.getLoc(); + ASTNode Result; + parseExprOrStmt(Result); + if (!Result.isNull()) { + auto Brace = BraceStmt::create(Context, StartLoc, Result, Tok.getLoc()); + TLCD->setBody(Brace); + } break; + } - case PersistentParserState::DelayedDeclKind::Decl: - TheParser.parseDeclDelayed(); + case PersistentParserState::CodeCompletionDelayedDeclKind::Decl: { + assert((DC->isTypeContext() || DC->isModuleScopeContext()) && + "Delayed decl must be a type member or a top-level decl"); + ContextChange CC(*this, DC); + + parseDecl(ParseDeclOptions(info.Flags), + /*IsAtStartOfLineOrPreviousHadSemi=*/true, [&](Decl *D) { + if (auto *NTD = dyn_cast(DC)) { + NTD->addMember(D); + } else if (auto *ED = dyn_cast(DC)) { + ED->addMember(D); + } else if (auto *SF = dyn_cast(DC)) { + SF->Decls.push_back(D); + } else { + llvm_unreachable("invalid decl context kind"); + } + }); break; + } + + case PersistentParserState::CodeCompletionDelayedDeclKind::FunctionBody: { + auto *AFD = cast(DC); + ParseFunctionBody CC(*this, AFD); + setLocalDiscriminatorToParamList(AFD->getParameters()); - case PersistentParserState::DelayedDeclKind::FunctionBody: { - TheParser.parseAbstractFunctionBodyDelayed(); + auto result = parseBraceItemList(diag::func_decl_without_brace); + AFD->setBody(result.getPtrOrNull()); break; } } - assert(!ParserState.hasDelayedDecl()); + + assert(!State->hasCodeCompletionDelayedDeclState() && + "Second pass should not set any code completion info"); CodeCompletion->doneParsing(); } diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 02523698cb9c6..5244697ecba2d 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -25,14 +25,12 @@ PersistentParserState::PersistentParserState() { } PersistentParserState::~PersistentParserState() { } -void PersistentParserState::delayDecl(DelayedDeclKind Kind, - unsigned Flags, - DeclContext *ParentContext, - SourceRange BodyRange, - SourceLoc PreviousLoc) { - assert(!CodeCompletionDelayedDeclState.get() && +void PersistentParserState::setCodeCompletionDelayedDeclState( + CodeCompletionDelayedDeclKind Kind, unsigned Flags, + DeclContext *ParentContext, SourceRange BodyRange, SourceLoc PreviousLoc) { + assert(!CodeCompletionDelayedDeclStat.get() && "only one decl can be delayed for code completion"); - CodeCompletionDelayedDeclState.reset(new DelayedDeclState( + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( Kind, Flags, ParentContext, BodyRange, PreviousLoc, ScopeInfo.saveCurrentScope())); } From e56e311a2360d7165a3a1cb1d8934959777e4e86 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Mon, 1 Jul 2019 10:08:41 -0700 Subject: [PATCH 113/283] Fix Swift following bitstream reader API update (#25845) * Fix Swift following bitstream reader API update Upstream change in rL364464 broke downstream Swift. (cherry picked from commit 50de105bf1f9050047ebb4cf07c01db41ab6ddcb) Conflicts: lib/Serialization/Deserialization.cpp lib/Serialization/ModuleFile.cpp tools/driver/modulewrap_main.cpp --- lib/ClangImporter/SwiftLookupTable.cpp | 35 ++- lib/Serialization/BCReadingExtras.h | 3 +- lib/Serialization/Deserialization.cpp | 199 +++++++------ lib/Serialization/DeserializeSIL.cpp | 207 ++++++++++---- lib/Serialization/ModuleFile.cpp | 369 +++++++++++++++++++++---- lib/Serialization/ModuleFile.h | 9 + 6 files changed, 632 insertions(+), 190 deletions(-) diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 7c8535a346f02..4cd7ac7ee6621 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -1482,7 +1482,13 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, // Look for the base name -> entities table record. SmallVector scratch; auto cursor = stream; - auto next = cursor.advance(); + llvm::Expected maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + llvm::BitstreamEntry next = maybeNext.get(); std::unique_ptr serializedTable; std::unique_ptr globalsAsMembersTable; ArrayRef categories; @@ -1495,14 +1501,27 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, // API notes format. if (cursor.SkipBlock()) return nullptr; - - next = cursor.advance(); + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected maybeKind = + cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + unsigned kind = maybeKind.get(); switch (kind) { case BASE_NAME_TO_ENTITIES_RECORD_ID: { // Already saw base name -> entities table. @@ -1554,7 +1573,13 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + next = maybeNext.get(); } if (!serializedTable) return nullptr; diff --git a/lib/Serialization/BCReadingExtras.h b/lib/Serialization/BCReadingExtras.h index 1ef3e8e33ad8e..b09ca4b52b932 100644 --- a/lib/Serialization/BCReadingExtras.h +++ b/lib/Serialization/BCReadingExtras.h @@ -38,7 +38,8 @@ class BCOffsetRAII { ~BCOffsetRAII() { if (Cursor) - Cursor->JumpToBit(Offset); + cantFail(Cursor->JumpToBit(Offset), + "BCOffsetRAII must be able to go back"); } }; diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 8e71fccdd34c3..7c19a63f4c104 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -138,13 +138,13 @@ void ExtensionError::anchor() {} /// Skips a single record in the bitstream. /// -/// Returns true if the next entry is a record of type \p recordKind. /// Destroys the stream position if the next entry is not a record. static void skipRecord(llvm::BitstreamCursor &cursor, unsigned recordKind) { - auto next = cursor.advance(AF_DontPopBlockAtEnd); + auto next = llvm::cantFail( + cursor.advance(AF_DontPopBlockAtEnd)); assert(next.Kind == llvm::BitstreamEntry::Record); - unsigned kind = cursor.skipRecord(next.ID); + unsigned kind = llvm::cantFail(cursor.skipRecord(next.ID)); assert(kind == recordKind); (void)kind; } @@ -226,8 +226,10 @@ ParameterList *ModuleFile::readParameterList() { using namespace decls_block; SmallVector scratch; - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); + unsigned recordID = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); assert(recordID == PARAMETERLIST); (void) recordID; @@ -259,7 +261,8 @@ Expected ModuleFile::readPattern(DeclContext *owningDC) { SmallVector scratch; BCOffsetRAII restoreOffset(DeclTypeCursor); - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -271,7 +274,8 @@ Expected ModuleFile::readPattern(DeclContext *owningDC) { pattern->setType(type); }; - unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch)); switch (kind) { case decls_block::PAREN_PATTERN: { bool isImplicit; @@ -302,10 +306,10 @@ Expected ModuleFile::readPattern(DeclContext *owningDC) { SmallVector elements; for ( ; count > 0; --count) { scratch.clear(); - next = DeclTypeCursor.advance(); + next = fatalIfUnexpected(DeclTypeCursor.advance()); assert(next.Kind == llvm::BitstreamEntry::Record); - kind = DeclTypeCursor.readRecord(next.ID, scratch); + kind = fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch)); assert(kind == decls_block::TUPLE_PATTERN_ELT); // FIXME: Add something for this record or remove it. @@ -397,10 +401,11 @@ SILLayout *ModuleFile::readSILLayout(llvm::BitstreamCursor &Cursor) { SmallVector scratch; - auto next = Cursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); assert(next.Kind == llvm::BitstreamEntry::Record); - unsigned kind = Cursor.readRecord(next.ID, scratch); + unsigned kind = fatalIfUnexpected(Cursor.readRecord(next.ID, scratch)); switch (kind) { case decls_block::SIL_LAYOUT: { GenericSignatureID rawGenericSig; @@ -444,13 +449,14 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, SmallVector scratch; - auto next = Cursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); assert(next.Kind == llvm::BitstreamEntry::Record); if (getContext().Stats) getContext().Stats->getFrontendCounters().NumConformancesDeserialized++; - unsigned kind = Cursor.readRecord(next.ID, scratch); + unsigned kind = fatalIfUnexpected(Cursor.readRecord(next.ID, scratch)); switch (kind) { case INVALID_PROTOCOL_CONFORMANCE: { return ProtocolConformanceRef::forInvalid(); @@ -591,8 +597,8 @@ NormalProtocolConformance *ModuleFile::readNormalConformance( // Find the conformance record. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(conformanceEntry); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(conformanceEntry)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -602,9 +608,11 @@ NormalProtocolConformance *ModuleFile::readNormalConformance( ArrayRef rawIDs; SmallVector scratch; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, scratch); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); if (kind != NORMAL_PROTOCOL_CONFORMANCE) fatal(); + NormalProtocolConformanceLayout::readRecord(scratch, protoID, contextID, typeCount, valueCount, conformanceCount, @@ -651,11 +659,13 @@ GenericParamList *ModuleFile::maybeReadGenericParams(DeclContext *DC) { SmallVector scratch; StringRef blobData; - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) return nullptr; - unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch, &blobData); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch, &blobData)); if (kind != GENERIC_PARAM_LIST) return nullptr; lastRecordOffset.reset(); @@ -700,12 +710,14 @@ llvm::Error ModuleFile::readGenericRequirementsChecked( lastRecordOffset.reset(); bool shouldContinue = true; - auto entry = Cursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); - unsigned recordID = Cursor.readRecord(entry.ID, scratch, &blobData); + unsigned recordID = fatalIfUnexpected( + Cursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case GENERIC_REQUIREMENT: { uint8_t rawKind; @@ -829,29 +841,36 @@ llvm::Error ModuleFile::readGenericRequirementsChecked( } /// Advances past any records that might be part of a requirement signature. -static void skipGenericRequirements(llvm::BitstreamCursor &Cursor) { +static llvm::Error skipGenericRequirements(llvm::BitstreamCursor &Cursor) { using namespace decls_block; BCOffsetRAII lastRecordOffset(Cursor); while (true) { - auto entry = Cursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + Cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind != llvm::BitstreamEntry::Record) break; - unsigned recordID = Cursor.skipRecord(entry.ID); - switch (recordID) { + Expected maybeRecordID = Cursor.skipRecord(entry.ID); + if (!maybeRecordID) + return maybeRecordID.takeError(); + switch (maybeRecordID.get()) { case GENERIC_REQUIREMENT: case LAYOUT_REQUIREMENT: break; default: // This record is not a generic requirement. - return; + return llvm::Error::success(); } lastRecordOffset.reset(); } + return llvm::Error::success(); } GenericSignature ModuleFile::getGenericSignature( @@ -879,18 +898,20 @@ ModuleFile::getGenericSignatureChecked(serialization::GenericSignatureID ID) { // Read the generic signature. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(sigOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(sigOffset)); // Read the parameter types. SmallVector paramTypes; StringRef blobData; SmallVector scratch; - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case GENERIC_SIGNATURE: { ArrayRef rawParamIDs; @@ -902,6 +923,7 @@ ModuleFile::getGenericSignatureChecked(serialization::GenericSignatureID ID) { } break; } + case SIL_GENERIC_SIGNATURE: { ArrayRef rawParamIDs; SILGenericSignatureLayout::readRecord(scratch, rawParamIDs); @@ -977,16 +999,18 @@ ModuleFile::getSubstitutionMapChecked(serialization::SubstitutionMapID id) { // Read the substitution map. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(substitutionsOrOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(substitutionsOrOffset)); // Read the substitution map. - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); StringRef blobData; SmallVector scratch; - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (recordID != SUBSTITUTION_MAP) fatal(); @@ -1031,13 +1055,15 @@ ModuleFile::getSubstitutionMapChecked(serialization::SubstitutionMapID id) { bool ModuleFile::readDefaultWitnessTable(ProtocolDecl *proto) { using namespace decls_block; - auto entry = DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) return true; SmallVector witnessIDBuffer; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, witnessIDBuffer); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, witnessIDBuffer)); assert(kind == DEFAULT_WITNESS_TABLE); (void)kind; @@ -1190,7 +1216,8 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { assert(baseModule && "missing dependency"); PrettyXRefTrace pathTrace(*baseModule); - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -1203,8 +1230,8 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // In particular, operator path pieces represent actual operators here, but // filters on operator functions when they appear later on. scratch.clear(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case XREF_TYPE_PATH_PIECE: case XREF_VALUE_PATH_PIECE: { @@ -1232,7 +1259,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { auto maybeType = getTypeChecked(TID); if (!maybeType) { // FIXME: Don't throw away the inner error's information. - llvm::consumeError(maybeType.takeError()); + consumeError(maybeType.takeError()); return llvm::make_error("couldn't decode type", pathTrace, name); } @@ -1306,13 +1333,14 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { auto getXRefDeclNameForError = [&]() -> DeclName { DeclName result = pathTrace.getLastName(); while (--pathLen) { - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) return Identifier(); scratch.clear(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case XREF_TYPE_PATH_PIECE: { IdentifierID IID; @@ -1375,13 +1403,14 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // For remaining path pieces, filter or drill down into the results we have. while (--pathLen) { - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); scratch.clear(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case XREF_TYPE_PATH_PIECE: { if (values.size() == 1 && isa(values.front())) { @@ -1487,7 +1516,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { auto maybeType = getTypeChecked(TID); if (!maybeType) { // FIXME: Don't throw away the inner error's information. - llvm::consumeError(maybeType.takeError()); + consumeError(maybeType.takeError()); return llvm::make_error("couldn't decode type", pathTrace, memberName); } @@ -1778,8 +1807,8 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { return declContextOrOffset; BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(declContextOrOffset); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(declContextOrOffset)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -1788,8 +1817,8 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { SmallVector scratch; StringRef blobData; - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch(recordID) { case decls_block::ABSTRACT_CLOSURE_EXPR_CONTEXT: { TypeID closureTypeID; @@ -3220,7 +3249,8 @@ class swift::DeclDeserializer { proto->setLazyRequirementSignature(&MF, MF.DeclTypeCursor.GetCurrentBitNo()); - skipGenericRequirements(MF.DeclTypeCursor); + if (llvm::Error Err = skipGenericRequirements(MF.DeclTypeCursor)) + MF.fatal(std::move(Err)); proto->setMemberLoader(&MF, MF.DeclTypeCursor.GetCurrentBitNo()); @@ -3837,7 +3867,7 @@ ModuleFile::getDeclChecked(DeclID DID) { if (!declOrOffset.isComplete()) { ++NumDeclsLoaded; BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(declOrOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(declOrOffset)); Expected deserialized = DeclDeserializer(*this, declOrOffset).getDeclCheckedImpl(); @@ -3865,14 +3895,15 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { StringRef blobData; while (true) { BCOffsetRAII restoreOffset(MF.DeclTypeCursor); - auto entry = MF.DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize decls represented by sub-blocks. MF.fatal(); } - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (isDeclAttrRecord(recordID)) { DeclAttribute *Attr = nullptr; @@ -4166,7 +4197,8 @@ DeclDeserializer::getDeclCheckedImpl() { if (declOrOffset.isComplete()) return declOrOffset; - auto entry = MF.DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize decls represented by sub-blocks. MF.fatal(); @@ -4174,8 +4206,8 @@ DeclDeserializer::getDeclCheckedImpl() { SmallVector scratch; StringRef blobData; - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); PrettyDeclDeserialization stackTraceEntry( &MF, declOrOffset, static_cast(recordID)); @@ -4549,13 +4581,14 @@ class swift::TypeDeserializer { // The tuple record itself is empty. Read all trailing elements. SmallVector elements; while (true) { - auto entry = MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (recordID != decls_block::TUPLE_TYPE_ELT) break; @@ -4608,13 +4641,14 @@ class swift::TypeDeserializer { SmallVector params; while (true) { - auto entry = MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (recordID != decls_block::FUNCTION_PARAM) break; @@ -4903,7 +4937,7 @@ class swift::TypeDeserializer { } BCOffsetRAII saveOffset(MF.DeclTypeCursor); - MF.DeclTypeCursor.JumpToBit(layoutOrOffset); + MF.fatalIfNotSuccess(MF.DeclTypeCursor.JumpToBit(layoutOrOffset)); auto layout = MF.readSILLayout(MF.DeclTypeCursor); if (!layout) MF.fatal(); @@ -5135,7 +5169,7 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { return typeOrOffset; BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(typeOrOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(typeOrOffset)); auto result = TypeDeserializer(*this).getTypeCheckedImpl(); if (!result) @@ -5160,7 +5194,8 @@ Expected TypeDeserializer::getTypeCheckedImpl() { if (auto s = ctx.Stats) s->getFrontendCounters().NumTypesDeserialized++; - auto entry = MF.DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize types represented by sub-blocks. @@ -5169,8 +5204,8 @@ Expected TypeDeserializer::getTypeCheckedImpl() { SmallVector scratch; StringRef blobData; - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { #define CASE(RECORD_NAME) \ @@ -5282,14 +5317,15 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) { IDC = cast(container); BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(contextData); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); SmallVector memberIDBuffer; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, memberIDBuffer); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, memberIDBuffer)); assert(kind == decls_block::MEMBERS); (void)kind; @@ -5339,7 +5375,7 @@ ModuleFile::loadAllConformances(const Decl *D, uint64_t contextData, = decodeLazyConformanceContextData(contextData); BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(bitPosition); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(bitPosition)); while (numConformances--) { auto conf = readConformance(DeclTypeCursor); @@ -5371,8 +5407,8 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, // Find the conformance record. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(contextData); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); assert(entry.Kind == llvm::BitstreamEntry::Record && "registered lazy loader incorrectly"); @@ -5382,7 +5418,8 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, ArrayRef rawIDs; SmallVector scratch; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, scratch); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); (void) kind; assert(kind == NORMAL_PROTOCOL_CONFORMANCE && "registered lazy loader incorrectly"); @@ -5573,7 +5610,7 @@ void ModuleFile::loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData, SmallVectorImpl &reqs) { BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(contextData); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); readGenericRequirements(reqs, DeclTypeCursor); } @@ -5602,11 +5639,13 @@ Optional ModuleFile::maybeReadInlinableBodyText() { BCOffsetRAII restoreOffset(DeclTypeCursor); StringRef blobData; - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) return None; - unsigned recKind = DeclTypeCursor.readRecord(next.ID, scratch, &blobData); + unsigned recKind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch, &blobData)); if (recKind != INLINABLE_BODY_TEXT) return None; @@ -5621,11 +5660,13 @@ Optional ModuleFile::maybeReadForeignErrorConvention() { BCOffsetRAII restoreOffset(DeclTypeCursor); - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) return None; - unsigned recKind = DeclTypeCursor.readRecord(next.ID, scratch); + unsigned recKind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch)); switch (recKind) { case FOREIGN_ERROR_CONVENTION: restoreOffset.reset(); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 3eeae17f1c77b..61767d434862e 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -140,7 +140,7 @@ SILDeserializer::SILDeserializer( return; // Load any abbrev records at the start of the block. - SILCursor.advance(); + MF->fatalIfUnexpected(SILCursor.advance()); llvm::BitstreamCursor cursor = SILIndexCursor; // We expect SIL_FUNC_NAMES first, then SIL_VTABLE_NAMES, then @@ -149,14 +149,15 @@ SILDeserializer::SILDeserializer( // omitted if no entries exist in the module file. unsigned kind = 0; while (kind != sil_index_block::SIL_PROPERTY_OFFSETS) { - auto next = cursor.advance(); + llvm::BitstreamEntry next = MF->fatalIfUnexpected(cursor.advance()); if (next.Kind == llvm::BitstreamEntry::EndBlock) return; SmallVector scratch; StringRef blobData; unsigned prevKind = kind; - kind = cursor.readRecord(next.ID, scratch, &blobData); + kind = + MF->fatalIfUnexpected(cursor.readRecord(next.ID, scratch, &blobData)); assert((next.Kind == llvm::BitstreamEntry::Record && kind > prevKind && (kind == sil_index_block::SIL_FUNC_NAMES || @@ -186,9 +187,10 @@ SILDeserializer::SILDeserializer( } // Read SIL_FUNC|VTABLE|GLOBALVAR_OFFSETS record. - next = cursor.advance(); + next = MF->fatalIfUnexpected(cursor.advance()); scratch.clear(); - unsigned offKind = cursor.readRecord(next.ID, scratch, &blobData); + unsigned offKind = + MF->fatalIfUnexpected(cursor.readRecord(next.ID, scratch, &blobData)); (void)offKind; if (kind == sil_index_block::SIL_FUNC_NAMES) { assert((next.Kind == llvm::BitstreamEntry::Record && @@ -448,9 +450,14 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, return cacheEntry.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(cacheEntry.getOffset()); - - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(cacheEntry.getOffset())) + return std::move(Err); + + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readSILFunction.\n"); MF->fatal(); @@ -458,7 +465,11 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_FUNCTION && "expect a sil function"); (void)kind; @@ -491,7 +502,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, return llvm::make_error( name, takeErrorInfo(astType.takeError())); } - llvm::consumeError(astType.takeError()); + consumeError(astType.takeError()); return existingFn; } auto ty = getSILType(astType.get(), SILValueCategory::Object); @@ -627,11 +638,18 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // Read and instantiate the specialize attributes. while (numSpecAttrs--) { - auto next = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeNext = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeNext) + return maybeNext.takeError(); + llvm::BitstreamEntry next = maybeNext.get(); assert(next.Kind == llvm::BitstreamEntry::Record); scratch.clear(); - kind = SILCursor.readRecord(next.ID, scratch); + llvm::Expected maybeKind = SILCursor.readRecord(next.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + unsigned kind = maybeKind.get(); assert(kind == SIL_SPECIALIZE_ATTR && "Missing specialization attribute"); unsigned exported; @@ -658,7 +676,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // If the next entry is the end of the block, then this function has // no contents. - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + entry = maybeEntry.get(); bool isEmptyFunction = (entry.Kind == llvm::BitstreamEntry::EndBlock); assert((!isEmptyFunction || !genericEnv) && "generic environment without body?!"); @@ -686,7 +707,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setGenericEnvironment(genericEnv); scratch.clear(); - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + kind = maybeKind.get(); SILBasicBlock *CurrentBB = nullptr; @@ -745,12 +769,19 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); // EndBlock means the end of this SILFunction. if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + kind = maybeKind.get(); } // If fn is empty, we failed to deserialize its body. Return nullptr to signal @@ -2493,7 +2524,7 @@ SILFunction *SILDeserializer::lookupSILFunction(SILFunction *InFunc) { /*declarationOnly*/ false); if (!maybeFunc) { // Ignore the error; treat it as if we didn't have a definition. - llvm::consumeError(maybeFunc.takeError()); + consumeError(maybeFunc.takeError()); return nullptr; } @@ -2526,9 +2557,14 @@ bool SILDeserializer::hasSILFunction(StringRef Name, return !Linkage || cacheEntry.get()->getLinkage() == *Linkage; BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(cacheEntry.getOffset()); - - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(cacheEntry.getOffset())) + MF->fatal(std::move(Err)); + + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in hasSILFunction.\n"); MF->fatal(); @@ -2536,7 +2572,11 @@ bool SILDeserializer::hasSILFunction(StringRef Name, SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_FUNCTION && "expect a sil function"); (void)kind; @@ -2590,7 +2630,7 @@ SILFunction *SILDeserializer::lookupSILFunction(StringRef name, if (!maybeFunc) { // Ignore the error; treat it as if we didn't have a definition. - llvm::consumeError(maybeFunc.takeError()); + consumeError(maybeFunc.takeError()); return nullptr; } @@ -2625,8 +2665,13 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { return globalVarOrOffset; BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(globalVarOrOffset); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(globalVarOrOffset)) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readGlobalVar.\n"); return nullptr; @@ -2634,7 +2679,11 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_GLOBALVAR && "expect a sil global var"); (void)kind; @@ -2698,7 +2747,7 @@ void SILDeserializer::getAllSILFunctions() { false/*errorIfEmptyBody*/); if (!maybeFunc) { // Ignore the error; treat it as if we didn't have a definition. - llvm::consumeError(maybeFunc.takeError()); + consumeError(maybeFunc.takeError()); } } } @@ -2713,8 +2762,13 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { return vTableOrOffset; BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(vTableOrOffset); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(vTableOrOffset)) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readVTable.\n"); return nullptr; @@ -2722,7 +2776,11 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_VTABLE && "expect a sil vtable"); (void)kind; @@ -2740,11 +2798,17 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) // This vtable has no contents. return nullptr; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); std::vector vtableEntries; // Another SIL_VTABLE record means the end of this VTable. @@ -2770,11 +2834,17 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) // EndBlock means the end of this VTable. break; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); } // If we've already serialized the module, don't mark the witness table @@ -2820,8 +2890,13 @@ SILProperty *SILDeserializer::readProperty(DeclID PId) { return propOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(propOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(propOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readProperty.\n"); return nullptr; @@ -2829,7 +2904,11 @@ SILProperty *SILDeserializer::readProperty(DeclID PId) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_PROPERTY && "expect a sil_property"); (void)kind; @@ -2859,7 +2938,10 @@ void SILDeserializer::readWitnessTableEntries( std::vector &conditionalConformances) { SmallVector scratch; - unsigned kind = SILCursor.readRecord(entry.ID, scratch); + llvm::Expected maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); // Another record means the end of this WitnessTable. while (kind != SIL_WITNESS_TABLE && @@ -2921,11 +3003,18 @@ void SILDeserializer::readWitnessTableEntries( // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) // EndBlock means the end of this WitnessTable. break; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); } } @@ -2941,8 +3030,13 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, return wTableOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(wTableOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(wTableOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readWitnessTable.\n"); return nullptr; @@ -2950,7 +3044,11 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_WITNESS_TABLE && "expect a sil witnesstable"); (void)kind; @@ -3017,7 +3115,10 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) return nullptr; @@ -3084,8 +3185,13 @@ readDefaultWitnessTable(DeclID WId, SILDefaultWitnessTable *existingWt) { return wTableOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(wTableOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(wTableOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in " "readDefaultWitnessTable.\n"); @@ -3094,7 +3200,11 @@ readDefaultWitnessTable(DeclID WId, SILDefaultWitnessTable *existingWt) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_DEFAULT_WITNESS_TABLE && "expect a sil default witness table"); (void)kind; @@ -3141,7 +3251,10 @@ readDefaultWitnessTable(DeclID WId, SILDefaultWitnessTable *existingWt) { // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) return nullptr; diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 3d97cc57621eb..710254f943a47 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -44,16 +44,31 @@ static_assert(IsTriviallyDestructible::value, static bool checkModuleSignature(llvm::BitstreamCursor &cursor, ArrayRef signature) { - for (unsigned char byte : signature) - if (cursor.AtEndOfStream() || cursor.Read(8) != byte) + for (unsigned char byte : signature) { + if (cursor.AtEndOfStream()) return false; + if (Expected maybeRead = + cursor.Read(8)) { + if (maybeRead.get() != byte) + return false; + } else { + consumeError(maybeRead.takeError()); + return false; + } + } return true; } static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID, bool shouldReadBlockInfo = true) { - auto next = cursor.advance(); + Expected maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); if (next.Kind != llvm::BitstreamEntry::SubBlock) return false; @@ -72,7 +87,12 @@ static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, if (next.ID != ID) return false; - cursor.EnterSubBlock(ID); + if (llvm::Error Err = cursor.EnterSubBlock(ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } + return true; } @@ -83,7 +103,13 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch, ExtendedValidationInfo &extendedInfo) { while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; @@ -100,7 +126,14 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor, scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case options_block::SDK_PATH: extendedInfo.setSDKPath(blobData); @@ -145,7 +178,14 @@ validateControlBlock(llvm::BitstreamCursor &cursor, bool versionSeen = false; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + result.status = Status::Malformed; + return result; + } + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; @@ -156,7 +196,12 @@ validateControlBlock(llvm::BitstreamCursor &cursor, if (entry.Kind == llvm::BitstreamEntry::SubBlock) { if (entry.ID == OPTIONS_BLOCK_ID && extendedInfo) { - cursor.EnterSubBlock(OPTIONS_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(OPTIONS_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + result.status = Status::Malformed; + return result; + } if (!readOptionsBlock(cursor, scratch, *extendedInfo)) { result.status = Status::Malformed; return result; @@ -174,7 +219,15 @@ validateControlBlock(llvm::BitstreamCursor &cursor, scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + result.status = Status::Malformed; + return result; + } + unsigned kind = maybeKind.get(); switch (kind) { case control_block::METADATA: { if (versionSeen) { @@ -247,7 +300,13 @@ static bool validateInputBlock( SmallString<256> dependencyFullPathBuffer; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return true; + } + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; @@ -256,7 +315,14 @@ static bool validateInputBlock( scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return true; + } + unsigned kind = maybeKind.get(); switch (kind) { case input_block::FILE_DEPENDENCY: { bool isHashBased = scratch[2] != 0; @@ -323,12 +389,25 @@ ValidationInfo serialization::validateSerializedAST( llvm::BitstreamEntry topLevelEntry; while (!cursor.AtEndOfStream()) { - topLevelEntry = cursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + result.status = Status::Malformed; + return result; + } + topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; if (topLevelEntry.ID == CONTROL_BLOCK_ID) { - cursor.EnterSubBlock(CONTROL_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + result.status = Status::Malformed; + return result; + } result = validateControlBlock(cursor, scratch, {SWIFTMODULE_VERSION_MAJOR, SWIFTMODULE_VERSION_MINOR}, @@ -338,7 +417,12 @@ ValidationInfo serialization::validateSerializedAST( } else if (dependencies && result.status == Status::Valid && topLevelEntry.ID == INPUT_BLOCK_ID) { - cursor.EnterSubBlock(INPUT_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(INPUT_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + result.status = Status::Malformed; + return result; + } if (validateInputBlock(cursor, scratch, *dependencies)) { result.status = Status::Malformed; return result; @@ -827,13 +911,23 @@ ModuleFile::readObjCMethodTable(ArrayRef fields, StringRef blobData) { } bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { - cursor.EnterSubBlock(INDEX_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(INDEX_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry entry = maybeEntry.get(); switch (entry.Kind) { case llvm::BitstreamEntry::EndBlock: return true; @@ -844,13 +938,25 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::SubBlock: if (entry.ID == DECL_MEMBER_TABLES_BLOCK_ID) { DeclMemberTablesCursor = cursor; - DeclMemberTablesCursor.EnterSubBlock(DECL_MEMBER_TABLES_BLOCK_ID); + if (llvm::Error Err = DeclMemberTablesCursor.EnterSubBlock( + DECL_MEMBER_TABLES_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } llvm::BitstreamEntry subentry; do { // Scan forward, to load the cursor with any abbrevs we'll need while // seeking inside this block later. - subentry = DeclMemberTablesCursor.advance( - llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + Expected maybeEntry = + DeclMemberTablesCursor.advance( + llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + subentry = maybeEntry.get(); } while (!DeclMemberTablesCursor.AtEndOfStream() && subentry.Kind != llvm::BitstreamEntry::Record && subentry.Kind != llvm::BitstreamEntry::EndBlock); @@ -862,7 +968,14 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::Record: scratch.clear(); blobData = {}; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case index_block::DECL_OFFSETS: @@ -1044,13 +1157,23 @@ ModuleFile::readGroupTable(ArrayRef Fields, StringRef BlobData) { } bool ModuleFile::readCommentBlock(llvm::BitstreamCursor &cursor) { - cursor.EnterSubBlock(COMMENT_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(COMMENT_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry entry = maybeEntry.get(); switch (entry.Kind) { case llvm::BitstreamEntry::EndBlock: return true; @@ -1066,7 +1189,14 @@ bool ModuleFile::readCommentBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::Record: scratch.clear(); - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case comment_block::DECL_COMMENTS: @@ -1181,13 +1311,24 @@ bool ModuleFile::readModuleDocIfPresent() { ValidationInfo info; while (!docCursor.AtEndOfStream()) { - topLevelEntry = docCursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + docCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; switch (topLevelEntry.ID) { case CONTROL_BLOCK_ID: { - docCursor.EnterSubBlock(CONTROL_BLOCK_ID); + if (llvm::Error Err = docCursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } info = validateControlBlock(docCursor, scratch, {SWIFTDOC_VERSION_MAJOR, @@ -1270,14 +1411,21 @@ ModuleFile::readDeclUSRsTable(ArrayRef fields, StringRef blobData) { } bool ModuleFile::readDeclLocsBlock(llvm::BitstreamCursor &cursor) { - cursor.EnterSubBlock(DECL_LOCS_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + consumeError(std::move(Err)); + return false; + } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); - switch (entry.Kind) { + Expected entry = cursor.advance(); + if (!entry) { + consumeError(entry.takeError()); + return false; + } + switch (entry->Kind) { case llvm::BitstreamEntry::EndBlock: return true; @@ -1292,9 +1440,13 @@ bool ModuleFile::readDeclLocsBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::Record: scratch.clear(); - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); - - switch (kind) { + Expected kind = + cursor.readRecord(entry->ID, scratch, &blobData); + if (!kind) { + consumeError(kind.takeError()); + return false; + } + switch (*kind) { case decl_locs_block::BASIC_DECL_LOCS: BasicDeclLocsData = blobData; break; @@ -1326,20 +1478,28 @@ bool ModuleFile::readModuleSourceInfoIfPresent() { } SmallVector scratch; - llvm::BitstreamEntry topLevelEntry; bool hasValidControlBlock = false; ValidationInfo info; + unsigned kind = llvm::BitstreamEntry::Error; while (!infoCursor.AtEndOfStream()) { - topLevelEntry = infoCursor.advance(AF_DontPopBlockAtEnd); - if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) + Expected topLevelEntry = + infoCursor.advance(AF_DontPopBlockAtEnd); + if (!topLevelEntry) { + consumeError(topLevelEntry.takeError()); + return false; + } + kind = topLevelEntry->Kind; + if (kind != llvm::BitstreamEntry::SubBlock) break; - switch (topLevelEntry.ID) { + switch (topLevelEntry->ID) { case CONTROL_BLOCK_ID: { - infoCursor.EnterSubBlock(CONTROL_BLOCK_ID); - + if (llvm::Error Err = infoCursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + consumeError(std::move(Err)); + return false; + } info = validateControlBlock(infoCursor, scratch, {SWIFTSOURCEINFO_VERSION_MAJOR, SWIFTSOURCEINFO_VERSION_MINOR}, @@ -1368,7 +1528,7 @@ bool ModuleFile::readModuleSourceInfoIfPresent() { } } - if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock) + if (kind != llvm::BitstreamEntry::EndBlock) return false; return true; @@ -1405,13 +1565,26 @@ ModuleFile::ModuleFile( llvm::BitstreamEntry topLevelEntry; while (!cursor.AtEndOfStream()) { - topLevelEntry = cursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error diagnostic on the floor. + consumeError(maybeEntry.takeError()); + info.status = error(Status::Malformed); + return; + } + topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; switch (topLevelEntry.ID) { case CONTROL_BLOCK_ID: { - cursor.EnterSubBlock(CONTROL_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } info = validateControlBlock(cursor, scratch, {SWIFTMODULE_VERSION_MAJOR, @@ -1436,13 +1609,33 @@ ModuleFile::ModuleFile( return; } - cursor.EnterSubBlock(INPUT_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(INPUT_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } - auto next = cursor.advance(); + Expected maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + info.status = error(Status::Malformed); + return; + } + unsigned kind = maybeKind.get(); switch (kind) { case input_block::IMPORTED_MODULE: { unsigned rawImportControl; @@ -1504,7 +1697,14 @@ ModuleFile::ModuleFile( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + next = maybeNext.get(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) @@ -1522,8 +1722,22 @@ ModuleFile::ModuleFile( // The decls-and-types block is lazily loaded. Save the cursor and load // any abbrev records at the start of the block. DeclTypeCursor = cursor; - DeclTypeCursor.EnterSubBlock(DECLS_AND_TYPES_BLOCK_ID); - if (DeclTypeCursor.advance().Kind == llvm::BitstreamEntry::Error) + if (llvm::Error Err = + DeclTypeCursor.EnterSubBlock(DECLS_AND_TYPES_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } + + Expected maybeCursor = DeclTypeCursor.advance(); + if (!maybeCursor) { + // FIXME this drops the error on the floor. + consumeError(maybeCursor.takeError()); + info.status = error(Status::Malformed); + return; + } + if (maybeCursor.get().Kind == llvm::BitstreamEntry::Error) info.status = error(Status::Malformed); // With the main cursor, skip over the block and continue. @@ -1540,13 +1754,34 @@ ModuleFile::ModuleFile( return; } - cursor.EnterSubBlock(IDENTIFIER_DATA_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(IDENTIFIER_DATA_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } - auto next = cursor.advanceSkippingSubblocks(); + Expected maybeNext = + cursor.advanceSkippingSubblocks(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + info.status = error(Status::Malformed); + return; + } + unsigned kind = maybeKind.get(); switch (kind) { case identifier_block::IDENTIFIER_DATA: @@ -1559,7 +1794,14 @@ ModuleFile::ModuleFile( break; } - next = cursor.advanceSkippingSubblocks(); + maybeNext = cursor.advanceSkippingSubblocks(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + next = maybeNext.get(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) { @@ -1581,7 +1823,12 @@ ModuleFile::ModuleFile( case SIL_INDEX_BLOCK_ID: { // Save the cursor. SILIndexCursor = cursor; - SILIndexCursor.EnterSubBlock(SIL_INDEX_BLOCK_ID); + if (llvm::Error Err = SILIndexCursor.EnterSubBlock(SIL_INDEX_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } // With the main cursor, skip over the block and continue. if (cursor.SkipBlock()) { @@ -1594,7 +1841,12 @@ ModuleFile::ModuleFile( case SIL_BLOCK_ID: { // Save the cursor. SILCursor = cursor; - SILCursor.EnterSubBlock(SIL_BLOCK_ID); + if (llvm::Error Err = SILCursor.EnterSubBlock(SIL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } // With the main cursor, skip over the block and continue. if (cursor.SkipBlock()) { @@ -2138,16 +2390,17 @@ ModuleFile::loadNamedMembers(const IterableDeclContext *IDC, DeclBaseName N, DeclMembersTables[subTableOffset]; if (!subTable) { BCOffsetRAII restoreOffset(DeclMemberTablesCursor); - DeclMemberTablesCursor.JumpToBit(subTableOffset); - auto entry = DeclMemberTablesCursor.advance(); + fatalIfNotSuccess(DeclMemberTablesCursor.JumpToBit(subTableOffset)); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclMemberTablesCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { fatal(); return None; } SmallVector scratch; StringRef blobData; - unsigned kind = DeclMemberTablesCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned kind = fatalIfUnexpected( + DeclMemberTablesCursor.readRecord(entry.ID, scratch, &blobData)); assert(kind == decl_member_tables_block::DECL_MEMBERS); (void)kind; subTable = readDeclMembersTable(scratch, blobData); @@ -2166,7 +2419,7 @@ ModuleFile::loadNamedMembers(const IterableDeclContext *IDC, DeclBaseName N, } else { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(mem.takeError()); - llvm::consumeError(mem.takeError()); + consumeError(mem.takeError()); // Treat this as a cache-miss to the caller and let them attempt // to refill through the normal loadAllMembers() path. @@ -2293,7 +2546,7 @@ void ModuleFile::getTopLevelDecls(SmallVectorImpl &results) { if (!declOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); - llvm::consumeError(declOrError.takeError()); + consumeError(declOrError.takeError()); continue; } results.push_back(declOrError.get()); diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index c73b3a6b6ad4b..862ecd93e5f2e 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -474,6 +474,15 @@ class ModuleFile /// Emits one last diagnostic, logs the error, and then aborts for the stack /// trace. LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error error); + void fatalIfNotSuccess(llvm::Error error) { + if (error) + fatal(std::move(error)); + } + template T fatalIfUnexpected(llvm::Expected expected) { + if (expected) + return std::move(expected.get()); + fatal(expected.takeError()); + } LLVM_ATTRIBUTE_NORETURN void fatal() { fatal(llvm::make_error( From 47c6ce2af71bc9ff24be43c088ee3d7ccf12da37 Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih Date: Wed, 3 Jul 2019 14:27:04 -0700 Subject: [PATCH 114/283] [Bitcode] Update includes: llvm/Bitcode -> llvm/Bitstream The Bitstream part of Bitcode moved to llvm/Bitstream in LLVM. This updates the uses in swift. See r365091 [Bitcode] Move Bitstream to a separate library. (cherry picked from commit 1cd8e193579f13365ba25f48baccbb70abd6f0f1) --- lib/ClangImporter/SwiftLookupTable.cpp | 4 ++-- lib/Frontend/SerializedDiagnosticConsumer.cpp | 2 +- lib/Serialization/BCReadingExtras.h | 2 +- lib/Serialization/ModuleFile.h | 2 +- lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/Serialization.cpp | 2 +- tools/driver/modulewrap_main.cpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 4cd7ac7ee6621..632edc4527a0e 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -30,9 +30,9 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Bitcode/BitstreamReader.h" -#include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/DJB.h" #include "llvm/Support/OnDiskHashTable.h" diff --git a/lib/Frontend/SerializedDiagnosticConsumer.cpp b/lib/Frontend/SerializedDiagnosticConsumer.cpp index 6b1346bae2935..001063e817f90 100644 --- a/lib/Frontend/SerializedDiagnosticConsumer.cpp +++ b/lib/Frontend/SerializedDiagnosticConsumer.cpp @@ -28,7 +28,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Bitstream/BitstreamWriter.h" // For constant values only. #include "clang/Frontend/SerializedDiagnosticPrinter.h" diff --git a/lib/Serialization/BCReadingExtras.h b/lib/Serialization/BCReadingExtras.h index b09ca4b52b932..928a2cb82580b 100644 --- a/lib/Serialization/BCReadingExtras.h +++ b/lib/Serialization/BCReadingExtras.h @@ -13,7 +13,7 @@ #ifndef SWIFT_SERIALIZATION_BCREADINGEXTRAS_H #define SWIFT_SERIALIZATION_BCREADINGEXTRAS_H -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" namespace swift { namespace serialization { diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 862ecd93e5f2e..a18febcfdab1b 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -26,7 +26,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 3c8112c4f6995..674fb63d59976 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -22,7 +22,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/Types.h" #include "llvm/Bitcode/RecordLayout.h" -#include "llvm/Bitcode/BitCodes.h" +#include "llvm/Bitstream/BitCodes.h" #include "llvm/ADT/PointerEmbeddedInt.h" namespace swift { diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index b11e78fbe0039..1fc0f7e4a48fc 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -45,8 +45,8 @@ #include "swift/Strings.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Config/config.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Chrono.h" diff --git a/tools/driver/modulewrap_main.cpp b/tools/driver/modulewrap_main.cpp index 886d7a62fba45..71452baf91366 100644 --- a/tools/driver/modulewrap_main.cpp +++ b/tools/driver/modulewrap_main.cpp @@ -27,7 +27,7 @@ #include "swift/SIL/TypeLowering.h" #include "swift/Subsystems.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/FileSystem.h" From 8ff60abf6fdb2af4b9fa0ba8b7fa729ee75a1f5f Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sun, 10 Nov 2019 17:11:13 -0800 Subject: [PATCH 115/283] Remove unused TypeCheckers --- lib/Frontend/Frontend.cpp | 17 ++++++++++++----- lib/IDE/REPLCodeCompletion.cpp | 1 - lib/Immediate/REPL.cpp | 1 - lib/Sema/CSGen.cpp | 5 ++--- lib/Sema/InstrumenterSupport.cpp | 4 +--- lib/Sema/TypeCheckConstraints.cpp | 1 + lib/Sema/TypeCheckDecl.cpp | 10 +++------- lib/Sema/TypeCheckProtocolInference.cpp | 4 ++-- lib/Sema/TypeCheckREPL.cpp | 4 +--- lib/Sema/TypeCheckStmt.cpp | 3 --- 10 files changed, 22 insertions(+), 28 deletions(-) diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 1ce19d9e519b7..141d5d214664a 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -852,12 +852,12 @@ void CompilerInstance::parseAndCheckTypesUpTo( }) && "some files have not yet had their imports resolved"); MainModule->setHasResolvedImports(); - // If the limiting AST stage is name binding, we're done. - if (limitStage <= SourceFile::NameBound) { - return; - } - forEachFileToTypeCheck([&](SourceFile &SF) { + if (limitStage == SourceFile::NameBound) { + bindExtensions(SF); + return; + } + performTypeChecking(SF, PersistentState->getTopLevelContext()); if (!Context->hadError() && Invocation.getFrontendOptions().PCMacro) { @@ -874,9 +874,16 @@ void CompilerInstance::parseAndCheckTypesUpTo( }); if (Invocation.isCodeCompletion()) { + assert(limitStage == SourceFile::NameBound); performCodeCompletionSecondPass(*PersistentState.get(), *Invocation.getCodeCompletionFactory()); } + + // If the limiting AST stage is name binding, we're done. + if (limitStage <= SourceFile::NameBound) { + return; + } + finishTypeChecking(); } diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index b5256bc556c86..489413bcfdf75 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -212,7 +212,6 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, do { parseIntoSourceFile(SF, *BufferID, &Done, nullptr, &PersistentState); } while (!Done); - llvm::SaveAndRestore clearTyOpts(Ctx.TypeCheckerOpts, {}); performTypeChecking(SF, PersistentState.getTopLevelContext(), OriginalDeclCount); diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index a57aab42f2d21..829ed795883b9 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -193,7 +193,6 @@ typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, &PersistentState); } while (!Done); - llvm::SaveAndRestore clearTyOpts(Ctx.TypeCheckerOpts, {}); performTypeChecking(REPLInputFile, PersistentState.getTopLevelContext()); return REPLModule; } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 1a88f0262ec1e..97ea5e6012294 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3861,9 +3861,8 @@ getMemberDecls(InterestedMemberKind Kind) { ResolvedMemberResult swift::resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name) { ResolvedMemberResult Result; - // If the current ast context has no type checker, create one for it. - auto *TC = DC.getASTContext().getLegacyGlobalTypeChecker(); - assert(TC && "Must have type checker to make global query!"); + assert(DC.getASTContext().getLegacyGlobalTypeChecker() && + "Must have type checker to make global query!"); ConstraintSystem CS(&DC, None); // Look up all members of BaseTy with the given Name. diff --git a/lib/Sema/InstrumenterSupport.cpp b/lib/Sema/InstrumenterSupport.cpp index af902e1c7a8fb..0e0c059bcf85b 100644 --- a/lib/Sema/InstrumenterSupport.cpp +++ b/lib/Sema/InstrumenterSupport.cpp @@ -116,9 +116,7 @@ bool InstrumenterBase::doTypeCheckImpl(ASTContext &Ctx, DeclContext *DC, DiagnosticSuppression suppression(Ctx.Diags); ErrorGatherer errorGatherer(Ctx.Diags); - auto *TC = Ctx.getLegacyGlobalTypeChecker(); - assert(TC && "Must have type checker installed!"); - TC->typeCheckExpression(parsedExpr, DC); + TypeChecker::typeCheckExpression(parsedExpr, DC); if (parsedExpr) { ErrorFinder errorFinder; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 79c4495d1acb3..71dbe2ceb30ce 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2161,6 +2161,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, auto &Context = dc->getASTContext(); FallbackDiagnosticListener diagListener(Context, options, listener); auto *TC = Context.getLegacyGlobalTypeChecker(); + assert(TC && "Must have a global type checker set"); return TC->typeCheckExpressionImpl(expr, dc, convertType, convertTypePurpose, options, diagListener, baseCS); } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index afa323ab59e41..55cf4c49ef8bc 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1666,11 +1666,10 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, { - auto *TC = ED->getASTContext().getLegacyGlobalTypeChecker(); - assert(TC && "Must have a global type checker set"); Expr *exprToCheck = prevValue; - if (TC->typeCheckExpression(exprToCheck, ED, TypeLoc::withoutLoc(rawTy), - CTP_EnumCaseRawValue)) { + if (TypeChecker::typeCheckExpression(exprToCheck, ED, + TypeLoc::withoutLoc(rawTy), + CTP_EnumCaseRawValue)) { TypeChecker::checkEnumElementErrorHandling(elt, exprToCheck); } } @@ -3180,9 +3179,6 @@ class DeclChecker : public DeclVisitor { bool shouldSkipBodyTypechecking(const AbstractFunctionDecl *AFD) { - // FIXME: Remove TypeChecker dependency. - auto &TC = *Ctx.getLegacyGlobalTypeChecker(); - // Make sure we're in the mode that's skipping function bodies. if (!getASTContext().TypeCheckerOpts.SkipNonInlinableFunctionBodies) return false; diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index 75214dcdae865..ffa58350ce96a 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -208,14 +208,14 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( // because those have to be explicitly declared on the type somewhere // so won't be affected by whatever answer inference comes up with. auto selfTy = extension->getSelfInterfaceType(); - auto *tc = getASTContext().getLegacyGlobalTypeChecker(); for (const Requirement &reqt : extensionSig->getRequirements()) { switch (reqt.getKind()) { case RequirementKind::Conformance: case RequirementKind::Superclass: // FIXME: This is the wrong check if (selfTy->isEqual(reqt.getFirstType()) && - !tc->isSubtypeOf(conformance->getType(), reqt.getSecondType(), dc)) + !TypeChecker::isSubtypeOf(conformance->getType(), + reqt.getSecondType(), dc)) return false; break; diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index 0f45b17892870..9b83c0ddcad41 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -255,10 +255,8 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { BraceStmt *Body = builder.createBodyStmt(Loc, EndLoc); CE->setBody(Body, false); - // FIXME: Remove TypeChecker dependency. - auto &TC = *Context.getLegacyGlobalTypeChecker(); TypeChecker::typeCheckClosureBody(CE); - TC.ClosuresWithUncomputedCaptures.push_back(CE); + TypeChecker::computeCaptures(CE); auto *TheCall = CallExpr::createImplicit(Context, CE, { E }, { }); TheCall->getArg()->setType(AnyFunctionType::composeInput(Context, args, false)); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 4136a8e5cabeb..d1177eedabcb5 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1280,9 +1280,6 @@ class StmtChecker : public StmtVisitor { } Stmt *visitSwitchStmt(SwitchStmt *switchStmt) { - // FIXME: Remove TypeChecker dependency. - auto &TC = *Ctx.getLegacyGlobalTypeChecker(); - // Type-check the subject expression. Expr *subjectExpr = switchStmt->getSubjectExpr(); auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC); From 63e72909b5df1a95ae2fbc6d2358f1f000b6df19 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 6 Nov 2019 17:55:25 -0800 Subject: [PATCH 116/283] [Sanitizers] Add Driver/Frontend option to enable sanitizer instrumentation that supports error recovery. The new option `-sanitize-recover=` takes a list of sanitizers that recovery instrumentation should be enabled for. Currently we only support it for Address Sanitizer. If the option is not specified then the generated instrumentation does not allow error recovery. This option mirrors the `-fsanitize-recover=` option of Clang. We don't enable recoverable instrumentation by default because it may lead to code size blow up (control flow has to be resumable). The motivation behind this change is that today, setting `ASAN_OPTIONS=halt_on_error=0` at runtime doesn't always work. If you compile without the `-sanitize-recover=address` option (equivalent to the current behavior of the swift compiler) then the generated instrumentation doesn't allow for error recovery. What this means is that if you set `ASAN_OPTIONS=halt_on_error=0` at runtime and if an ASan issue is caught via instrumentation then the process will always halt regardless of how `halt_on_error` is set. However, if ASan catches an issue via one of its interceptors (e.g. memcpy) then `the halt_on_error` runtime option is respected. With `-sanitize-recover=address` the generated instrumentation allows for error recovery which means that the `halt_on_error` runtime option is also respected when the ASan issue is caught by instrumentation. ASan's default for `halt_on_error` is true which means this issue only effects people who choose to not use the default behavior. rdar://problem/56346688 --- include/swift/AST/DiagnosticsFrontend.def | 4 + include/swift/AST/IRGenOptions.h | 4 + include/swift/Option/Options.td | 9 ++ include/swift/Option/SanitizerOptions.h | 4 + lib/Driver/Driver.cpp | 8 ++ lib/Driver/ToolChains.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 6 ++ lib/IRGen/IRGen.cpp | 10 ++- lib/Option/SanitizerOptions.cpp | 43 ++++++++++ test/Driver/sanitize_recover.swift | 18 ++++ test/IRGen/address_sanitizer_recover.swift | 16 ++++ test/Sanitizers/asan_interface.h | 4 + test/Sanitizers/asan_recover.swift | 98 ++++++++++++++++++++++ 13 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 test/Driver/sanitize_recover.swift create mode 100644 test/IRGen/address_sanitizer_recover.swift create mode 100644 test/Sanitizers/asan_interface.h create mode 100644 test/Sanitizers/asan_recover.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index b171c0f7dd92b..d752405bcb83c 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -71,6 +71,10 @@ ERROR(error_option_requires_sanitizer, none, "option '%0' requires a sanitizer to be enabled. Use -sanitize= to " "enable a sanitizer", (StringRef)) +WARNING(warning_option_requires_specific_sanitizer, none, + "option '%0' has no effect when '%1' sanitizer is disabled. Use -sanitize=%1 to " + "enable the sanitizer", (StringRef, StringRef)) + ERROR(error_option_missing_required_argument, none, "option '%0' is missing a required argument (%1)", (StringRef, StringRef)) diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 2e6aae30fa3b3..30aae5b3438e7 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -106,6 +106,9 @@ class IRGenOptions { /// Which sanitizer is turned on. OptionSet Sanitizers; + /// Which sanitizer(s) have recovery instrumentation enabled. + OptionSet SanitizersWithRecoveryInstrumentation; + /// Path prefixes that should be rewritten in debug info. PathRemapper DebugPrefixMap; @@ -239,6 +242,7 @@ class IRGenOptions { : DWARFVersion(2), OutputKind(IRGenOutputKind::LLVMAssembly), Verify(true), OptMode(OptimizationMode::NotSet), Sanitizers(OptionSet()), + SanitizersWithRecoveryInstrumentation(OptionSet()), DebugInfoLevel(IRGenDebugInfoLevel::None), DebugInfoFormat(IRGenDebugInfoFormat::None), DisableClangModuleSkeletonCUs(false), UseJIT(false), diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 166730ec17174..28150a20e1d08 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -889,6 +889,15 @@ def sanitize_EQ : CommaJoined<["-"], "sanitize=">, Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"">, HelpText<"Turn on runtime checks for erroneous behavior.">; +def sanitize_recover_EQ + : CommaJoined<["-"], "sanitize-recover=">, + Flags<[FrontendOption, NoInteractiveOption]>, + MetaVarName<"">, + HelpText<"Specify which sanitizer runtime checks (see -sanitize=) will " + "generate instrumentation that allows error recovery. Listed " + "checks should be comma separated. Default behavior is to not " + "allow error recovery.">; + def sanitize_coverage_EQ : CommaJoined<["-"], "sanitize-coverage=">, Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"">, diff --git a/include/swift/Option/SanitizerOptions.h b/include/swift/Option/SanitizerOptions.h index f19500c8ac4c0..8a29853e3790d 100644 --- a/include/swift/Option/SanitizerOptions.h +++ b/include/swift/Option/SanitizerOptions.h @@ -36,6 +36,10 @@ OptionSet parseSanitizerArgValues( const llvm::Triple &Triple, DiagnosticEngine &Diag, llvm::function_ref sanitizerRuntimeLibExists); +OptionSet parseSanitizerRecoverArgValues( + const llvm::opt::Arg *A, const OptionSet &enabledSanitizers, + DiagnosticEngine &Diags, bool emitWarnings); + /// Parses a -sanitize-coverage= argument's value. llvm::SanitizerCoverageOptions parseSanitizerCoverageArgValue( const llvm::opt::Arg *A, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index c696ac204bf93..e318a5965564f 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1636,6 +1636,14 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared); }); + if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) { + // Just validate the args. The frontend will parse these again and actually + // use them. To avoid emitting warnings multiple times we surpress warnings + // here but not in the frontend. + (void)parseSanitizerRecoverArgValues(A, OI.SelectedSanitizers, Diags, + /*emitWarnings=*/false); + } + if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) { // Check that the sanitizer coverage flags are supported if supplied. diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 1dd32b67bd156..4d08cb52d8899 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -214,6 +214,7 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping); inputArgs.AddLastArg(arguments, options::OPT_warnings_as_errors); inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ); + inputArgs.AddLastArg(arguments, options::OPT_sanitize_recover_EQ); inputArgs.AddLastArg(arguments, options::OPT_sanitize_coverage_EQ); inputArgs.AddLastArg(arguments, options::OPT_static); inputArgs.AddLastArg(arguments, options::OPT_swift_version); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index bd7e22b9392f9..fa5a1d6f4f9a1 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -877,6 +877,12 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, IRGenOpts.Sanitizers = Opts.Sanitizers; } + if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) { + IRGenOpts.SanitizersWithRecoveryInstrumentation = + parseSanitizerRecoverArgValues(A, Opts.Sanitizers, Diags, + /*emitWarnings=*/true); + } + if (auto A = Args.getLastArg(OPT_enable_verify_exclusivity, OPT_disable_verify_exclusivity)) { Opts.VerifyExclusivity diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index a7f3fc0643fbe..b81e05c509769 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -126,8 +126,14 @@ static void addSwiftMergeFunctionsPass(const PassManagerBuilder &Builder, static void addAddressSanitizerPasses(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { - PM.add(createAddressSanitizerFunctionPass()); - PM.add(createModuleAddressSanitizerLegacyPassPass()); + auto &BuilderWrapper = + static_cast(Builder); + auto recover = + bool(BuilderWrapper.IRGOpts.SanitizersWithRecoveryInstrumentation & + SanitizerKind::Address); + PM.add(createAddressSanitizerFunctionPass(/*CompileKernel=*/false, recover)); + PM.add(createModuleAddressSanitizerLegacyPassPass(/*CompileKernel=*/false, + recover)); } static void addThreadSanitizerPass(const PassManagerBuilder &Builder, diff --git a/lib/Option/SanitizerOptions.cpp b/lib/Option/SanitizerOptions.cpp index 781427f004677..45f11d04ec257 100644 --- a/lib/Option/SanitizerOptions.cpp +++ b/lib/Option/SanitizerOptions.cpp @@ -185,6 +185,49 @@ OptionSet swift::parseSanitizerArgValues( return sanitizerSet; } +OptionSet swift::parseSanitizerRecoverArgValues( + const llvm::opt::Arg *A, const OptionSet &enabledSanitizers, + DiagnosticEngine &Diags, bool emitWarnings) { + OptionSet sanitizerRecoverSet; + + // Find the sanitizer kind. + for (const char *arg : A->getValues()) { + Optional optKind = parse(arg); + + // Unrecognized sanitizer option + if (!optKind.hasValue()) { + Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument, + A->getOption().getPrefixedName(), arg); + continue; + } + SanitizerKind kind = optKind.getValue(); + + // Only support ASan for now. + if (kind != SanitizerKind::Address) { + Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument, + A->getOption().getPrefixedName(), arg); + continue; + } + + // Check that the sanitizer is enabled. + if (!(enabledSanitizers & kind)) { + SmallString<128> b; + if (emitWarnings) { + Diags.diagnose(SourceLoc(), + diag::warning_option_requires_specific_sanitizer, + (A->getOption().getPrefixedName() + toStringRef(kind)) + .toStringRef(b), + toStringRef(kind)); + } + continue; + } + + sanitizerRecoverSet |= kind; + } + + return sanitizerRecoverSet; +} + std::string swift::getSanitizerList(const OptionSet &Set) { std::string list; #define SANITIZER(_, kind, name, file) \ diff --git a/test/Driver/sanitize_recover.swift b/test/Driver/sanitize_recover.swift new file mode 100644 index 0000000000000..8f81f0e19d421 --- /dev/null +++ b/test/Driver/sanitize_recover.swift @@ -0,0 +1,18 @@ +// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=foo %s 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_INVALID_ARG %s +// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=thread %s 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_UNSUPPORTED_ARG %s +// RUN: %swiftc_driver -v -sanitize-recover=address %s -o %t 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION %s +// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITH_RECOVER %s +// RUN: %swiftc_driver -driver-print-jobs -sanitize=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITHOUT_RECOVER --implicit-check-not='-sanitize-recover=address' %s + +// SAN_RECOVER_INVALID_ARG: unsupported argument 'foo' to option '-sanitize-recover=' +// SAN_RECOVER_UNSUPPORTED_ARG: unsupported argument 'thread' to option '-sanitize-recover=' + +// SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION: warning: option '-sanitize-recover=address' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer +// SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION-NOT: warning: option '-sanitize-recover=address' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer + +// ASAN_WITH_RECOVER: swift +// ASAN_WITH_RECOVER-DAG: -sanitize=address +// ASAN_WITH_RECOVER-DAG: -sanitize-recover=address + +// ASAN_WITHOUT_RECOVER: swift +// ASAN_WITHOUT_RECOVER: -sanitize=address diff --git a/test/IRGen/address_sanitizer_recover.swift b/test/IRGen/address_sanitizer_recover.swift new file mode 100644 index 0000000000000..259fbbb5b5da8 --- /dev/null +++ b/test/IRGen/address_sanitizer_recover.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-recover=address %s | %FileCheck %s -check-prefix=ASAN_RECOVER +// RUN: %target-swift-frontend -emit-ir -sanitize=address %s | %FileCheck %s -check-prefix=ASAN_NO_RECOVER +// RUN: %target-swift-frontend -emit-ir -sanitize-recover=address %s | %FileCheck %s -check-prefix=NO_ASAN_RECOVER + +// ASAN_RECOVER: declare void @__asan_loadN_noabort( +// ASAN_NO_RECOVER: declare void @__asan_loadN( + +let size:Int = 128; +let x = UnsafeMutablePointer.allocate(capacity: size) +x.initialize(repeating: 0, count: size) +x.advanced(by: 0).pointee = 5; +print("Read first element:\(x.advanced(by: 0).pointee)") +x.deallocate(); + +// There should be no ASan instrumentation in this case. +// NO_ASAN_RECOVER-NOT: declare void @__asan_load diff --git a/test/Sanitizers/asan_interface.h b/test/Sanitizers/asan_interface.h new file mode 100644 index 0000000000000..8eaf265b74e61 --- /dev/null +++ b/test/Sanitizers/asan_interface.h @@ -0,0 +1,4 @@ +// This file is a swift bridging header to ASan's interface. It exists so +// we don't need to worry about where the header lives and instead let Clang +// figure out where the header lives. +#include "sanitizer/asan_interface.h" diff --git a/test/Sanitizers/asan_recover.swift b/test/Sanitizers/asan_recover.swift new file mode 100644 index 0000000000000..4853001268ad1 --- /dev/null +++ b/test/Sanitizers/asan_recover.swift @@ -0,0 +1,98 @@ +// REQUIRES: executable_test +// REQUIRES: asan_runtime +// UNSUPPORTED: windows + +// Check with recovery instrumentation and runtime option to continue execution. +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -sanitize-recover=address -import-objc-header %S/asan_interface.h -emit-ir -o %t.asan_recover.ll +// RUN: %FileCheck -check-prefix=CHECK-IR -input-file=%t.asan_recover.ll %s +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -sanitize-recover=address -import-objc-header %S/asan_interface.h -o %t_asan_recover +// RUN: %target-codesign %t_asan_recover +// RUN: env %env-ASAN_OPTIONS=halt_on_error=0 %target-run %t_asan_recover > %t_asan_recover.stdout 2> %t_asan_recover.stderr +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDERR,CHECK-RECOVER-STDERR -input-file=%t_asan_recover.stderr %s +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDOUT,CHECK-RECOVER-STDOUT -input-file=%t_asan_recover.stdout %s + +// Check with recovery instrumentation but without runtime option to continue execution. +// RUN: not env %env-ASAN_OPTIONS=abort_on_error=0,halt_on_error=1 %target-run %t_asan_recover > %t_asan_no_runtime_recover.stdout 2> %t_asan_no_runtime_recover.stderr +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDERR -input-file=%t_asan_no_runtime_recover.stderr %s +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDOUT,CHECK-NO-RECOVER-STDOUT -input-file=%t_asan_no_runtime_recover.stdout %s + +// Check that without recovery instrumentation and runtime option to continue execution that error recovery does not happen. +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -import-objc-header %S/asan_interface.h -o %t_asan_no_recover +// RUN: %target-codesign %t_asan_no_recover +// RUN: not env %env-ASAN_OPTIONS=abort_on_error=0,halt_on_error=0 %target-run %t_asan_no_recover > %t_asan_no_recover.stdout 2> %t_asan_no_recover.stderr +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDERR -input-file=%t_asan_no_recover.stderr %s +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDOUT,CHECK-NO-RECOVER-STDOUT -input-file=%t_asan_no_recover.stdout %s + +// We need to test reads via instrumentation not via runtime so try to check +// for calls to unwanted interceptor/runtime functions. +// CHECK-IR-NOT: call {{.+}} @__asan_memcpy +// CHECK-IR-NOT: call {{.+}} @memcpy + +// FIXME: We need this so we can flush stdout but this won't +// work on other Platforms (e.g. Windows). +#if os(Linux) + import Glibc +#else + import Darwin.C +#endif + +// CHECK-COMMON-STDOUT: START +print("START") +fflush(stdout) + +let size:Int = 128; + +// In this test we need multiple issues to occur that ASan can detect. +// Allocating a buffer and artificially poisoning it seems like the best way to +// test this because there's no undefined behavior happening. Hopefully this +// means that the program behaviour after ASan catches an issue should be +// consistent. If we did something different like two use-after-free issues the +// behaviour could be very unpredicatable resulting in a flakey test. +var x = UnsafeMutablePointer.allocate(capacity: size) +x.initialize(repeating: 0, count: size) +__asan_poison_memory_region(UnsafeMutableRawPointer(x), size) + +// Perform accesses that ASan will catch. Note it is important here that +// the reads are performed **in** the instrumented code so that the +// instrumentation catches the access to poisoned memory. I tried doing: +// +// ``` +// var x = x.advanced(by: 0).pointee +// print(x) +// ``` +// +// However, this generated code that called into memcpy rather than performing +// a direct read which meant that ASan caught an issue via its interceptors +// rather than from instrumentation, which does not test the right thing here. +// +// Doing: +// +// ``` +// print("Read first element:\(x.advanced(by: 0).pointee)") +// ``` +// +// seems to do the right thing right now but this seems really fragile. + +// First error +// NOTE: Testing for stackframe `#0` should ensure that the poison read +// happened in instrumentation and not in an interceptor. +// CHECK-COMMON-STDERR: AddressSanitizer: use-after-poison +// CHECK-COMMON-STDERR: #0 0x{{.+}} in main {{.*}}asan_recover.swift:[[@LINE+1]] +print("Read first element:\(x.advanced(by: 0).pointee)") +fflush(stdout) +// CHECK-RECOVER-STDOUT: Read first element:0 + +// Second error +// CHECK-RECOVER-STDERR: AddressSanitizer: use-after-poison +// CHECK-RECOVER-STDERR: #0 0x{{.+}} in main {{.*}}asan_recover.swift:[[@LINE+1]] +print("Read second element:\(x.advanced(by: 1).pointee)") +fflush(stdout) +// CHECK-RECOVER-STDOUT: Read second element:0 + +__asan_unpoison_memory_region(UnsafeMutableRawPointer(x), size) + +x.deallocate(); +// CHECK-NO-RECOVER-STDOUT-NOT: DONE +// CHECK-RECOVER-STDOUT: DONE +print("DONE") +fflush(stdout) From 901ca16ecaa6a521fee2a0fc777ed1ad92b3825d Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 12 Nov 2019 11:48:05 -0800 Subject: [PATCH 117/283] [frontend] Fix obvious typo. Just found via inspection. --- lib/Frontend/CompilerInvocation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a91375574da1f..1baf724a86c6b 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -843,7 +843,7 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, // -Ounchecked might also set removal of runtime asserts (cond_fail). Opts.RemoveRuntimeAsserts |= Args.hasArg(OPT_RemoveRuntimeAsserts); - Opts.EnableARCOptimizations |= !Args.hasArg(OPT_disable_arc_opts); + Opts.EnableARCOptimizations &= !Args.hasArg(OPT_disable_arc_opts); Opts.EnableOSSAOptimizations &= !Args.hasArg(OPT_disable_ossa_opts); Opts.DisableSILPerfOptimizations |= Args.hasArg(OPT_disable_sil_perf_optzns); Opts.VerifyAll |= Args.hasArg(OPT_sil_verify_all); From 0f3dd52972e3e4975af94c3dbb97610ab9296e8d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 13:23:31 -0800 Subject: [PATCH 118/283] Use correct function type to prevent conflicting argument types --- lib/SILGen/SILGenApply.cpp | 21 ++++++++++----------- lib/SILGen/SILGenExpr.cpp | 4 +--- lib/SILGen/SILGenFunction.h | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 18d1ef321aba5..23ddd4592d2b5 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6068,26 +6068,25 @@ RValue SILGenFunction::emitDynamicSubscriptExpr(DynamicSubscriptExpr *e, } SmallVector SILGenFunction::emitKeyPathSubscriptOperands( - SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr, - CanSILFunctionType fnType) { + SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr) { Type interfaceType = subscript->getInterfaceType(); + CanFunctionType substFnType = + subs ? cast(interfaceType->castTo() + ->substGenericArgs(subs) + ->getCanonicalType()) + : cast(interfaceType->getCanonicalType()); + AbstractionPattern origFnType(substFnType); + auto fnType = getLoweredType(origFnType, substFnType).castTo(); SmallVector argValues; SmallVector delayedArgs; ArgEmitter emitter( *this, fnType.getPointer()->getRepresentation(), - /*yield*/ true, + /*yield*/ false, /*isForCoroutine*/ false, ClaimedParamsRef(fnType, fnType.getPointer()->getParameters()), argValues, delayedArgs, /*foreign error*/ None, ImportAsMemberStatus()); - - CanFunctionType substFnType = - subs ? cast(interfaceType->castTo() - ->substGenericArgs(subs) - ->getCanonicalType()) - : cast(interfaceType->getCanonicalType()); - - AbstractionPattern origFnType(substFnType); + auto prepared = prepareSubscriptIndices(subscript, subs, // Strategy doesn't matter diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index cf39e1cf832b5..ada5eba9ab35e 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3576,12 +3576,10 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { if (kind == KeyPathExpr::Component::Kind::Property) break; - auto subscriptFn = - loweredComponents.back().getComputedPropertyId().getFunction(); auto subscript = cast(decl); auto loweredArgs = SGF.emitKeyPathSubscriptOperands( subscript, component.getDeclRef().getSubstitutions(), - component.getIndexExpr(), subscriptFn->getLoweredFunctionType()); + component.getIndexExpr()); for (auto &arg : loweredArgs) { operands.push_back(arg.forward(SGF)); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 480d69cfc59be..a0fdcc3bb40d3 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -667,7 +667,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SmallVector emitKeyPathSubscriptOperands(SubscriptDecl *subscript, SubstitutionMap subs, - Expr *indexExpr, CanSILFunctionType fnType); + Expr *indexExpr); /// Convert a block to a native function with a thunk. ManagedValue emitBlockToFunc(SILLocation loc, From 08244697d11f760deea9f13aeba0d83b119ee3c2 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 13:24:49 -0800 Subject: [PATCH 119/283] Format properly --- lib/SILGen/SILGenApply.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 23ddd4592d2b5..fcb1c5e8721d7 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6076,7 +6076,8 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( ->getCanonicalType()) : cast(interfaceType->getCanonicalType()); AbstractionPattern origFnType(substFnType); - auto fnType = getLoweredType(origFnType, substFnType).castTo(); + auto fnType = + getLoweredType(origFnType, substFnType).castTo(); SmallVector argValues; SmallVector delayedArgs; ArgEmitter emitter( @@ -6086,7 +6087,7 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( ClaimedParamsRef(fnType, fnType.getPointer()->getParameters()), argValues, delayedArgs, /*foreign error*/ None, ImportAsMemberStatus()); - + auto prepared = prepareSubscriptIndices(subscript, subs, // Strategy doesn't matter From 853dfbefa0e6c539b300497a08e542bbca314d9b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 13:34:01 -0800 Subject: [PATCH 120/283] Copy labels and conformances --- lib/Sema/CSApply.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 24d33e1599be7..ac319578edb25 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4615,12 +4615,10 @@ namespace { conformances.push_back(hashableConformance); } - auto component = - KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr( - ref, newIndexExpr, newLabels, resolvedTy, componentLoc, {}); - - component.setSubscriptIndexHashableConformances(conformances); - return component; + return KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr( + ref, newIndexExpr, cs.getASTContext().AllocateCopy(newLabels), + resolvedTy, componentLoc, + cs.getASTContext().AllocateCopy(conformances)); } Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) { From 5e1e9075b2f4e5323cae72d5981b9981c2c35d2b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 13:35:44 -0800 Subject: [PATCH 121/283] Fix spacing --- lib/SILGen/SILGenApply.cpp | 1 + lib/SILGen/SILGenExpr.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index fcb1c5e8721d7..2a004c70d69be 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6078,6 +6078,7 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( AbstractionPattern origFnType(substFnType); auto fnType = getLoweredType(origFnType, substFnType).castTo(); + SmallVector argValues; SmallVector delayedArgs; ArgEmitter emitter( diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index ada5eba9ab35e..7b4038d406e26 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3638,6 +3638,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { llvm_unreachable("not resolved"); } } + StringRef objcString; if (auto objcExpr = dyn_cast_or_null (E->getObjCStringLiteralExpr())) From 800feda1d77894a18c3d25a56c2ac65e25c41006 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 13:35:44 -0800 Subject: [PATCH 122/283] Fix spacing --- lib/SILGen/SILGenApply.cpp | 1 + lib/SILGen/SILGenExpr.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index fcb1c5e8721d7..2a004c70d69be 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6078,6 +6078,7 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( AbstractionPattern origFnType(substFnType); auto fnType = getLoweredType(origFnType, substFnType).castTo(); + SmallVector argValues; SmallVector delayedArgs; ArgEmitter emitter( diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index ada5eba9ab35e..7b4038d406e26 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3638,6 +3638,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { llvm_unreachable("not resolved"); } } + StringRef objcString; if (auto objcExpr = dyn_cast_or_null (E->getObjCStringLiteralExpr())) From 775c0625c68bc93ff93e08a54f2ab953c229f23b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 13:36:52 -0800 Subject: [PATCH 123/283] Fix spacing (again) --- lib/SILGen/SILGenExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 7b4038d406e26..8b94e44bd4469 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3638,7 +3638,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { llvm_unreachable("not resolved"); } } - + StringRef objcString; if (auto objcExpr = dyn_cast_or_null (E->getObjCStringLiteralExpr())) From 2c7e34881441071d9e33db86007ae9d09b47ffc4 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 7 Nov 2019 14:03:41 -0800 Subject: [PATCH 124/283] EscapeAnalysis: rework graph update and merge algorithms The two major take aways from this patch are: (1) Impose graph structure and reduce superfluous nodes and edges. Incrementally make the connection graph and the APIs used to construct it more structured. _This allows node properties based on the SILValue to be reliably added to nodes_ Although that was the initial motiviation, there are other benefits. Non-content nodes now have verifiable SILValues. Content nodes now have meaningful SILValues even though they can't be guaranteed due to merging. As a result it is *much* easier to debug the graph and correlate it with the SIL. Rather than a web of connection graph nodes with no identity and edges that don't correspond to anything in SIL, the graph nodes now have value number that correspond to the instruction used to dereference the node. The edges also exhibit structure now. A pointsTo edge now (in practice) always corresponds to a real pointer deference in the SIL. Doing this required more than just adding some helpers, it was also necessary to rewrite the graph merge and update algorithms. (2) Split up underlying functionality into more explicit steps Breaks apart the most complex parts of the graph algorithms into small self-contained, self-checked steps. The purpose of each step is clear and it's possible to reason about correctness from basic invariants. Each merge step can now run full graph verification. This was also done to move toward an invariant that the graph is never mutated during a query. But to finish that goal, we need to add a use-point query. With that, there will be no node creation, use point propagation, new defer edges, etc. after graph building. At the very least, this will make it sane to debug the output of the analysis. --- Here is a change-by-change description in diff order: Replace `updatePointsTo` with `initializePointsTo` and `mergePointsTo`. Merging is very simple on its own. Initialization requires some extra consideration for graph invariants. This separation makes it possible to write stong asserts and to independently reason about the correctness of each step based on static invariants. Replace `getContentNode` with `createContentNode`, and add two higher level APIs `createMergedContent`, and `getFieldContent`. This makes explicit the important cases of merging nodes and creating a special nodes for class fields. This slightly simplifies adding properties to content nodes and helps understand the structure of the graph. Factor out an `escapeContentsOf` helper for use elsewhere... Add a `getValueContent` helper. This is where we can tie the properties of content nodes to the address values that are used to address that content. This now also ensures that a Value node's value field is consistent with all SILValues that map to it. Add -escapes-internal-verify to check that the graph is in a valid state after every merge or update step. This verification drove the partial rewrite of mergeAllScheduledNodes. ConnectionGraph::defer implementation: explictly handle the three possible cases of pointsTo initialization or pointsTo merging at the top level, so that those underlying implementations do not need to dynamically handle weirdly different scenarios. ConnectionGraph::initializePointsTo implementation: this simplified implementation is possible by relying on invariants that can be checked at each merge/update step. The major functional difference is that it avoids creating unnecessary pointsTo edges. The previous implementation often created pointsTo edges when adding defer edges just to be conservative. Fixing this saved my sanity during debugging because the pointsTo edges now always correspond to a SIL operations that dereference the pointer. I'm also arguing without evidence that this should be much more efficient. ConnectionGraph::mergeAllScheduledNodes implementation: Add verification to each step so that we can prove the other utilities that are used while merging aren't making incorrect assumptions about the graph state. Remove checks for merged nodes now that the graph is consistently valid. Also remove a loop at the end that didn't seem to do anything. The diff is impossible to review, but the idea is basically the same. As long as it's still possible to scan through the steps in the new code without getting totally lost, then the goal was achieved. ConnectionGraph::mergePointsTo: This is extremely simple now. In all the places where we used to call updatePointsTo, and now call mergePointsTo, it's a lot easier for someone debugging the code to reason about what could possibly happen at that point. `createMergedContent` is a placeholder for transferring node properties. The `getFieldContent` helper may seem silly, but I find it helpful to see all the important ways that content can be created in one place next to the createContentNode, and I like the way that the creation of the special "field content" node is more explicit in the source. ConnectionGraph::mergeFrom implementation: this is only a minor cleanup to remove some control flow nesting and use the CGNodeWorklist abstraction. In AnalyzeInstruction, add EscapeAnalysis::getValueContent helper. It eliminates an extra step of going through the value node to get at its content node. This is where we can derive content node properties from the SILValue that dereferences the content. We can update the content node's associated value 'V' if it's useful. It's also a place to put assertions specific to the first level of content. In AnalyzeInstruction, Array semantic calls: add support for getValueContent so we can derive node properties. This is also nice because it's explicit about which nodes are value content vs. field content. In AnalyzeInstruction, cleanup Release handling: use the explicit APIs: getValueContent, getFieldContent, and escapeContentsOf. In AnalyzeInstruction, assert that load-like things can't produce addresses. In AnalyzeInstruction, add comments to clarify object projection handling. In AnalyzeInstruction, add comments to explain store handling. In AnalyzeInstruction, drop the assumption that all partial applies hold pointers. In AnalyzeInstruction, handle aggregates differently so that Value nodes are always consistent with their SILValue and can be verified. Aggregates nodes are still coalesced if they only have a single pointer-type subelement. If we arbitrarily coalesced an aggregate with just one of its subelements then there would be no consistent way to identify the value that corresponds to a connection graph node. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 113 ++- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 887 +++++++++++------- test/SILOptimizer/escape_analysis.sil | 168 ++-- 3 files changed, 700 insertions(+), 468 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 732cb08754df5..6452460206189 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -403,17 +403,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Checks an invariant of the connection graph: The points-to nodes of /// the defer-successors must match with the points-to of this node. - bool matchPointToOfDefers() const { - for (CGNode *Def : defersTo) { - if (pointsTo != Def->pointsTo) - return false; - } - /// A defer-path in the graph must not end without the specified points-to - /// node. - if (pointsTo && !pointsToIsEdge && defersTo.empty()) - return false; - return true; - } + bool matchPointToOfDefers(bool allowMerge = false) const; friend class CGNodeMap; friend class ConnectionGraph; @@ -480,7 +470,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { llvm_unreachable("Unhandled EscapeState in switch."); } - /// Returns the content node if of this node if it exists in the graph. + /// Returns the content node of this node if it exists in the graph. CGNode *getContentNodeOrNull() const { return pointsTo; } @@ -603,14 +593,25 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } + /// Initialize the 'pointsTo' fields of all nodes in the defer web of \p + /// initialiNode. + /// + /// If \p createEdge is true, a proper pointsTo edge will be created from \p + /// initialNode to \p pointsTo. + void initializePointsTo(CGNode *initialNode, CGNode *newPointsTo, + bool createEdge = false); + + void initializePointsToEdge(CGNode *initialNode, CGNode *newPointsTo) { + initializePointsTo(initialNode, newPointsTo, true); + } + /// Merges all nodes which are added to the ToMerge list. void mergeAllScheduledNodes(); - /// Transitively updates pointsTo of all nodes in the defer-edge web, - /// starting at \p InitialNode. - /// If a node in the web already points to another content node, the other - /// content node is scheduled to be merged with \p pointsTo. - void updatePointsTo(CGNode *InitialNode, CGNode *pointsTo); + /// Transitively update pointsTo of all nodes in the defer-edge web, + /// reaching and reachable from \p initialNode. All nodes in this defer web + /// must already have an initialized `pointsTo`. + void mergePointsTo(CGNode *initialNode, CGNode *pointsTo); /// Utility function to clear the isInWorkList flags of all nodes in /// \p WorkList. @@ -627,12 +628,20 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Returns null, if V is not a "pointer". CGNode *getNode(ValueBase *V, bool createIfNeeded = true); - /// Gets or creates a content node to which \a AddrNode points to during - /// initial graph construction. This may not be called after defer edges - /// have been created. Doing so would break the invariant that all - /// non-content nodes ultimately have a pointsTo edge to a single content - /// node. - CGNode *getContentNode(CGNode *AddrNode); + /// Helper to create a content node and update the pointsTo graph. \p + /// addrNode will point to the new content node. The new content node is + /// directly initialized with the remaining function arguments. + CGNode *createContentNode(CGNode *addrNode, SILValue addrVal); + + /// Create a new content node based on an existing content node to support + /// graph merging. + /// + /// \p destAddrNode will point to to new content. The content's initial + /// state will be initialized based on the \p srcContent node. + CGNode *createMergedContent(CGNode *destAddrNode, CGNode *srcContent); + + /// Get a node represnting the field data within the given RC node. + CGNode *getFieldContent(CGNode *rcNode); /// Get or creates a pseudo node for the function return value. CGNode *getReturnNode() { @@ -679,31 +688,21 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return Idx; } - /// Specifies that the node's value escapes to global or unidentified - /// memory. - void setEscapesGlobal(CGNode *Node) { - Node->mergeEscapeState(EscapeState::Global); - - // Make sure to have a content node. Otherwise we may end up not merging - // the global-escape state into a caller graph (only content nodes are - // merged). Either the node itself is a content node or we let the node - // point to one. - if (Node->Type != NodeType::Content) - getContentNode(Node); + void escapeContentsOf(CGNode *Node) { + CGNode *escapedContent = Node->getContentNodeOrNull(); + if (!escapedContent) { + escapedContent = createContentNode(Node, Node->V); + } + escapedContent->markEscaping(); } /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). /// Returns the \p From node or its merge-target in case \p From was merged /// during adding the edge. - /// The \p EdgeAdded is set to true if there was no defer-edge between - /// \p From and \p To, yet. - CGNode *defer(CGNode *From, CGNode *To, bool &EdgeAdded) { - if (addDeferEdge(From, To)) - EdgeAdded = true; - mergeAllScheduledNodes(); - return From->getMergeTarget(); - } + /// \p Changed is set to true if a defer edge was added or any nodes were + /// merged. + CGNode *defer(CGNode *From, CGNode *To, bool &Changed); /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). @@ -802,7 +801,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void dumpCG() const; /// Checks if the graph is OK. - void verify() const; + void verify(bool allowMerge = false) const; /// Just verifies the graph structure. This function can also be called /// during the graph is modified, e.g. in mergeAllScheduledNodes(). @@ -889,9 +888,22 @@ class EscapeAnalysis : public BottomUpIPAnalysis { SILValue getPointerRoot(SILValue value) const; /// If \p pointer is a pointer, set it to global escaping. - void setEscapesGlobal(ConnectionGraph *ConGraph, ValueBase *pointer) { - if (CGNode *Node = ConGraph->getNode(pointer)) - ConGraph->setEscapesGlobal(Node); + void setEscapesGlobal(ConnectionGraph *conGraph, ValueBase *pointer) { + CGNode *Node = conGraph->getNode(pointer); + if (!Node) + return; + + if (Node->isContent()) { + Node->markEscaping(); + return; + } + Node->mergeEscapeState(EscapeState::Global); + + // Make sure to have a content node. Otherwise we may end up not merging + // the global-escape state into a caller graph (only content nodes are + // merged). Either the node itself is a content node or we let the node + // point to one. + conGraph->escapeContentsOf(Node); } /// Gets or creates FunctionEffects for \p F. @@ -902,6 +914,15 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return FInfo; } + /// Get or create the node representing the memory pointed to by \p + /// addrVal. If \p addrVal is an address, then return the content node for the + /// variable's memory. Otherwise, \p addrVal may contain a reference, so + /// return the content node for the referenced heap object. + /// + /// Note that \p addrVal cannot be an address within a heap object, such as + /// an address from ref_element_addr or project_box. + CGNode *getValueContent(ConnectionGraph *conGraph, SILValue addrVal); + /// Build a connection graph for reach callee from the callee list. bool buildConnectionGraphForCallees(SILInstruction *Caller, CalleeList Callees, diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index fbe37439c5eb7..532f42c0498b7 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -25,6 +25,11 @@ using namespace swift; using CGNode = EscapeAnalysis::CGNode; +static llvm::cl::opt EnableInternalVerify( + "escapes-internal-verify", + llvm::cl::desc("Enable internal verification of escape analysis"), + llvm::cl::init(false)); + // Returns true if \p Ty recursively contains a reference. If \p mustBeRef is // true, only return true if the type is guaranteed to hold a reference. If \p // mustBeRef is false, only return false if the type is guaranteed not to hold a @@ -188,6 +193,23 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const { return SILValue(); return TEI->getOperand(); } + case ValueKind::StructInst: + case ValueKind::TupleInst: + case ValueKind::EnumInst: { + // Allow a single-operand aggregate to share its operand's node. + auto *SVI = cast(value); + SILValue pointerOperand; + for (SILValue opV : SVI->getOperandValues()) { + if (!isPointer(opV)) + continue; + + if (pointerOperand) + return SILValue(); + + pointerOperand = opV; + } + return pointerOperand; + } default: return SILValue(); } @@ -386,43 +408,180 @@ EscapeAnalysis::ConnectionGraph::getNode(ValueBase *V, bool createIfNeeded) { return Node->getMergeTarget(); } -EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph::getContentNode( - CGNode *AddrNode) { - // Do we already have a content node (which is not necessarily an immediate - // successor of AddrNode)? - if (AddrNode->pointsTo) - return AddrNode->pointsTo; +CGNode *EscapeAnalysis::ConnectionGraph::defer(CGNode *From, CGNode *To, + bool &Changed) { + if (!From->canAddDeferred(To)) + return From; - CGNode *Node = allocNode(AddrNode->V, NodeType::Content); - updatePointsTo(AddrNode, Node); - assert(ToMerge.empty() && - "Initially setting pointsTo should not require any node merges"); - return Node; + CGNode *FromPointsTo = From->pointsTo; + CGNode *ToPointsTo = To->pointsTo; + // If necessary, merge nodes while the graph is still in a valid state. + if (FromPointsTo && ToPointsTo && FromPointsTo != ToPointsTo) { + // We are adding an edge between two pointers which point to different + // content nodes. This will require merging the content nodes (and maybe + // other content nodes as well), because of the graph invariance 4). + // + // Once the pointee's are merged, the defer edge can be added without + // creating an inconsistency. + scheduleToMerge(FromPointsTo, ToPointsTo); + mergeAllScheduledNodes(); + Changed = true; + } + // 'From' and 'To' may have been merged, so addDeferred may no longer succeed. + if (From->getMergeTarget()->addDeferred(To->getMergeTarget())) + Changed = true; + + // If pointsTo on either side of the defer was uninitialized, initialize that + // side of the defer web. Do this after adding the new edge to avoid creating + // useless pointsTo edges. + if (!FromPointsTo && ToPointsTo) + initializePointsTo(From, ToPointsTo); + else if (FromPointsTo && !ToPointsTo) + initializePointsTo(To, FromPointsTo); + + return From->getMergeTarget(); } -bool EscapeAnalysis::ConnectionGraph::addDeferEdge(CGNode *From, CGNode *To) { - if (!From->addDeferred(To)) - return false; +// Precondition: The pointsTo fields of all nodes in initializeNode's defer web +// are either uninitialized or already initialized to newPointsTo. +void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode, + CGNode *newPointsTo, + bool createEdge) { + // Track nodes that require pointsTo edges. + llvm::SmallVector pointsToEdges; + + // Step 1: Visit each node that reaches or is reachable via defer edges until + // reaching a node with the newPointsTo or with a proper pointsTo edge. + + // A worklist to gather updated nodes in the defer web. + CGNodeWorklist updatedNodes(this); + updatedNodes.push(initialNode); + if (createEdge) + pointsToEdges.push_back(initialNode); + unsigned updateCount = 1; + assert(updateCount == updatedNodes.size()); + // Augment the worlist with the nodes that were reached via backward + // traversal. It's not as precise as DFS, but helps avoid redundant pointsTo + // edges in most cases. + llvm::SmallPtrSet backwardReachable; + + auto visitDeferTarget = [&](CGNode *node, bool isSuccessor) { + if (updatedNodes.contains(node)) + return true; - CGNode *FromPointsTo = From->pointsTo; - CGNode *ToPointsTo = To->pointsTo; - if (FromPointsTo != ToPointsTo) { - if (!ToPointsTo) { - updatePointsTo(To, FromPointsTo->getMergeTarget()); - assert(ToMerge.empty() && - "Initially setting pointsTo should not require any node merges"); - } else { - // We are adding an edge between two pointers which point to different - // content nodes. This will require to merge the content nodes (and maybe - // other content nodes as well), because of the graph invariance 4). - updatePointsTo(From, ToPointsTo->getMergeTarget()); + if (node->pointsTo) { + assert(node->pointsTo == newPointsTo); + // Since this node already had a pointsTo, it must reach a pointsTo + // edge. Stop traversing the defer-web here--this is complete becaused + // nodes are initialized one at a time, each time a new defer edge is + // created. If this were not complete, then the backward traversal below + // in Step 2 could reach uninitialized nodes not seen here in Step 1. + pointsToEdges.push_back(node); + return true; } - } - return true; + ++updateCount; + if (node->defersTo.empty()) { + // If this node is the end of a defer-edge path with no pointsTo + // edge. Create a "fake" pointsTo edge to maintain the graph invariant + // (this changes the structure of the graph but adding this edge has no + // effect on the process of merging nodes or creating new defer edges). + pointsToEdges.push_back(node); + } + updatedNodes.push(node); + if (!isSuccessor) + backwardReachable.insert(node); + + return true; + }; + // updatedNodes may grow during this loop. + unsigned nextUpdatedNodeIdx = 0; + for (; nextUpdatedNodeIdx < updatedNodes.size(); ++nextUpdatedNodeIdx) + updatedNodes[nextUpdatedNodeIdx]->visitDefers(visitDeferTarget); + // Reset this worklist so others can be used, but updateNode.nodeVector still + // holds all the nodes found by step 1. + updatedNodes.reset(); + + // Step 2: Update pointsTo fields by propagating backward from nodes that + // already have a pointsTo edge. + assert(nextUpdatedNodeIdx == updatedNodes.size()); + --nextUpdatedNodeIdx; + bool processBackwardReachable = false; + do { + while (!pointsToEdges.empty()) { + CGNode *edgeNode = pointsToEdges.pop_back_val(); + if (!edgeNode->pointsTo) { + edgeNode->setPointsToEdge(newPointsTo); + newPointsTo->mergeUsePoints(edgeNode); + assert(updateCount--); + } + // If edgeNode is already set to newPointsTo, it either was already + // up-to-date before calling initializePointsTo, or it was visited during + // a previous iteration of the backward traversal below. Rather than + // distinguish these cases, always retry backward traversal--it just won't + // revisit any edges in the later case. + backwardTraverse(edgeNode, [&](Predecessor pred) { + if (pred.getInt() != EdgeType::Defer) + return Traversal::Backtrack; + + CGNode *predNode = pred.getPointer(); + if (predNode->pointsTo) { + assert(predNode->pointsTo->getMergeTarget() == newPointsTo); + return Traversal::Backtrack; + } + predNode->pointsTo = newPointsTo; + newPointsTo->mergeUsePoints(predNode); + assert(updateCount--); + return Traversal::Follow; + }); + } + // For all nodes visited in step 1, if any node was not backward-reachable + // from a pointsTo edge, create an edge for it and restart traversal. + // + // First process all forward-reachable nodes in backward order, then process + // all backwardReachable nodes in forward order. + while (nextUpdatedNodeIdx != updatedNodes.size()) { + CGNode *node = updatedNodes[nextUpdatedNodeIdx]; + // When processBackwardReachable == true, the backwardReachable set is + // empty and all forward reachable nodes already have a pointsTo edge. + if (!backwardReachable.count(node)) { + if (!node->pointsTo) { + pointsToEdges.push_back(node); + break; + } + } + if (processBackwardReachable) { + ++nextUpdatedNodeIdx; + continue; + } + if (nextUpdatedNodeIdx > 0) { + --nextUpdatedNodeIdx; + continue; + } + // reverse direction + backwardReachable.clear(); + processBackwardReachable = true; + } + // This outer loop is exceedingly unlikely to execute more than twice. + } while (!pointsToEdges.empty()); + assert(updateCount == 0); } void EscapeAnalysis::ConnectionGraph::mergeAllScheduledNodes() { + // Each merge step is self contained and verifiable, with one exception. When + // merging a node that points to itself with a node points to another node, + // multiple merge steps are necessary to make the defer web consistent. + // Example: + // NodeA pointsTo-> From + // From defersTo-> NodeA (an indirect self-cycle) + // To pointsTo-> NodeB + // Merged: + // NodeA pointsTo-> To + // To defersTo-> NodeA (To *should* pointTo itself) + // To pointsTo-> NodeB (but still has a pointsTo edge to NodeB) while (!ToMerge.empty()) { + if (EnableInternalVerify) + verify(/*allowMerge=*/true); + CGNode *From = ToMerge.pop_back_val(); CGNode *To = From->getMergeTarget(); assert(To != From && "Node scheduled to merge but no merge target set"); @@ -430,204 +589,178 @@ void EscapeAnalysis::ConnectionGraph::mergeAllScheduledNodes() { assert(From->Type == NodeType::Content && "Can only merge content nodes"); assert(To->Type == NodeType::Content && "Can only merge content nodes"); - // Unlink the predecessors and redirect the incoming pointsTo edge. - // Note: we don't redirect the defer-edges because we don't want to trigger - // updatePointsTo (which is called by addDeferEdge) right now. + // Redirect the incoming pointsTo edge and unlink the defer predecessors. + // + // Don't redirect the defer-edges because it may trigger mergePointsTo() or + // initializePointsTo(). By ensuring that 'From' is unreachable first, the + // graph appears consistent during those operations. for (Predecessor Pred : From->Preds) { CGNode *PredNode = Pred.getPointer(); if (Pred.getInt() == EdgeType::PointsTo) { - assert(PredNode->getPointsToEdge() == From && - "Incoming pointsTo edge not set in predecessor"); + assert(PredNode->getPointsToEdge() == From + && "Incoming pointsTo edge not set in predecessor"); if (PredNode != From) PredNode->setPointsToEdge(To); } else { assert(PredNode != From); auto Iter = PredNode->findDeferred(From); - assert(Iter != PredNode->defersTo.end() && - "Incoming defer-edge not found in predecessor's defer list"); + assert(Iter != PredNode->defersTo.end() + && "Incoming defer-edge not found in predecessor's defer list"); PredNode->defersTo.erase(Iter); } } - // Unlink and redirect the outgoing pointsTo edge. - if (CGNode *PT = From->getPointsToEdge()) { - if (PT != From) { - PT->removeFromPreds(Predecessor(From, EdgeType::PointsTo)); - } else { - PT = To; - } - if (CGNode *ExistingPT = To->getPointsToEdge()) { - // The To node already has an outgoing pointsTo edge, so the only thing - // we can do is to merge both content nodes. - scheduleToMerge(ExistingPT, PT); - } else { - To->setPointsToEdge(PT); - } - } // Unlink the outgoing defer edges. for (CGNode *Defers : From->defersTo) { assert(Defers != From && "defer edge may not form a self-cycle"); Defers->removeFromPreds(Predecessor(From, EdgeType::Defer)); } - // Redirect the incoming defer edges. This may trigger other node merges. - // Note that the Pred iterator may be invalidated (because we may add - // edges in the loop). So we don't do: for (Pred : From->Preds) {...} - for (unsigned PredIdx = 0; PredIdx < From->Preds.size(); ++PredIdx) { - CGNode *PredNode = From->Preds[PredIdx].getPointer(); - if (From->Preds[PredIdx].getInt() == EdgeType::Defer) { - assert(PredNode != From && "defer edge may not form a self-cycle"); - addDeferEdge(PredNode, To); - } - } - // Redirect the outgoing defer edges, which may also trigger other node - // merges. - for (CGNode *Defers : From->defersTo) { - addDeferEdge(To, Defers); - } - // There is no point in updating the pointsTo if the To node will be - // merged to another node eventually. - if (!To->mergeTo) { - // Ensure that graph invariance 4) is kept. At this point there may be still - // some violations because of the new adjacent edges of the To node. - for (unsigned PredIdx = 0; PredIdx < To->Preds.size(); ++PredIdx) { - if (To->Preds[PredIdx].getInt() == EdgeType::PointsTo) { - CGNode *PredNode = To->Preds[PredIdx].getPointer(); - for (unsigned PPIdx = 0; PPIdx < PredNode->Preds.size(); ++PPIdx) { - if (PredNode->Preds[PPIdx].getInt() == EdgeType::Defer) - updatePointsTo(PredNode->Preds[PPIdx].getPointer(), To); - } - for (CGNode *Def : PredNode->defersTo) { - updatePointsTo(Def, To); - } - } - } - if (CGNode *ToPT = To->getPointsToEdge()) { - ToPT = ToPT->getMergeTarget(); - for (CGNode *ToDef : To->defersTo) { - updatePointsTo(ToDef, ToPT); - assert(!ToPT->mergeTo); - } - for (unsigned PredIdx = 0; PredIdx < To->Preds.size(); ++PredIdx) { - if (To->Preds[PredIdx].getInt() == EdgeType::Defer) - updatePointsTo(To->Preds[PredIdx].getPointer(), ToPT); + // Handle self-cycles on From by creating a self-cycle at To. + auto redirectPointsTo = [&](CGNode *pointsTo) { + return (pointsTo == From) ? To : pointsTo; + }; + // Redirect the outgoing From -> pointsTo edge. + if (From->pointsToIsEdge) { + From->pointsTo->removeFromPreds(Predecessor(From, EdgeType::PointsTo)); + if (To->pointsToIsEdge) { + // If 'To' had a pointsTo edge to 'From', then it was redirected above. + // Otherwise FromPT and ToPT will be merged below; nothing to do here. + assert(To->pointsTo != From); + } else { + // If 'To' has no pointsTo at all, initialize its defer web. + if (!To->pointsTo) { + initializePointsToEdge(To, redirectPointsTo(From->pointsTo)); + } else { + // Upgrade 'To's pointsTo to an edge to preserve the fact that 'From' + // had a pointsTo edge. + To->pointsToIsEdge = true; + To->pointsTo = redirectPointsTo(To->pointsTo); + To->pointsTo->Preds.push_back(Predecessor(To, EdgeType::PointsTo)); } } - To->mergeEscapeState(From->State); } + // Merge 'From->pointsTo' and 'To->pointsTo' if needed, regardless of + // whether either is a proper edge. Merging may be needed because other + // nodes may have points-to edges to From->PointsTo that won't be visited + // when updating 'From's defer web. + // + // If To doesn't already have a points-to, it will simply be initialized + // when updating the merged defer web below. + if (CGNode *toPT = To->pointsTo) { + // If 'To' already points to 'From', then it will already point to 'From's + // pointTo after merging. An additional merge would be too conservative. + if (From->pointsTo && toPT != From) + scheduleToMerge(redirectPointsTo(From->pointsTo), toPT); + } + // Redirect adjacent defer edges, and immediately update all points-to + // fields in the defer web. + // + // Calling initializePointsTo may create new pointsTo edges from nodes in + // the defer-web. It is unsafe to mutate or query the graph in its currently + // inconsistent state. However, this particular case is safe because: + // - The graph is only locally inconsistent w.r.t. nodes still connected to + // 'From' via defer edges. + // - 'From' itself is no longer reachable via graph edges (it may only be + // referenced in points-to fields which haven't all been updated). + // - Calling initializePointsTo on one from 'From's deferred nodes implies + // that all nodes in 'From's defer web had a null pointsTo. + // - 'To's defer web remains consistent each time a new defer edge is + // added below. Any of 'To's existing deferred nodes either still need to + // be initialized or have already been initialized to the same pointsTo. + // + // Start by updating 'To's own pointsTo field. + if (To->pointsTo == From) + mergePointsTo(To, To); + + auto mergeDeferPointsTo = [&](CGNode *deferred, bool isSuccessor) { + assert(From != deferred && "defer edge may not form a self-cycle"); + if (To == deferred) + return true; + + // In case 'deferred' points to 'From', update its pointsTo before + // exposing it to 'To's defer web. + if (deferred->pointsTo == From) + mergePointsTo(deferred, To); + + if (isSuccessor) + To->addDeferred(deferred); + else + deferred->addDeferred(To); + + if (deferred->pointsTo && To->pointsTo) + mergePointsTo(deferred, To->pointsTo); + else if (deferred->pointsTo) + initializePointsTo(To, deferred->pointsTo); + else if (To->pointsTo) + initializePointsTo(deferred, To->pointsTo); + + return true; + }; + // Redirect the adjacent defer edges. + From->visitDefers(mergeDeferPointsTo); + + // Update the web of nodes that originally pointed to 'From' via 'From's old + // pointsTo predecessors (which are now attached to 'To'). + for (unsigned PredIdx = 0; PredIdx < To->Preds.size(); ++PredIdx) { + auto predEdge = To->Preds[PredIdx]; + if (predEdge.getInt() != EdgeType::PointsTo) + continue; + predEdge.getPointer()->visitDefers( + [&](CGNode *deferred, bool /*isSucc*/) { + mergePointsTo(deferred, To); + return true; + }); + } + To->mergeEscapeState(From->State); + // Cleanup the merged node. From->isMerged = true; From->Preds.clear(); From->defersTo.clear(); From->pointsTo = nullptr; } + if (EnableInternalVerify) + verify(/*allowMerge=*/true); } -void EscapeAnalysis::ConnectionGraph:: -updatePointsTo(CGNode *InitialNode, CGNode *pointsTo) { - // Visit all nodes in the defer web, which don't have the right pointsTo set. - assert(!pointsTo->mergeTo); - llvm::SmallVector WorkList; - WorkList.push_back(InitialNode); - InitialNode->isInWorkList = true; - bool isInitialSet = false; - for (unsigned Idx = 0; Idx < WorkList.size(); ++Idx) { - auto *Node = WorkList[Idx]; - if (Node->pointsTo == pointsTo) - continue; - - if (Node->pointsTo) { - // Mismatching: we need to merge! - scheduleToMerge(Node->pointsTo, pointsTo); - } else { - isInitialSet = true; - } +// As a result of a merge, update the pointsTo field of initialNode and +// everything in its defer web to newPointsTo. +// +// This may modify the graph by redirecting a pointsTo edges. +void EscapeAnalysis::ConnectionGraph::mergePointsTo(CGNode *initialNode, + CGNode *newPointsTo) { + CGNode *oldPointsTo = initialNode->pointsTo; + assert(oldPointsTo && "merging content should not initialize any pointsTo"); + if (oldPointsTo == newPointsTo) + return; - // If the node already has a pointsTo _edge_ we don't change it (we don't - // want to change the structure of the graph at this point). - if (!Node->pointsToIsEdge) { - if (Node->defersTo.empty()) { - // This node is the end of a defer-edge path with no pointsTo connected. - // We create an edge to pointsTo (agreed, this changes the structure of - // the graph but adding this edge is harmless). - Node->setPointsToEdge(pointsTo); - } else { - Node->pointsTo = pointsTo; - } - // Update use-points if the use-point information is already calculated. - pointsTo->mergeUsePoints(Node); - } + CGNodeWorklist updateNodes(this); + auto updatePointsTo = [&](CGNode *node) { + if (node->pointsTo == newPointsTo) + return; + // If the original graph was: 'node->From->To->newPointsTo' or + // 'node->From->From', then node is already be updated to point to + // 'To' and 'To' must be merged with newPointsTo. We must still update + // pointsTo so that all nodes in the defer web have the same pointsTo. + assert(node->pointsTo == oldPointsTo + || node->pointsTo->getMergeTarget() == newPointsTo); + if (node->pointsToIsEdge) { + node->pointsTo->removeFromPreds(Predecessor(node, EdgeType::PointsTo)); + node->setPointsToEdge(newPointsTo); + } else + node->pointsTo = newPointsTo; + updateNodes.push(node); + }; + updatePointsTo(initialNode); - // Add all adjacent nodes to the WorkList. - for (auto *Deferred : Node->defersTo) { - if (!Deferred->isInWorkList) { - WorkList.push_back(Deferred); - Deferred->isInWorkList = true; - } - } - for (Predecessor Pred : Node->Preds) { - if (Pred.getInt() == EdgeType::Defer) { - CGNode *PredNode = Pred.getPointer(); - if (!PredNode->isInWorkList) { - WorkList.push_back(PredNode); - PredNode->isInWorkList = true; - } - } - } - } - if (isInitialSet) { - // Here we handle a special case: all defer-edge paths must eventually end - // in a points-to edge to pointsTo. We ensure this by setting the edge on - // nodes which have no defer-successors (see above). But this does not cover - // the case where there is a terminating cycle in the defer-edge path, - // e.g. A -> B -> C -> B - // We find all nodes which don't reach a points-to edge and add additional - // points-to edges to fix that. - llvm::SmallVector PotentiallyInCycles; - - // Keep all nodes with a points-to edge in the WorkList and remove all other - // nodes. - unsigned InsertionPoint = 0; - for (CGNode *Node : WorkList) { - if (Node->pointsToIsEdge) { - WorkList[InsertionPoint++] = Node; - } else { - Node->isInWorkList = false; - PotentiallyInCycles.push_back(Node); - } - } - WorkList.set_size(InsertionPoint); - unsigned Idx = 0; - while (!PotentiallyInCycles.empty()) { - - // Propagate the "reaches-a-points-to-edge" backwards in the defer-edge - // sub-graph by adding those nodes to the WorkList. - while (Idx < WorkList.size()) { - auto *Node = WorkList[Idx++]; - for (Predecessor Pred : Node->Preds) { - if (Pred.getInt() == EdgeType::Defer) { - CGNode *PredNode = Pred.getPointer(); - if (!PredNode->isInWorkList) { - WorkList.push_back(PredNode); - PredNode->isInWorkList = true; - } - } - } - } - // Check if we still have some nodes which don't reach a points-to edge, - // i.e. points not yet in the WorkList. - while (!PotentiallyInCycles.empty()) { - auto *Node = PotentiallyInCycles.pop_back_val(); - if (!Node->isInWorkList) { - // We create a points-to edge for the first node which doesn't reach - // a points-to edge yet. - Node->setPointsToEdge(pointsTo); - WorkList.push_back(Node); - Node->isInWorkList = true; - break; - } - } - } - } - clearWorkListFlags(WorkList); + // Visit each node that reaches or is reachable via defer edges until reaching + // a node with the newPointsTo. + auto visitDeferTarget = [&](CGNode *node, bool /*isSuccessor*/) { + if (!updateNodes.contains(node)) + updatePointsTo(node); + return true; + }; + for (unsigned Idx = 0; Idx < updateNodes.size(); ++Idx) + updateNodes[Idx]->visitDefers(visitDeferTarget); } void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { @@ -708,6 +841,40 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { } while (Changed); } +CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(CGNode *addrNode, + SILValue addrVal) { + CGNode *newContent = allocNode(addrVal, NodeType::Content); + initializePointsToEdge(addrNode, newContent); + assert(ToMerge.empty() + && "Initially setting pointsTo should not require any node merges"); + return newContent; +} + +// Create a content node for merging based on an address node in the destination +// graph and a content node in the source graph. +CGNode * +EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, + CGNode *srcContent) { + // destAddrNode may itself be a content node, so its value may be null. Since + // we don't have the original pointer value, build a new content node based + // on the source content. + // + // TODO: node properties will come from `srcContent` here... + return createContentNode(destAddrNode, destAddrNode->V); +} + +// Get a node representing the field data within the given reference-counted +// node. The caller has already determined that rcNode represents the head of a +// heap object rather than field content or the address of a local variable or +// argument. +CGNode *EscapeAnalysis::ConnectionGraph::getFieldContent(CGNode *rcNode) { + assert(rcNode->isContent()); + if (rcNode->pointsTo) + return rcNode->pointsTo; + + return createContentNode(rcNode, rcNode->V); +} + bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, CGNodeMap &Mapping) { // The main point of the merging algorithm is to map each content node in the @@ -737,46 +904,43 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, if (!SourcePT) continue; - CGNode *MappedDestPT = Mapping.get(SourcePT); - if (!DestNd->pointsTo) { - // The following getContentNode() will create a new content node. + CGNode *DestPT = DestNd->pointsTo; + if (!DestPT) { + DestPT = createMergedContent(DestNd, SourcePT); Changed = true; } - CGNode *DestPT = getContentNode(DestNd); - if (MappedDestPT) { - // We already found the destination node through another path. - if (DestPT != MappedDestPT) { - // There are two content nodes in this graph which map to the same - // content node in the source graph -> we have to merge them. - scheduleToMerge(DestPT, MappedDestPT); - mergeAllScheduledNodes(); - Changed = true; - NodesMerged = true; - } - assert(SourcePT->isInWorkList); - } else { - // It's the first time we see the destination node, so we add it to the - // mapping. + CGNode *MappedDestPT = Mapping.get(SourcePT); + if (!MappedDestPT) { + // This is the first time the dest node is seen; just add the mapping. Mapping.add(SourcePT, DestPT); + continue; } + // We already found the destination node through another path. + assert(Mapping.getMappedNodes().contains(SourcePT)); + if (DestPT == MappedDestPT) + continue; + + // There are two content nodes in this graph which map to the same + // content node in the source graph -> we have to merge them. + scheduleToMerge(DestPT, MappedDestPT); + mergeAllScheduledNodes(); + Changed = true; + NodesMerged = true; } } while (NodesMerged); - - Mapping.getMappedNodes().reset(); + Mapping.getMappedNodes().reset(); // Make way for a different worklist. // Second step: add the source graph's defer edges to this graph. - llvm::SmallVector WorkList; for (CGNode *SourceNd : Mapping.getMappedNodes().nodeVector) { - assert(WorkList.empty()); - WorkList.push_back(SourceNd); - SourceNd->isInWorkList = true; + CGNodeWorklist Worklist(SourceGraph); + Worklist.push(SourceNd); CGNode *DestFrom = Mapping.get(SourceNd); assert(DestFrom && "node should have been merged to the graph"); // Collect all nodes which are reachable from the SourceNd via a path // which only contains defer-edges. - for (unsigned Idx = 0; Idx < WorkList.size(); ++Idx) { - CGNode *SourceReachable = WorkList[Idx]; + for (unsigned Idx = 0; Idx < Worklist.size(); ++Idx) { + CGNode *SourceReachable = Worklist[Idx]; CGNode *DestReachable = Mapping.get(SourceReachable); // Create the edge in this graph. Note: this may trigger merging of // content nodes. @@ -788,16 +952,9 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, // node of the defer-edge to escaping. Changed |= DestFrom->mergeEscapeState(EscapeState::Global); } - - for (auto *Deferred : SourceReachable->defersTo) { - if (!Deferred->isInWorkList) { - WorkList.push_back(Deferred); - Deferred->isInWorkList = true; - } - } + for (auto *Deferred : SourceReachable->defersTo) + Worklist.tryPush(Deferred); } - clearWorkListFlags(WorkList); - WorkList.clear(); } return Changed; } @@ -1255,7 +1412,24 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { #endif } -void EscapeAnalysis::ConnectionGraph::verify() const { +/// Checks an invariant of the connection graph: The points-to nodes of +/// the defer-successors must match with the points-to of this node. +bool CGNode::matchPointToOfDefers(bool allowMerge) const { + auto redirect = [allowMerge](CGNode *node) { + return (allowMerge && node) ? node->getMergeTarget() : node; + }; + for (CGNode *Def : defersTo) { + if (redirect(pointsTo) != redirect(Def->pointsTo)) + return false; + } + /// A defer-path in the graph must not end without the specified points-to + /// node. + if (pointsTo && !pointsToIsEdge && defersTo.empty()) + return false; + return true; +} + +void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { #ifndef NDEBUG verifyStructure(); @@ -1264,7 +1438,7 @@ void EscapeAnalysis::ConnectionGraph::verify() const { // ConnectionGraph invariant #4: For any node N, all paths starting at N // which consist of only defer-edges and a single trailing points-to edge // must lead to the same - assert(Nd->matchPointToOfDefers()); + assert(Nd->matchPointToOfDefers(allowMerge)); } #endif } @@ -1329,6 +1503,36 @@ static bool linkBBArgs(SILBasicBlock *BB) { return true; } +EscapeAnalysis::CGNode * +EscapeAnalysis::getValueContent(ConnectionGraph *conGraph, SILValue addrVal) { + CGNode *addrNode = conGraph->getNode(addrVal); + if (!addrNode) + return nullptr; + + if (CGNode *content = addrNode->getPointsToEdge()) + return content; + + SILValue addrNodeValue = addrNode->getValueOrNull(); + SILValue baseAddr = getPointerRoot(addrVal); + if (addrNode->isContent()) { + // Try to maintain an invariant that a node with content is a pointer. + if (!isPointer(addrNodeValue) && isPointer(baseAddr)) + addrNode->updateValue(baseAddr); + } else { + assert(isPointer(addrNodeValue)); + assert(addrNodeValue == getPointerRoot(addrVal)); + } + // Have we already merged a content node? + if (CGNode *content = addrNode->getContentNodeOrNull()) { + // TODO: Merge node properties here. + return content; + } + if (!isPointer(baseAddr)) + return nullptr; + + return conGraph->createContentNode(addrNode, baseAddr); +} + void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1488,56 +1692,56 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // allocated array buffer. The call is like a struct(buffer) // instruction. if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0))) { - CGNode *ArrayNode = ConGraph->getNode(ASC.getCallResult()); - CGNode *ArrayContent = ConGraph->getContentNode(ArrayNode); + SILValue ArrayBase = ASC.getCallResult(); + CGNode *ArrayContent = getValueContent(ConGraph, ArrayBase); + assert(ArrayContent && "Array base must have a node"); ConGraph->defer(ArrayContent, BufferNode); } return; } break; case ArrayCallKind::kGetElement: - if (CGNode *AddrNode = ConGraph->getNode(ASC.getSelf())) { - CGNode *DestNode = nullptr; + if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { + CGNode *LoadedElement = nullptr; // This is like a load from a ref_element_addr. if (ASC.hasGetElementDirectResult()) { - DestNode = ConGraph->getNode(ASC.getCallResult()); + LoadedElement = ConGraph->getNode(ASC.getCallResult()); } else { - CGNode *DestAddrNode = ConGraph->getNode(FAS.getArgument(0)); - assert(DestAddrNode && "indirect result must have node"); // The content of the destination address. - DestNode = ConGraph->getContentNode(DestAddrNode); + LoadedElement = getValueContent(ConGraph, FAS.getArgument(0)); + assert(LoadedElement && "indirect result must have node"); } - if (DestNode) { - // One content node for going from the array buffer pointer to - // the element address (like ref_element_addr). - CGNode *RefElement = ConGraph->getContentNode(AddrNode); - // Another content node to actually load the element. - CGNode *ArrayContent = ConGraph->getContentNode(RefElement); - ConGraph->defer(DestNode, ArrayContent); + if (LoadedElement) { + CGNode *ArrayElementStorage = + ConGraph->getFieldContent(ArrayRefNode); + ConGraph->defer(LoadedElement, ArrayElementStorage); return; } } break; case ArrayCallKind::kGetElementAddress: // This is like a ref_element_addr. - if (CGNode *SelfNode = ConGraph->getNode(ASC.getSelf())) { - ConGraph->defer(ConGraph->getNode(ASC.getCallResult()), - ConGraph->getContentNode(SelfNode)); + if (CGNode *ArrayRefNode = getValueContent(ConGraph, ASC.getSelf())) { + ConGraph->defer(ConGraph->getNode(ASC.getCallResult()), ArrayRefNode); } return; case ArrayCallKind::kWithUnsafeMutableBufferPointer: // Model this like an escape of the elements of the array and a capture // of anything captured by the closure. // Self is passed inout. - if (CGNode *AddrArrayStruct = ConGraph->getNode(ASC.getSelf())) { - CGNode *ArrayStructValueNode = - ConGraph->getContentNode(AddrArrayStruct); + if (CGNode *ArrayStructValue = + getValueContent(ConGraph, ASC.getSelf())) { + // One content node for going from the array buffer pointer to // the element address (like ref_element_addr). - CGNode *RefElement = ConGraph->getContentNode(ArrayStructValueNode); - // Another content node to actually load the element. - CGNode *ArrayContent = ConGraph->getContentNode(RefElement); - ConGraph->setEscapesGlobal(ArrayContent); + CGNode *ArrayRefNode = ArrayStructValue->getContentNodeOrNull(); + if (!ArrayRefNode) { + ArrayRefNode = ConGraph->createContentNode( + ArrayStructValue, ArrayStructValue->getValueOrNull()); + } + // Another content node for the element storage. + CGNode *ArrayElementStorage = ConGraph->getFieldContent(ArrayRefNode); + ArrayElementStorage->markEscaping(); // The first non indirect result is the closure. auto Args = FAS.getArgumentsWithoutIndirectResults(); setEscapesGlobal(ConGraph, Args[0]); @@ -1655,24 +1859,24 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::ReleaseValueInst: { + // A release instruction may deallocate the pointer operand. This may + // capture anything pointed to by the released object, but not the pointer + // to the object itself (because it will be a dangling pointer after + // deallocation). SILValue OpV = I->getOperand(0); - if (CGNode *AddrNode = ConGraph->getNode(OpV)) { - // A release instruction may deallocate the pointer operand. This may - // capture any content of the released object, but not the pointer to - // the object itself (because it will be a dangling pointer after - // deallocation). - CGNode *CapturedByDeinit = ConGraph->getContentNode(AddrNode); - // Get the content node for the object's properties. The object header - // itself cannot escape from the deinit. - CapturedByDeinit = ConGraph->getContentNode(CapturedByDeinit); - if (deinitIsKnownToNotCapture(OpV)) { - // Presumably this is necessary because, even though the deinit - // doesn't escape the immediate properties of this class, it may - // indirectly escape some other memory content(?) - CapturedByDeinit = ConGraph->getContentNode(CapturedByDeinit); - } - ConGraph->setEscapesGlobal(CapturedByDeinit); + CGNode *rcContent = getValueContent(ConGraph, OpV); + if (!rcContent) + return; + + CGNode *fieldContent = ConGraph->getFieldContent(rcContent); + if (!deinitIsKnownToNotCapture(OpV)) { + fieldContent->markEscaping(); + return; } + // This deinit is known to not directly capture it's own field content, + // however, indirect deinitializers could still capture anything pointed + // to by those fields. + ConGraph->escapeContentsOf(fieldContent); return; } @@ -1680,26 +1884,35 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::LoadInst: - // We treat ref_element_addr like a load (see NodeType::Content). + assert(!cast(I)->getType().isAddress()); + LLVM_FALLTHROUGH; case SILInstructionKind::RefElementAddrInst: case SILInstructionKind::RefTailAddrInst: case SILInstructionKind::ProjectBoxInst: case SILInstructionKind::InitExistentialAddrInst: case SILInstructionKind::OpenExistentialAddrInst: { + // Loads and projections into RC objects have a similar pattern: + // + // For RC object projections, get the non-address reference operand and + // return an RC content node that the reference directly points to. It is + // as-if the RC content node holds the pointer to the object fields. + // + // For loads, get the address-type operand and return the content node + // that the address directly points to. The load's address may itself come + // from a ref_element_addr, project_box or open_existential, in which + // case, the loaded content will be the field content, not the RC content. auto SVI = cast(I); - if (isPointer(SVI)) { - CGNode *AddrNode = ConGraph->getNode(SVI->getOperand(0)); - if (!AddrNode) { - // A load from an address we don't handle -> be conservative. - CGNode *ValueNode = ConGraph->getNode(SVI); - ConGraph->setEscapesGlobal(ValueNode); - return; - } - CGNode *PointsTo = ConGraph->getContentNode(AddrNode); - // No need for a separate node for the load instruction: - // just reuse the content node. + if (!isPointer(SVI)) + return; + + SILValue pointerVal = SVI->getOperand(0); + if (CGNode *PointsTo = getValueContent(ConGraph, pointerVal)) { ConGraph->setNode(SVI, PointsTo); + return; } + // A load or projection from an address we don't handle -> be + // conservative. + setEscapesGlobal(ConGraph, SVI); return; } case SILInstructionKind::CopyAddrInst: { @@ -1710,53 +1923,59 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, } // A copy_addr is like a 'store (load src) to dest'. - CGNode *SrcAddrNode = ConGraph->getNode(I->getOperand(CopyAddrInst::Src)); - if (!SrcAddrNode) { + SILValue srcAddr = I->getOperand(CopyAddrInst::Src); + CGNode *loadedContent = getValueContent(ConGraph, srcAddr); + if (!loadedContent) { setAllEscaping(I, ConGraph); break; } - - CGNode *LoadedValue = ConGraph->getContentNode(SrcAddrNode); - CGNode *DestAddrNode = - ConGraph->getNode(I->getOperand(CopyAddrInst::Dest)); - if (DestAddrNode) { - // Create a defer-edge from the loaded to the stored value. - CGNode *PointsTo = ConGraph->getContentNode(DestAddrNode); - ConGraph->defer(PointsTo, LoadedValue); - } else { - // A store to an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(LoadedValue); + SILValue destAddr = I->getOperand(CopyAddrInst::Dest); + // Create a defer-edge from the store location to the loaded content. + if (CGNode *destContent = getValueContent(ConGraph, destAddr)) { + ConGraph->defer(destContent, loadedContent); + return; } + // A store to an address we don't handle -> be conservative. + setEscapesGlobal(ConGraph, srcAddr); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::StoreInst: - if (CGNode *ValueNode = - ConGraph->getNode(I->getOperand(StoreInst::Src))) { - CGNode *AddrNode = ConGraph->getNode(I->getOperand(StoreInst::Dest)); - if (AddrNode) { - // Create a defer-edge from the content to the stored value. - CGNode *PointsTo = ConGraph->getContentNode(AddrNode); - ConGraph->defer(PointsTo, ValueNode); - } else { - // A store to an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(ValueNode); - } + case SILInstructionKind::StoreInst: { + SILValue srcVal = I->getOperand(StoreInst::Src); + CGNode *valueNode = ConGraph->getNode(srcVal); + // If the stored value isn't tracked, ignore the store. + if (!valueNode) + return; + + // The store destination content is always one pointsTo level away from + // its address. Either the address points to a variable or argument, and + // the pointee is removed by a level of pointer indirection, or the + // address corresponds is a projection within a reference counted object + // (via ref_element_addr, project_box, or open_existential_addr) where the + // stored field content is chained one level below the RC content. + SILValue destAddr = I->getOperand(StoreInst::Dest); + if (CGNode *pointsTo = getValueContent(ConGraph, destAddr)) { + // Create a defer-edge from the content to the stored value. + ConGraph->defer(pointsTo, valueNode); + return; } + // A store to an address we don't handle -> be conservative. + setEscapesGlobal(ConGraph, srcVal); return; + } case SILInstructionKind::PartialApplyInst: { // The result of a partial_apply is a thick function which stores the // boxed partial applied arguments. We create defer-edges from the // partial_apply values to the arguments. auto PAI = cast(I); - CGNode *ResultNode = ConGraph->getNode(PAI); - assert(ResultNode && "thick functions must have a CG node"); - for (const Operand &Op : PAI->getAllOperands()) { - if (CGNode *ArgNode = ConGraph->getNode(Op.get())) { - ResultNode = ConGraph->defer(ResultNode, ArgNode); + if (CGNode *ResultNode = ConGraph->getNode(PAI)) { + for (const Operand &Op : PAI->getAllOperands()) { + if (CGNode *ArgNode = ConGraph->getNode(Op.get())) { + ResultNode = ConGraph->defer(ResultNode, ArgNode); + } } } return; @@ -1773,20 +1992,11 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::EnumInst: { // Aggregate composition is like assigning the aggregate fields to the // resulting aggregate value. - auto SVI = cast(I); - CGNode *ResultNode = nullptr; - for (const Operand &Op : SVI->getAllOperands()) { - if (CGNode *FieldNode = ConGraph->getNode(Op.get())) { - if (!ResultNode) { - // A small optimization to reduce the graph size: we re-use the - // first field node as result node. - ConGraph->setNode(SVI, FieldNode); - ResultNode = FieldNode; - assert(isPointer(SVI)); - } else { - ResultNode = ConGraph->defer(ResultNode, FieldNode); - } - } + auto svi = cast(I); + CGNode *resultNode = ConGraph->getNode(svi); + for (const Operand &operand : svi->getAllOperands()) { + if (CGNode *subNode = ConGraph->getNode(operand.get())) + ConGraph->defer(resultNode, subNode); } return; } @@ -1800,9 +2010,8 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, auto *TEI = cast(I); assert(isExtractOfArrayUninitializedPointer(TEI) && "tuple_extract should be handled as projection"); - CGNode *ArrayNode = ConGraph->getNode(TEI->getOperand()); - CGNode *ArrayElements = ConGraph->getContentNode(ArrayNode); - ConGraph->setNode(TEI, ArrayElements); + if (CGNode *ArrayElements = getValueContent(ConGraph, TEI->getOperand())) + ConGraph->setNode(TEI, ArrayElements); return; } case SILInstructionKind::UncheckedRefCastAddrInst: { @@ -2098,7 +2307,7 @@ bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, // In this case the apply is only a use-point for V1 and V1's content node. // As V1's content node is the same as V's content node, we also make the // check for the content node. - CGNode *ContentNode = ConGraph->getContentNode(Node); + CGNode *ContentNode = getValueContent(ConGraph, V); if (ContentNode->escapesInsideFunction(V)) return true; @@ -2190,8 +2399,8 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; // Check if both nodes may point to the same content. - CGNode *Content1 = ConGraph->getContentNode(Node1); - CGNode *Content2 = ConGraph->getContentNode(Node2); + CGNode *Content1 = getValueContent(ConGraph, V1); + CGNode *Content2 = getValueContent(ConGraph, V2); SILType T1 = V1->getType(); SILType T2 = V2->getType(); @@ -2205,11 +2414,11 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { // have to go down one content level if just one of the values is a // ref-counted object. if (T1.isAddress() && hasReferenceSemantics(T2)) { - Content2 = ConGraph->getContentNode(Content2); + Content2 = ConGraph->getFieldContent(Content2); return Content1 == Content2; } if (T2.isAddress() && hasReferenceSemantics(T1)) { - Content1 = ConGraph->getContentNode(Content1); + Content1 = ConGraph->getFieldContent(Content1); return Content1 == Content2; } return true; diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 53759dbb463dd..72ae47bc383fb 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -99,8 +99,8 @@ bb0(%0 : $Int): // CHECK-NEXT: Con %0.1 Esc: A, Succ: %2 // CHECK-NEXT: Arg %1 Esc: G, Succ: // CHECK-NEXT: Val %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: %1 +// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: G, Succ: %1 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { bb0(%0 : $*Y, %1 : $X): @@ -120,8 +120,8 @@ bb0(%0 : $*Y, %1 : $X): // CHECK-NEXT: Arg %0 Esc: A, Succ: (%3.1) // CHECK-NEXT: Arg %1 Esc: A, Succ: // CHECK-NEXT: Val %3 Esc: %3, Succ: (%3.1), %0 -// CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: A, Succ: %1 +// CHECK-NEXT: Con %3.1 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: %1 // CHECK-NEXT: Ret Esc: R, Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { @@ -151,12 +151,12 @@ bb0: // CHECK-LABEL: CG of test_linked_list // CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.1) // CHECK-NEXT: Val %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%11.1) +// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: %0, %1, %4 // CHECK-NEXT: Val %4 Esc: A, Succ: (%1.1) // CHECK-NEXT: Val %7 Esc: %11, Succ: (%1.1) -// CHECK-NEXT: Val %11 Esc: %11, Succ: (%1.1), %7, %11.1 -// CHECK-NEXT: Con %11.1 Esc: A, Succ: (%1.1), %0, %1, %4 -// CHECK-NEXT: Ret Esc: R, Succ: %11.1 +// CHECK-NEXT: Val %11 Esc: %11, Succ: (%1.1), %2, %7 +// CHECK-NEXT: Ret Esc: R, Succ: %2 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -186,11 +186,11 @@ bb2: // CHECK-LABEL: CG of create_chain // CHECK-NEXT: Arg %0 Esc: A, Succ: (%7.1) // CHECK-NEXT: Val %1 Esc: A, Succ: (%7.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: %0, %1, %4 // CHECK-NEXT: Val %4 Esc: A, Succ: (%7.1) // CHECK-NEXT: Val %7 Esc: %11, Succ: (%7.1) -// CHECK-NEXT: Con %7.1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Val %11 Esc: R, Succ: (%7.1), %1.1 +// CHECK-NEXT: Con %7.1 Esc: A, Succ: (%8) +// CHECK-NEXT: Con %8 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Val %11 Esc: R, Succ: (%7.1), %8 // CHECK-NEXT: Ret Esc: R, Succ: %11 // CHECK-NEXT: End sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -212,10 +212,10 @@ bb0(%0 : $LinkedNode): // CHECK-LABEL: CG of loadNext // CHECK-NEXT: Arg %0 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Val %2 Esc: %2, Succ: (%2.1), %0, %2.2 -// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Ret Esc: R, Succ: %2.2 +// CHECK-NEXT: Val %2 Esc: %2, Succ: (%2.1), %0, %3 +// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Ret Esc: R, Succ: %3 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -252,13 +252,13 @@ bb0(%0 : $LinkedNode): // CHECK-LABEL: CG of load_next3 // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) -// CHECK-NEXT: Con %0.5 Esc: A, Succ: (%0.6) -// CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.6 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%1) +// CHECK-NEXT: Con %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con %5 Esc: A, Succ: +// CHECK-NEXT: Ret Esc: R, Succ: %5 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -278,9 +278,9 @@ sil_global @global_x : $X // CHECK-LABEL: CG of call_store_pointer // CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.2 +// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%5) +// CHECK-NEXT: Con %5 Esc: G, Succ: +// CHECK-NEXT: Ret Esc: R, Succ: %5 // CHECK-NEXT: End sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -311,10 +311,10 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of store_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%3) // CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0.2 +// CHECK-NEXT: Con %1.1 Esc: G, Succ: %3 +// CHECK-NEXT: Con %3 Esc: G, Succ: // CHECK-NEXT: End sil @store_content : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -329,9 +329,9 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of call_store_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.2 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: G, Succ: +// CHECK-NEXT: Ret Esc: R, Succ: %4 // CHECK-NEXT: End sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -405,12 +405,12 @@ sil @call_copy_addr_content : $@convention(thin) () -> () { // CHECK-LABEL: CG of test_partial_apply // CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: (%6.3) +// CHECK-NEXT: Arg %2 Esc: A, Succ: (%7.1) // CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: (%6.1) // CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: (%6.1) -// CHECK-NEXT: Con %6.1 Esc: %14,%15,%16,%17, Succ: (%6.2) -// CHECK-NEXT: Con %6.2 Esc: %14,%15,%16,%17, Succ: %2 -// CHECK-NEXT: Con %6.3 Esc: G, Succ: +// CHECK-NEXT: Con %6.1 Esc: %14,%15,%16,%17, Succ: (%7) +// CHECK-NEXT: Con %7 Esc: %14,%15,%16,%17, Succ: %2 +// CHECK-NEXT: Con %7.1 Esc: G, Succ: // CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6 // CHECK-NEXT: End sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 { @@ -460,10 +460,10 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 // CHECK-LABEL: CG of closure2 // CHECK-NEXT: Arg %0 Esc: G, Succ: // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: A, Succ: (%1.3) -// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) -// CHECK-NEXT: Con %1.4 Esc: G, Succ: %0 +// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: G, Succ: (%4) +// CHECK-NEXT: Con %4 Esc: G, Succ: %0 // CHECK-NEXT: End sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } ) -> () { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): @@ -565,9 +565,9 @@ sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: G, Succ: %0.2 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%1) +// CHECK-NEXT: Con %1 Esc: G, Succ: +// CHECK-NEXT: Val %4 Esc: G, Succ: %1 // CHECK-NEXT: Ret Esc: R, Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -580,8 +580,8 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of let_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg %0 Esc: G, Succ: +// CHECK-NEXT: Con %0.1 Esc: G, Succ: // CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) // CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 // CHECK-NEXT: Val %4 Esc: G, Succ: %0 @@ -621,11 +621,11 @@ bb2(%5 : $LinkedNode): // CHECK-LABEL: CG of loadNext2 // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Val %4 Esc: R, Succ: %0.4 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%1) +// CHECK-NEXT: Con %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Val %4 Esc: R, Succ: %1.2, %4.2 // CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) // CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1) // CHECK-NEXT: Ret Esc: R, Succ: %4 @@ -641,12 +641,12 @@ bb0(%0 : $LinkedNode): // CHECK-LABEL: CG of returnNext2 // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Val %3 Esc: R, Succ: (%0.3), %0.4 -// CHECK-NEXT: Val %8 Esc: R, Succ: %0.2, %3 +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%5) +// CHECK-NEXT: Val %3 Esc: R, Succ: (%5.1), %5.2 +// CHECK-NEXT: Con %5 Esc: A, Succ: (%5.1) +// CHECK-NEXT: Con %5.1 Esc: A, Succ: (%5.2) +// CHECK-NEXT: Con %5.2 Esc: A, Succ: (%5.1) +// CHECK-NEXT: Val %8 Esc: R, Succ: %3, %5 // CHECK-NEXT: Ret Esc: R, Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { @@ -670,10 +670,10 @@ bb3(%8 : $LinkedNode): // A single-cycle recursion test. // CHECK-LABEL: CG of single_cycle_recursion -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Val %5 Esc: R, Succ: (%0.2), %0.1 +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2) +// CHECK-NEXT: Val %5 Esc: R, Succ: (%2.1), %2 // CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5 // CHECK-NEXT: Ret Esc: R, Succ: %7 // CHECK-NEXT: End @@ -758,11 +758,11 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error // Test that the deinit of a box itself does not capture anything. // CHECK-LABEL: CG of test_release_of_partial_apply_with_box -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.3) +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%2.1) // CHECK-NEXT: Val %1 Esc: %6, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: %6, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: %6, Succ: %0 -// CHECK-NEXT: Con %1.3 Esc: G, Succ: +// CHECK-NEXT: Con %1.1 Esc: %6, Succ: (%2) +// CHECK-NEXT: Con %2 Esc: %6, Succ: %0 +// CHECK-NEXT: Con %2.1 Esc: G, Succ: // CHECK-NEXT: Val %5 Esc: %6, Succ: %1 // CHECK-NEXT: End sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () { @@ -784,8 +784,8 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // CHECK-LABEL: CG of store_to_unknown_reference // CHECK-NEXT: Arg %0 Esc: G, Succ: // CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: %0 +// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -833,8 +833,8 @@ bb0: // CHECK-LABEL: CG of create_and_store_x // CHECK-NEXT: Val %0 Esc: G, Succ: // CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: %0 +// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { bb0: @@ -850,9 +850,10 @@ bb0: // Test types which are considered as pointers. // CHECK-LABEL: CG of pointer_types -// CHECK-NEXT: Arg %0 Esc: A, Succ: %1 +// CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %7 Esc: R, Succ: %0 +// CHECK-NEXT: Val %4 Esc: R, Succ: %0, %1 +// CHECK-NEXT: Val %7 Esc: R, Succ: %4 // CHECK-NEXT: Ret Esc: R, Succ: %7 // CHECK-NEXT: End sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y { @@ -871,11 +872,11 @@ bb1(%7 : $(Pointer, Pointer)): // CHECK-LABEL: CG of defer_edge_cycle // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: +// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%2), %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%0.2), %0.1 +// CHECK-NEXT: Con %1.1 Esc: A, Succ: %0.1 +// CHECK-NEXT: Con %2 Esc: A, Succ: (%6) +// CHECK-NEXT: Con %6 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { entry(%0 : $*Y, %1 : $*Y): @@ -968,8 +969,8 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): // CHECK-LABEL: CG of test_existential_addr // CHECK-NEXT: Arg %0 Esc: A, Succ: // CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: , Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: , Succ: %0 +// CHECK-NEXT: Con %1.1 Esc: , Succ: (%2) +// CHECK-NEXT: Con %2 Esc: , Succ: %0 // CHECK-NEXT: End sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -1073,10 +1074,11 @@ bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type): sil_global @global_y : $SomeData // CHECK-LABEL: CG of test_node_merge_during_struct_inst -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%4.1) +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%4.1) // CHECK-NEXT: Val %1 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Val %4 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: G, Succ: (%4.1), %0, %1, %4 +// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: G, Succ: (%4.1), %1 +// CHECK-NEXT: Val %10 Esc: , Succ: %0, %4, %4.1 // CHECK-NEXT: End sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () { bb0(%0 : $Y): @@ -1244,10 +1246,10 @@ bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): // CHECK-LABEL: CG of arraysemantics_createUninitialized // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: (%4.2) +// CHECK-NEXT: Val %2 Esc: R, Succ: (%6) // CHECK-NEXT: Val %4 Esc: R, Succ: (%4.1) // CHECK-NEXT: Con %4.1 Esc: R, Succ: %2 -// CHECK-NEXT: Con %4.2 Esc: R, Succ: %0 +// CHECK-NEXT: Con %6 Esc: R, Succ: %0 // CHECK-NEXT: Ret Esc: R, Succ: %4 // CHECK-NEXT: End sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array { @@ -1451,10 +1453,10 @@ bb0(%0 : $X): // Z.deinit // CHECK-LABEL: CG of $s4main1ZCfD // CHECK: Arg %0 Esc: A, Succ: (%0.1) -// CHECK: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK: Con %0.2 Esc: G, Succ: +// CHECK: Con %0.1 Esc: A, Succ: (%1) +// CHECK: Con %1 Esc: G, Succ: // CHECK: Val %3 Esc: G, Succ: (%3.1) -// CHECK: Con %3.1 Esc: G, Succ: %0.2 +// CHECK: Con %3.1 Esc: G, Succ: %1 // CHECK: End sil @$s4main1ZCfD: $@convention(method) (@owned Z) -> () { bb0(%0 : $Z): From 701392f7e7f486ac00bd7ca68639368c5630310e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 12 Nov 2019 14:36:45 -0800 Subject: [PATCH 125/283] [NFC] Swap the order of checks in redeclaration checking An nth order effect of computing isInvalid() is that we can potentially wind up type checking during redeclaration checking. This wouldn't normally be a problem, but under the current validation order it's more likely that the DeclChecker has already fired before we run redeclaration checking. If we move to any other validation order, the request wins. For VarDecl, that means the PatternBindingInitializerRequest fires and we type check with the pattern binding as the decl context. Under the previous scheme, we would visit the accessor decl and use that as the decl context. Here's the rub: The request case records a cascading dependency edge while the DeclChecker case records a private edge. This means the frontend can wind up emitting two different reference dependency sets for the same primaries as in NameBinding/reference-dependencies-consistency.swift Fix this by sinking the interface type computation after the access control check which, thankfully, does not depend on the interface type. --- lib/Sema/TypeCheckDecl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index dc4eb8cadf0b4..04547450b44b8 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -736,10 +736,6 @@ static void checkRedeclaration(ASTContext &ctx, ValueDecl *current) { if (!conflicting(currentSig, otherSig)) continue; - // Skip invalid declarations. - if (other->isInvalid()) - continue; - // Skip declarations in other files. // In practice, this means we will warn on a private declaration that // shadows a non-private one, but only in the file where the shadowing @@ -748,6 +744,10 @@ static void checkRedeclaration(ASTContext &ctx, ValueDecl *current) { if (!other->isAccessibleFrom(currentDC)) continue; + // Skip invalid declarations. + if (other->isInvalid()) + continue; + // Thwart attempts to override the same declaration more than once. const auto *currentOverride = current->getOverriddenDecl(); const auto *otherOverride = other->getOverriddenDecl(); From 6413f4341ae821d221ae5f26329e41e021430f83 Mon Sep 17 00:00:00 2001 From: Marc Rasi Date: Fri, 8 Nov 2019 11:35:14 -0800 Subject: [PATCH 126/283] [AutoDiff upstream] AST bits for @differentiable fn ty --- include/swift/AST/AutoDiff.h | 13 +++- include/swift/AST/Types.h | 67 +++++++++++++++---- lib/AST/ASTDemangler.cpp | 6 +- lib/AST/ASTPrinter.cpp | 17 +++++ lib/SILGen/SILGen.cpp | 8 +-- lib/SILGen/SILGenExpr.cpp | 12 ++-- lib/SILOptimizer/Transforms/Outliner.cpp | 11 +-- .../UtilityPasses/BugReducerTester.cpp | 12 ++-- lib/Sema/ConstraintSystem.cpp | 12 ++-- lib/Sema/TypeCheckType.cpp | 24 ++++++- lib/Serialization/Deserialization.cpp | 52 ++++++++++---- lib/Serialization/ModuleFormat.h | 16 ++++- lib/Serialization/Serialization.cpp | 24 +++++-- .../ModuleInterface/differentiation.swift | 8 +++ .../SIL/Serialization/differentiation.swift | 28 ++++++++ .../Serialization/differentiation.swift | 12 ++++ tools/sil-opt/SILOpt.cpp | 8 +++ 17 files changed, 267 insertions(+), 63 deletions(-) create mode 100644 test/AutoDiff/ModuleInterface/differentiation.swift create mode 100644 test/AutoDiff/SIL/Serialization/differentiation.swift create mode 100644 test/AutoDiff/Serialization/differentiation.swift diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index 6c2fee70cb12a..61f348fe49b29 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// // -// SWIFT_ENABLE_TENSORFLOW // This file defines AST support for automatic differentiation. // //===----------------------------------------------------------------------===// @@ -18,7 +17,11 @@ #ifndef SWIFT_AST_AUTODIFF_H #define SWIFT_AST_AUTODIFF_H +#include + +#include "swift/AST/Identifier.h" #include "swift/AST/IndexSubset.h" +#include "swift/Basic/SourceLoc.h" #include "swift/Basic/Range.h" namespace swift { @@ -86,6 +89,12 @@ class ParsedAutoDiffParameter { } }; +enum class DifferentiabilityKind : uint8_t { + NonDifferentiable = 0, + Normal = 1, + Linear = 2 +}; + } // end namespace swift #endif // SWIFT_AST_AUTODIFF_H diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 89bb823f7633c..b6cb3fb366557 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -17,6 +17,7 @@ #ifndef SWIFT_TYPES_H #define SWIFT_TYPES_H +#include "swift/AST/AutoDiff.h" #include "swift/AST/DeclContext.h" #include "swift/AST/GenericParamKey.h" #include "swift/AST/Identifier.h" @@ -300,8 +301,8 @@ class alignas(1 << TypeAlignInBits) TypeBase { } protected: - enum { NumAFTExtInfoBits = 6 }; - enum { NumSILExtInfoBits = 6 }; + enum { NumAFTExtInfoBits = 8 }; + enum { NumSILExtInfoBits = 8 }; union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) + @@ -2875,14 +2876,16 @@ class AnyFunctionType : public TypeBase { // If bits are added or removed, then TypeBase::AnyFunctionTypeBits // and NumMaskBits must be updated, and they must match. // - // |representation|noEscape|throws| - // | 0 .. 3 | 4 | 5 | + // |representation|noEscape|throws|differentiability| + // | 0 .. 3 | 4 | 5 | 6 .. 7 | // enum : unsigned { - RepresentationMask = 0xF << 0, - NoEscapeMask = 1 << 4, - ThrowsMask = 1 << 5, - NumMaskBits = 6 + RepresentationMask = 0xF << 0, + NoEscapeMask = 1 << 4, + ThrowsMask = 1 << 5, + DifferentiabilityMaskOffset = 6, + DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + NumMaskBits = 8 }; unsigned Bits; // Naturally sized for speed. @@ -2905,13 +2908,24 @@ class AnyFunctionType : public TypeBase { // Constructor with no defaults. ExtInfo(Representation Rep, bool IsNoEscape, - bool Throws) + bool Throws, + DifferentiabilityKind DiffKind) : ExtInfo(Rep, Throws) { Bits |= (IsNoEscape ? NoEscapeMask : 0); + Bits |= ((unsigned)DiffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask; } bool isNoEscape() const { return Bits & NoEscapeMask; } bool throws() const { return Bits & ThrowsMask; } + bool isDifferentiable() const { + return getDifferentiabilityKind() > + DifferentiabilityKind::NonDifferentiable; + } + DifferentiabilityKind getDifferentiabilityKind() const { + return DifferentiabilityKind((Bits & DifferentiabilityMask) >> + DifferentiabilityMaskOffset); + } Representation getRepresentation() const { unsigned rawRep = Bits & RepresentationMask; assert(rawRep <= unsigned(Representation::Last) @@ -3069,6 +3083,11 @@ class AnyFunctionType : public TypeBase { return getExtInfo().throws(); } + bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } + DifferentiabilityKind getDifferentiabilityKind() const { + return getExtInfo().getDifferentiabilityKind(); + } + /// Returns a new function type exactly like this one but with the ExtInfo /// replaced. AnyFunctionType *withExtInfo(ExtInfo info) const; @@ -3716,14 +3735,16 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // If bits are added or removed, then TypeBase::SILFunctionTypeBits // and NumMaskBits must be updated, and they must match. - // |representation|pseudogeneric| noescape | - // | 0 .. 3 | 4 | 5 | + // |representation|pseudogeneric| noescape |differentiability| + // | 0 .. 3 | 4 | 5 | 6 .. 7 | // enum : unsigned { RepresentationMask = 0xF << 0, PseudogenericMask = 1 << 4, NoEscapeMask = 1 << 5, - NumMaskBits = 6 + DifferentiabilityMaskOffset = 6, + DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + NumMaskBits = 8 }; unsigned Bits; // Naturally sized for speed. @@ -3737,10 +3758,13 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, ExtInfo() : Bits(0) { } // Constructor for polymorphic type. - ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape) { + ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape, + DifferentiabilityKind diffKind) { Bits = ((unsigned) rep) | (isPseudogeneric ? PseudogenericMask : 0) | - (isNoEscape ? NoEscapeMask : 0); + (isNoEscape ? NoEscapeMask : 0) | + (((unsigned)diffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask); } /// Is this function pseudo-generic? A pseudo-generic function @@ -3750,6 +3774,16 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // Is this function guaranteed to be no-escape by the type system? bool isNoEscape() const { return Bits & NoEscapeMask; } + bool isDifferentiable() const { + return getDifferentiabilityKind() != + DifferentiabilityKind::NonDifferentiable; + } + + DifferentiabilityKind getDifferentiabilityKind() const { + return DifferentiabilityKind((Bits & DifferentiabilityMask) >> + DifferentiabilityMaskOffset); + } + /// What is the abstract representation of this function value? Representation getRepresentation() const { return Representation(Bits & RepresentationMask); @@ -4154,6 +4188,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, getRepresentation() == SILFunctionTypeRepresentation::Thick; } + bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } + DifferentiabilityKind getDifferentiabilityKind() const { + return getExtInfo().getDifferentiabilityKind(); + } + bool isNoReturnFunction(SILModule &M) const; // Defined in SILType.cpp /// Create a SILFunctionType with the same parameters, results, and attributes as this one, but with diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 7afbbe0d85dac..bc6fd217db0cc 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -479,9 +479,9 @@ Type ASTBuilder::createImplFunctionType( break; } - auto einfo = SILFunctionType::ExtInfo(representation, - flags.isPseudogeneric(), - !flags.isEscaping()); + auto einfo = SILFunctionType::ExtInfo( + representation, flags.isPseudogeneric(), !flags.isEscaping(), + DifferentiabilityKind::NonDifferentiable); llvm::SmallVector funcParams; llvm::SmallVector funcYields; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index af61b5e16c02b..26bdbb69840b3 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3789,6 +3789,14 @@ class TypePrinter : public TypeVisitor { if (Options.SkipAttributes) return; + if (!Options.excludeAttrKind(TAK_differentiable) && + info.isDifferentiable()) { + if (info.getDifferentiabilityKind() == DifferentiabilityKind::Linear) { + Printer << "@differentiable(linear) "; + } else { + Printer << "@differentiable "; + } + } if (Options.PrintFunctionRepresentationAttrs && !Options.excludeAttrKind(TAK_convention) && @@ -3833,6 +3841,15 @@ class TypePrinter : public TypeVisitor { if (Options.SkipAttributes) return; + if (!Options.excludeAttrKind(TAK_differentiable) && + info.isDifferentiable()) { + if (info.getDifferentiabilityKind() == DifferentiabilityKind::Linear) { + Printer << "@differentiable(linear) "; + } else { + Printer << "@differentiable "; + } + } + if (Options.PrintFunctionRepresentationAttrs && !Options.excludeAttrKind(TAK_convention) && info.getRepresentation() != SILFunctionType::Representation::Thick) { diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index b1e5e9c22937f..5fc9b6ee6d658 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -415,10 +415,10 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, : ParameterConvention::Indirect_In_Guaranteed }, }; - auto extInfo = - SILFunctionType::ExtInfo(SILFunctionTypeRepresentation::Thin, - /*pseudogeneric*/false, - /*non-escaping*/false); + auto extInfo = SILFunctionType::ExtInfo( + SILFunctionTypeRepresentation::Thin, + /*pseudogeneric*/ false, + /*non-escaping*/ false, DifferentiabilityKind::NonDifferentiable); auto functionTy = SILFunctionType::get(sig, extInfo, SILCoroutineKind::YieldOnce, diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 65f4e81ffc9a7..c8f2658d15e5e 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2669,7 +2669,8 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, - /*noescape*/ false), + /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, result, None, @@ -2811,7 +2812,8 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, - /*noescape*/ false), + /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, {}, None, @@ -2987,7 +2989,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, - /*noescape*/ false), + /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, results, None, @@ -3162,7 +3165,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, - /*noescape*/ false), + /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, results, None, diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index f3b4432454460..d350a63081a55 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -288,7 +288,8 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { ResultConvention::Owned)); auto ExtInfo = SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, /*noescape*/ false); + /*pseudogeneric*/ false, /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable); auto FunctionType = SILFunctionType::get( nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, @@ -1108,10 +1109,10 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { OrigSigIdx++; } - auto ExtInfo = - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false); + auto ExtInfo = SILFunctionType::ExtInfo( + SILFunctionType::Representation::Thin, + /*pseudogeneric*/ false, + /*noescape*/ false, DifferentiabilityKind::NonDifferentiable); SmallVector Results; // If we don't have a bridged return we changed from @autoreleased to @owned diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index def0c752a44ba..5ac534a26e9fa 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -83,13 +83,13 @@ class BugReducerTester : public SILFunctionTransform { ResultInfoArray.push_back( SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned)); auto FuncType = SILFunctionType::get( - nullptr, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - false /*isPseudoGeneric*/, - false /*noescape*/), + nullptr, + SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, + false /*isPseudoGeneric*/, false /*noescape*/, + DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, - ArrayRef(), ArrayRef(), - ResultInfoArray, None, - SubstitutionMap(), false, + ArrayRef(), ArrayRef(), ResultInfoArray, + None, SubstitutionMap(), false, getFunction()->getModule().getASTContext()); SILOptFunctionBuilder FunctionBuilder(*this); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index eadd085b89378..a212442a7fe6b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1642,7 +1642,8 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, auto bodyClosure = FunctionType::get(arg, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ true, - /*throws*/ true)); + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable)); FunctionType::Param args[] = { FunctionType::Param(noescapeClosure), FunctionType::Param(bodyClosure, CS.getASTContext().getIdentifier("do")), @@ -1651,7 +1652,8 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, refType = FunctionType::get(args, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ false, - /*throws*/ true)); + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable)); openedFullType = refType; return true; } @@ -1674,7 +1676,8 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, auto bodyClosure = FunctionType::get(bodyArgs, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ true, - /*throws*/ true)); + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable)); FunctionType::Param args[] = { FunctionType::Param(existentialTy), FunctionType::Param(bodyClosure, CS.getASTContext().getIdentifier("do")), @@ -1682,7 +1685,8 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, refType = FunctionType::get(args, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ false, - /*throws*/ true)); + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable)); openedFullType = refType; return true; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 6343b72a66764..bc37804211e6a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2150,9 +2150,21 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } } + if (attrs.has(TAK_differentiable) && + !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnose(attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); + } + + DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; + if (attrs.has(TAK_differentiable)) { + diffKind = attrs.linear ? DifferentiabilityKind::Linear + : DifferentiabilityKind::Normal; + } + // Resolve the function type directly with these attributes. SILFunctionType::ExtInfo extInfo(rep, attrs.has(TAK_pseudogeneric), - attrs.has(TAK_noescape)); + attrs.has(TAK_noescape), diffKind); ty = resolveSILFunctionType(fnRepr, options, coroutineKind, extInfo, calleeConvention, witnessMethodProtocol); @@ -2208,9 +2220,15 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, diag::experimental_differentiable_programming_disabled); } + DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; + if (attrs.has(TAK_differentiable)) { + diffKind = attrs.linear ? DifferentiabilityKind::Linear + : DifferentiabilityKind::Normal; + } + // Resolve the function type directly with these attributes. - FunctionType::ExtInfo extInfo(rep, /*noescape=*/false, - fnRepr->throws()); + FunctionType::ExtInfo extInfo(rep, /*noescape=*/false, fnRepr->throws(), + diffKind); ty = resolveASTFunctionType(fnRepr, options, extInfo); if (!ty || ty->hasError()) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 8e71fccdd34c3..98acdb3d98313 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -15,6 +15,7 @@ #include "ModuleFile.h" #include "ModuleFormat.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/Expr.h" #include "swift/AST/ForeignErrorConvention.h" @@ -4247,6 +4248,26 @@ getActualFunctionTypeRepresentation(uint8_t rep) { } } +/// Translate from the Serialization differentiability kind enum values to the +/// AST strongly-typed enum. +/// +/// The former is guaranteed to be stable, but may not reflect this version of +/// the AST. +static Optional +getActualDifferentiabilityKind(uint8_t rep) { + switch (rep) { +#define CASE(THE_CC) \ + case (uint8_t)serialization::DifferentiabilityKind::THE_CC: \ + return swift::DifferentiabilityKind::THE_CC; + CASE(NonDifferentiable) + CASE(Normal) + CASE(Linear) +#undef CASE + default: + return None; + } +} + /// Translate from the Serialization function type repr enum values to the AST /// strongly-typed enum. /// @@ -4577,22 +4598,18 @@ class swift::TypeDeserializer { StringRef blobData, bool isGeneric) { TypeID resultID; - uint8_t rawRepresentation; + uint8_t rawRepresentation, rawDiffKind; bool noescape = false, throws; GenericSignature genericSig = GenericSignature(); if (!isGeneric) { - decls_block::FunctionTypeLayout::readRecord(scratch, resultID, - rawRepresentation, - noescape, - throws); + decls_block::FunctionTypeLayout::readRecord( + scratch, resultID, rawRepresentation, noescape, throws, rawDiffKind); } else { GenericSignatureID rawGenericSig; - decls_block::GenericFunctionTypeLayout::readRecord(scratch, - resultID, - rawRepresentation, - throws, - rawGenericSig); + decls_block::GenericFunctionTypeLayout::readRecord( + scratch, resultID, rawRepresentation, throws, rawDiffKind, + rawGenericSig); genericSig = MF.getGenericSignature(rawGenericSig); } @@ -4600,7 +4617,12 @@ class swift::TypeDeserializer { if (!representation.hasValue()) MF.fatal(); - auto info = FunctionType::ExtInfo(*representation, noescape, throws); + auto diffKind = getActualDifferentiabilityKind(rawDiffKind); + if (!diffKind.hasValue()) + MF.fatal(); + + auto info = + FunctionType::ExtInfo(*representation, noescape, throws, *diffKind); auto resultTy = MF.getTypeChecked(resultID); if (!resultTy) @@ -4924,6 +4946,7 @@ class swift::TypeDeserializer { uint8_t rawCoroutineKind; uint8_t rawCalleeConvention; uint8_t rawRepresentation; + uint8_t rawDiffKind; bool pseudogeneric = false; bool noescape; bool hasErrorResult; @@ -4939,6 +4962,7 @@ class swift::TypeDeserializer { rawRepresentation, pseudogeneric, noescape, + rawDiffKind, hasErrorResult, numParams, numYields, @@ -4951,7 +4975,11 @@ class swift::TypeDeserializer { = getActualSILFunctionTypeRepresentation(rawRepresentation); if (!representation.hasValue()) MF.fatal(); - SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape); + auto diffKind = getActualDifferentiabilityKind(rawDiffKind); + if (!diffKind.hasValue()) + MF.fatal(); + SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape, + *diffKind); // Process the coroutine kind. auto coroutineKind = getActualSILCoroutineKind(rawCoroutineKind); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 3c8112c4f6995..3896a8ac5ef01 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 523; // @_nonEphemeral +const uint16_t SWIFTMODULE_VERSION_MINOR = 524; // function type differentiability /// A standard hash seed used for all string hashes in a serialized module. /// @@ -223,6 +223,15 @@ enum class FunctionTypeRepresentation : uint8_t { }; using FunctionTypeRepresentationField = BCFixed<4>; +// These IDs must \em not be renumbered or reordered without incrementing +// the module version. +enum class DifferentiabilityKind : uint8_t { + NonDifferentiable = 0, + Normal, + Linear, +}; +using DifferentiabilityKindField = BCFixed<2>; + enum class ForeignErrorConventionKind : uint8_t { ZeroResult, NonZeroResult, @@ -876,7 +885,8 @@ namespace decls_block { TypeIDField, // output FunctionTypeRepresentationField, // representation BCFixed<1>, // noescape? - BCFixed<1> // throws? + BCFixed<1>, // throws? + DifferentiabilityKindField // differentiability kind // trailed by parameters >; @@ -950,6 +960,7 @@ namespace decls_block { TypeIDField, // output FunctionTypeRepresentationField, // representation BCFixed<1>, // throws? + DifferentiabilityKindField, // differentiability kind GenericSignatureIDField // generic signture // trailed by parameters @@ -962,6 +973,7 @@ namespace decls_block { SILFunctionTypeRepresentationField, // representation BCFixed<1>, // pseudogeneric? BCFixed<1>, // noescape? + DifferentiabilityKindField, // differentiability kind BCFixed<1>, // error result? BCVBR<6>, // number of parameters BCVBR<5>, // number of yields diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index b11e78fbe0039..fbdb17f2d83cb 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -15,6 +15,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/Expr.h" #include "swift/AST/FileSystem.h" @@ -3603,6 +3604,18 @@ static uint8_t getRawStableFunctionTypeRepresentation( llvm_unreachable("bad calling convention"); } +/// Translate from the AST differentiability kind enum to the Serialization enum +/// values, which are guaranteed to be stable. +static uint8_t getRawStableDifferentiabilityKind( + swift::DifferentiabilityKind diffKind) { + switch (diffKind) { + SIMPLE_CASE(DifferentiabilityKind, NonDifferentiable) + SIMPLE_CASE(DifferentiabilityKind, Normal) + SIMPLE_CASE(DifferentiabilityKind, Linear) + } + llvm_unreachable("bad differentiability kind"); +} + /// Translate from the AST function representation enum to the Serialization enum /// values, which are guaranteed to be stable. static uint8_t getRawStableSILFunctionTypeRepresentation( @@ -3925,13 +3938,13 @@ class Serializer::TypeSerializer : public TypeVisitor { void visitFunctionType(const FunctionType *fnTy) { using namespace decls_block; - unsigned abbrCode = S.DeclTypeAbbrCodes[FunctionTypeLayout::Code]; FunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), fnTy->isNoEscape(), - fnTy->throws()); + fnTy->throws(), + getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind())); serializeFunctionTypeParams(fnTy); } @@ -3939,13 +3952,13 @@ class Serializer::TypeSerializer : public TypeVisitor { void visitGenericFunctionType(const GenericFunctionType *fnTy) { using namespace decls_block; assert(!fnTy->isNoEscape()); - auto genericSig = fnTy->getGenericSignature(); unsigned abbrCode = S.DeclTypeAbbrCodes[GenericFunctionTypeLayout::Code]; GenericFunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), fnTy->throws(), + getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()), S.addGenericSignatureRef(genericSig)); serializeFunctionTypeParams(fnTy); @@ -4005,12 +4018,15 @@ class Serializer::TypeSerializer : public TypeVisitor { auto stableCalleeConvention = getRawStableParameterConvention(fnTy->getCalleeConvention()); + auto stableDiffKind = + getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()); + unsigned abbrCode = S.DeclTypeAbbrCodes[SILFunctionTypeLayout::Code]; SILFunctionTypeLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, stableCoroutineKind, stableCalleeConvention, stableRepresentation, fnTy->isPseudogeneric(), fnTy->isNoEscape(), - fnTy->hasErrorResult(), fnTy->getParameters().size(), + stableDiffKind, fnTy->hasErrorResult(), fnTy->getParameters().size(), fnTy->getNumYields(), fnTy->getNumResults(), S.addGenericSignatureRef(sig), variableData); diff --git a/test/AutoDiff/ModuleInterface/differentiation.swift b/test/AutoDiff/ModuleInterface/differentiation.swift new file mode 100644 index 0000000000000..6b3a791fb9ca3 --- /dev/null +++ b/test/AutoDiff/ModuleInterface/differentiation.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t.swiftinterface -enable-library-evolution -enable-experimental-differentiable-programming %s +// RUN: %FileCheck %s < %t.swiftinterface + +public func a(f: @differentiable (Float) -> Float) {} +// CHECK: public func a(f: @differentiable (Swift.Float) -> Swift.Float) + +public func b(f: @differentiable(linear) (Float) -> Float) {} +// CHECK: public func b(f: @differentiable(linear) (Swift.Float) -> Swift.Float) diff --git a/test/AutoDiff/SIL/Serialization/differentiation.swift b/test/AutoDiff/SIL/Serialization/differentiation.swift new file mode 100644 index 0000000000000..e72bdd001b680 --- /dev/null +++ b/test/AutoDiff/SIL/Serialization/differentiation.swift @@ -0,0 +1,28 @@ +// RUN: %empty-directory(%t) +// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name differentiation -enable-experimental-differentiable-programming +// RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.2.sib -module-name differentiation -enable-experimental-differentiable-programming +// RUN: %target-sil-opt %t/tmp.2.sib -module-name differentiation -emit-sorted-sil -enable-experimental-differentiable-programming | %FileCheck %s + +sil_stage raw + +import Swift + +sil @a : $@convention(thin) (@differentiable (Float) -> Float) -> @differentiable (Float) -> Float { +bb0(%0 : $@differentiable (Float) -> Float): + return %0 : $@differentiable (Float) -> Float +} + +// CHECK-LABEL: sil @a : $@convention(thin) (@differentiable (Float) -> Float) -> @differentiable (Float) -> Float { +// CHECK: bb0([[ARG:%.*]] : $@differentiable (Float) -> Float): +// CHECK: return [[ARG]] : $@differentiable (Float) -> Float +// CHECK: } + +sil @b : $@convention(thin) (@differentiable(linear) (Float) -> Float) -> @differentiable(linear) (Float) -> Float { +bb0(%0 : $@differentiable(linear) (Float) -> Float): + return %0 : $@differentiable(linear) (Float) -> Float +} + +// CHECK-LABEL: sil @b : $@convention(thin) (@differentiable(linear) (Float) -> Float) -> @differentiable(linear) (Float) -> Float { +// CHECK: bb0([[ARG:%.*]] : $@differentiable(linear) (Float) -> Float): +// CHECK: return [[ARG]] : $@differentiable(linear) (Float) -> Float +// CHECK: } diff --git a/test/AutoDiff/Serialization/differentiation.swift b/test/AutoDiff/Serialization/differentiation.swift new file mode 100644 index 0000000000000..d5f1276c3f494 --- /dev/null +++ b/test/AutoDiff/Serialization/differentiation.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -emit-module -parse-as-library -enable-experimental-differentiable-programming -o %t +// RUN: llvm-bcanalyzer %t/differentiation.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -disable-sil-linking -enable-sil-verify-all %t/differentiation.swiftmodule -enable-experimental-differentiable-programming -o - | %FileCheck %s + +// BCANALYZER-NOT: UnknownCode + +func a(_ f: @differentiable (Float) -> Float) {} +// CHECK: func a(_ f: @differentiable (Float) -> Float) + +func b(_ f: @differentiable(linear) (Float) -> Float) {} +// CHECK: func b(_ f: @differentiable(linear) (Float) -> Float) diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index 33ded49e2b4bb..91e00140cd426 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -214,6 +214,11 @@ EnableExperimentalStaticAssert( "enable-experimental-static-assert", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Enable experimental #assert")); +static llvm::cl::opt EnableExperimentalDifferentiableProgramming( + "enable-experimental-differentiable-programming", llvm::cl::Hidden, + llvm::cl::init(false), + llvm::cl::desc("Enable experimental differentiable programming")); + /// Regular expression corresponding to the value given in one of the /// -pass-remarks* command line flags. Passes whose name matches this regexp /// will emit a diagnostic. @@ -329,6 +334,9 @@ int main(int argc, char **argv) { Invocation.getLangOptions().EnableExperimentalStaticAssert = EnableExperimentalStaticAssert; + Invocation.getLangOptions().EnableExperimentalDifferentiableProgramming = + EnableExperimentalDifferentiableProgramming; + // Setup the SIL Options. SILOptions &SILOpts = Invocation.getSILOptions(); SILOpts.InlineThreshold = SILInlineThreshold; From 99555dc9510df0b9827cdf2f24637b49fced53d0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 12 Nov 2019 15:42:17 -0800 Subject: [PATCH 127/283] [Constraint system] Add ConstraintSystem::getExprDepth() and use it for cleanup. Rather than passing around or create depth maps at a few places in the constraint solver, introduce getExprDepth() and use it consistently. --- lib/Sema/CSRanking.cpp | 15 +++++++-------- lib/Sema/ConstraintSystem.cpp | 7 +++---- lib/Sema/ConstraintSystem.h | 15 +++++++++++++-- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index a6c735e00c9b1..6f9df4e0dc40d 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -710,8 +710,7 @@ static Type getUnlabeledType(Type type, ASTContext &ctx) { SolutionCompareResult ConstraintSystem::compareSolutions( ConstraintSystem &cs, ArrayRef solutions, - const SolutionDiff &diff, unsigned idx1, unsigned idx2, - llvm::DenseMap> &weights) { + const SolutionDiff &diff, unsigned idx1, unsigned idx2) { if (cs.getASTContext().LangOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); log.indent(cs.solverState->depth * 2) @@ -743,9 +742,9 @@ SolutionCompareResult ConstraintSystem::compareSolutions( auto getWeight = [&](ConstraintLocator *locator) -> unsigned { if (auto *anchor = locator->getAnchor()) { - auto weight = weights.find(anchor); - if (weight != weights.end()) - return weight->getSecond().first + 1; + auto weight = cs.getExprDepth(anchor); + if (weight) + return *weight + 1; } return 1; @@ -1197,7 +1196,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, SmallVector losers(viable.size(), false); unsigned bestIdx = 0; for (unsigned i = 1, n = viable.size(); i != n; ++i) { - switch (compareSolutions(*this, viable, diff, i, bestIdx, ExprWeights)) { + switch (compareSolutions(*this, viable, diff, i, bestIdx)) { case SolutionCompareResult::Identical: // FIXME: Might want to warn about this in debug builds, so we can // find a way to eliminate the redundancy in the search space. @@ -1221,7 +1220,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, if (i == bestIdx) continue; - switch (compareSolutions(*this, viable, diff, bestIdx, i, ExprWeights)) { + switch (compareSolutions(*this, viable, diff, bestIdx, i)) { case SolutionCompareResult::Identical: // FIXME: Might want to warn about this in debug builds, so we can // find a way to eliminate the redundancy in the search space. @@ -1273,7 +1272,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, if (losers[j]) continue; - switch (compareSolutions(*this, viable, diff, i, j, ExprWeights)) { + switch (compareSolutions(*this, viable, diff, i, j)) { case SolutionCompareResult::Identical: // FIXME: Dub one of these the loser arbitrarily? break; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index eadd085b89378..aded34a06982b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2695,7 +2695,6 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, // Heuristically, all other things being equal, we should complain about the // ambiguous expression that (1) has the most overloads, (2) is deepest, or // (3) comes earliest in the expression. - auto depthMap = expr->getDepthMap(); auto indexMap = expr->getPreorderIndexMap(); for (unsigned i = 0, n = diff.overloads.size(); i != n; ++i) { @@ -2711,10 +2710,10 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, continue; unsigned index = it->second; - auto e = depthMap.find(anchor); - if (e == depthMap.end()) + auto optDepth = getExprDepth(anchor); + if (!optDepth) continue; - unsigned depth = e->second.first; + unsigned depth = *optDepth; // If we don't have a name to hang on to, it'll be hard to diagnose this // overload. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index b04e69f4a2d05..dc0959f6bd62c 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1991,6 +1991,18 @@ class ConstraintSystem { return nullptr; } + /// Retrieve the depth of the given expression. + Optional getExprDepth(Expr *expr) const { + auto e = ExprWeights.find(expr); + if (e != ExprWeights.end()) + return e->second.first; + + if (baseCS && baseCS != this) + return baseCS->getExprDepth(expr); + + return None; + } + /// Returns a locator describing the callee for the anchor of a given locator. /// /// - For an unresolved dot/member anchor, this will be a locator describing @@ -3659,8 +3671,7 @@ class ConstraintSystem { /// \param idx2 The index of the second solution. static SolutionCompareResult compareSolutions(ConstraintSystem &cs, ArrayRef solutions, - const SolutionDiff &diff, unsigned idx1, unsigned idx2, - llvm::DenseMap> &weights); + const SolutionDiff &diff, unsigned idx1, unsigned idx2); public: /// Increase the score of the given kind for the current (partial) solution From a650ff03a69d09af6bda59c8b3bbfb6f18adb555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Tue, 12 Nov 2019 16:42:42 -0800 Subject: [PATCH 128/283] [msvc] Fix another test using forward declared enums. Forward declared enums create a warning in MSVC which turns into an error when -warnings-as-errors is passed. This disable the warning in the header to avoid the error making the test fail. --- test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h b/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h index b7d0f2271a940..c7048060a9957 100644 --- a/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h +++ b/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h @@ -5,6 +5,9 @@ enum _name _name; \ enum __attribute__((enum_extensibility(closed))) _name + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference" typedef MY_ENUM(RegularEnum) { RegularEnumA, RegularEnumB @@ -47,3 +50,4 @@ enum __attribute__((enum_extensibility(closed))) UnavailableCases { UnavailableCasesB, UnavailableCasesThisIsTheUnavailableOne __attribute__((availability(swift, unavailable))) }; +#pragma clang diagnostic pop \ No newline at end of file From f06b5688e24bec19a3ed3eebaffa0727b83a2ce3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 12 Nov 2019 17:12:40 -0800 Subject: [PATCH 129/283] [IDE] Drop an unnecessarily inefficient use of depth maps --- lib/IDE/Refactoring.cpp | 48 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 88d4ef3650fbf..b148785f6d070 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -2939,30 +2939,36 @@ static NumberLiteralExpr *getTrailingNumberLiteral(ResolvedCursorInfo Tok) { // This cursor must point to the start of an expression. if (Tok.Kind != CursorInfoKind::ExprStart) return nullptr; - Expr *Parent = Tok.TrailingExpr; - assert(Parent); - - // Check if an expression is a number literal. - auto IsLiteralNumber = [&](Expr *E) -> NumberLiteralExpr* { - if (auto *NL = dyn_cast(E)) { - - // The sub-expression must have the same start loc with the outermost - // expression, i.e. the cursor position. - if (Parent->getStartLoc().getOpaquePointerValue() == - E->getStartLoc().getOpaquePointerValue()) { - return NL; - } - } - return nullptr; - }; + // For every sub-expression, try to find the literal expression that matches // our criteria. - for (auto Pair: Parent->getDepthMap()) { - if (auto Result = IsLiteralNumber(Pair.getFirst())) { - return Result; + class FindLiteralNumber : public ASTWalker { + Expr * const parent; + + public: + NumberLiteralExpr *found = nullptr; + + explicit FindLiteralNumber(Expr *parent) : parent(parent) { } + + std::pair walkToExprPre(Expr *expr) override { + if (auto *literal = dyn_cast(expr)) { + // The sub-expression must have the same start loc with the outermost + // expression, i.e. the cursor position. + if (!found && + parent->getStartLoc().getOpaquePointerValue() == + expr->getStartLoc().getOpaquePointerValue()) { + found = literal; + } + } + + return { found == nullptr, expr }; } - } - return nullptr; + }; + + auto parent = Tok.TrailingExpr; + FindLiteralNumber finder(parent); + parent->walk(finder); + return finder.found; } static std::string insertUnderscore(StringRef Text) { From a8afb8403186d1746384db275c40c887acb2db2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferrie=CC=80re?= Date: Tue, 12 Nov 2019 17:39:36 -0800 Subject: [PATCH 130/283] [test] Disable sometimes slow test rdar54580427.swift rdar://problem/57138194 https://bugs.swift.org/browse/SR-11770 --- validation-test/Sema/type_checker_perf/slow/rdar54580427.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift b/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift index 346d9dccebab1..071587356ae13 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift @@ -1,6 +1,7 @@ // FIXME: This should be linear instead of exponential. // RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes --invert-result %s -Xfrontend=-solver-expression-time-threshold=1 // REQUIRES: asserts,no_asan +// REQUIRES: rdar57138194,SR11770 enum Val { case d([String: Val]) From 3a9845229560565ad5cd94dae18ddc23be24f305 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 12 Nov 2019 18:12:43 -0800 Subject: [PATCH 131/283] Add FileCheck tests --- lib/SILGen/SILGenApply.cpp | 4 ++-- lib/SILGen/SILGenFunction.h | 8 ++++++++ test/SILGen/keypaths.swift | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 2a004c70d69be..87961ce4f6afe 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6082,10 +6082,10 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( SmallVector argValues; SmallVector delayedArgs; ArgEmitter emitter( - *this, fnType.getPointer()->getRepresentation(), + *this, fnType->getRepresentation(), /*yield*/ false, /*isForCoroutine*/ false, - ClaimedParamsRef(fnType, fnType.getPointer()->getParameters()), argValues, + ClaimedParamsRef(fnType, fnType->getParameters()), argValues, delayedArgs, /*foreign error*/ None, ImportAsMemberStatus()); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index a0fdcc3bb40d3..91d616286dbbb 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -665,6 +665,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction IsFreeFunctionWitness_t isFree, bool isSelfConformance); + /// Generates subscript arguments for keypath. This function handles lowering of all index expressions + /// including default arguments. + /// + /// \returns Lowered index arguments. + /// \param subscript - The subscript decl who's arguments are being lowered. + /// \param subs - Used to get subscript function type and to substitute generic args. + /// \param indexExpr - An expression holding the indices of the subscript (either a TupleExpr + /// or a ParanExpr). SmallVector emitKeyPathSubscriptOperands(SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr); diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 2c4cf966dce2d..f84bb016981f8 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -355,7 +355,21 @@ struct SubscriptDefaults3 { } } -// CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts +struct SubscriptDefaults4 { + subscript(x x: T, y y: T = 0) -> T { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults5 { + subscript(x x: T, y y: T = #function) -> T { + get { fatalError() } + set { fatalError() } + } +} + +// CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts1x1y1syx_q_SStSHRzSHR_r0_lF func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[] _ = \Subscripts.[generic: x] @@ -398,9 +412,28 @@ func subscripts(x: T, y: U, s: String) { _ = \SubscriptDefaults2.[] _ = \SubscriptDefaults2.[0] - _ = \SubscriptDefaults3.[] _ = \SubscriptDefaults3.[0] + _ = \SubscriptDefaults5.[x: ""] + _ = \SubscriptDefaults5.[x: "", y: ""] +} + +// CHECK-LABEL: sil hidden [ossa] @{{.*}}check_default_subscripts +func check_default_subscripts() { + // CHECK: [[INTINIT:%[0-9]+]] = integer_literal $Builtin.Int64, 0 + // CHECK: [[I:%[0-9]+]] = struct $Int ([[INTINIT]] : $Builtin.Int64) + // CHECK: [[DFN:%[0-9]+]] = function_ref @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipfA0_ : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 + // CHECK: [[ALLOC:%[0-9]+]] = alloc_stack $Int + // CHECK: apply [[DFN]]([[ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 + // CHECK: [[LOAD:%[0-9]+]] = load [[ALLOC]] : $*Int + // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[I]], [[LOAD]]) + _ = \SubscriptDefaults4.[x: 0] + // CHECK: [[INTX:%[0-9]+]] = integer_literal $Builtin.Int64, 0 + // CHECK: [[IX:%[0-9]+]] = struct $Int ([[INTX]] : $Builtin.Int64) + // CHECK: [[INTY:%[0-9]+]] = integer_literal $Builtin.Int64, 0 + // CHECK: [[IY:%[0-9]+]] = struct $Int ([[INTY]] : $Builtin.Int64) + // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[IX]], [[XY]]) + _ = \SubscriptDefaults4.[x: 0, y: 0] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From f2ec557619f2a70e88e362b0ca8cc4d70bd0f9d9 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Thu, 7 Nov 2019 17:01:11 -0800 Subject: [PATCH 132/283] [OSLogOptimization] Improve the `replaceAndFixLifetimes` function of the OSLogOptimization pass. This commit contain two changes: - It handles non-OSSA better (but it is meant to be phased out) so that array and closure folding can be supported - It fixes a bug in the OSSA folding by making sure that when an owned value replaces a guaranteed value, the owned value is borrowed and the borrow is used in place of the guaranteed value. --- .../Mandatory/OSLogOptimization.cpp | 51 +++++++++++++++---- .../OSLogPrototypeCompileTest.sil | 12 +++-- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 4d2c1233b3a08..5fb7057904c3c 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -631,12 +631,10 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, "cannot constant fold a terminator instruction"); assert(foldedInst && "constant value does not have a defining instruction"); - // First, replace all uses of originalVal by foldedVal, and then adjust their - // lifetimes if necessary. - originalVal->replaceAllUsesWith(foldedVal); - if (originalVal->getType().isTrivial(*fun)) { assert(foldedVal->getType().isTrivial(*fun)); + // Just replace originalVal by foldedVal. + originalVal->replaceAllUsesWith(foldedVal); return; } assert(!foldedVal->getType().isTrivial(*fun)); @@ -644,12 +642,34 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, if (!fun->hasOwnership()) { // In non-ownership SIL, handle only folding of struct_extract instruction, // which is the only important instruction that should be folded by this - // pass. Note that folding an arbitrary instruction in non-ownership SIL - // makes updating reference counts of the original value much harder and - // error prone. + // pass. The logic used here is not correct in general and overfits a + // specific form of SIL. This code should be removed once OSSA is enabled + // for this pass. // TODO: this code can be safely removed once ownership SIL becomes the // default SIL this pass works on. - assert(isa(originalInst)); + assert(isa(originalInst) && + !originalVal->getType().isAddress()); + + // First, replace all uses of originalVal by foldedVal, and then adjust + // their lifetimes if necessary. + originalVal->replaceAllUsesWith(foldedVal); + + unsigned retainCount = 0; + unsigned consumeCount = 0; + for (Operand *use : foldedVal->getUses()) { + SILInstruction *user = use->getUser(); + if (isa(user) || isa(user)) + consumeCount++; + if (isa(user)) + retainCount++; + // Note that there could other consuming operations but they are not + // handled here as this code should be phased out soon. + } + if (consumeCount > retainCount) { + // The original value was at +1 and therefore consumed at the end. Since + // the foldedVal is also at +1 there is nothing to be done. + return; + } cleanupAtEndOfLifetime(foldedInst, [&](SILInstruction *lifetimeEndInst) { SILBuilderWithScope builder(lifetimeEndInst); builder.emitReleaseValue(lifetimeEndInst->getLoc(), foldedVal); @@ -661,6 +681,7 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, "constant value must have owned ownership kind"); if (originalVal.getOwnershipKind() == ValueOwnershipKind::Owned) { + originalVal->replaceAllUsesWith(foldedVal); // Destroy originalVal, which is now unused, immediately after its // definition. Note that originalVal's destorys are now transferred to // foldedVal. @@ -671,10 +692,20 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, return; } - // Here, originalVal is not owned. Hence, destroy foldedVal at the end of its + // Here, originalVal is not owned. Hence, borrow form foldedVal and use the + // borrow in place of originalVal. Also, destroy foldedVal at the end of its // lifetime. - cleanupAtEndOfLifetime(foldedInst, [&](SILInstruction *lifetimeEndInst) { + assert(originalVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed); + + SILBuilderWithScope builder(&*std::next(foldedInst->getIterator())); + BeginBorrowInst *borrow = + builder.createBeginBorrow(foldedInst->getLoc(), foldedVal); + + originalVal->replaceAllUsesWith(borrow); + + cleanupAtEndOfLifetime(borrow, [&](SILInstruction *lifetimeEndInst) { SILBuilderWithScope builder(lifetimeEndInst); + builder.createEndBorrow(lifetimeEndInst->getLoc(), borrow); builder.emitDestroyValueOperation(lifetimeEndInst->getLoc(), foldedVal); }); return; diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil index cce273c92e101..f15550f479a8a 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.sil +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -59,7 +59,8 @@ bb0: return %13 : $() // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString - // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) + // CHECK-DAG: [[BORROW]] = begin_borrow [[STRINGCONST:%[0-9]+]] // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" @@ -175,7 +176,8 @@ bb0: // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) // CHECK-DAG: [[BORROW]] = begin_borrow [[COPYVAL:%[0-9]+]] - // CHECK-DAG: [[COPYVAL]] = copy_value [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[COPYVAL]] = copy_value [[BORROW2:%[0-9]+]] + // CHECK-DAG: [[BORROW2]] = begin_borrow [[STRINGCONST:%[0-9]+]] // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" @@ -278,7 +280,7 @@ bb1: cond_br %2, bb2, bb3 // Check release of the folded value of %15. // CHECK-LABEL: bb1: - // CHECK-NEXT: destroy_value [[STRINGCONST1:%[0-9]+]] : $String + // CHECK: destroy_value [[STRINGCONST1:%[0-9]+]] : $String bb2: %12 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () @@ -396,10 +398,10 @@ bb0: return %17 : $() // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[CONSTCOPY:%[0-9]+]]) - // CHECK-DAG: [[CONSTCOPY]] = copy_value [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[CONSTCOPY]] = copy_value [[BORROW:%[0-9]+]] + // CHECK-DAG: [[BORROW]] = begin_borrow [[STRINGCONST:%[0-9]+]] // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" // CHECK-DAG: destroy_value [[STRINGCONST]] : $String } - From 6f086afee2484965888901cf359874c37a4d2f02 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Thu, 7 Nov 2019 17:28:04 -0800 Subject: [PATCH 133/283] [SIL Optimization][OSLogOptimization] Improve the OSLogOptimization pass so that it constant folds array symbolic values inferred by the constant evaluator when evaluting os log calls. --- .../Mandatory/OSLogOptimization.cpp | 154 ++++++++++- .../OSLogPrototypeCompileTest.sil | 260 ++++++++++++++++++ 2 files changed, 413 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 5fb7057904c3c..a418a3f3c7f32 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -89,6 +89,7 @@ #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILLocation.h" #include "swift/SIL/SILModule.h" +#include "swift/SIL/TypeLowering.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" @@ -101,6 +102,7 @@ #include "llvm/ADT/MapVector.h" using namespace swift; +using namespace Lowering; template static void diagnose(ASTContext &Context, SourceLoc loc, Diag diag, @@ -239,6 +241,12 @@ static bool isStringType(SILType silType, ASTContext &astContext) { return nominalDecl && nominalDecl == astContext.getStringDecl(); } +/// Return true if and only if the given SIL type represents an Array type. +static bool isArrayType(SILType silType, ASTContext &astContext) { + NominalTypeDecl *nominalDecl = silType.getNominalOrBoundGenericNominal(); + return nominalDecl && nominalDecl == astContext.getArrayDecl(); +} + /// Decide if the given instruction (which could possibly be a call) should /// be constant evaluated. /// @@ -290,6 +298,28 @@ static bool isFoldableString(SILValue value, SILInstruction *definingInst, !getStringMakeUTF8Init(definingInst); } +/// Return true iff the given value is an array and is not an initialization +/// of an array from an array literal. +static bool isFoldableArray(SILValue value, SILInstruction *definingInst, + ASTContext &astContext) { + assert(definingInst); + if (!isArrayType(value->getType(), astContext)) + return false; + + // Check if this is not an initialization of an array from a literal. + SILInstruction *constructorInst = definingInst; + if (isa(definingInst) || + isa(definingInst)) { + constructorInst = definingInst->getOperand(0)->getDefiningInstruction(); + } + ApplyInst *apply = dyn_cast(definingInst); + if (!apply) + return true; + SILFunction *callee = apply->getCalleeFunction(); + return !callee || !callee->hasSemanticsAttr("array.init.empty") || + !callee->hasSemanticsAttr("array.uninitialized_intrinsic"); +} + /// Check whether a SILValue is foldable. String, integer, array and /// function values are foldable with the following exceptions: /// - Addresses cannot be folded. @@ -308,7 +338,8 @@ static bool isSILValueFoldable(SILValue value) { !isa(definingInst) && !isa(definingInst) && (isFoldableIntOrBool(value, definingInst, astContext) || - isFoldableString(value, definingInst, astContext))); + isFoldableString(value, definingInst, astContext) || + isFoldableArray(value, definingInst, astContext))); } /// Diagnose failure during evaluation of a call to a constant-evaluable @@ -450,6 +481,110 @@ static Optional collectConstants(FoldState &foldState) { return None; // No error. } +/// Generate SIL code to create an array of constant size from the given +/// SILValues \p elements. This function creates the same sequence of SIL +/// instructions that would be generated for initializing an array from an array +/// literal of the form [element1, element2, ..., elementn]. +/// +/// \param elements SILValues that the array should contain +/// \param arrayType the type of the array that must be created. +/// \param builder SILBuilder that provides the context for emitting the code +/// for the array. +/// \param loc SILLocation to use in the emitted instructions. +/// \return the SILValue of the array that is created with the given \c +/// elements. +static SILValue emitCodeForConstantArray(ArrayRef elements, + CanType arrayType, SILBuilder &builder, + SILLocation loc) { + ASTContext &astContext = builder.getASTContext(); + assert(astContext.getArrayDecl() == + arrayType->getNominalOrBoundGenericNominal()); + SILModule &module = builder.getModule(); + SILFunction &fun = builder.getFunction(); + + // Create a SILValue for the number of elements. + unsigned numElements = elements.size(); + SILValue numElementsSIL = builder.createIntegerLiteral( + loc, SILType::getBuiltinWordType(astContext), numElements); + + // Find the SILFunction that corresponds to _allocateUninitializedArray. + FuncDecl *arrayAllocateDecl = astContext.getAllocateUninitializedArray(); + assert(arrayAllocateDecl); + std::string allocatorMangledName = + SILDeclRef(arrayAllocateDecl, SILDeclRef::Kind::Func).mangle(); + SILFunction *arrayAllocateFun = + module.findFunction(allocatorMangledName, SILLinkage::PublicExternal); + assert(arrayAllocateFun); + + // Call the _allocateUninitializedArray function with numElementsSIL. The + // call returns a two-element tuple, where the first element is the newly + // created array and the second element is a pointer to the internal storage + // of the array. + SubstitutionMap subMap = arrayType->getContextSubstitutionMap( + module.getSwiftModule(), astContext.getArrayDecl()); + FunctionRefInst *arrayAllocateRef = + builder.createFunctionRef(loc, arrayAllocateFun); + ApplyInst *applyInst = builder.createApply( + loc, arrayAllocateRef, subMap, ArrayRef(numElementsSIL), false); + + // Extract the elements of the tuple returned by the call to the allocator. + SILValue arraySIL; + SILValue storagePointerSIL; + if (fun.hasOwnership()) { + DestructureTupleInst *destructureInst = + builder.createDestructureTuple(loc, applyInst); + arraySIL = destructureInst->getResults()[0]; + storagePointerSIL = destructureInst->getResults()[1]; + } else { + SILType arraySILType = SILType::getPrimitiveObjectType(arrayType); + arraySIL = builder.createTupleExtract(loc, applyInst, 0, arraySILType); + storagePointerSIL = builder.createTupleExtract( + loc, applyInst, 1, SILType::getRawPointerType(astContext)); + } + + if (elements.empty()) { + // Nothing more to be done if we are creating an empty array. + return arraySIL; + } + + // Convert the pointer to the storage to an address. The elements will be + // stored into offsets from this address. + SILType elementSILType = elements[0]->getType(); + PointerToAddressInst *storageAddr = builder.createPointerToAddress( + loc, storagePointerSIL, elementSILType.getAddressType(), + /*isStrict*/ true, + /*isInvariant*/ false); + + // Iterate over the elements and store them into the storage address + // after offsetting it appropriately. + + // Create a TypeLowering for emitting stores. Note that TypeLowering + // provides a utility for emitting stores for storing trivial and + // non-trivial values, and also handles OSSA and non-OSSA. + const TypeLowering &elementTypeLowering = + builder.getTypeLowering(elementSILType); + + unsigned elementIndex = 0; + for (SILValue elementSIL : elements) { + // Compute the address where the element must be stored. + SILValue currentStorageAddr; + if (elementIndex != 0) { + SILValue indexSIL = builder.createIntegerLiteral( + loc, SILType::getBuiltinWordType(astContext), elementIndex); + currentStorageAddr = builder.createIndexAddr(loc, storageAddr, indexSIL); + } else { + currentStorageAddr = storageAddr; + } + // Store the generated element into the currentStorageAddr. This is an + // initializing store and therefore there is no need to free any existing + // element. + elementTypeLowering.emitStore(builder, loc, elementSIL, currentStorageAddr, + StoreOwnershipQualifier::Init); + elementIndex++; + } + return arraySIL; +} + /// Generate SIL code that computes the constant given by the symbolic value /// `symVal`. Note that strings and struct-typed constant values will require /// multiple instructions to be emitted. @@ -532,6 +667,23 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, loc, aggregateType, ArrayRef(newPropertySIL)); return newStructInst; } + case SymbolicValue::Array: { + assert(expectedType->isEqual(symVal.getArrayType())); + CanType elementType; + ArrayRef arrayElements = + symVal.getStorageOfArray().getStoredElements(elementType); + + // Emit code for the symbolic values corresponding to the array elements. + SmallVector elementSILValues; + for (SymbolicValue elementSymVal : arrayElements) { + SILValue elementSIL = emitCodeForSymbolicValue(elementSymVal, elementType, + builder, loc, stringInfo); + elementSILValues.push_back(elementSIL); + } + SILValue arraySIL = emitCodeForConstantArray( + elementSILValues, expectedType->getCanonicalType(), builder, loc); + return arraySIL; + } default: { llvm_unreachable("Symbolic value kind is not supported"); } diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil index f15550f479a8a..6e3c8e70bd6c8 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.sil +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -405,3 +405,263 @@ bb0: // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" // CHECK-DAG: destroy_value [[STRINGCONST]] : $String } + +// Check folding of arrays by the OSLogOptimization pass. + +/// A simplified stub for OSLogInterpolation type for testing array folding. +struct OSLogInterpolationArrayStub { + var arguments: [Int64] +} + +/// A simplified stub for OSLogMessage for testing array folding. +struct OSLogMessageArrayStub { + var interpolation: OSLogInterpolationArrayStub +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub { +bb0(%0 : $Builtin.Int1): + // Create an array with elements 99, 98 and 90 + %1 = integer_literal $Builtin.Word, 3 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array, Builtin.RawPointer), 0 + %5 = tuple_extract %3 : $(Array, Builtin.RawPointer), 1 + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*Int64 + %7 = integer_literal $Builtin.Int64, 99 + %8 = struct $Int64 (%7 : $Builtin.Int64) + store %8 to %6 : $*Int64 + %10 = integer_literal $Builtin.Word, 1 + %11 = index_addr %6 : $*Int64, %10 : $Builtin.Word + %12 = integer_literal $Builtin.Int64, 98 + %13 = struct $Int64 (%12 : $Builtin.Int64) + store %13 to %11 : $*Int64 + %15 = integer_literal $Builtin.Word, 2 + %16 = index_addr %6 : $*Int64, %15 : $Builtin.Word + %17 = integer_literal $Builtin.Int64, 90 + %18 = struct $Int64 (%17 : $Builtin.Int64) + store %18 to %16 : $*Int64 + + // Create an instance of OSLogMessageArrayStub using the above array. + %20 = struct $OSLogInterpolationArrayStub(%4 : $Array) + %21 = struct $OSLogMessageArrayStub(%20 : $OSLogInterpolationArrayStub) + return %21 : $OSLogMessageArrayStub +} + +// _allocateUninitializedArray(_:) +sil [serialized] [always_inline] [_semantics "array.uninitialized_intrinsic"] @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + +/// A function that models the use of an array. +sil @useArray: $@convention(thin) (@guaranteed Array) -> () + +// CHECK-LABEL: @testConstantFoldingOfArray +sil [ossa] @testConstantFoldingOfArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + + // Use the arguments property of OSLogMessageArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageArrayStub + %4 = struct_extract %3 : $OSLogMessageArrayStub, #OSLogMessageArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationArrayStub, #OSLogInterpolationArrayStub.arguments + %6 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + end_borrow %3 : $OSLogMessageArrayStub + destroy_value %2 : $OSLogMessageArrayStub + %8 = tuple () + return %8 : $() + // CHECK: [[ELEM1:%[0-9]+]] = integer_literal $Builtin.Int64, 99 + // CHECK: [[ELEM1INT:%[0-9]+]] = struct $Int64 ([[ELEM1]] : $Builtin.Int64) + // CHECK: [[ELEM2:%[0-9]+]] = integer_literal $Builtin.Int64, 98 + // CHECK: [[ELEM2INT:%[0-9]+]] = struct $Int64 ([[ELEM2]] : $Builtin.Int64) + // CHECK: [[ELEM3:%[0-9]+]] = integer_literal $Builtin.Int64, 90 + // CHECK: [[ELEM3INT:%[0-9]+]] = struct $Int64 ([[ELEM3]] : $Builtin.Int64) + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 3 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: ([[ARRAY:%[0-9]+]], [[STORAGEPTR:%[0-9]+]]) = destructure_tuple [[TUPLE]] + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[ARRAY]] + // CHECK: [[STORAGEADDR:%[0-9]+]] = pointer_to_address [[STORAGEPTR]] : $Builtin.RawPointer to [strict] $*Int64 + // CHECK: store [[ELEM1INT]] to [trivial] [[STORAGEADDR]] : $*Int64 + // CHECK: [[INDEX1:%[0-9]+]] = integer_literal $Builtin.Word, 1 + // CHECK: [[INDEXADDR1:%[0-9]+]] = index_addr [[STORAGEADDR]] : $*Int64, [[INDEX1]] : $Builtin.Word + // CHECK: store [[ELEM2INT]] to [trivial] [[INDEXADDR1]] : $*Int64 + // CHECK: [[INDEX2:%[0-9]+]] = integer_literal $Builtin.Word, 2 + // CHECK: [[INDEXADDR2:%[0-9]+]] = index_addr [[STORAGEADDR]] : $*Int64, [[INDEX2]] : $Builtin.Word + // CHECK: store [[ELEM3INT]] to [trivial] [[INDEXADDR2]] : $*Int64 + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArray + // CHECK: apply [[USEREF]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[ARRAY]] : $Array +} + +// CHECK-LABEL: @testConstantFoldingOfArrayNonOSSA +sil @testConstantFoldingOfArrayNonOSSA : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + + // Use the arguments property of OSLogMessageArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %4 = struct_extract %2 : $OSLogMessageArrayStub, #OSLogMessageArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationArrayStub, #OSLogInterpolationArrayStub.arguments + %6 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + release_value %2 : $OSLogMessageArrayStub + %8 = tuple () + return %8 : $() + // CHECK: [[ELEM1:%[0-9]+]] = integer_literal $Builtin.Int64, 99 + // CHECK: [[ELEM1INT:%[0-9]+]] = struct $Int64 ([[ELEM1]] : $Builtin.Int64) + // CHECK: [[ELEM2:%[0-9]+]] = integer_literal $Builtin.Int64, 98 + // CHECK: [[ELEM2INT:%[0-9]+]] = struct $Int64 ([[ELEM2]] : $Builtin.Int64) + // CHECK: [[ELEM3:%[0-9]+]] = integer_literal $Builtin.Int64, 90 + // CHECK: [[ELEM3INT:%[0-9]+]] = struct $Int64 ([[ELEM3]] : $Builtin.Int64) + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 3 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: [[ARRAY:%[0-9]+]] = tuple_extract [[TUPLE]] : $(Array, Builtin.RawPointer), 0 + // CHECK: [[STORAGEPTR:%[0-9]+]] = tuple_extract [[TUPLE]] : $(Array, Builtin.RawPointer), 1 + // CHECK: [[STORAGEADDR:%[0-9]+]] = pointer_to_address [[STORAGEPTR]] : $Builtin.RawPointer to [strict] $*Int64 + // CHECK: store [[ELEM1INT]] to [[STORAGEADDR]] : $*Int64 + // CHECK: [[INDEX1:%[0-9]+]] = integer_literal $Builtin.Word, 1 + // CHECK: [[INDEXADDR1:%[0-9]+]] = index_addr [[STORAGEADDR]] : $*Int64, [[INDEX1]] : $Builtin.Word + // CHECK: store [[ELEM2INT]] to [[INDEXADDR1]] : $*Int64 + // CHECK: [[INDEX2:%[0-9]+]] = integer_literal $Builtin.Word, 2 + // CHECK: [[INDEXADDR2:%[0-9]+]] = index_addr [[STORAGEADDR]] : $*Int64, [[INDEX2]] : $Builtin.Word + // CHECK: store [[ELEM3INT]] to [[INDEXADDR2]] : $*Int64 + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArray + // CHECK: apply [[USEREF]]([[ARRAY]]) + // CHECK: release_value [[ARRAY]] : $Array +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageStubEmptyArrayInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub { +bb0(%0 : $Builtin.Int1): + // Create an empty array + %1 = integer_literal $Builtin.Word, 0 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array, Builtin.RawPointer), 0 + + // Create an instance of OSLogMessageArrayStub using the above array. + %20 = struct $OSLogInterpolationArrayStub(%4 : $Array) + %21 = struct $OSLogMessageArrayStub(%20 : $OSLogInterpolationArrayStub) + return %21 : $OSLogMessageArrayStub +} + +// CHECK-LABEL: @testConstantFoldingOfEmptyArray +sil [ossa] @testConstantFoldingOfEmptyArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageStubEmptyArrayInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + + // Use the arguments property of OSLogMessageArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageArrayStub + %4 = struct_extract %3 : $OSLogMessageArrayStub, #OSLogMessageArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationArrayStub, #OSLogInterpolationArrayStub.arguments + %6 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + end_borrow %3 : $OSLogMessageArrayStub + destroy_value %2 : $OSLogMessageArrayStub + %8 = tuple () + return %8 : $() + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 0 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: ([[ARRAY:%[0-9]+]], [[STORAGEPTR:%[0-9]+]]) = destructure_tuple [[TUPLE]] + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[ARRAY]] + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArray + // CHECK: apply [[USEREF]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[ARRAY]] : $Array +} + +/// A simplified stub for OSLogInterpolation type for testing folding of array +/// of strings. +struct OSLogInterpolationStringArrayStub { + var arguments: [String] +} + +/// A simplified stub for OSLogMessage for testing folding of array of strings. +struct OSLogMessageStringArrayStub { + var interpolation: OSLogInterpolationStringArrayStub +} + +/// A stub for OSLogMessage.init. The os_log optimization is driven by this +/// function. This function must take at least one argument which is required +/// by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageStringArrayStubInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStringArrayStub { +bb0(%0 : $String): + // Create an array with one element "a" + %1 = integer_literal $Builtin.Word, 1 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array, Builtin.RawPointer), 0 + %5 = tuple_extract %3 : $(Array, Builtin.RawPointer), 1 + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*String + store %0 to %6 : $*String + + // Create an instance of OSLogMessageArrayStub using the above array. + %20 = struct $OSLogInterpolationStringArrayStub(%4 : $Array) + %21 = struct $OSLogMessageStringArrayStub(%20 : $OSLogInterpolationStringArrayStub) + return %21 : $OSLogMessageStringArrayStub +} + +/// A function that models the use of an array. +sil @useArrayString: $@convention(thin) (@guaranteed Array) -> () + +// CHECK-LABEL: @testConstantFoldingOfStringArray +sil [ossa] @testConstantFoldingOfStringArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStringArrayStub instance. + %10 = string_literal utf8 "ab" + %11 = integer_literal $Builtin.Word, 2 + %12 = integer_literal $Builtin.Int1, -1 + %13 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %14 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %15 = apply %14(%10, %11, %12, %13) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %1 = function_ref @oslogMessageStringArrayStubInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStringArrayStub + %2 = apply %1(%15) : $@convention(thin) (@owned String) -> @owned OSLogMessageStringArrayStub + + // Use the arguments property of OSLogMessageStringArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageStringArrayStub + %4 = struct_extract %3 : $OSLogMessageStringArrayStub, #OSLogMessageStringArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationStringArrayStub, #OSLogInterpolationStringArrayStub.arguments + %6 = function_ref @useArrayString : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + end_borrow %3 : $OSLogMessageStringArrayStub + destroy_value %2 : $OSLogMessageStringArrayStub + %8 = tuple () + return %8 : $() + // Skip the first instance of "ab". + // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" + // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" + // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 1 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: ([[ARRAY:%[0-9]+]], [[STORAGEPTR:%[0-9]+]]) = destructure_tuple [[TUPLE]] + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[ARRAY]] + // CHECK: [[STORAGEADDR:%[0-9]+]] = pointer_to_address [[STORAGEPTR]] : $Builtin.RawPointer to [strict] $*String + // CHECK: store [[STRINGCONST]] to [init] [[STORAGEADDR]] : $*String + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArrayString + // CHECK: apply [[USEREF]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[ARRAY]] : $Array +} From 633bc7947ddd77fa8e885a21246ececaf9b39f59 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Thu, 7 Nov 2019 17:43:12 -0800 Subject: [PATCH 134/283] [OSLogOptimization] Improve the OSLogOptimization pass so that it can fold a symbolic closure, which is the representation of a closure literal in the constant evaluator. This commit improves the function emitCodeForSymbolicValue so that given a symbolic closure it can emit SIL code for constructing the closure. This improvement enables folding the arguments array, which is an array of closures, by its constant value inferred by constant evaluating the new OSLog calls. --- .../Mandatory/OSLogOptimization.cpp | 156 +++++++-- .../OSLogPrototypeCompileTest.sil | 320 ++++++++++++++++++ .../OSLogPrototypeCompileTest.swift | 75 ++++ 3 files changed, 526 insertions(+), 25 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index a418a3f3c7f32..e6d183876b1aa 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -282,42 +282,52 @@ evaluateOrSkip(ConstExprStepEvaluator &stepEval, /// Return true iff the given value is a stdlib Int or Bool and it not a direct /// construction of Int or Bool. -static bool isFoldableIntOrBool(SILValue value, SILInstruction *definingInst, - ASTContext &astContext) { - assert(definingInst); - return !isa(definingInst) && - isIntegerOrBoolType(value->getType(), astContext); +static bool isFoldableIntOrBool(SILValue value, ASTContext &astContext) { + return isIntegerOrBoolType(value->getType(), astContext) && + !isa(value); } /// Return true iff the given value is a string and is not an initialization /// of an string from a string literal. -static bool isFoldableString(SILValue value, SILInstruction *definingInst, - ASTContext &astContext) { - assert(definingInst); +static bool isFoldableString(SILValue value, ASTContext &astContext) { return isStringType(value->getType(), astContext) && - !getStringMakeUTF8Init(definingInst); + (!isa(value) || + !getStringMakeUTF8Init(cast(value))); } /// Return true iff the given value is an array and is not an initialization /// of an array from an array literal. -static bool isFoldableArray(SILValue value, SILInstruction *definingInst, - ASTContext &astContext) { - assert(definingInst); +static bool isFoldableArray(SILValue value, ASTContext &astContext) { if (!isArrayType(value->getType(), astContext)) return false; - - // Check if this is not an initialization of an array from a literal. + // If value is an initialization of an array from a literal or an empty array + // initializer, it need not be folded. Arrays constructed from literals use a + // function with semantics: "array.uninitialized_intrinsic" that returns + // a pair, where the first element of the pair is the array. + SILInstruction *definingInst = value->getDefiningInstruction(); + if (!definingInst) + return true; SILInstruction *constructorInst = definingInst; if (isa(definingInst) || isa(definingInst)) { constructorInst = definingInst->getOperand(0)->getDefiningInstruction(); } - ApplyInst *apply = dyn_cast(definingInst); - if (!apply) + if (!constructorInst || !isa(constructorInst)) return true; - SILFunction *callee = apply->getCalleeFunction(); - return !callee || !callee->hasSemanticsAttr("array.init.empty") || - !callee->hasSemanticsAttr("array.uninitialized_intrinsic"); + SILFunction *callee = cast(constructorInst)->getCalleeFunction(); + return !callee || + (!callee->hasSemanticsAttr("array.init.empty") && + !callee->hasSemanticsAttr("array.uninitialized_intrinsic")); +} + +/// Return true iff the given value is a closure but is not a creation of a +/// closure e.g., through partial_apply or thin_to_thick_function or +/// convert_function. +static bool isFoldableClosure(SILValue value) { + return value->getType().is() && + (!isa(value) && !isa(value) && + !isa(value) && + !isa(value)); } /// Check whether a SILValue is foldable. String, integer, array and @@ -337,9 +347,9 @@ static bool isSILValueFoldable(SILValue value) { !isa(definingInst) && !isa(definingInst) && !isa(definingInst) && - (isFoldableIntOrBool(value, definingInst, astContext) || - isFoldableString(value, definingInst, astContext) || - isFoldableArray(value, definingInst, astContext))); + (isFoldableIntOrBool(value, astContext) || + isFoldableString(value, astContext) || + isFoldableArray(value, astContext) || isFoldableClosure(value))); } /// Diagnose failure during evaluation of a call to a constant-evaluable @@ -585,6 +595,39 @@ static SILValue emitCodeForConstantArray(ArrayRef elements, return arraySIL; } +/// Given a SILValue \p value, return the instruction immediately following the +/// definition of the value. That is, if the value is defined by an +/// instruction, return the instruction following the definition. Otherwise, if +/// the value is a basic block parameter, return the first instruction of the +/// basic block. +SILInstruction *getInstructionFollowingValueDefinition(SILValue value) { + SILInstruction *definingInst = value->getDefiningInstruction(); + if (definingInst) { + return &*std::next(definingInst->getIterator()); + } + // Here value must be a basic block argument. + SILBasicBlock *bb = value->getParentBlock(); + return &*bb->begin(); +} + +/// Given a SILValue \p value, create a copy of the value using copy_value in +/// OSSA or retain in non-OSSA, if \p value is a non-trivial type. Otherwise, if +/// \p value is a trivial type, return the value itself. +SILValue makeOwnedCopyOfSILValue(SILValue value, SILFunction &fun) { + SILType type = value->getType(); + if (type.isTrivial(fun)) + return value; + assert(!type.isAddress() && "cannot make owned copy of addresses"); + + SILInstruction *instAfterValueDefinition = + getInstructionFollowingValueDefinition(value); + SILLocation copyLoc = instAfterValueDefinition->getLoc(); + SILBuilderWithScope builder(instAfterValueDefinition); + const TypeLowering &typeLowering = builder.getTypeLowering(type); + SILValue copy = typeLowering.emitCopyValue(builder, copyLoc, value); + return copy; +} + /// Generate SIL code that computes the constant given by the symbolic value /// `symVal`. Note that strings and struct-typed constant values will require /// multiple instructions to be emitted. @@ -684,6 +727,61 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, elementSILValues, expectedType->getCanonicalType(), builder, loc); return arraySIL; } + case SymbolicValue::Closure: { + assert(expectedType->is() || + expectedType->is()); + + SymbolicClosure *closure = symVal.getClosure(); + SubstitutionMap callSubstMap = closure->getCallSubstitutionMap(); + SILModule &module = builder.getModule(); + ArrayRef captures = closure->getCaptures(); + + // Recursively emit code for all captured values that are mapped to a + // symbolic value. If there is a captured value that is not mapped + // to a symbolic value, use the captured value as such (after possibly + // copying non-trivial captures). + SmallVector capturedSILVals; + for (SymbolicClosureArgument capture : captures) { + SILValue captureOperand = capture.first; + Optional captureSymVal = capture.second; + if (!captureSymVal) { + SILFunction &fun = builder.getFunction(); + assert(captureOperand->getFunction() == &fun && + "non-constant captured arugment not defined in this function"); + // If the captureOperand is a non-trivial value, it should be copied + // as it now used in a new folded closure. + SILValue captureCopy = makeOwnedCopyOfSILValue(captureOperand, fun); + capturedSILVals.push_back(captureCopy); + continue; + } + // Here, we have a symbolic value for the capture. Therefore, use it to + // create a new constant at this point. Note that the captured operand + // type may have generic parameters which has to be substituted with the + // substitution map that was inferred by the constant evaluator at the + // partial-apply site. + SILType operandType = captureOperand->getType(); + SILType captureType = operandType.subst(module, callSubstMap); + SILValue captureSILVal = emitCodeForSymbolicValue( + captureSymVal.getValue(), captureType.getASTType(), builder, loc, + stringInfo); + capturedSILVals.push_back(captureSILVal); + } + + FunctionRefInst *functionRef = + builder.createFunctionRef(loc, closure->getTarget()); + SILType closureType = closure->getClosureType(); + ParameterConvention convention = + closureType.getAs()->getCalleeConvention(); + PartialApplyInst *papply = builder.createPartialApply( + loc, functionRef, callSubstMap, capturedSILVals, convention); + // The type of the created closure must be a lowering of the expected type. + SILType resultType = papply->getType(); + CanType expectedCanType = expectedType->getCanonicalType(); + assert(expectedType->is() + ? resultType.getASTType() == expectedCanType + : resultType.is()); + return papply; + } default: { llvm_unreachable("Symbolic value kind is not supported"); } @@ -997,8 +1095,9 @@ static void constantFold(SILInstruction *start, /// marks the begining of the string interpolation that is used to create an /// OSLogMessage instance. This function traverses the backward data-dependence /// chain of the given OSLogMessage initializer: \p oslogInit. As a special case -/// it avoid chasing the data-dependenceies through a partial-apply as they are -/// considered as constants. +/// it avoids chasing the data-dependencies from the captured values of +/// partial-apply instructions, as a partial apply instruction is considered as +/// a constant regardless of the constantness of its captures. static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) { auto oslogInitCallSite = FullApplySite(oslogInit); SILFunction *callee = oslogInitCallSite.getCalleeFunction(); @@ -1023,7 +1122,14 @@ static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) { // Partial applies are used to capture the dynamic arguments passed to // the string interpolation. Their arguments are not required to be // known at compile time and they need not be constant evaluated. - // Therefore, do not follow this dependency chain. + // Therefore, follow only the dependency chain along function ref operand. + SILInstruction *definingInstruction = + inst->getOperand(0)->getDefiningInstruction(); + assert(definingInstruction && "no function-ref operand in partial-apply"); + if (seenInstructions.insert(definingInstruction).second) { + worklist.push_back(definingInstruction); + candidateStartInstructions.insert(definingInstruction); + } continue; } diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil index 6e3c8e70bd6c8..3193ce9c5cd8c 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.sil +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -665,3 +665,323 @@ bb0: // CHECK: end_borrow [[BORROW]] // CHECK: destroy_value [[ARRAY]] : $Array } + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageArrayInterpolationInit : $@convention(thin) (@owned OSLogInterpolationArrayStub) + -> @owned OSLogMessageArrayStub { +bb0(%0 : @owned $OSLogInterpolationArrayStub): + %1 = struct $OSLogMessageArrayStub(%0 : $OSLogInterpolationArrayStub) + return %1 : $OSLogMessageArrayStub +} + +// CHECK-LABEL: @testNoFoldingOfArrayLiterals +sil [ossa] @testNoFoldingOfArrayLiterals : $@convention(thin) () -> () { +bb0: + // Create an empty array + %1 = integer_literal $Builtin.Word, 0 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + (%4, %ignore) = destructure_tuple %3 : $(Array, Builtin.RawPointer) + %5 = copy_value %4 : $Array + %6 = struct $OSLogInterpolationArrayStub(%4 : $Array) + %7 = function_ref @oslogMessageArrayInterpolationInit : $@convention(thin) (@owned OSLogInterpolationArrayStub) -> @owned OSLogMessageArrayStub + %8 = apply %7(%6) : $@convention(thin) (@owned OSLogInterpolationArrayStub) -> @owned OSLogMessageArrayStub + // Use the array literal. + %9 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %10 = apply %9(%5) : $@convention(thin) (@guaranteed Array) -> () + destroy_value %5 : $Array + destroy_value %8 : $OSLogMessageArrayStub + %11 = tuple () + return %11 : $() + // There should be only one instance of _allocateUninitializedArray + // CHECK-LABEL: function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-NOT: function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF +} + +// Check folding of closures by the OSLogOptimization pass. + +/// A simplified stub for OSLogInterpolation type for testing closure folding. +struct OSLogInterpolationClosureStub { + var closure: () -> Int32 +} + +/// A simplified stub for OSLogMessage for testing closure folding. +struct OSLogMessageClosureStub { + var interpolation: OSLogInterpolationClosureStub +} + +sil private @idFunction : $@convention(thin) (Int32) -> Int32 { +bb0(%0 : $Int32): + return %0 : $Int32 +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub { +bb0(%0 : $Builtin.Int1): + %7 = integer_literal $Builtin.Int32, 81 + %8 = struct $Int32 (%7 : $Builtin.Int32) + %9 = function_ref @idFunction : $@convention(thin) (Int32) -> Int32 + %10 = partial_apply [callee_guaranteed] %9(%8) : $@convention(thin) (Int32) -> Int32 + + // Create an instance of OSLogMessageClosureStub using the above closure. + %20 = struct $OSLogInterpolationClosureStub(%10 : $@callee_guaranteed () -> Int32) + %21 = struct $OSLogMessageClosureStub(%20 : $OSLogInterpolationClosureStub) + return %21 : $OSLogMessageClosureStub +} + +/// A function that models the use of a closure. +sil @useClosure: $@convention(thin) (@callee_guaranteed () -> Int32) -> () + +// CHECK-LABEL: @testConstantFoldingOfClosure +sil [ossa] @testConstantFoldingOfClosure : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageClosureStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + + // Use the closure property of OSLogMessageClosureStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageClosureStub + %4 = struct_extract %3 : $OSLogMessageClosureStub, #OSLogMessageClosureStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationClosureStub, #OSLogInterpolationClosureStub.closure + %6 = function_ref @useClosure : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + %7 = apply %6(%5) : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + end_borrow %3 : $OSLogMessageClosureStub + destroy_value %2 : $OSLogMessageClosureStub + %8 = tuple () + return %8 : $() + // CHECK: [[LIT:%[0-9]+]] = integer_literal $Builtin.Int32, 81 + // CHECK: [[INT:%[0-9]+]] = struct $Int32 ([[LIT]] : $Builtin.Int32) + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @idFunction + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[INT]]) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 + // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +sil private @constantFunction : $@convention(thin) () -> Int32 { +bb0: + %0 = integer_literal $Builtin.Int32, 91 + %1 = struct $Int32 (%0 : $Builtin.Int32) + return %1 : $Int32 +} + +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageThinClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub { +bb0(%0 : $Builtin.Int1): + %7 = integer_literal $Builtin.Int32, 81 + %8 = struct $Int32 (%7 : $Builtin.Int32) + %9 = function_ref @constantFunction : $@convention(thin) () -> Int32 + %10 = thin_to_thick_function %9 : $@convention(thin) () -> Int32 to $@callee_guaranteed () -> Int32 + // Create an instance of OSLogMessageClosureStub using the above closure. + %20 = struct $OSLogInterpolationClosureStub(%10 : $@callee_guaranteed () -> Int32) + %21 = struct $OSLogMessageClosureStub(%20 : $OSLogInterpolationClosureStub) + return %21 : $OSLogMessageClosureStub +} + +// CHECK-LABEL: @testConstantFoldingOfThinClosure +sil [ossa] @testConstantFoldingOfThinClosure : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageClosureStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageThinClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + + %3 = begin_borrow %2 : $OSLogMessageClosureStub + %4 = struct_extract %3 : $OSLogMessageClosureStub, #OSLogMessageClosureStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationClosureStub, #OSLogInterpolationClosureStub.closure + %6 = function_ref @useClosure : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + %7 = apply %6(%5) : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + end_borrow %3 : $OSLogMessageClosureStub + destroy_value %2 : $OSLogMessageClosureStub + %8 = tuple () + return %8 : $() + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @constantFunction + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]() + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 + // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +/// A simplified stub for OSLogInterpolation type for testing folding of +/// closures with non-trivial captures. +struct OSLogInterpolationStringCapture { + var closure: () -> String +} + +/// A simplified stub for OSLogMessage for testing folding of closures with +/// non-trivial captures. +struct OSLogMessageStringCapture { + var interpolation: OSLogInterpolationStringCapture +} + +sil [ossa] @idString : $@convention(thin) (@guaranteed String) -> @owned String { +bb0(%0 : @guaranteed $String): + %1 = copy_value %0 : $String + return %1 : $String +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageStringCaptureInit : $@convention(thin) (@owned OSLogInterpolationStringCapture) + -> @owned OSLogMessageStringCapture { +bb0(%0 : @owned $OSLogInterpolationStringCapture): + %5 = struct $OSLogMessageStringCapture(%0 : $OSLogInterpolationStringCapture) + return %5 : $OSLogMessageStringCapture +} + +sil @useStringCapture: $@convention(thin) (@callee_guaranteed () -> @owned String) -> () + +// CHECK-LABEL: @testConstantFoldingOfStringCapture +sil [ossa] @testConstantFoldingOfStringCapture : $@convention(thin) (@guaranteed String) -> () { +bb0(%0 : @guaranteed $String): + %1 = function_ref @idString : $@convention(thin) (@guaranteed String) -> @owned String + %2 = copy_value %0 : $String + %3 = partial_apply [callee_guaranteed] %1(%2) : $@convention(thin) (@guaranteed String) -> @owned String + %4 = struct $OSLogInterpolationStringCapture(%3 : $@callee_guaranteed () -> @owned String) + %5 = function_ref @oslogMessageStringCaptureInit : $@convention(thin) (@owned OSLogInterpolationStringCapture) -> @owned OSLogMessageStringCapture + %6 = apply %5(%4) : $@convention(thin) (@owned OSLogInterpolationStringCapture) -> @owned OSLogMessageStringCapture + + %13 = begin_borrow %6 : $OSLogMessageStringCapture + %14 = struct_extract %13 : $OSLogMessageStringCapture, #OSLogMessageStringCapture.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStringCapture, #OSLogInterpolationStringCapture.closure + %16 = function_ref @useStringCapture : $@convention(thin) (@callee_guaranteed () -> @owned String) -> () + %17 = apply %16(%15) : $@convention(thin) (@callee_guaranteed () -> @owned String) -> () + end_borrow %13 : $OSLogMessageStringCapture + destroy_value %6 : $OSLogMessageStringCapture + %18 = tuple () + return %18 : $() + // CHECK: [[FUNREFORIG:%[0-9]+]] = function_ref @idString + // CHECK: [[ORIGCAPTURE:%[0-9]+]] = copy_value %0 : $String + // CHECK: [[NEWCAPTURE:%[0-9]+]] = copy_value [[ORIGCAPTURE]] : $String + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @idString + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[NEWCAPTURE]]) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> @owned String + // CHECK: [[USE:%[0-9]+]] = function_ref @useStringCapture + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +sil [ossa] @genericFunction : $@convention(thin) (@in_guaranteed U, @in_guaranteed V) -> Int32 { +bb0(%0 : $*U, %1 : $*V): + %2 = integer_literal $Builtin.Int32, 99 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageGenericClosureInit : $@convention(thin) (@owned OSLogInterpolationClosureStub) -> @owned OSLogMessageClosureStub { +bb0(%0 : @owned $OSLogInterpolationClosureStub): + %5 = struct $OSLogMessageClosureStub(%0 : $OSLogInterpolationClosureStub) + return %5 : $OSLogMessageClosureStub +} + +// CHECK-LABEL: @testConstantFoldingOfGenericClosure +sil [ossa] @testConstantFoldingOfGenericClosure : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int1, 1 + %1 = alloc_stack $Int64 + %2 = alloc_stack $Bool + %3 = struct $Bool (%0 : $Builtin.Int1) + store %3 to [trivial] %2 : $*Bool + %4 = integer_literal $Builtin.Int64, 81 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %1 : $*Int64 + + %6 = function_ref @genericFunction : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Int32 + %7 = partial_apply [callee_guaranteed] %6(%1, %2) : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Int32 + %8 = struct $OSLogInterpolationClosureStub(%7 : $@callee_guaranteed () -> Int32) + %11 = function_ref @oslogMessageGenericClosureInit : $@convention(thin) (@owned OSLogInterpolationClosureStub) -> @owned OSLogMessageClosureStub + %12 = apply %11(%8) : $@convention(thin) (@owned OSLogInterpolationClosureStub) -> @owned OSLogMessageClosureStub + + %13 = begin_borrow %12 : $OSLogMessageClosureStub + %14 = struct_extract %13 : $OSLogMessageClosureStub, #OSLogMessageClosureStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationClosureStub, #OSLogInterpolationClosureStub.closure + %16 = function_ref @useClosure : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + %17 = apply %16(%15) : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + end_borrow %13 : $OSLogMessageClosureStub + destroy_value %12 : $OSLogMessageClosureStub + dealloc_stack %2 : $*Bool + dealloc_stack %1 : $*Int64 + %18 = tuple () + return %18 : $() + // CHECK: [[FUNREFORIG:%[0-9]+]] = function_ref @genericFunction + // CHECK: [[CLOSUREORIG:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREFORIG]]([[CAPTURE1:%[0-9]+]], [[CAPTURE2:%[0-9]+]]) + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @genericFunction + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[CAPTURE1]], [[CAPTURE2]]) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 + // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +// Check folding of array of closures. This is essentially the feature needed +// by the OSLog overlay. + +/// A simplified stub for OSLogInterpolation type for testing folding of array +/// of closures. +struct OSLogInterpolationClosureArrayStub { + var closure: [() -> Int32] +} + +/// A simplified stub for OSLogMessage for testing folding of array of closures. +struct OSLogMessageClosureArrayStub { + var interpolation: OSLogInterpolationClosureArrayStub +} + +sil private @closure1 : $@convention(thin) (Int32) -> Int32 { +bb0(%0 : $Int32): + return %0 : $Int32 +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageClosureArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureArrayStub { +bb0(%0 : $Builtin.Int1): + %1 = integer_literal $Builtin.Word, 1 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2<() -> Int32>(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array<() -> Int32>, Builtin.RawPointer), 0 + %5 = tuple_extract %3 : $(Array<() -> Int32>, Builtin.RawPointer), 1 + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*@callee_guaranteed () -> Int32 + %7 = integer_literal $Builtin.Int32, 81 + %8 = struct $Int32 (%7 : $Builtin.Int32) + %9 = function_ref @closure1 : $@convention(thin) (Int32) -> Int32 + %10 = partial_apply [callee_guaranteed] %9(%8) : $@convention(thin) (Int32) -> Int32 + store %10 to %6 : $*@callee_guaranteed () -> Int32 + + // Create an instance of OSLogMessageClosureArrayStub using the above array + // of closures. + %20 = struct $OSLogInterpolationClosureArrayStub(%4 : $Array<() -> Int32>) + %21 = struct $OSLogMessageClosureArrayStub(%20 : $OSLogInterpolationClosureArrayStub) + return %21 : $OSLogMessageClosureArrayStub +} + +/// A function that models the use of an array of closures. +sil @useClosureArray: $@convention(thin) (@guaranteed Array<() -> Int32>) -> () + +// CHECK-LABEL: @testConstantFoldingOfClosureArray +sil [ossa] @testConstantFoldingOfClosureArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageClosureArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageClosureArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureArrayStub + + // Use the arguments property of OSLogMessageClosureArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageClosureArrayStub + %4 = struct_extract %3 : $OSLogMessageClosureArrayStub, #OSLogMessageClosureArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationClosureArrayStub, #OSLogInterpolationClosureArrayStub.closure + %6 = function_ref @useClosureArray : $@convention(thin) (@guaranteed Array<() -> Int32>) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array<() -> Int32>) -> () + end_borrow %3 : $OSLogMessageClosureArrayStub + destroy_value %2 : $OSLogMessageClosureArrayStub + %8 = tuple () + return %8 : $() +} diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index fde045984c4be..d0678c8af61c2 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -44,6 +44,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest34testInterpolationWithFormatOptionsL_1hy0aB06LoggerV_tF @@ -77,6 +88,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest44testInterpolationWithFormatOptionsAndPrivacyL_1hy0aB06LoggerV_tF @@ -113,6 +135,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest38testInterpolationWithMultipleArgumentsL_1hy0aB06LoggerV_tF @@ -155,6 +188,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 3 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 9 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest25testLogMessageWithoutDataL_1hy0aB06LoggerV_tF @@ -192,6 +236,16 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 0 + + // Check whether argument array is folded. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 0 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testEscapingOfPercentsL_1hy0aB06LoggerV_tF @@ -262,6 +316,16 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 48 + + // Check whether argument array is folded. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 144 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testInt32InterpolationL_1hy0aB06LoggerV_tF @@ -334,6 +398,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 2 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } } From 5b38c1efb494703b3e92b3442327b95c0a42d9ba Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 12 Nov 2019 13:49:15 -0800 Subject: [PATCH 135/283] build: adjust flags to XCTest cmake invocation Update the cmake invocation for XCTest to enable switching to a newer CMake version. --- utils/build-script-impl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 5813cbc8d124b..c011fcfd47f67 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2388,15 +2388,18 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_BUILD_TYPE:STRING="${XCTEST_BUILD_TYPE}" -DCMAKE_C_COMPILER:PATH="${LLVM_BIN}/clang" -DCMAKE_CXX_COMPILER:PATH="${LLVM_BIN}/clang++" - -DCMAKE_SWIFT_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" + -DCMAKE_Swift_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" -DCMAKE_INSTALL_LIBDIR:PATH="lib" + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules + -DFoundation_DIR=$(build_directory ${host} foundation)/cmake/modules + -DLLVM_DIR=$(build_directory ${host} llvm)/lib/cmake/llvm + -DXCTEST_PATH_TO_LIBDISPATCH_SOURCE:PATH=${LIBDISPATCH_SOURCE_DIR} -DXCTEST_PATH_TO_LIBDISPATCH_BUILD:PATH=$(build_directory ${host} libdispatch) - -DXCTEST_PATH_TO_FOUNDATION_BUILD:PATH=${FOUNDATION_BUILD_DIR} - + -DCMAKE_SWIFT_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" -DCMAKE_PREFIX_PATH:PATH=$(build_directory ${host} llvm) -DENABLE_TESTING=YES From bf4aaacb020e88700c81b8c8cd27fb0ca7549e7b Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 12 Nov 2019 19:35:05 -0800 Subject: [PATCH 136/283] Remove @frozen without library evolution warning This warning is produced if you annotate an enum with `@frozen` but build without library evolution enabled. The problem is that if you only build with library evolution enabled for release builds, this leaves you with a warning for all debug builds. The downside of this change is that if you never build with library evolution, you may have this unnecessary annotation without noticing. --- include/swift/AST/DiagnosticsSema.def | 2 -- lib/Sema/TypeCheckAttr.cpp | 1 - test/Serialization/attr-invalid.swift | 4 ++-- test/decl/enum/frozen-nonresilient.swift | 6 +++--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index df60139eea9e4..80523701cab2c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1481,8 +1481,6 @@ ERROR(indirect_case_without_payload,none, "enum case %0 without associated value cannot be 'indirect'", (Identifier)) ERROR(indirect_case_in_indirect_enum,none, "enum case in 'indirect' enum cannot also be 'indirect'", ()) -WARNING(enum_frozen_nonresilient,none, - "%0 has no effect without -enable-library-evolution", (DeclAttribute)) WARNING(enum_frozen_nonpublic,none, "%0 has no effect on non-public enums", (DeclAttribute)) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index acf1b4882c8cb..0d188bacbac77 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2464,7 +2464,6 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) { if (auto *ED = dyn_cast(D)) { if (!ED->getModuleContext()->isResilient()) { - diagnoseAndRemoveAttr(attr, diag::enum_frozen_nonresilient, attr); return; } diff --git a/test/Serialization/attr-invalid.swift b/test/Serialization/attr-invalid.swift index 623ee022bf45d..5c32dd9974f49 100644 --- a/test/Serialization/attr-invalid.swift +++ b/test/Serialization/attr-invalid.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/attr.swiftmodule %s -verify +// RUN: %target-swift-frontend -emit-module -o %t/attr.swiftmodule %s -verify -warnings-as-errors // RUN: llvm-bcanalyzer -dump %t/attr.swiftmodule | %FileCheck -check-prefix=CHECK-NON-RESILIENT %s // RUN: %target-swift-frontend -emit-module -o %t/attr_resilient.swiftmodule -enable-library-evolution -warnings-as-errors %s // RUN: llvm-bcanalyzer -dump %t/attr_resilient.swiftmodule | %FileCheck -check-prefix=CHECK-RESILIENT %s @@ -8,7 +8,7 @@ // CHECK-RESILIENT: Frozen_DECL_ATTR // CHECK-NON-RESILIENT-NOT: Frozen_DECL_ATTR -@frozen // expected-warning {{@frozen has no effect without -enable-library-evolution}} +@frozen // expected-no-warning public enum SomeEnum { case x } diff --git a/test/decl/enum/frozen-nonresilient.swift b/test/decl/enum/frozen-nonresilient.swift index c7682ecb0e7ca..34312ec89b3c7 100644 --- a/test/decl/enum/frozen-nonresilient.swift +++ b/test/decl/enum/frozen-nonresilient.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -warnings-as-errors -@frozen public enum Exhaustive {} // expected-warning {{@frozen has no effect without -enable-library-evolution}} {{1-9=}} +@frozen public enum Exhaustive {} // expected-no-warning -@frozen enum NotPublic {} // expected-warning {{@frozen has no effect without -enable-library-evolution}} {{1-9=}} +@frozen enum NotPublic {} // expected-no-warning From e587c3255cd86b8fb3441b7939df1d7b38b52d3e Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Fri, 16 Aug 2019 14:00:24 -0700 Subject: [PATCH 137/283] Convert NT paths to DOSPath when resolving Symlinks GetFinalPathNameByHandleW returns NTPaths (\\?\ Paths) which is an issue as they don't match the paths stored in the internal dictionaries which are DOS paths. --- .../lib/SwiftLang/SwiftLangSupport.cpp | 32 ++------------- .../tools/sourcekitd-test/sourcekitd-test.cpp | 41 +++---------------- 2 files changed, 9 insertions(+), 64 deletions(-) diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index 96b7a55b05e75..a0993222b1938 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -911,36 +911,10 @@ void SwiftLangSupport::printMemberDeclDescription(const swift::ValueDecl *VD, std::string SwiftLangSupport::resolvePathSymlinks(StringRef FilePath) { std::string InputPath = FilePath; -#if !defined(_WIN32) - char full_path[MAXPATHLEN]; - if (const char *path = realpath(InputPath.c_str(), full_path)) - return path; - - return InputPath; -#else - wchar_t full_path[MAX_PATH] = {0}; - llvm::SmallVector utf16Path; - llvm::convertUTF8ToUTF16String(InputPath.c_str(), utf16Path); - - HANDLE fileHandle = CreateFileW( - (LPCWSTR)utf16Path.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - - if (fileHandle == INVALID_HANDLE_VALUE) + llvm::SmallString<256> output; + if (llvm::sys::fs::real_path(InputPath, output)) return InputPath; - - DWORD numChars = GetFinalPathNameByHandleW(fileHandle, full_path, MAX_PATH, - FILE_NAME_NORMALIZED); - CloseHandle(fileHandle); - std::string utf8Path; - if (numChars > 0 && numChars <= MAX_PATH) { - llvm::ArrayRef pathRef((const char *)full_path, - (const char *)(full_path + numChars)); - return llvm::convertUTF16ToUTF8String(pathRef, utf8Path) ? utf8Path - : InputPath; - } - return InputPath; -#endif + return output.str(); } void SwiftLangSupport::getStatistics(StatisticsReceiver receiver) { diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 6a4f807ba916e..1e565e54a553c 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -49,35 +49,6 @@ using namespace sourcekitd_test; #if defined(_WIN32) namespace { int STDOUT_FILENO = _fileno(stdout); -const constexpr size_t MAXPATHLEN = MAX_PATH + 1; -char *realpath(const char *path, char *resolved_path) { - wchar_t full_path[MAXPATHLEN] = {0}; - llvm::SmallVector utf16Path; - llvm::convertUTF8ToUTF16String(path, utf16Path); - - HANDLE fileHandle = CreateFileW( - (LPCWSTR)utf16Path.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - - if (fileHandle == INVALID_HANDLE_VALUE) - return nullptr; - DWORD success = GetFinalPathNameByHandleW(fileHandle, full_path, MAX_PATH, - FILE_NAME_NORMALIZED); - CloseHandle(fileHandle); - if (!success) return nullptr; - - std::string utf8Path; - llvm::ArrayRef pathRef((const char *)full_path, - (const char *)(full_path + MAX_PATH)); - if (!llvm::convertUTF16ToUTF8String(pathRef, utf8Path)) - return nullptr; - - if (!resolved_path) { - resolved_path = static_cast(malloc(utf8Path.length() + 1)); - } - std::copy(std::begin(utf8Path), std::end(utf8Path), resolved_path); - return resolved_path; -} } #endif @@ -1445,9 +1416,9 @@ static void printCursorInfo(sourcekitd_variant_t Info, StringRef FilenameIn, } std::string Filename = FilenameIn; - char full_path[MAXPATHLEN]; - if (const char *path = realpath(Filename.c_str(), full_path)) - Filename = path; + llvm::SmallString<256> output; + if (!llvm::sys::fs::real_path(Filename, output)) + Filename = output.str(); const char *Kind = sourcekitd_uid_get_string_ptr(KindUID); const char *USR = sourcekitd_variant_dictionary_get_string(Info, KeyUSR); @@ -1617,9 +1588,9 @@ static void printRangeInfo(sourcekitd_variant_t Info, StringRef FilenameIn, } std::string Filename = FilenameIn; - char full_path[MAXPATHLEN]; - if (const char *path = realpath(Filename.c_str(), full_path)) - Filename = path; + llvm::SmallString<256> output; + if (llvm::sys::fs::real_path(Filename, output)) + Filename = output.str(); sourcekitd_variant_t OffsetObj = sourcekitd_variant_dictionary_get_value(Info, KeyOffset); From 0cd4fd979d43b3effc091150c735312f9c2d1d19 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 12 Nov 2019 22:36:34 -0800 Subject: [PATCH 138/283] Remove TypeChecker::getLangOpts() --- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeChecker.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 71dbe2ceb30ce..f1cfc39f830e8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2278,7 +2278,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, if (!result) return Type(); - if (getLangOpts().DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Type-checked expression---\n"; result->dump(log); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index a84765d67a4eb..b9b6a3192c45d 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -561,8 +561,6 @@ class TypeChecker final { TypeChecker& operator=(const TypeChecker&) = delete; ~TypeChecker(); - LangOptions &getLangOpts() const { return Context.LangOpts; } - static Type getArraySliceType(SourceLoc loc, Type elementType); static Type getDictionaryType(SourceLoc loc, Type keyType, Type valueType); static Type getOptionalType(SourceLoc loc, Type elementType); From f4d333d066bb5c65d63554acc296b05fd061f723 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 12 Nov 2019 22:39:49 -0800 Subject: [PATCH 139/283] Sink a bunch of semantic options into TypeCheckerOptions Sink - DebugConstraintSolver - DebugConstraintSolverAttempt - DebugConstraintSolverOnLines - DebugGenericSignatures - DebugForbidTypecheckPrefix - SolverMemoryThreshold - SolverBindingThreshold - SolverShrinkUnsolvedThreshold - SolverDisableShrink - EnableOperatorDesignatedTypes - DisableConstraintSolverPerformanceHacks - SolverEnableOperatorDesignatedTypes --- include/swift/Basic/LangOptions.h | 104 ++++++++--------- lib/Frontend/CompilerInvocation.cpp | 141 ++++++++++++------------ lib/Immediate/REPL.cpp | 4 +- lib/Parse/ParseDecl.cpp | 2 +- lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSGen.cpp | 2 +- lib/Sema/CSRanking.cpp | 14 +-- lib/Sema/CSSimplify.cpp | 4 +- lib/Sema/CSSolver.cpp | 53 ++++----- lib/Sema/CSStep.cpp | 6 +- lib/Sema/CSStep.h | 2 +- lib/Sema/ConstraintGraph.cpp | 26 ++--- lib/Sema/ConstraintSystem.cpp | 6 +- lib/Sema/ConstraintSystem.h | 6 +- lib/Sema/TypeCheckConstraints.cpp | 69 ++++++------ lib/Sema/TypeCheckDecl.cpp | 8 +- lib/Sema/TypeCheckGeneric.cpp | 4 +- lib/Sema/TypeCheckProtocol.cpp | 2 +- lib/Sema/TypeChecker.cpp | 4 +- tools/swift-ide-test/swift-ide-test.cpp | 7 +- 20 files changed, 237 insertions(+), 229 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index b2190db83029d..56bcabfd5946e 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -160,64 +160,19 @@ namespace swift { /// Flags for developers /// - /// Whether we are debugging the constraint solver. - /// - /// This option enables verbose debugging output from the constraint - /// solver. - bool DebugConstraintSolver = false; - - /// Specific solution attempt for which the constraint - /// solver should be debugged. - unsigned DebugConstraintSolverAttempt = 0; - - /// Line numbers to activate the constraint solver debugger. - /// Should be stored sorted. - llvm::SmallVector DebugConstraintSolverOnLines; - /// Enable named lazy member loading. bool NamedLazyMemberLoading = true; - - /// Debug the generic signatures computed by the generic signature builder. - bool DebugGenericSignatures = false; - - /// Triggers llvm fatal_error if typechecker tries to typecheck a decl or an - /// identifier reference with the provided prefix name. - /// This is for testing purposes. - std::string DebugForbidTypecheckPrefix; - - /// Whether to dump debug info for request evaluator cycles. - bool DebugDumpCycles = false; - + /// The path to which we should emit GraphViz output for the complete /// request-evaluator graph. std::string RequestEvaluatorGraphVizPath; - - /// The upper bound, in bytes, of temporary data that can be - /// allocated by the constraint solver. - unsigned SolverMemoryThreshold = 512 * 1024 * 1024; - - unsigned SolverBindingThreshold = 1024 * 1024; - - /// The upper bound to number of sub-expressions unsolved - /// before termination of the shrink phrase of the constraint solver. - unsigned SolverShrinkUnsolvedThreshold = 10; - - /// Disable the shrink phase of the expression type checker. - bool SolverDisableShrink = false; - - /// Disable constraint system performance hacks. - bool DisableConstraintSolverPerformanceHacks = false; - - /// Enable experimental operator designated types feature. - bool EnableOperatorDesignatedTypes = false; - + + /// Whether to dump debug info for request evaluator cycles. + bool DebugDumpCycles = false; + /// Enable SIL type lowering bool EnableSubstSILFunctionTypesForFunctionValues = false; - /// Enable constraint solver support for experimental - /// operator protocol designator feature. - bool SolverEnableOperatorDesignatedTypes = false; - /// Whether to diagnose an ephemeral to non-ephemeral conversion as an /// error. bool DiagnoseInvalidEphemeralnessAsError = false; @@ -503,6 +458,55 @@ namespace swift { /// Indicate that the type checker should skip type-checking non-inlinable /// function bodies. bool SkipNonInlinableFunctionBodies = false; + + /// + /// Flags for developers + /// + + /// Whether we are debugging the constraint solver. + /// + /// This option enables verbose debugging output from the constraint + /// solver. + bool DebugConstraintSolver = false; + + /// Specific solution attempt for which the constraint + /// solver should be debugged. + unsigned DebugConstraintSolverAttempt = 0; + + /// Line numbers to activate the constraint solver debugger. + /// Should be stored sorted. + llvm::SmallVector DebugConstraintSolverOnLines; + + /// Debug the generic signatures computed by the generic signature builder. + bool DebugGenericSignatures = false; + + /// Triggers llvm fatal_error if typechecker tries to typecheck a decl or an + /// identifier reference with the provided prefix name. + /// This is for testing purposes. + std::string DebugForbidTypecheckPrefix; + + /// The upper bound, in bytes, of temporary data that can be + /// allocated by the constraint solver. + unsigned SolverMemoryThreshold = 512 * 1024 * 1024; + + unsigned SolverBindingThreshold = 1024 * 1024; + + /// The upper bound to number of sub-expressions unsolved + /// before termination of the shrink phrase of the constraint solver. + unsigned SolverShrinkUnsolvedThreshold = 10; + + /// Disable the shrink phase of the expression type checker. + bool SolverDisableShrink = false; + + /// Enable experimental operator designated types feature. + bool EnableOperatorDesignatedTypes = false; + + /// Disable constraint system performance hacks. + bool DisableConstraintSolverPerformanceHacks = false; + + /// Enable constraint solver support for experimental + /// operator protocol designator feature. + bool SolverEnableOperatorDesignatedTypes = false; }; } // end namespace swift diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 076ec25ef5af8..2e722403a2864 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -284,18 +284,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableSubstSILFunctionTypesForFunctionValues |= Args.hasArg(OPT_enable_subst_sil_function_types_for_function_values); - Opts.EnableOperatorDesignatedTypes |= - Args.hasArg(OPT_enable_operator_designated_types); - Opts.DiagnoseInvalidEphemeralnessAsError |= Args.hasArg(OPT_enable_invalid_ephemeralness_as_error); - // Always enable operator designated types for the standard library. - Opts.EnableOperatorDesignatedTypes |= FrontendOpts.ParseStdlib; - - Opts.SolverEnableOperatorDesignatedTypes |= - Args.hasArg(OPT_solver_enable_operator_designated_types); - if (auto A = Args.getLastArg(OPT_enable_deserialization_recovery, OPT_disable_deserialization_recovery)) { Opts.EnableDeserializationRecovery @@ -353,9 +344,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.WarnIfASTScopeLookup |= Args.hasArg(OPT_warn_if_astscope_lookup); Opts.LazyASTScopes |= Args.hasArg(OPT_lazy_astscopes); - Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); Opts.NamedLazyMemberLoading &= !Args.hasArg(OPT_disable_named_lazy_member_loading); - Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); if (Args.hasArg(OPT_verify_syntax_tree)) { Opts.BuildSyntaxTree = true; @@ -390,34 +379,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableTestableAttrRequiresTestableModule = A->getOption().matches(OPT_enable_testable_attr_requires_testable_module); } - - if (const Arg *A = Args.getLastArg(OPT_debug_constraints_attempt)) { - unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(10, attempt)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.DebugConstraintSolverAttempt = attempt; - } - } - - for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) { - unsigned line; - if (StringRef(A->getValue()).getAsInteger(10, line)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.DebugConstraintSolverOnLines.push_back(line); - } - } - llvm::sort(Opts.DebugConstraintSolverOnLines); - if (const Arg *A = Args.getLastArg(OPT_debug_forbid_typecheck_prefix)) { - Opts.DebugForbidTypecheckPrefix = A->getValue(); - } - if (Args.getLastArg(OPT_debug_cycles)) Opts.DebugDumpCycles = true; @@ -432,31 +394,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, } } - if (const Arg *A = Args.getLastArg(OPT_solver_memory_threshold)) { - unsigned threshold; - if (StringRef(A->getValue()).getAsInteger(10, threshold)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.SolverMemoryThreshold = threshold; - } - } - - if (const Arg *A = Args.getLastArg(OPT_solver_shrink_unsolved_threshold)) { - unsigned threshold; - if (StringRef(A->getValue()).getAsInteger(10, threshold)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.SolverShrinkUnsolvedThreshold = threshold; - } - } - - if (Args.getLastArg(OPT_solver_disable_shrink)) - Opts.SolverDisableShrink = true; - if (const Arg *A = Args.getLastArg(OPT_value_recursion_threshold)) { unsigned threshold; if (StringRef(A->getValue()).getAsInteger(10, threshold)) { @@ -528,9 +465,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, (Target.isTvOS() && Target.isOSVersionLT(12, 2)) || (Target.isWatchOS() && Target.isOSVersionLT(5, 2)); - Opts.DisableConstraintSolverPerformanceHacks |= - Args.hasArg(OPT_disable_constraint_solver_performance_hacks); - // Must be processed after any other language options that could affect // platform conditions. bool UnsupportedOS, UnsupportedArch; @@ -557,8 +491,9 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, const FrontendOptions &FrontendOpts) { using namespace options; - auto setUnsignedIntegerArgument = [&Args, &Diags]( - options::ID optionID, unsigned radix, unsigned &valueToSet) { + auto setUnsignedIntegerArgument = [&Args, &Diags](options::ID optionID, + unsigned radix, + unsigned &valueToSet) { if (const Arg *A = Args.getLastArg(optionID)) { unsigned attempt; if (StringRef(A->getValue()).getAsInteger(radix, attempt)) { @@ -589,7 +524,75 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, // body skipping. Opts.SkipNonInlinableFunctionBodies |= Args.hasArg(OPT_tbd_is_installapi); - return false; + Opts.DisableConstraintSolverPerformanceHacks |= + Args.hasArg(OPT_disable_constraint_solver_performance_hacks); + + Opts.EnableOperatorDesignatedTypes |= + Args.hasArg(OPT_enable_operator_designated_types); + + // Always enable operator designated types for the standard library. + Opts.EnableOperatorDesignatedTypes |= FrontendOpts.ParseStdlib; + + Opts.SolverEnableOperatorDesignatedTypes |= + Args.hasArg(OPT_solver_enable_operator_designated_types); + + Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); + Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); + + bool HadError = false; + if (const Arg *A = Args.getLastArg(OPT_debug_constraints_attempt)) { + unsigned attempt; + if (StringRef(A->getValue()).getAsInteger(10, attempt)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.DebugConstraintSolverAttempt = attempt; + } + } + + for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) { + unsigned line; + if (StringRef(A->getValue()).getAsInteger(10, line)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.DebugConstraintSolverOnLines.push_back(line); + } + } + llvm::sort(Opts.DebugConstraintSolverOnLines); + + if (const Arg *A = Args.getLastArg(OPT_debug_forbid_typecheck_prefix)) { + Opts.DebugForbidTypecheckPrefix = A->getValue(); + } + + if (const Arg *A = Args.getLastArg(OPT_solver_memory_threshold)) { + unsigned threshold; + if (StringRef(A->getValue()).getAsInteger(10, threshold)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.SolverMemoryThreshold = threshold; + } + } + + if (const Arg *A = Args.getLastArg(OPT_solver_shrink_unsolved_threshold)) { + unsigned threshold; + if (StringRef(A->getValue()).getAsInteger(10, threshold)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.SolverShrinkUnsolvedThreshold = threshold; + } + } + + if (Args.getLastArg(OPT_solver_disable_shrink)) + Opts.SolverDisableShrink = true; + + return HadError; } static bool ParseClangImporterArgs(ClangImporterOptions &Opts, diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 829ed795883b9..4de8035c504ec 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -1162,9 +1162,9 @@ class REPLEnvironment { if (Tok.getText() == "debug") { L.lex(Tok); if (Tok.getText() == "on") { - CI.getASTContext().LangOpts.DebugConstraintSolver = true; + CI.getASTContext().TypeCheckerOpts.DebugConstraintSolver = true; } else if (Tok.getText() == "off") { - CI.getASTContext().LangOpts.DebugConstraintSolver = false; + CI.getASTContext().TypeCheckerOpts.DebugConstraintSolver = false; } else { llvm::outs() << "Unknown :constraints debug command; try :help\n"; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 12aad047d86e3..8e8efc9a30a0d 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6927,7 +6927,7 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, return makeParserCodeCompletionResult(); } - if (Context.LangOpts.EnableOperatorDesignatedTypes) { + if (Context.TypeCheckerOpts.EnableOperatorDesignatedTypes) { if (Tok.is(tok::identifier)) { SyntaxParsingContext GroupCtxt(SyntaxContext, SyntaxKind::IdentifierList); diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index aaac4e4370d38..3d2c4258ddfc4 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -85,7 +85,7 @@ ConstraintSystem::determineBestBindings() { } } - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); bindings.dump(typeVar, log, solverState->depth * 2); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 97ea5e6012294..afdd347f35eb6 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3776,7 +3776,7 @@ Type ConstraintSystem::generateConstraints(Pattern *pattern, } void ConstraintSystem::optimizeConstraints(Expr *e) { - if (getASTContext().LangOpts.DisableConstraintSolverPerformanceHacks) + if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return; SmallVector linkedExprs; diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 6f9df4e0dc40d..64b51dfebe7d7 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -35,7 +35,7 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) { unsigned index = static_cast(kind); CurrentScore.Data[index] += value; - if (getASTContext().LangOpts.DebugConstraintSolver && value > 0) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver && value > 0) { auto &log = getASTContext().TypeCheckerDebug->getStream(); if (solverState) log.indent(solverState->depth * 2); @@ -91,7 +91,7 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) { } bool ConstraintSystem::worseThanBestSolution() const { - if (getASTContext().LangOpts.DisableConstraintSolverPerformanceHacks) + if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return false; if (retainAllSolutions()) @@ -101,7 +101,7 @@ bool ConstraintSystem::worseThanBestSolution() const { CurrentScore <= *solverState->BestScore) return false; - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState->depth * 2) << "(solution is worse than the best solution)\n"; @@ -389,7 +389,7 @@ llvm::Expected CompareDeclSpecializationRequest::evaluate( Evaluator &eval, DeclContext *dc, ValueDecl *decl1, ValueDecl *decl2, bool isDynamicOverloadComparison) const { auto &C = decl1->getASTContext(); - if (C.LangOpts.DebugConstraintSolver) { + if (C.TypeCheckerOpts.DebugConstraintSolver) { auto &log = C.TypeCheckerDebug->getStream(); log << "Comparing declarations\n"; decl1->print(log); @@ -401,7 +401,7 @@ llvm::Expected CompareDeclSpecializationRequest::evaluate( } auto completeResult = [&C](bool result) { - if (C.LangOpts.DebugConstraintSolver) { + if (C.TypeCheckerOpts.DebugConstraintSolver) { auto &log = C.TypeCheckerDebug->getStream(); log << "comparison result: " << (result ? "better" : "not better") << "\n"; @@ -711,7 +711,7 @@ static Type getUnlabeledType(Type type, ASTContext &ctx) { SolutionCompareResult ConstraintSystem::compareSolutions( ConstraintSystem &cs, ArrayRef solutions, const SolutionDiff &diff, unsigned idx1, unsigned idx2) { - if (cs.getASTContext().LangOpts.DebugConstraintSolver) { + if (cs.getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); log.indent(cs.solverState->depth * 2) << "comparing solutions " << idx1 << " and " << idx2 <<"\n"; @@ -1179,7 +1179,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, if (viable.size() == 1) return 0; - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState->depth * 2) << "Comparing " << viable.size() << " viable solutions\n"; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 85bd58b9d617c..9b7984b0fb9ea 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7101,7 +7101,7 @@ Type ConstraintSystem::simplifyAppliedOverloads( // If we have a common result type, bind the expected result type to it. if (commonResultType && !commonResultType->is()) { ASTContext &ctx = getASTContext(); - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(common result type for $T" << fnTypeVar->getID() << " is " @@ -8125,7 +8125,7 @@ static bool isAugmentingFix(ConstraintFix *fix) { bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { auto &ctx = getASTContext(); - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(attempting fix "; diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index f30fb4e32ace3..19d61d81d4ed5 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -68,8 +68,9 @@ Solution ConstraintSystem::finalize() { Solution solution(*this, CurrentScore); // Update the best score we've seen so far. + auto &ctx = getASTContext(); if (!retainAllSolutions()) { - assert(getASTContext().LangOpts.DisableConstraintSolverPerformanceHacks || + assert(ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks || !solverState->BestScore || CurrentScore <= *solverState->BestScore); if (!solverState->BestScore || CurrentScore <= *solverState->BestScore) { @@ -89,7 +90,7 @@ Solution ConstraintSystem::finalize() { break; case FreeTypeVariableBinding::UnresolvedType: - assignFixedType(tv, getASTContext().TheUnresolvedType); + assignFixedType(tv, ctx.TheUnresolvedType); break; } } @@ -283,7 +284,7 @@ bool ConstraintSystem::simplify(bool ContinueAfterFailures) { failedConstraint = constraint; } - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 : 0) << "(failed constraint "; @@ -362,11 +363,11 @@ ConstraintSystem::SolverState::SolverState( // If we're supposed to debug a specific constraint solver attempt, // turn on debugging now. ASTContext &ctx = CS.getTypeChecker().Context; - LangOptions &langOpts = ctx.LangOpts; - OldDebugConstraintSolver = langOpts.DebugConstraintSolver; - if (langOpts.DebugConstraintSolverAttempt && - langOpts.DebugConstraintSolverAttempt == SolutionAttempt) { - langOpts.DebugConstraintSolver = true; + auto &tyOpts = ctx.TypeCheckerOpts; + OldDebugConstraintSolver = tyOpts.DebugConstraintSolver; + if (tyOpts.DebugConstraintSolverAttempt && + tyOpts.DebugConstraintSolverAttempt == SolutionAttempt) { + tyOpts.DebugConstraintSolver = true; llvm::raw_ostream &dbgOut = ctx.TypeCheckerDebug->getStream(); dbgOut << "---Constraint system #" << SolutionAttempt << "---\n"; CS.print(dbgOut); @@ -408,8 +409,8 @@ ConstraintSystem::SolverState::~SolverState() { } // Restore debugging state. - LangOptions &langOpts = CS.getTypeChecker().Context.LangOpts; - langOpts.DebugConstraintSolver = OldDebugConstraintSolver; + TypeCheckerOptions &tyOpts = CS.getTypeChecker().Context.TypeCheckerOpts; + tyOpts.DebugConstraintSolver = OldDebugConstraintSolver; // Write our local statistics back to the overall statistics. #define CS_STATISTIC(Name, Description) JOIN2(Overall,Name) += Name; @@ -595,7 +596,7 @@ bool ConstraintSystem::Candidate::solve( return false; auto &ctx = cs.getASTContext(); - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); log << "--- Solving candidate for shrinking at "; auto R = E->getSourceRange(); @@ -632,7 +633,7 @@ bool ConstraintSystem::Candidate::solve( cs.solveImpl(solutions); } - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); if (solutions.empty()) { log << "--- No Solutions ---\n"; @@ -716,7 +717,7 @@ void ConstraintSystem::Candidate::applySolutions( } void ConstraintSystem::shrink(Expr *expr) { - if (getASTContext().LangOpts.SolverDisableShrink) + if (getASTContext().TypeCheckerOpts.SolverDisableShrink) return; using DomainMap = llvm::SmallDenseMap>; @@ -1063,10 +1064,10 @@ void ConstraintSystem::shrink(Expr *expr) { } static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { - if (C.LangOpts.DebugConstraintSolver) + if (C.TypeCheckerOpts.DebugConstraintSolver) return true; - if (C.LangOpts.DebugConstraintSolverOnLines.empty()) + if (C.TypeCheckerOpts.DebugConstraintSolverOnLines.empty()) // No need to compute the line number to find out it's not present. return false; @@ -1082,7 +1083,7 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { assert(startLine <= endLine && "expr ends before it starts?"); - auto &lines = C.LangOpts.DebugConstraintSolverOnLines; + auto &lines = C.TypeCheckerOpts.DebugConstraintSolverOnLines; assert(std::is_sorted(lines.begin(), lines.end()) && "DebugConstraintSolverOnLines sorting invariant violated"); @@ -1101,7 +1102,7 @@ bool ConstraintSystem::solve(Expr *&expr, SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables) { llvm::SaveAndRestore debugForExpr( - getASTContext().LangOpts.DebugConstraintSolver, + getASTContext().TypeCheckerOpts.DebugConstraintSolver, debugConstraintSolverForExpr(getASTContext(), expr)); // Attempt to solve the constraint system. @@ -1140,7 +1141,7 @@ bool ConstraintSystem::solve(Expr *&expr, return true; } - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); if (solutions.size() == 1) { log << "---Solution---\n"; @@ -1162,7 +1163,7 @@ ConstraintSystem::solveImpl(Expr *&expr, ExprTypeCheckListener *listener, SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables) { - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Constraint solving for the expression at "; auto R = expr->getSourceRange(); @@ -1234,7 +1235,7 @@ ConstraintSystem::solveImpl(Expr *&expr, return SolutionKind::Error; } - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Initial constraints for the given expression---\n"; print(log, expr); @@ -1258,7 +1259,7 @@ bool ConstraintSystem::solve(SmallVectorImpl &solutions, // Solve the system. solveImpl(solutions); - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Solver statistics---\n"; log << "Total number of scopes explored: " << solverState->NumStatesExplored << "\n"; @@ -1392,7 +1393,7 @@ ConstraintSystem::filterDisjunction( continue; } - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(disabled disjunction term "; @@ -1453,7 +1454,7 @@ ConstraintSystem::filterDisjunction( recordDisjunctionChoice(disjunction->getLocator(), choiceIdx); } - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(introducing single enabled disjunction term "; @@ -2150,7 +2151,7 @@ void ConstraintSystem::partitionDisjunction( } // Partition SIMD operators. - if (!getASTContext().LangOpts.SolverEnableOperatorDesignatedTypes && + if (!getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && isOperatorBindOverload(Choices[0])) { forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { if (!isOperatorBindOverload(constraint)) @@ -2175,7 +2176,7 @@ void ConstraintSystem::partitionDisjunction( } }; - if (getASTContext().LangOpts.SolverEnableOperatorDesignatedTypes && + if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && isOperatorBindOverload(Choices[0])) { partitionForDesignatedTypes(Choices, forEachChoice, appendPartition); } @@ -2210,7 +2211,7 @@ Constraint *ConstraintSystem::selectDisjunction() { // disjunctions that we may not be able to short-circuit, allowing // us to eliminate behavior that is exponential in the number of // operators in the expression. - if (getASTContext().LangOpts.SolverEnableOperatorDesignatedTypes) { + if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { if (auto *disjunction = selectApplyDisjunction()) return disjunction; } diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 0435fe3cac6fe..02531a84edda4 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -516,7 +516,7 @@ bool DisjunctionStep::shouldSkip(const DisjunctionChoice &choice) const { if (!attemptFixes && choice.isUnavailable()) return true; - if (ctx.LangOpts.DisableConstraintSolverPerformanceHacks) + if (ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return false; // Don't attempt to solve for generic operators if we already have @@ -605,7 +605,7 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( if (currentChoice->getFix() && !lastSuccessfulChoice->getFix()) return true; - if (ctx.LangOpts.DisableConstraintSolverPerformanceHacks) + if (ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return false; if (auto restriction = currentChoice->getRestriction()) { @@ -633,7 +633,7 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload && !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl()) && - !ctx.LangOpts.SolverEnableOperatorDesignatedTypes) { + !ctx.TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { return true; } diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 4af98a6ed91f3..16eea6c72172b 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -234,7 +234,7 @@ class SolverStep { /// Check whether constraint solver is running in "debug" mode, /// which should output diagnostic information. bool isDebugMode() const { - return CS.getASTContext().LangOpts.DebugConstraintSolver; + return CS.getASTContext().TypeCheckerOpts.DebugConstraintSolver; } llvm::raw_ostream &getDebugLogger(bool indent = true) const { diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 66b309ed5a16b..ae6b073cbf1da 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -972,7 +972,7 @@ namespace { contractedCycle = false; for (const auto &edge : cycleEdges) { if (unionSets(edge.first, edge.second)) { - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); if (cs.solverState) log.indent(cs.solverState->depth * 2); @@ -1255,7 +1255,7 @@ bool ConstraintGraph::contractEdges() { rep2->getImpl().canBindToLValue()) || // Allow l-value contractions when binding parameter types. isParamBindingConstraint)) { - if (CS.getASTContext().LangOpts.DebugConstraintSolver) { + if (CS.getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); if (CS.solverState) log.indent(CS.solverState->depth * 2); @@ -1395,9 +1395,9 @@ void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) const { } void ConstraintGraphNode::dump() const { - llvm::SaveAndRestore - debug(TypeVar->getASTContext().LangOpts.DebugConstraintSolver, true); - print(llvm::dbgs(), 0); + PrintOptions PO; + PO.PrintTypesForDebugging = true; + print(llvm::dbgs(), 0, PO); } void ConstraintGraph::print(ArrayRef typeVars, @@ -1413,8 +1413,6 @@ void ConstraintGraph::dump() { } void ConstraintGraph::dump(llvm::raw_ostream &out) { - llvm::SaveAndRestore - debug(CS.getASTContext().LangOpts.DebugConstraintSolver, true); print(CS.getTypeVariables(), out); } @@ -1422,6 +1420,8 @@ void ConstraintGraph::printConnectedComponents( ArrayRef typeVars, llvm::raw_ostream &out) { auto components = computeConnectedComponents(typeVars); + PrintOptions PO; + PO.PrintTypesForDebugging = true; for (const auto& component : components) { out.indent(2); out << component.solutionIndex << ": "; @@ -1432,7 +1432,7 @@ void ConstraintGraph::printConnectedComponents( // Print all of the type variables in this connected component. interleave(component.typeVars, [&](TypeVariableType *typeVar) { - typeVar->print(out); + Type(typeVar).print(out, PO); }, [&] { out << ' '; @@ -1452,8 +1452,6 @@ void ConstraintGraph::printConnectedComponents( } void ConstraintGraph::dumpConnectedComponents() { - llvm::SaveAndRestore - debug(CS.getASTContext().LangOpts.DebugConstraintSolver, true); printConnectedComponents(CS.getTypeVariables(), llvm::dbgs()); } @@ -1559,18 +1557,20 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) { } // Make sure that the adjacencies we expect are the adjacencies we have. + PrintOptions PO; + PO.PrintTypesForDebugging = true; for (auto adj : expectedAdjacencies) { auto knownAdj = AdjacencyInfo.find(adj.first); requireWithContext(knownAdj != AdjacencyInfo.end(), "missing adjacency information for type variable", [&] { - llvm::dbgs() << " type variable=" << adj.first->getString() << 'n'; + llvm::dbgs() << " type variable=" << adj.first->getString(PO) << 'n'; }); requireWithContext(adj.second == knownAdj->second.NumConstraints, "wrong number of adjacencies for type variable", [&] { - llvm::dbgs() << " type variable=" << adj.first->getString() + llvm::dbgs() << " type variable=" << adj.first->getString(PO) << " (" << adj.second << " vs. " << knownAdj->second.NumConstraints << ")\n"; @@ -1584,7 +1584,7 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) { requireWithContext(AdjacencyInfo.count(adj.first) > 0, "extraneous adjacency info for type variable", [&] { - llvm::dbgs() << " type variable=" << adj.first->getString() << '\n'; + llvm::dbgs() << " type variable=" << adj.first->getString(PO) << '\n'; }); } } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index a20d6283d88f0..a9fad0c2f3c71 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2241,7 +2241,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } } - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 : 2) << "(overload set choice binding " @@ -2392,7 +2392,7 @@ bool OverloadChoice::isImplicitlyUnwrappedValueOrReturnValue() const { bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { auto &ctx = getASTContext(); - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log << "---Attempting to salvage and emit diagnostics---\n"; } @@ -2437,7 +2437,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { // If there are multiple solutions, try to diagnose an ambiguity. if (viable.size() > 1) { - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Ambiguity error: " << viable.size() << " solutions found---\n"; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 199a28e5f885f..1991e4459a027 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1252,7 +1252,7 @@ class ConstraintSystem { } unsigned threshold = - cs->getASTContext().LangOpts.SolverShrinkUnsolvedThreshold; + cs->getASTContext().TypeCheckerOpts.SolverShrinkUnsolvedThreshold; return unsolvedDisjunctions >= threshold; } }; @@ -3730,7 +3730,7 @@ class ConstraintSystem { used += s.getTotalMemory(); } MaxMemory = std::max(used, MaxMemory); - auto threshold = getASTContext().LangOpts.SolverMemoryThreshold; + auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold; if (MaxMemory > threshold) { return isExpressionAlreadyTooComplex= true; } @@ -3748,7 +3748,7 @@ class ConstraintSystem { // Bail out once we've looked at a really large number of // choices. - if (CountScopes > getASTContext().LangOpts.SolverBindingThreshold) { + if (CountScopes > getASTContext().TypeCheckerOpts.SolverBindingThreshold) { return isExpressionAlreadyTooComplex = true; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index f1cfc39f830e8..76c520c2b0bda 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2467,7 +2467,7 @@ getTypeOfCompletionOperatorImpl(DeclContext *DC, Expr *expr, if (!expr) return nullptr; - if (Context.LangOpts.DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Initial constraints for the given expression---\n"; expr->dump(log); @@ -2481,7 +2481,7 @@ getTypeOfCompletionOperatorImpl(DeclContext *DC, Expr *expr, return nullptr; auto &solution = viable[0]; - if (Context.LangOpts.DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Solution---\n"; solution.dump(log); @@ -3452,7 +3452,7 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, cs.getConstraintLocator(expr)); auto &Context = dc->getASTContext(); - if (Context.LangOpts.DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Initial constraints for the given expression---\n"; expr->dump(log); @@ -3468,7 +3468,7 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, } auto &solution = viable[0]; - if (Context.LangOpts.DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Solution---\n"; solution.dump(log); @@ -3486,7 +3486,7 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, solution.setExprTypes(expr); - if (Context.LangOpts.DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Type-checked expression---\n"; result->dump(log); @@ -3507,24 +3507,23 @@ void Solution::dump() const { } void Solution::dump(raw_ostream &out) const { - ASTContext &ctx = getConstraintSystem().getASTContext(); - llvm::SaveAndRestore debugSolver(ctx.LangOpts.DebugConstraintSolver, - true); + PrintOptions PO; + PO.PrintTypesForDebugging = true; - SourceManager *sm = &ctx.SourceMgr; + SourceManager *sm = &getConstraintSystem().getASTContext().SourceMgr; out << "Fixed score: " << FixedScore << "\n"; out << "Type variables:\n"; for (auto binding : typeBindings) { - auto &typeVar = binding.first->getImpl(); + auto &typeVar = binding.first; out.indent(2); - typeVar.print(out); + Type(typeVar).print(out, PO); out << " as "; binding.second.print(out); - if (auto *locator = typeVar.getLocator()) { + if (auto *locator = typeVar->getImpl().getLocator()) { out << " @ "; - locator->dump(&ctx.SourceMgr, out); + locator->dump(sm, out); } out << "\n"; } @@ -3546,30 +3545,30 @@ void Solution::dump(raw_ostream &out) const { choice.getDecl()->dumpRef(out); out << " as "; if (choice.getBaseType()) - out << choice.getBaseType()->getString() << "."; + out << choice.getBaseType()->getString(PO) << "."; out << choice.getDecl()->getBaseName() << ": " - << ovl.second.openedType->getString() << "\n"; + << ovl.second.openedType->getString(PO) << "\n"; break; case OverloadChoiceKind::BaseType: - out << "base type " << choice.getBaseType()->getString() << "\n"; + out << "base type " << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::KeyPathApplication: out << "key path application root " - << choice.getBaseType()->getString() << "\n"; + << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: out << "dynamic member lookup root " - << choice.getBaseType()->getString() + << choice.getBaseType()->getString(PO) << " name='" << choice.getName() << "'\n"; break; case OverloadChoiceKind::TupleIndex: - out << "tuple " << choice.getBaseType()->getString() << " index " + out << "tuple " << choice.getBaseType()->getString(PO) << " index " << choice.getTupleIndex() << "\n"; break; } @@ -3599,9 +3598,9 @@ void Solution::dump(raw_ostream &out) const { out << " opens "; interleave(opened.second.begin(), opened.second.end(), [&](OpenedType opened) { - opened.first->print(out); + Type(opened.first).print(out, PO); out << " -> "; - opened.second->print(out); + Type(opened.second).print(out, PO); }, [&]() { out << ", "; @@ -3615,7 +3614,7 @@ void Solution::dump(raw_ostream &out) const { for (const auto &openedExistential : OpenedExistentialTypes) { out.indent(2); openedExistential.first->dump(sm, out); - out << " opens to " << openedExistential.second->getString(); + out << " opens to " << openedExistential.second->getString(PO); out << "\n"; } } @@ -3670,13 +3669,13 @@ void ConstraintSystem::print(raw_ostream &out, Expr *E) const { void ConstraintSystem::print(raw_ostream &out) const { // Print all type variables as $T0 instead of _ here. - llvm::SaveAndRestore X(getASTContext().LangOpts.DebugConstraintSolver, - true); + PrintOptions PO; + PO.PrintTypesForDebugging = true; out << "Score: " << CurrentScore << "\n"; - if (contextualType.getType()) { - out << "Contextual Type: " << contextualType.getType(); + if (auto ty = contextualType.getType()) { + out << "Contextual Type: " << ty.getString(PO); if (TypeRepr *TR = contextualType.getTypeRepr()) { out << " at "; TR->getSourceRange().print(out, getASTContext().SourceMgr, /*text*/false); @@ -3687,7 +3686,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "Type Variables:\n"; for (auto tv : getTypeVariables()) { out.indent(2); - tv->getImpl().print(out); + out << tv->getString(PO); if (tv->getImpl().canBindToLValue()) out << " [lvalue allowed]"; if (tv->getImpl().canBindToInOut()) @@ -3704,7 +3703,7 @@ void ConstraintSystem::print(raw_ostream &out) const { } } else { out << " equivalent to "; - rep->print(out); + out << rep->getString(PO); } if (auto *locator = tv->getImpl().getLocator()) { @@ -3759,23 +3758,23 @@ void ConstraintSystem::print(raw_ostream &out) const { break; case OverloadChoiceKind::BaseType: - out << "base type " << choice.getBaseType()->getString() << "\n"; + out << "base type " << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::KeyPathApplication: out << "key path application root " - << choice.getBaseType()->getString() << "\n"; + << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: out << "dynamic member lookup:" - << choice.getBaseType()->getString() << " name=" + << choice.getBaseType()->getString(PO) << " name=" << choice.getName() << "\n"; break; case OverloadChoiceKind::TupleIndex: - out << "tuple " << choice.getBaseType()->getString() << " index " + out << "tuple " << choice.getBaseType()->getString(PO) << " index " << choice.getTupleIndex() << "\n"; break; } @@ -3800,9 +3799,9 @@ void ConstraintSystem::print(raw_ostream &out) const { out << " opens "; interleave(opened.second.begin(), opened.second.end(), [&](OpenedType opened) { - opened.first->print(out); + Type(opened.first).print(out, PO); out << " -> "; - opened.second->print(out); + Type(opened.second).print(out, PO); }, [&]() { out << ", "; @@ -3816,7 +3815,7 @@ void ConstraintSystem::print(raw_ostream &out) const { for (const auto &openedExistential : OpenedExistentialTypes) { out.indent(2); openedExistential.first->dump(&getTypeChecker().Context.SourceMgr, out); - out << " opens to " << openedExistential.second->getString(); + out << " opens to " << openedExistential.second->getString(PO); out << "\n"; } } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index dc4eb8cadf0b4..4cd4530236af6 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2089,7 +2089,7 @@ llvm::Expected OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator, InfixOperatorDecl *IOD) const { auto enableOperatorDesignatedTypes = - IOD->getASTContext().LangOpts.EnableOperatorDesignatedTypes; + IOD->getASTContext().TypeCheckerOpts.EnableOperatorDesignatedTypes; auto &Diags = IOD->getASTContext().Diags; PrecedenceGroupDecl *group = nullptr; @@ -2330,7 +2330,9 @@ class DeclChecker : public DeclVisitor { (void)IOD->getPrecedenceGroup(); } else { auto nominalTypes = OD->getDesignatedNominalTypes(); - if (nominalTypes.empty() && Ctx.LangOpts.EnableOperatorDesignatedTypes) { + const auto wantsDesignatedTypes = + Ctx.TypeCheckerOpts.EnableOperatorDesignatedTypes; + if (nominalTypes.empty() && wantsDesignatedTypes) { auto identifiers = OD->getIdentifiers(); auto identifierLocs = OD->getIdentifierLocs(); if (checkDesignatedTypes(OD, identifiers, identifierLocs, Ctx)) @@ -3100,7 +3102,7 @@ class DeclChecker : public DeclVisitor { if (!SF || SF->Kind != SourceFileKind::Interface) TypeChecker::inferDefaultWitnesses(PD); - if (PD->getASTContext().LangOpts.DebugGenericSignatures) { + if (PD->getASTContext().TypeCheckerOpts.DebugGenericSignatures) { auto requirementsSig = GenericSignature::get({PD->getProtocolSelfType()}, PD->getRequirementSignature()); diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index f3a0b7571eb4a..7033d7531f8ff 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -467,7 +467,7 @@ GenericSignature TypeChecker::checkGenericSignature( // Debugging of the generic signature builder and generic signature // generation. - if (dc->getASTContext().LangOpts.DebugGenericSignatures) { + if (dc->getASTContext().TypeCheckerOpts.DebugGenericSignatures) { if (auto *VD = dyn_cast_or_null(dc->getAsDecl())) { VD->dumpRef(llvm::errs()); } else { @@ -589,7 +589,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator, // Debugging of the generic signature builder and generic signature // generation. - if (GC->getASTContext().LangOpts.DebugGenericSignatures) { + if (GC->getASTContext().TypeCheckerOpts.DebugGenericSignatures) { PD->printContext(llvm::errs()); llvm::errs() << "\n"; llvm::errs() << "Generic signature: "; diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 4fef9724ed8fb..9fc4866fe1b56 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4858,7 +4858,7 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, // Check all conformances. groupChecker.checkAllConformances(); - if (Context.LangOpts.DebugGenericSignatures) { + if (Context.TypeCheckerOpts.DebugGenericSignatures) { // Now that they're filled out, print out information about the conformances // here, when requested. for (auto conformance : conformances) { diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 33935fd7556ae..a5b022b860c83 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -677,7 +677,7 @@ TypeChecker &swift::createTypeChecker(ASTContext &Ctx) { } void TypeChecker::checkForForbiddenPrefix(ASTContext &C, DeclBaseName Name) { - if (C.LangOpts.DebugForbidTypecheckPrefix.empty()) + if (C.TypeCheckerOpts.DebugForbidTypecheckPrefix.empty()) return; // Don't touch special names or empty names. @@ -685,7 +685,7 @@ void TypeChecker::checkForForbiddenPrefix(ASTContext &C, DeclBaseName Name) { return; StringRef Str = Name.getIdentifier().str(); - if (Str.startswith(C.LangOpts.DebugForbidTypecheckPrefix)) { + if (Str.startswith(C.TypeCheckerOpts.DebugForbidTypecheckPrefix)) { std::string Msg = "forbidden typecheck occurred: "; Msg += Str; llvm::report_fatal_error(Msg); diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index a8243cb9fb326..98862e524cad0 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -3359,12 +3359,11 @@ int main(int argc, char *argv[]) { for (auto &Arg : options::ClangXCC) { InitInvok.getClangImporterOptions().ExtraArgs.push_back(Arg); } - InitInvok.getLangOptions().DebugForbidTypecheckPrefix = - options::DebugForbidTypecheckPrefix; InitInvok.getLangOptions().EnableObjCAttrRequiresFoundation = !options::DisableObjCAttrRequiresFoundationModule; - - InitInvok.getLangOptions().DebugConstraintSolver = + InitInvok.getTypeCheckerOptions().DebugForbidTypecheckPrefix = + options::DebugForbidTypecheckPrefix; + InitInvok.getTypeCheckerOptions().DebugConstraintSolver = options::DebugConstraintSolver; for (auto ConfigName : options::BuildConfigs) From d890b8ad410311b8df515f0456aa2f18c4f771df Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 12 Nov 2019 22:41:14 -0800 Subject: [PATCH 140/283] Remove some save-and-restores An awful pattern we use throughout the compiler is to save and restore global flags just for little things. In this case, it was just to turn on some extra options in AST printing for type variables. The kicker is that the ASTDumper doesn't even respect this flag. Add this as a PrintOption and remove the offending save-and-restores. This doesn't quite get them all: we appear to have productized this pattern in the REPL. --- include/swift/AST/PrintOptions.h | 4 ++++ lib/AST/ASTDumper.cpp | 28 +++++----------------------- lib/AST/ASTPrinter.cpp | 4 ++-- lib/Sema/CSStep.cpp | 6 ++++-- lib/Sema/CSStep.h | 4 +++- lib/Sema/Constraint.cpp | 20 ++++++++++---------- lib/Sema/ConstraintGraph.cpp | 5 +++-- lib/Sema/ConstraintGraph.h | 3 ++- lib/Sema/ConstraintLocator.cpp | 5 ++++- lib/Sema/ConstraintSystem.cpp | 6 ++++-- lib/Sema/ConstraintSystem.h | 13 +++++++------ lib/Sema/SourceLoader.cpp | 4 ++-- lib/Sema/TypeCheckConstraints.cpp | 10 +++++----- 13 files changed, 55 insertions(+), 57 deletions(-) diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 8ab8382f9ee9d..1c82d1a06aef4 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -270,6 +270,10 @@ struct PrintOptions { /// Whether to skip keywords with a prefix of underscore such as __consuming. bool SkipUnderscoredKeywords = false; + /// Prints type variables and unresolved types in an expanded notation suitable + /// for debugging. + bool PrintTypesForDebugging = false; + /// How to print opaque return types. enum class OpaqueReturnTypePrintingMode { /// 'some P1 & P2'. diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 6bb9bd5e02add..6d080d89a47dc 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1262,15 +1262,6 @@ void ParameterList::dump() const { } void ParameterList::dump(raw_ostream &OS, unsigned Indent) const { - llvm::Optional> X; - - // Make sure to print type variables if we can get to ASTContext. - if (size() != 0 && get(0)) { - auto &ctx = get(0)->getASTContext(); - X.emplace(llvm::SaveAndRestore(ctx.LangOpts.DebugConstraintSolver, - true)); - } - PrintDecl(OS, Indent).printParameterList(this); llvm::errs() << '\n'; } @@ -1293,9 +1284,6 @@ void Decl::dump(const char *filename) const { } void Decl::dump(raw_ostream &OS, unsigned Indent) const { - // Make sure to print type variables. - llvm::SaveAndRestore X(getASTContext().LangOpts.DebugConstraintSolver, - true); PrintDecl(OS, Indent).visit(const_cast(this)); OS << '\n'; } @@ -1407,8 +1395,6 @@ void SourceFile::dump() const { } void SourceFile::dump(llvm::raw_ostream &OS) const { - llvm::SaveAndRestore X(getASTContext().LangOpts.DebugConstraintSolver, - true); PrintDecl(OS).visitSourceFile(*this); llvm::errs() << '\n'; } @@ -1826,13 +1812,17 @@ class PrintExpr : public ExprVisitor { } raw_ostream &printCommon(Expr *E, const char *C) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + OS.indent(Indent); PrintWithColorRAII(OS, ParenthesisColor) << '('; PrintWithColorRAII(OS, ExprColor) << C; if (E->isImplicit()) PrintWithColorRAII(OS, ExprModifierColor) << " implicit"; - PrintWithColorRAII(OS, TypeColor) << " type='" << GetTypeOfExpr(E) << '\''; + PrintWithColorRAII(OS, TypeColor) << " type='"; + PrintWithColorRAII(OS, TypeColor) << GetTypeOfExpr(E).getString(PO) << '\''; // If we have a source range and an ASTContext, print the source range. if (auto Ty = GetTypeOfExpr(E)) { @@ -3749,14 +3739,10 @@ namespace { } // end anonymous namespace void Type::dump() const { - // Make sure to print type variables. dump(llvm::errs()); } void Type::dump(raw_ostream &os, unsigned indent) const { - // Make sure to print type variables. - llvm::SaveAndRestore X(getPointer()->getASTContext().LangOpts. - DebugConstraintSolver, true); PrintType(os, indent).visit(*this, ""); os << "\n"; } @@ -3767,10 +3753,6 @@ void TypeBase::dump() const { } void TypeBase::dump(raw_ostream &os, unsigned indent) const { - auto &ctx = const_cast(this)->getASTContext(); - - // Make sure to print type variables. - llvm::SaveAndRestore X(ctx.LangOpts.DebugConstraintSolver, true); Type(const_cast(this)).dump(os, indent); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index d5ac1fff4f8ed..f48b5a60f7f24 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3595,7 +3595,7 @@ class TypePrinter : public TypeVisitor { } void visitUnresolvedType(UnresolvedType *T) { - if (T->getASTContext().LangOpts.DebugConstraintSolver) + if (Options.PrintTypesForDebugging) Printer << "<>"; else Printer << "_"; @@ -4331,7 +4331,7 @@ class TypePrinter : public TypeVisitor { #include "swift/AST/ReferenceStorage.def" void visitTypeVariableType(TypeVariableType *T) { - if (T->getASTContext().LangOpts.DebugConstraintSolver) { + if (Options.PrintTypesForDebugging) { Printer << "$T" << T->getID(); return; } diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 02531a84edda4..12c635155b2aa 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -415,13 +415,15 @@ StepResult ComponentStep::finalize(bool isSuccess) { void TypeVariableStep::setup() { ++CS.solverState->NumTypeVariablesBound; if (isDebugMode()) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; auto &log = getDebugLogger(); log << "Initial bindings: "; interleave(InitialBindings.begin(), InitialBindings.end(), [&](const Binding &binding) { - log << TypeVar->getString() - << " := " << binding.BindingType->getString(); + log << TypeVar->getString(PO) + << " := " << binding.BindingType->getString(PO); }, [&log] { log << ", "; }); diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 16eea6c72172b..ee3e120e86b38 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -601,7 +601,9 @@ class TypeVariableStep final : public BindingStep { StepResult resume(bool prevFailed) override; void print(llvm::raw_ostream &Out) override { - Out << "TypeVariableStep for " << TypeVar->getString() << " with #" + PrintOptions PO; + PO.PrintTypesForDebugging = true; + Out << "TypeVariableStep for " << TypeVar->getString(PO) << " with #" << InitialBindings.size() << " initial bindings\n"; } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index a4a8098dc978f..1e743d565d9c7 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -21,7 +21,6 @@ #include "swift/Basic/Compiler.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/SaveAndRestore.h" #include using namespace swift; @@ -265,6 +264,10 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { } void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { + // Print all type variables as $T0 instead of _ here. + PrintOptions PO; + PO.PrintTypesForDebugging = true; + if (Kind == ConstraintKind::Disjunction) { Out << "disjunction"; if (shouldRememberChoice()) @@ -286,7 +289,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { return; } - getFirstType()->print(Out); + Out << getFirstType()->getString(PO); bool skipSecond = false; @@ -315,17 +318,17 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { case ConstraintKind::OneWayEqual: Out << " one-way bind to "; break; case ConstraintKind::KeyPath: Out << " key path from "; - getSecondType()->print(Out); + Out << getSecondType()->getString(PO); Out << " -> "; - getThirdType()->print(Out); + Out << getThirdType()->getString(PO); skipSecond = true; break; case ConstraintKind::KeyPathApplication: Out << " key path projecting "; - getSecondType()->print(Out); + Out << getSecondType()->getString(PO); Out << " -> "; - getThirdType()->print(Out); + Out << getThirdType()->getString(PO); skipSecond = true; break; case ConstraintKind::OptionalObject: @@ -396,7 +399,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } if (!skipSecond) - getSecondType()->print(Out); + Out << getSecondType()->getString(PO); if (auto restriction = getRestriction()) { Out << ' ' << getName(*restriction); @@ -420,9 +423,6 @@ void Constraint::dump(SourceManager *sm) const { } void Constraint::dump(ConstraintSystem *CS) const { - // Print all type variables as $T0 instead of _ here. - llvm::SaveAndRestore X(CS->getASTContext().LangOpts. - DebugConstraintSolver, true); // Disable MSVC warning: only for use within the debugger. #if SWIFT_COMPILER_IS_MSVC #pragma warning(push) diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index ae6b073cbf1da..130a50ad1d35f 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -1319,9 +1319,10 @@ void ConstraintGraph::incrementConstraintsPerContractionCounter() { #pragma mark Debugging output -void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) const { +void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent, + PrintOptions PO) const { out.indent(indent); - TypeVar->print(out); + Type(TypeVar).print(out, PO); out << ":\n"; // Print constraints. diff --git a/lib/Sema/ConstraintGraph.h b/lib/Sema/ConstraintGraph.h index 0013309f993de..f3041fcb4fe6c 100644 --- a/lib/Sema/ConstraintGraph.h +++ b/lib/Sema/ConstraintGraph.h @@ -160,7 +160,8 @@ class ConstraintGraphNode { mutable SmallVector EquivalenceClass; /// Print this graph node. - void print(llvm::raw_ostream &out, unsigned indent) const; + void print(llvm::raw_ostream &out, unsigned indent, + PrintOptions PO = PrintOptions()) const; SWIFT_DEBUG_DUMP; diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 5012f54b9f574..18a29bbde65bf 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -261,6 +261,9 @@ void ConstraintLocator::dump(ConstraintSystem *CS) const { void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + out << "locator@" << (void*) this << " ["; if (anchor) { @@ -295,7 +298,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { switch (elt.getKind()) { case GenericParameter: { auto gpElt = elt.castTo(); - out << "generic parameter '" << gpElt.getType()->getString() << "'"; + out << "generic parameter '" << gpElt.getType()->getString(PO) << "'"; break; } case ApplyArgument: diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index a9fad0c2f3c71..53395f6e4e7e6 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2242,11 +2242,13 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 : 2) << "(overload set choice binding " - << boundType->getString() << " := " - << refType->getString() << ")\n"; + << boundType->getString(PO) << " := " + << refType->getString(PO) << ")\n"; } // If this overload is disfavored, note that. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 1991e4459a027..c5aa1c80a2873 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3432,13 +3432,12 @@ class ConstraintSystem { if (NumDefaultableBindings > 0) out << "#defaultable_bindings=" << NumDefaultableBindings << " "; + PrintOptions PO; + PO.PrintTypesForDebugging = true; out << "bindings={"; interleave(Bindings, [&](const PotentialBinding &binding) { auto type = binding.BindingType; - auto &ctx = type->getASTContext(); - llvm::SaveAndRestore debugConstraints( - ctx.LangOpts.DebugConstraintSolver, true); switch (binding.Kind) { case AllowedBindingKind::Exact: break; @@ -3454,7 +3453,7 @@ class ConstraintSystem { if (binding.DefaultedProtocol) out << "(default from " << binding.DefaultedProtocol->getName() << ") "; - out << type.getString(); + out << type.getString(PO); }, [&]() { out << "; "; }); out << "}"; @@ -4188,8 +4187,10 @@ class TypeVariableBinding { bool attempt(ConstraintSystem &cs) const; void print(llvm::raw_ostream &Out, SourceManager *) const { - Out << "type variable " << TypeVar->getString() - << " := " << Binding.BindingType->getString(); + PrintOptions PO; + PO.PrintTypesForDebugging = true; + Out << "type variable " << TypeVar->getString(PO) + << " := " << Binding.BindingType->getString(PO); } }; diff --git a/lib/Sema/SourceLoader.cpp b/lib/Sema/SourceLoader.cpp index 3026605d6faf5..690631dc9f5cb 100644 --- a/lib/Sema/SourceLoader.cpp +++ b/lib/Sema/SourceLoader.cpp @@ -106,8 +106,8 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, /*isSystem=*/false); // Turn off debugging while parsing other modules. - llvm::SaveAndRestore turnOffDebug(Ctx.LangOpts.DebugConstraintSolver, - false); + llvm::SaveAndRestore + turnOffDebug(Ctx.TypeCheckerOpts.DebugConstraintSolver, false); unsigned bufferID; if (auto BufID = diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 76c520c2b0bda..5b11e9ef6f079 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3686,7 +3686,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "Type Variables:\n"; for (auto tv : getTypeVariables()) { out.indent(2); - out << tv->getString(PO); + Type(tv).print(out, PO); if (tv->getImpl().canBindToLValue()) out << " [lvalue allowed]"; if (tv->getImpl().canBindToInOut()) @@ -3703,7 +3703,7 @@ void ConstraintSystem::print(raw_ostream &out) const { } } else { out << " equivalent to "; - out << rep->getString(PO); + Type(rep).print(out, PO); } if (auto *locator = tv->getImpl().getLocator()) { @@ -3751,10 +3751,10 @@ void ConstraintSystem::print(raw_ostream &out) const { case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaUnwrappedOptional: if (choice.getBaseType()) - out << choice.getBaseType()->getString() << "."; + out << choice.getBaseType()->getString(PO) << "."; out << choice.getDecl()->getBaseName() << ": " - << resolved->BoundType->getString() << " == " - << resolved->ImpliedType->getString() << "\n"; + << resolved->BoundType->getString(PO) << " == " + << resolved->ImpliedType->getString(PO) << "\n"; break; case OverloadChoiceKind::BaseType: From aa98f254ac57cdd4d97b6ae682d8f5b54033bd5c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 12 Nov 2019 23:00:03 -0800 Subject: [PATCH 141/283] Cleanup integer option parsing a bit --- lib/Frontend/CompilerInvocation.cpp | 79 ++++++++++------------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 2e722403a2864..d65943b109b11 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -491,28 +491,35 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, const FrontendOptions &FrontendOpts) { using namespace options; - auto setUnsignedIntegerArgument = [&Args, &Diags](options::ID optionID, - unsigned radix, - unsigned &valueToSet) { - if (const Arg *A = Args.getLastArg(optionID)) { - unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(radix, attempt)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - } else { - valueToSet = attempt; - } - } - }; - - setUnsignedIntegerArgument(OPT_warn_long_function_bodies, 10, + bool HadError = false; + auto setUnsignedIntegerArgument = + [&Args, &Diags, &HadError](options::ID optionID, unsigned &valueToSet) { + if (const Arg *A = Args.getLastArg(optionID)) { + unsigned attempt; + if (StringRef(A->getValue()).getAsInteger(/*radix*/ 10, attempt)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + valueToSet = attempt; + } + } + }; + + setUnsignedIntegerArgument(OPT_warn_long_function_bodies, Opts.WarnLongFunctionBodies); - setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, 10, + setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, Opts.WarnLongExpressionTypeChecking); - setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, 10, + setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, Opts.ExpressionTimeoutThreshold); - setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, 10, + setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, Opts.SwitchCheckingInvocationThreshold); + setUnsignedIntegerArgument(OPT_debug_constraints_attempt, + Opts.DebugConstraintSolverAttempt); + setUnsignedIntegerArgument(OPT_solver_memory_threshold, + Opts.SolverMemoryThreshold); + setUnsignedIntegerArgument(OPT_solver_shrink_unsolved_threshold, + Opts.SolverShrinkUnsolvedThreshold); Opts.DebugTimeFunctionBodies |= Args.hasArg(OPT_debug_time_function_bodies); Opts.DebugTimeExpressions |= @@ -539,21 +546,9 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); - bool HadError = false; - if (const Arg *A = Args.getLastArg(OPT_debug_constraints_attempt)) { - unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(10, attempt)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.DebugConstraintSolverAttempt = attempt; - } - } - for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) { unsigned line; - if (StringRef(A->getValue()).getAsInteger(10, line)) { + if (StringRef(A->getValue()).getAsInteger(/*radix*/ 10, line)) { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, A->getAsString(Args), A->getValue()); HadError = true; @@ -567,28 +562,6 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, Opts.DebugForbidTypecheckPrefix = A->getValue(); } - if (const Arg *A = Args.getLastArg(OPT_solver_memory_threshold)) { - unsigned threshold; - if (StringRef(A->getValue()).getAsInteger(10, threshold)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.SolverMemoryThreshold = threshold; - } - } - - if (const Arg *A = Args.getLastArg(OPT_solver_shrink_unsolved_threshold)) { - unsigned threshold; - if (StringRef(A->getValue()).getAsInteger(10, threshold)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.SolverShrinkUnsolvedThreshold = threshold; - } - } - if (Args.getLastArg(OPT_solver_disable_shrink)) Opts.SolverDisableShrink = true; From 417c82f42d9a8f1e1df25a27e1a57a3871c0ebb3 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 12 Nov 2019 23:10:26 -0800 Subject: [PATCH 142/283] Strip TypeChecker of its ASTContext If you can get a TypeChecker at this point, you had one all along. Its constructor and destructor are now defaultable. --- lib/Sema/CSSolver.cpp | 4 ++-- lib/Sema/TypeCheckConstraints.cpp | 17 +++++++++-------- lib/Sema/TypeChecker.cpp | 7 +------ lib/Sema/TypeChecker.h | 5 ++--- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 19d61d81d4ed5..fbffaa6456646 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -362,7 +362,7 @@ ConstraintSystem::SolverState::SolverState( // If we're supposed to debug a specific constraint solver attempt, // turn on debugging now. - ASTContext &ctx = CS.getTypeChecker().Context; + ASTContext &ctx = CS.getASTContext(); auto &tyOpts = ctx.TypeCheckerOpts; OldDebugConstraintSolver = tyOpts.DebugConstraintSolver; if (tyOpts.DebugConstraintSolverAttempt && @@ -409,7 +409,7 @@ ConstraintSystem::SolverState::~SolverState() { } // Restore debugging state. - TypeCheckerOptions &tyOpts = CS.getTypeChecker().Context.TypeCheckerOpts; + TypeCheckerOptions &tyOpts = CS.getASTContext().TypeCheckerOpts; tyOpts.DebugConstraintSolver = OldDebugConstraintSolver; // Write our local statistics back to the overall statistics. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5b11e9ef6f079..c9baacd0c229b 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2172,6 +2172,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, TypeCheckExprOptions options, ExprTypeCheckListener &listener, ConstraintSystem *baseCS) { + auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); @@ -3717,14 +3718,14 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "\nActive Constraints:\n"; for (auto &constraint : ActiveConstraints) { out.indent(2); - constraint.print(out, &getTypeChecker().Context.SourceMgr); + constraint.print(out, &getASTContext().SourceMgr); out << "\n"; } out << "\nInactive Constraints:\n"; for (auto &constraint : InactiveConstraints) { out.indent(2); - constraint.print(out, &getTypeChecker().Context.SourceMgr); + constraint.print(out, &getASTContext().SourceMgr); out << "\n"; } @@ -3732,7 +3733,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "\nRetired Constraints:\n"; solverState->forEachRetired([&](Constraint &constraint) { out.indent(2); - constraint.print(out, &getTypeChecker().Context.SourceMgr); + constraint.print(out, &getASTContext().SourceMgr); out << "\n"; }); } @@ -3786,7 +3787,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "\nDisjunction choices:\n"; for (auto &choice : DisjunctionChoices) { out.indent(2); - choice.first->dump(&getTypeChecker().Context.SourceMgr, out); + choice.first->dump(&getASTContext().SourceMgr, out); out << " is #" << choice.second << "\n"; } } @@ -3795,7 +3796,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "\nOpened types:\n"; for (const auto &opened : OpenedTypes) { out.indent(2); - opened.first->dump(&getTypeChecker().Context.SourceMgr, out); + opened.first->dump(&getASTContext().SourceMgr, out); out << " opens "; interleave(opened.second.begin(), opened.second.end(), [&](OpenedType opened) { @@ -3814,7 +3815,7 @@ void ConstraintSystem::print(raw_ostream &out) const { out << "\nOpened existential types:\n"; for (const auto &openedExistential : OpenedExistentialTypes) { out.indent(2); - openedExistential.first->dump(&getTypeChecker().Context.SourceMgr, out); + openedExistential.first->dump(&getASTContext().SourceMgr, out); out << " opens to " << openedExistential.second->getString(PO); out << "\n"; } @@ -3823,7 +3824,7 @@ void ConstraintSystem::print(raw_ostream &out) const { if (!DefaultedConstraints.empty()) { out << "\nDefaulted constraints: "; interleave(DefaultedConstraints, [&](ConstraintLocator *locator) { - locator->dump(&getTypeChecker().Context.SourceMgr, out); + locator->dump(&getASTContext().SourceMgr, out); }, [&] { out << ", "; }); @@ -3832,7 +3833,7 @@ void ConstraintSystem::print(raw_ostream &out) const { if (failedConstraint) { out << "\nFailed constraint:\n"; out.indent(2); - failedConstraint->print(out, &getTypeChecker().Context.SourceMgr); + failedConstraint->print(out, &getASTContext().SourceMgr); out << "\n"; } diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index a5b022b860c83..f594b85636a17 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -55,17 +55,12 @@ using namespace swift; TypeChecker &TypeChecker::createForContext(ASTContext &ctx) { assert(!ctx.getLegacyGlobalTypeChecker() && "Cannot install more than one instance of the global type checker!"); - auto *TC = new TypeChecker(ctx); + auto *TC = new TypeChecker(); ctx.installGlobalTypeChecker(TC); ctx.addCleanup([=](){ delete TC; }); return *ctx.getLegacyGlobalTypeChecker(); } -TypeChecker::TypeChecker(ASTContext &Ctx) - : Context(Ctx) {} - -TypeChecker::~TypeChecker() {} - ProtocolDecl *TypeChecker::getProtocol(ASTContext &Context, SourceLoc loc, KnownProtocolKind kind) { auto protocol = Context.getProtocol(kind); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b9b6a3192c45d..3e115ca4fde84 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -542,9 +542,9 @@ class TypeChecker final { std::vector ClosuresWithUncomputedCaptures; private: - ASTContext &Context; + TypeChecker() = default; + ~TypeChecker() = default; - TypeChecker(ASTContext &Ctx); friend class ASTContext; friend class constraints::ConstraintSystem; friend class TypeCheckFunctionBodyUntilRequest; @@ -559,7 +559,6 @@ class TypeChecker final { public: TypeChecker(const TypeChecker&) = delete; TypeChecker& operator=(const TypeChecker&) = delete; - ~TypeChecker(); static Type getArraySliceType(SourceLoc loc, Type elementType); static Type getDictionaryType(SourceLoc loc, Type keyType, Type valueType); From d5861998563b709835d766430227b40fcd6b252d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 10:04:23 -0800 Subject: [PATCH 143/283] Strip TypeChecker of some friends TypeChecker has no friends. --- lib/Sema/TypeChecker.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 3e115ca4fde84..b7e55dbd93445 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -546,8 +546,6 @@ class TypeChecker final { ~TypeChecker() = default; friend class ASTContext; - friend class constraints::ConstraintSystem; - friend class TypeCheckFunctionBodyUntilRequest; public: /// Create a new type checker instance for the given ASTContext, if it From a4bd098584752db74e58717775d6eb308cf1a0a2 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 10:06:01 -0800 Subject: [PATCH 144/283] [Gardening] Reorganize the header a bit Try to move the doc comments for the name lookup section so that it actually contains name-lookup-related things. --- lib/Sema/TypeChecker.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b7e55dbd93445..c5d58ac9544e2 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -925,16 +925,6 @@ class TypeChecker final { /// struct or class. static void addImplicitConstructors(NominalTypeDecl *typeDecl); - /// \name Name lookup - /// - /// Routines that perform name lookup. - /// - /// During type checking, these routines should be used instead of - /// \c MemberLookup and \c UnqualifiedLookup, because these routines will - /// lazily introduce declarations and (FIXME: eventually) perform recursive - /// type-checking that the AST-level lookup routines don't. - /// - /// @{ private: Optional boolType; @@ -1335,6 +1325,17 @@ class TypeChecker final { static Type deriveTypeWitness(DeclContext *DC, NominalTypeDecl *nominal, AssociatedTypeDecl *assocType); + /// \name Name lookup + /// + /// Routines that perform name lookup. + /// + /// During type checking, these routines should be used instead of + /// \c MemberLookup and \c UnqualifiedLookup, because these routines will + /// lazily introduce declarations and (FIXME: eventually) perform recursive + /// type-checking that the AST-level lookup routines don't. + /// + /// @{ + /// Perform unqualified name lookup at the given source location /// within a particular declaration context. /// @@ -1371,10 +1372,6 @@ class TypeChecker final { NameLookupOptions options = defaultMemberLookupOptions); - /// Check whether the given declaration can be written as a - /// member of the given base type. - static bool isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl); - /// Look up a member type within the given type. /// /// This routine looks for member types with the given name within the @@ -1414,6 +1411,9 @@ class TypeChecker final { /// Given an pre-folded expression, find LHS from the expression if a binary /// operator \c name appended to the expression. static Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); + /// Check whether the given declaration can be written as a + /// member of the given base type. + static bool isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl); /// @} From 0c54eecdc3b436a382809f898e15837b74735995 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 10:06:07 -0800 Subject: [PATCH 145/283] Remove an unused member --- lib/Sema/TypeChecker.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c5d58ac9544e2..11fde8473209a 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -925,9 +925,6 @@ class TypeChecker final { /// struct or class. static void addImplicitConstructors(NominalTypeDecl *typeDecl); -private: - Optional boolType; - public: /// Fold the given sequence expression into an (unchecked) expression /// tree. From 4f9f6b94a4c5f9bd93a07c170bac02aa2cc866f0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 10:07:24 -0800 Subject: [PATCH 146/283] Privatize findLHS It's only used from code completion, but it has a dependency on operator lookup so it's gotta stay a member for now --- lib/Sema/TypeChecker.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 11fde8473209a..0215444b4aeba 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -930,6 +930,12 @@ class TypeChecker final { /// tree. static Expr *foldSequence(SequenceExpr *expr, DeclContext *dc); +private: + /// Given an pre-folded expression, find LHS from the expression if a binary + /// operator \c name appended to the expression. + static Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); + +public: /// Type check the given expression. /// /// \param expr The expression to type-check, which will be modified in @@ -1326,11 +1332,6 @@ class TypeChecker final { /// /// Routines that perform name lookup. /// - /// During type checking, these routines should be used instead of - /// \c MemberLookup and \c UnqualifiedLookup, because these routines will - /// lazily introduce declarations and (FIXME: eventually) perform recursive - /// type-checking that the AST-level lookup routines don't. - /// /// @{ /// Perform unqualified name lookup at the given source location @@ -1405,9 +1406,6 @@ class TypeChecker final { Identifier name, SourceLoc nameLoc); - /// Given an pre-folded expression, find LHS from the expression if a binary - /// operator \c name appended to the expression. - static Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); /// Check whether the given declaration can be written as a /// member of the given base type. static bool isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl); From 6ff321aa5f18f5925fcafb04479615e3927c55cb Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Nov 2019 10:24:58 -0800 Subject: [PATCH 147/283] build: adjust cmake invocation for export targets This adjusts the cmake invocation for Foundation to use the export targets rather than computing the locations by hand. --- utils/build-script-impl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index c011fcfd47f67..569ec7780d04d 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2916,6 +2916,7 @@ for host in "${ALL_HOSTS[@]}"; do LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} + -Ddispatch_DIR=${LIBDISPATCH_BUILD_DIR}/cmake/modules ) else LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) @@ -2924,20 +2925,22 @@ for host in "${ALL_HOSTS[@]}"; do SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" LLVM_BIN="$(build_directory_bin ${LOCAL_HOST} llvm)" + # NOTE(compnerd) the time has come to enable tests now cmake_options=( ${cmake_options[@]} -DCMAKE_BUILD_TYPE:STRING=${FOUNDATION_BUILD_TYPE} -DCMAKE_C_COMPILER:PATH=${LLVM_BIN}/clang -DCMAKE_CXX_COMPILER:PATH=${LLVM_BIN}/clang++ - -DCMAKE_SWIFT_COMPILER:PATH=${SWIFTC_BIN} -DCMAKE_Swift_COMPILER:PATH=${SWIFTC_BIN} -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} ${LIBDISPATCH_BUILD_ARGS[@]} - # NOTE(compnerd) the time has come to enable tests now -DENABLE_TESTING:BOOL=YES + -DXCTest_DIR=$(build_directory ${host} xctest)/cmake/modules + + -DCMAKE_SWIFT_COMPILER:PATH=${SWIFTC_BIN} -DFOUNDATION_PATH_TO_XCTEST_BUILD:PATH=$(build_directory ${host} xctest) ) From af75939bf64f66a4affe80c377cc8b00104d158d Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Nov 2019 10:43:56 -0800 Subject: [PATCH 148/283] Update WindowsBuild.md Update rules for the Windows build after CMake 3.15 upgrade. This simplifies the rules, relies on the export targets and removes the unnecessary variables. --- docs/WindowsBuild.md | 52 ++++++-------------------------------------- 1 file changed, 7 insertions(+), 45 deletions(-) diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index e42e015c69037..9aa98d27a84f1 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -98,45 +98,20 @@ ninja -C S:\b\libdispatch check ## Build swift-corelibs-foundation ```cmd -md "S:\b\foundation" -cd "S:\b\foundation -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_SWIFT_COMPILER=S:\b\toolchain\bin\swiftc.exe^ - -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib"^ - -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include"^ - -DENABLE_TESTING=NO^ - -DICU_ROOT="S:/Library/icu-64"^ - -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2.lib"^ - -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include"^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=S:\b\libdispatch^ - S:\swift-corelibs-foundation -ninja +cmake -B S:\b\foundation -G Ninja -S S:\toolchain\swift-corelibs-foundation -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang-cl -DCMAKE_Swift_COMPILER=S:\b\toolchain\bin\swiftc.exe -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib" -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include" -DICU_ROOT="S:/Library/icu-64" -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2.lib" -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include" -DENABLE_TESTING=NO -Ddisptch_DIR=S:/b/libdispatch/cmake/modules +ninja -C S:\b\foundation ``` - Add Foundation to your path: ```cmd -path S:\b\foundation;%PATH% +path S:\b\foundation\Foundation;%PATH% ``` ## Build swift-corelibs-xctest ```cmd -md "S:\b\xctest" -cd "S:\b\xctest" -cmake -G Ninja^ - -DBUILD_SHARED_LIBS=YES^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_SWIFT_COMPILER=S:\b\toolchain\bin\swiftc.exe^ - -DXCTEST_PATH_TO_FOUNDATION_BUILD=S:\b\foundation^ - -DXCTEST_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch^ - -DXCTEST_PATH_TO_LIBDISPATCH_BUILD=S:\b\libdispatch^ - -DLIT_COMMAND=S:\llvm\utils\lit\lit.py^ - -DPYTHON_EXECUTABLE=C:\Python27\python.exe^ - S:\swift-corelibs-xctest -ninja +cmake -B S:\b\xctest -G Ninja -S S:\toolchain\swift-corelibs-xctest -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -Ddispatch_DIR=S:\b\dispatch\cmake\modules -DFoundation_DIR=S:\b\foundation\cmake\modules -DLIT_COMMAND=S:\toolchain\llvm\utils\lit\lit.py -DPYTHON_EXECUTABLE=C:\Python27\python.exe +ninja -C S:\b\xctest ``` - Add XCTest to your path: @@ -153,21 +128,8 @@ ninja -C S:\b\xctest check-xctest ## Rebuild Foundation ```cmd -cd "S:\b\foundation -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_Swift_COMPILER=S:\b\toolchain\bin\swiftc.exe^ - -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib"^ - -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include"^ - -DENABLE_TESTING=YES^ - -DICU_ROOT="S:/Library/icu-64"^ - -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2.lib"^ - -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include"^ - -Ddispatch_DIR=S:\b\libdispatch\cmake\modules^ - -DFOUNDATION_PATH_TO_XCTEST_BUILD=S:\b\xctest^ - S:\swift-corelibs-foundation -ninja +cmake -B S:\b\foundation -G Ninja -S S:\toolchain\swift-corelibs-foundation -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang-cl -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib" -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include" -DICU_ROOT="S:/Library/icu-64" -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2.lib" -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include" -DENABLE_TESTING=YES -Ddisptch_DIR=S:/b/libdispatch/cmake/modules -DXCTest_DIR=S:/b/xctest/cmake/modules +ninja -C S:\b\foundation ``` ## Test Foundation From cd8f54583630c078299f37cc0db2a6f42ba11e24 Mon Sep 17 00:00:00 2001 From: Aubrey Mills Date: Wed, 13 Nov 2019 13:48:30 -0500 Subject: [PATCH 149/283] Update DifferentiableProgramming.md (#28230) Changed "Numerical computing in Swift Swift is an expressive..." to "Numerical computing in Swift is an expressive..." --- docs/DifferentiableProgramming.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index 78b68eb48d732..fa536af3de8c9 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -238,13 +238,12 @@ Differentiation is the process of computing derivatives. See the ["Math Introduction"](#math-introduction) section below for more details. Derivatives are a fundamental tool in calculus and have applications in many -domains, notably deep learning. Numerical computing in Swift Swift is an -expressive, high-performance language that is a great fit for numerical -applications. Recent proposals have paved the way for low-level numerical -computing in Swift: [AdditiveArithmetic][SE-0233], SIMD [[1][SE-0229]] -[[2][SE-0251]], [generic math functions][SE-0246]. However, high-level numerical -computing applications, including machine learning and artificial intelligence, -require more work. +domains, notably deep learning. Numerical computing in Swift is an expressive, +high-performance language that is a great fit for numerical applications. Recent +proposals have paved the way for low-level numerical computing in Swift: +[AdditiveArithmetic][SE-0233], SIMD [[1][SE-0229]] [[2][SE-0251]], [generic math +functions][SE-0246]. However, high-level numerical computing applications, +including machine learning and artificial intelligence, require more work. We believe that first-class differentiable programming is a big step towards high-level numerical computing support and will make Swift a real contender in From dfb9a80b569c74b7968fca2db13eda59d029fc31 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 13 Nov 2019 10:52:09 -0800 Subject: [PATCH 150/283] [semantic-arc-opts] Do not perform the (guaranteed (copy)) -> (guaranteed) transform if all destroys are dead end and we have a local borrow scope. This is an analogous fix to a previous fix where we were eliminating copies that were post-dominated by dead ends such that the copy_value did not have any destroys. rdar://56807157 --- .../Mandatory/SemanticARCOpts.cpp | 32 ++++++++---- test/SILOptimizer/semantic-arc-opts.sil | 50 +++++++++++++++++-- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 8aec01ed072f1..175bb0efc6709 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -406,24 +406,38 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // dead end blocks that use the value in a non-consuming way. // // TODO: There may be some way of sinking this into the loop below. - if (destroys.empty() && - llvm::any_of(borrowScopeIntroducers, - [](BorrowScopeIntroducingValue borrowScope) { - return borrowScope.isLocalScope(); - })) { + bool haveAnyLocalScopes = llvm::any_of( + borrowScopeIntroducers, [](BorrowScopeIntroducingValue borrowScope) { + return borrowScope.isLocalScope(); + }); + + if (destroys.empty() && haveAnyLocalScopes) { return false; } // If we reached this point, then we know that all of our users can accept a - // guaranteed value and our owned value is destroyed only by - // destroy_value. Check if all of our destroys are joint post-dominated by the - // our end borrow scope set. If they do not, then the copy_value is lifetime - // extending the guaranteed value, we can not eliminate it. + // guaranteed value and our owned value is destroyed only by a set of + // destroy_values. Check if: + // + // 1. All of our destroys are joint post-dominated by our end borrow scope + // set. If they do not, then the copy_value is lifetime extending the + // guaranteed value, we can not eliminate it. + // + // 2. If all of our destroy_values are dead end. In such a case, the linear + // lifetime checker will not perform any checks since it assumes that dead + // end destroys can be ignored. Since we are going to end the program + // anyways, we want to be conservative here and optimize only if we do not + // need to insert an end_borrow since all of our borrow introducers are + // non-local scopes. { SmallVector destroysForLinearLifetimeCheck; + bool foundNonDeadEnd = false; for (auto *dvi : destroys) { + foundNonDeadEnd |= !getDeadEndBlocks().isDeadEnd(dvi->getParent()); destroysForLinearLifetimeCheck.push_back(&dvi->getAllOperands()[0]); } + if (!foundNonDeadEnd && haveAnyLocalScopes) + return false; SmallVector scratchSpace; SmallPtrSet visitedBlocks; if (llvm::any_of(borrowScopeIntroducers, diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index e05cb475e7966..9842a6c65d9a3 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -824,8 +824,8 @@ bb0: unreachable } -// Make sure we do perform the optimization if our borrowed value is an -// argument. +// Make sure that since we have a guaranteed argument and do not need to reason +// about end_borrows, we handle this. // // CHECK-LABEL: sil [ossa] @guaranteed_arg_used_by_postdominating_no_return_function : $@convention(thin) (@guaranteed NativeObjectPair) -> MyNever { // CHECK-NOT: copy_value @@ -839,6 +839,23 @@ bb0(%0 : @guaranteed $NativeObjectPair): unreachable } + +// Make sure that since our borrow introducer is a begin_borrow, we do not +// eliminate the copy. +// +// CHECK-LABEL: sil [ossa] @borrowed_val_used_by_postdominating_no_return_function : $@convention(thin) (@owned NativeObjectPair) -> MyNever { +// CHECK: copy_value +// CHECK: } // end sil function 'borrowed_val_used_by_postdominating_no_return_function' +sil [ossa] @borrowed_val_used_by_postdominating_no_return_function : $@convention(thin) (@owned NativeObjectPair) -> MyNever { +bb0(%0 : @owned $NativeObjectPair): + %1 = begin_borrow %0 : $NativeObjectPair + %2 = struct_extract %1 : $NativeObjectPair, #NativeObjectPair.obj1 + %3 = copy_value %2 : $Builtin.NativeObject + %func = function_ref @unreachable_guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever + apply %func(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever + unreachable +} + // Just make sure that we do not crash on this. We should be able to eliminate // everything here. // @@ -855,4 +872,31 @@ bb0(%0 : @guaranteed $NativeObjectPair): destroy_value %2 : $Builtin.NativeObject %9999 = tuple() return %9999 : $() -} \ No newline at end of file +} + +// Just make sure we do not crash here. +// +// CHECK-LABEL: sil [ossa] @do_not_insert_end_borrow_given_deadend : $@convention(thin) (@guaranteed ClassLet) -> () { +// CHECK: copy_value +// CHECK: } // end sil function 'do_not_insert_end_borrow_given_deadend' +sil [ossa] @do_not_insert_end_borrow_given_deadend : $@convention(thin) (@guaranteed ClassLet) -> () { +bb0(%x : @guaranteed $ClassLet): + %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () + %p = ref_element_addr %x : $ClassLet, #ClassLet.aLet + %v = load_borrow %p : $*Klass + %c = copy_value %v : $Klass + end_borrow %v : $Klass + apply %f(%c) : $@convention(thin) (@guaranteed Klass) -> () + cond_br undef, bb1, bb2 + +bb1: + destroy_value %c : $Klass + br bb3 + +bb2: + destroy_value %c : $Klass + br bb3 + +bb3: + unreachable +} From 999b32e0fd52945e24ea3b44f9779b89f4951897 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 8 Nov 2019 11:39:46 -0800 Subject: [PATCH 151/283] EscapeAnalysis: add a refcount flag to content nodes. This property will allow alias analysis to be safely optmistic when querying the connection graph. A node that is known to have a ref count is know to keep alive everything it points to. Therefore, calling a deinitializer on a different reference cannot release the RC node's contents. --- .../SILOptimizer/Analysis/EscapeAnalysis.h | 30 ++++++-- .../SILOptimizer/Analysis/ValueTracking.h | 2 +- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 68 +++++++++++++++---- 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 6452460206189..60822d94f171f 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -305,13 +305,19 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// True if the merge is finished (see mergeTo). In this state this node /// is completely unlinked from the graph, bool isMerged = false; - + + /// True if this is a content node that owns a reference count. Such a + /// content node necessarilly keeps alive all content it points to until it + /// is released. This can be conservatively false. + bool hasRC = false; + /// The type of the node (mainly distinguishes between content and value /// nodes). NodeType Type; /// The constructor. - CGNode(ValueBase *V, NodeType Type) : V(V), UsePoints(0), Type(Type) { + CGNode(ValueBase *V, NodeType Type, bool hasRC) + : V(V), UsePoints(0), hasRC(hasRC), Type(Type) { switch (Type) { case NodeType::Argument: case NodeType::Value: @@ -424,6 +430,12 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Return true if this node represents content. bool isContent() const { return Type == NodeType::Content; } + + /// Return true if this node represents an entire reference counted object. + bool hasRefCount() const { return hasRC; } + + void setRefCount() { hasRC = true; } + /// Returns the escape state. EscapeState getEscapeState() const { return State; } @@ -565,10 +577,14 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Removes all nodes from the graph. void clear(); - + /// Allocates a node of a given type. - CGNode *allocNode(ValueBase *V, NodeType Type) { - CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type); + /// + /// hasRC is set for Content nodes based on the type and origin of + /// the pointer. + CGNode *allocNode(ValueBase *V, NodeType Type, bool hasRC = false) { + assert(Type == NodeType::Content || !hasRC); + CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type, hasRC); Nodes.push_back(Node); return Node; } @@ -631,7 +647,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Helper to create a content node and update the pointsTo graph. \p /// addrNode will point to the new content node. The new content node is /// directly initialized with the remaining function arguments. - CGNode *createContentNode(CGNode *addrNode, SILValue addrVal); + CGNode *createContentNode(CGNode *addrNode, SILValue addrVal, bool hasRC); /// Create a new content node based on an existing content node to support /// graph merging. @@ -691,7 +707,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void escapeContentsOf(CGNode *Node) { CGNode *escapedContent = Node->getContentNodeOrNull(); if (!escapedContent) { - escapedContent = createContentNode(Node, Node->V); + escapedContent = createContentNode(Node, Node->V, /*hasRC=*/false); } escapedContent->markEscaping(); } diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index 2a3e39eafb9a5..cb13571026b4d 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -47,7 +47,7 @@ bool pointsToLocalObject(SILValue V); /// allocated object). /// /// - an address projection based on an exclusive argument with no levels of -/// indirection. +/// indirection (e.g. ref_element_addr, project_box, etc.). inline bool isUniquelyIdentified(SILValue V) { return pointsToLocalObject(V) || isExclusiveArgument(getUnderlyingAddressRoot(V)); diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 532f42c0498b7..10a7df0bfd737 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -117,10 +117,9 @@ static bool mayContainReference(SILType Ty, const SILFunction &F) { // Returns true if the type \p Ty must be a reference or must transitively // contain a reference. If \p Ty is itself an address, return false. -// Will be used in a subsequent commit. -// static bool mustContainReference(SILType Ty, const SILFunction &F) { -// return findRecursiveRefType(Ty, F, true); -//} +static bool mustContainReference(SILType Ty, const SILFunction &F) { + return findRecursiveRefType(Ty, F, true); +} bool EscapeAnalysis::isPointer(ValueBase *V) const { auto *F = V->getFunction(); @@ -334,6 +333,15 @@ class EscapeAnalysis::CGNodeMap { void EscapeAnalysis::CGNode::mergeProperties(CGNode *fromNode) { if (!V) V = fromNode->V; + + // TODO: Optimistically merge hasRC. 'this' node can only be merged with + // `fromNode` if their pointer values are compatible. If `fromNode->hasRC` is + // true, then it is guaranteed to represent the head of a heap object. Thus, + // it can only be merged with 'this' when the pointer values that access + // 'this' are also references. + // + // For now, this is pessimistic until we understand performance implications. + hasRC &= fromNode->hasRC; } template @@ -842,8 +850,9 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { } CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(CGNode *addrNode, - SILValue addrVal) { - CGNode *newContent = allocNode(addrVal, NodeType::Content); + SILValue addrVal, + bool hasRC) { + CGNode *newContent = allocNode(addrVal, NodeType::Content, hasRC); initializePointsToEdge(addrNode, newContent); assert(ToMerge.empty() && "Initially setting pointsTo should not require any node merges"); @@ -860,7 +869,7 @@ EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, // on the source content. // // TODO: node properties will come from `srcContent` here... - return createContentNode(destAddrNode, destAddrNode->V); + return createContentNode(destAddrNode, destAddrNode->V, srcContent->hasRC); } // Get a node representing the field data within the given reference-counted @@ -872,7 +881,7 @@ CGNode *EscapeAnalysis::ConnectionGraph::getFieldContent(CGNode *rcNode) { if (rcNode->pointsTo) return rcNode->pointsTo; - return createContentNode(rcNode, rcNode->V); + return createContentNode(rcNode, rcNode->V, /*hasRC=*/false); } bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, @@ -1140,6 +1149,8 @@ std::string CGForDotView::getNodeLabel(const Node *Node) const { switch (Node->OrigNode->Type) { case swift::EscapeAnalysis::NodeType::Content: + if (Node->OrigNode->hasRefCount()) + O << "rc-"; O << "content"; break; case swift::EscapeAnalysis::NodeType::Return: @@ -1177,7 +1188,11 @@ std::string CGForDotView::getNodeAttributes(const Node *Node) const { std::string attr; switch (Orig->Type) { case swift::EscapeAnalysis::NodeType::Content: - attr = "style=\"rounded\""; + attr = "style=\"rounded"; + if (Orig->hasRefCount()) { + attr += ",filled"; + } + attr += "\""; break; case swift::EscapeAnalysis::NodeType::Argument: case swift::EscapeAnalysis::NodeType::Return: @@ -1296,6 +1311,9 @@ void EscapeAnalysis::ConnectionGraph::dumpCG() const { void EscapeAnalysis::CGNode::dump() const { llvm::errs() << getTypeStr(); + if (hasRefCount()) + llvm::errs() << " [rc]"; + if (V) llvm::errs() << ": " << *V; else @@ -1406,6 +1424,9 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { OS << Separator << NodeStr(Def); Separator = ", "; } + if (Nd->hasRefCount()) + OS << " [rc]"; + OS << '\n'; } OS << "End\n"; @@ -1439,6 +1460,10 @@ void EscapeAnalysis::ConnectionGraph::verify(bool allowMerge) const { // which consist of only defer-edges and a single trailing points-to edge // must lead to the same assert(Nd->matchPointToOfDefers(allowMerge)); + if (Nd->isContent() && Nd->V) { + if (Nd->hasRefCount()) + assert(mayContainReference(Nd->V->getType(), *F)); + } } #endif } @@ -1522,15 +1547,26 @@ EscapeAnalysis::getValueContent(ConnectionGraph *conGraph, SILValue addrVal) { assert(isPointer(addrNodeValue)); assert(addrNodeValue == getPointerRoot(addrVal)); } + auto *F = addrVal->getFunction(); + bool hasRC = mustContainReference(baseAddr->getType(), *F) + || mustContainReference(addrVal->getType(), *F); + // Have we already merged a content node? if (CGNode *content = addrNode->getContentNodeOrNull()) { - // TODO: Merge node properties here. + // hasRC might not match if one of the values pointing to this content was + // cast to an unknown type. If any of the types must contain a reference, + // then the content should contain a reference. + if (content->hasRefCount()) + assert(mayContainReference(baseAddr->getType(), *F)); + else if (hasRC) + content->setRefCount(); + return content; } if (!isPointer(baseAddr)) return nullptr; - return conGraph->createContentNode(addrNode, baseAddr); + return conGraph->createContentNode(addrNode, baseAddr, hasRC); } void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, @@ -1737,8 +1773,11 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, CGNode *ArrayRefNode = ArrayStructValue->getContentNodeOrNull(); if (!ArrayRefNode) { ArrayRefNode = ConGraph->createContentNode( - ArrayStructValue, ArrayStructValue->getValueOrNull()); - } + ArrayStructValue, ArrayStructValue->getValueOrNull(), + /*hasRC=*/true); + } else + ArrayRefNode->setRefCount(); + // Another content node for the element storage. CGNode *ArrayElementStorage = ConGraph->getFieldContent(ArrayRefNode); ArrayElementStorage->markEscaping(); @@ -1868,6 +1907,9 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, if (!rcContent) return; + // rcContent->hasRefCount() may or may not be true depending on whether + // the type could be analyzed. Either way, treat it structurally like a + // refcounted object. CGNode *fieldContent = ConGraph->getFieldContent(rcContent); if (!deinitIsKnownToNotCapture(OpV)) { fieldContent->markEscaping(); From 53015f5e77ce4971c070c2e7f92cc4055ce25680 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 10:50:24 -0800 Subject: [PATCH 152/283] [Gardening] Remove handleSILGenericParams from TypeChecker --- include/swift/Subsystems.h | 3 +-- lib/ParseSIL/ParseSIL.cpp | 29 ++++++++++++----------------- lib/Sema/TypeCheckDecl.cpp | 26 -------------------------- lib/Sema/TypeChecker.cpp | 23 +++++++++++++++++++++-- lib/Sema/TypeChecker.h | 4 ---- 5 files changed, 34 insertions(+), 51 deletions(-) diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index f8c808d185cb9..f0be93228ee3c 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -228,8 +228,7 @@ namespace swift { bool ProduceDiagnostics = true); /// Expose TypeChecker's handling of GenericParamList to SIL parsing. - GenericEnvironment *handleSILGenericParams(ASTContext &Ctx, - GenericParamList *genericParams, + GenericEnvironment *handleSILGenericParams(GenericParamList *genericParams, DeclContext *DC); /// Turn the given module into SIL IR. diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 706aa218fcd52..178da31ae7a18 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -1281,24 +1281,20 @@ bool SILParser::parseSILType(SILType &Result, // Resolve the generic environments for parsed generic function and box types. class HandleSILGenericParamsWalker : public ASTWalker { - ASTContext &C; SourceFile *SF; public: - HandleSILGenericParamsWalker(ASTContext &C, - SourceFile *SF) - : C(C), SF(SF) - {} - + HandleSILGenericParamsWalker(SourceFile *SF) : SF(SF) {} + bool walkToTypeReprPre(TypeRepr *T) override { if (auto fnType = dyn_cast(T)) { if (auto generics = fnType->getGenericParams()) { - auto env = handleSILGenericParams(C, generics, SF); + auto env = handleSILGenericParams(generics, SF); fnType->setGenericEnvironment(env); } } if (auto boxType = dyn_cast(T)) { if (auto generics = boxType->getGenericParams()) { - auto env = handleSILGenericParams(C, generics, SF); + auto env = handleSILGenericParams(generics, SF); boxType->setGenericEnvironment(env); } } @@ -1306,9 +1302,8 @@ bool SILParser::parseSILType(SILType &Result, } }; - TyR.get() - ->walk(HandleSILGenericParamsWalker(P.Context, &P.SF)); - + TyR.get()->walk(HandleSILGenericParamsWalker(&P.SF)); + // Save the top-level function generic environment if there was one. if (auto fnType = dyn_cast(TyR.get())) if (auto env = fnType->getGenericEnvironment()) @@ -2039,7 +2034,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Member, bool FnTypeRequired) { if (auto generics = fnType->getGenericParams()) { assert(!Ty.wasValidated() && Ty.getType().isNull()); - genericEnv = handleSILGenericParams(P.Context, generics, &P.SF); + genericEnv = handleSILGenericParams(generics, &P.SF); fnType->setGenericEnvironment(genericEnv); } } @@ -3108,8 +3103,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { { Scope genericsScope(&P, ScopeKind::Generics); generics = P.maybeParseGenericParams().getPtrOrNull(); - patternEnv = handleSILGenericParams(P.Context, generics, &P.SF); - + patternEnv = handleSILGenericParams(generics, &P.SF); + if (P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) return true; @@ -5717,8 +5712,8 @@ bool SILParserTUState::parseSILProperty(Parser &P) { Scope toplevelScope(&P, ScopeKind::TopLevel); Scope genericsScope(&P, ScopeKind::Generics); generics = P.maybeParseGenericParams().getPtrOrNull(); - patternEnv = handleSILGenericParams(P.Context, generics, &P.SF); - + patternEnv = handleSILGenericParams(generics, &P.SF); + if (patternEnv) { if (patternEnv->getGenericSignature()->getCanonicalSignature() != VD->getInnermostDeclContext()->getGenericSignatureOfContext() @@ -6028,7 +6023,7 @@ ProtocolConformanceRef SILParser::parseProtocolConformance( auto *genericParams = P.maybeParseGenericParams().getPtrOrNull(); if (genericParams) { - genericEnv = handleSILGenericParams(P.Context, genericParams, &P.SF); + genericEnv = handleSILGenericParams(genericParams, &P.SF); } auto retVal = parseProtocolConformanceHelper(proto, genericEnv, context, diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4cd4530236af6..258a650c7646d 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -634,32 +634,6 @@ HasCircularRawValueRequest::evaluate(Evaluator &evaluator, return result; } -/// Expose TypeChecker's handling of GenericParamList to SIL parsing. -GenericEnvironment * -TypeChecker::handleSILGenericParams(GenericParamList *genericParams, - DeclContext *DC) { - if (genericParams == nullptr) - return nullptr; - - SmallVector nestedList; - for (; genericParams; genericParams = genericParams->getOuterParameters()) { - nestedList.push_back(genericParams); - } - - std::reverse(nestedList.begin(), nestedList.end()); - - for (unsigned i = 0, e = nestedList.size(); i < e; ++i) { - auto genericParams = nestedList[i]; - genericParams->setDepth(i); - } - - auto sig = TypeChecker::checkGenericSignature( - nestedList.back(), DC, - /*parentSig=*/nullptr, - /*allowConcreteGenericParams=*/true); - return (sig ? sig->getGenericEnvironment() : nullptr); -} - /// Check whether \c current is a redeclaration. static void checkRedeclaration(ASTContext &ctx, ValueDecl *current) { // If we've already checked this declaration, don't do it again. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index f594b85636a17..92c593b5c13c6 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -554,9 +554,28 @@ bool swift::performTypeLocChecking(ASTContext &Ctx, TypeLoc &T, /// Expose TypeChecker's handling of GenericParamList to SIL parsing. GenericEnvironment * -swift::handleSILGenericParams(ASTContext &Ctx, GenericParamList *genericParams, +swift::handleSILGenericParams(GenericParamList *genericParams, DeclContext *DC) { - return TypeChecker::handleSILGenericParams(genericParams, DC); + if (genericParams == nullptr) + return nullptr; + + SmallVector nestedList; + for (; genericParams; genericParams = genericParams->getOuterParameters()) { + nestedList.push_back(genericParams); + } + + std::reverse(nestedList.begin(), nestedList.end()); + + for (unsigned i = 0, e = nestedList.size(); i < e; ++i) { + auto genericParams = nestedList[i]; + genericParams->setDepth(i); + } + + auto sig = + TypeChecker::checkGenericSignature(nestedList.back(), DC, + /*parentSig=*/nullptr, + /*allowConcreteGenericParams=*/true); + return (sig ? sig->getGenericEnvironment() : nullptr); } void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 0215444b4aeba..f033eb9c90f5e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -613,10 +613,6 @@ class TypeChecker final { static void checkUnsupportedProtocolType(ASTContext &ctx, GenericParamList *genericParams); - /// Expose TypeChecker's handling of GenericParamList to SIL parsing. - static GenericEnvironment * - handleSILGenericParams(GenericParamList *genericParams, DeclContext *DC); - /// Resolve a reference to the given type declaration within a particular /// context. /// From 431ca98246e44044d3989719b917cd90f8db0188 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 13 Nov 2019 14:05:59 -0800 Subject: [PATCH 153/283] [Diagnostics] Port explicit closure result contextual mismatch Detect and diagnose a problem when explicitly specified closure result type doesn't match what is expected by the context: Example: ```swift func foo(_: () -> Int) {} foo { () -> String in "" } // `Int` vs. `String` ``` --- lib/Sema/CSDiagnostics.cpp | 11 +++++++++++ lib/Sema/CSSimplify.cpp | 19 ++++++++++++++++++- test/Constraints/closures.swift | 4 ++-- test/Sema/immutability.swift | 4 ++-- ...ubstring_to_string_conversion_swift4.swift | 2 +- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 3949ae2b4981b..52d10ea2add1e 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1986,6 +1986,17 @@ bool ContextualFailure::diagnoseAsError() { Diag diagnostic; switch (path.back().getKind()) { case ConstraintLocator::ClosureResult: { + auto *closure = cast(getRawAnchor()); + if (closure->hasExplicitResultType() && + closure->getExplicitResultTypeLoc().getTypeRepr()) { + auto resultRepr = closure->getExplicitResultTypeLoc().getTypeRepr(); + emitDiagnostic(resultRepr->getStartLoc(), + diag::incorrect_explicit_closure_result, getFromType(), + getToType()) + .fixItReplace(resultRepr->getSourceRange(), getToType().getString()); + return true; + } + diagnostic = diag::cannot_convert_closure_result; break; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index d40a5a05ad1b6..40bb1169360a0 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3265,8 +3265,25 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::FunctionResult: { + auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); + // If this is a mismatch between contextual type and (trailing) + // closure with explicitly specified result type let's record it + // as contextual type mismatch. + if (loc->isLastElement() || + loc->isLastElement()) { + auto *argExpr = simplifyLocatorToAnchor(loc); + if (argExpr && isa(argExpr)) { + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, + getConstraintLocator(argExpr, ConstraintLocator::ClosureResult))); + break; + } + } + LLVM_FALLTHROUGH; + } + case ConstraintLocator::Member: - case ConstraintLocator::FunctionResult: case ConstraintLocator::DynamicLookupResult: { // Most likely this is an attempt to use get-only subscript as mutating, // or assign a value of a result of function/member ref e.g. `foo() = 42` diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index e5fb5935039a1..ea01170a3e090 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -765,7 +765,7 @@ overloaded { print("hi"); print("bye") } // multiple expression closure without func not_overloaded(_ handler: () -> Int) {} not_overloaded { } // empty body -// expected-error@-1 {{cannot convert value of type '() -> ()' to expected argument type '() -> Int'}} +// expected-error@-1 {{cannot convert value of type '()' to closure result type 'Int'}} not_overloaded { print("hi") } // single-expression closure // expected-error@-1 {{cannot convert value of type '()' to closure result type 'Int'}} @@ -786,7 +786,7 @@ func test() -> Int? { } var fn: () -> [Int] = {} -// expected-error@-1 {{cannot convert value of type '() -> ()' to specified type '() -> [Int]'}} +// expected-error@-1 {{cannot convert value of type '()' to closure result type '[Int]'}} fn = {} // expected-error@-1 {{cannot assign value of type '() -> ()' to type '() -> [Int]'}} diff --git a/test/Sema/immutability.swift b/test/Sema/immutability.swift index ec930f573911b..999c9eb8fdad6 100644 --- a/test/Sema/immutability.swift +++ b/test/Sema/immutability.swift @@ -656,8 +656,8 @@ func sr4214() { let closure = { val in val.x = 7 } as (inout MutableSubscripts) -> () // Ok var v = MutableSubscripts() closure(&v) - // FIXME: This diagnostic isn't really all that much better - // expected-error@+1 {{cannot convert value of type '(inout MutableSubscripts) -> ()' to expected argument type '(_) -> _'}} + // expected-error@+2 {{declared closure result '()' is incompatible with contextual type 'MutableSubscripts'}} + // expected-error@+1 {{cannot convert value of type '(inout MutableSubscripts) -> ()' to expected argument type '(MutableSubscripts) -> MutableSubscripts'}} sequence(v) { (state : inout MutableSubscripts) -> () in _ = MutableSubscripts.initialize(from: &state) return () diff --git a/test/Sema/substring_to_string_conversion_swift4.swift b/test/Sema/substring_to_string_conversion_swift4.swift index 10ab545feeacb..291f48d2c4161 100644 --- a/test/Sema/substring_to_string_conversion_swift4.swift +++ b/test/Sema/substring_to_string_conversion_swift4.swift @@ -45,7 +45,7 @@ do { // CTP_ClosureResult do { - [ss].map { (x: Substring) -> String in x } // expected-error {{cannot convert value of type 'Substring' to closure result type 'String'}} {{42-42=String(}} {{43-43=)}} + [ss].map { (x: Substring) -> String in x } // expected-error {{declared closure result 'Substring' is incompatible with contextual type 'String'}} } // CTP_ArrayElement From eb3a1b3f82190e3e450e1ae5acfc680d4a1b9ed9 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 13 Nov 2019 14:06:51 -0800 Subject: [PATCH 154/283] [CSDiag] NFC: Remove obsolete explicit closure result mismatch handling --- lib/Sema/CSDiag.cpp | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index dfd1e4f6dbab9..3dffaebca7da1 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -3243,35 +3243,6 @@ bool FailureDiagnosis::diagnoseClosureExpr( return true; } - // If the body of the closure looked ok, then look for a contextual type - // error. This is necessary because FailureDiagnosis::diagnoseExprFailure - // doesn't do this for closures. - if (contextualType) { - auto fnType = contextualType->getAs(); - if (!fnType || fnType->isEqual(CS.getType(CE))) - return false; - - auto contextualResultType = fnType->getResult(); - // If the result type was unknown, it doesn't really make - // sense to diagnose from expected to unknown here. - if (isInvalidClosureResultType(contextualResultType)) - return false; - - // If the closure had an explicitly written return type incompatible with - // the contextual type, diagnose that. - if (CE->hasExplicitResultType() && - CE->getExplicitResultTypeLoc().getTypeRepr()) { - auto explicitResultTy = CE->getExplicitResultTypeLoc().getType(); - if (fnType && !explicitResultTy->isEqual(contextualResultType)) { - auto repr = CE->getExplicitResultTypeLoc().getTypeRepr(); - diagnose(repr->getStartLoc(), diag::incorrect_explicit_closure_result, - explicitResultTy, fnType->getResult()) - .fixItReplace(repr->getSourceRange(),fnType->getResult().getString()); - return true; - } - } - } - // Otherwise, we can't produce a specific diagnostic. return false; } From ea1a46c84d9b9e18e967f8df68f7427ba08f22ef Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 8 Nov 2019 17:19:31 -0800 Subject: [PATCH 155/283] [ConstraintFix] Rather than creating a coalesced fix right before diagnosing failures in applySolutionFixes, coalesce fixes and diagnose failures in one method on ConstraintFix. This eliminates the need for the `DefaultGenericArgument` fix (which was renamed from `ExplicitlySpecifyGenericArguments`) to have an array of missing parameters, which was only used when the fixes were coalesced. Instead, the coalesced arguments are used to create the `MissingGenericArgumentsFailure` diagnostic directly. --- lib/Sema/CSApply.cpp | 5 ++- lib/Sema/CSBindings.cpp | 6 ++-- lib/Sema/CSFix.cpp | 43 ++++++++++---------------- lib/Sema/CSFix.h | 67 ++++++++++++++++------------------------- lib/Sema/CSSimplify.cpp | 2 +- 5 files changed, 48 insertions(+), 75 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 83352fd9ace0b..3215bb72e56ba 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7379,9 +7379,8 @@ bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) { auto *primaryFix = fixes[0]; ArrayRef secondaryFixes{fixes.begin() + 1, fixes.end()}; - auto *coalescedFix = primaryFix->coalescedWith(secondaryFixes); - auto diagnosed = coalescedFix->diagnose(); - if (coalescedFix->isWarning()) { + auto diagnosed = primaryFix->coalesceAndDiagnose(secondaryFixes); + if (primaryFix->isWarning()) { assert(diagnosed && "warnings should always be diagnosed"); (void)diagnosed; } else { diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index aaac4e4370d38..9b6de5a9131a1 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -991,11 +991,11 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { cs.DefaultedConstraints.push_back(Binding.DefaultableBinding); if (locator->isForGenericParameter() && type->isHole()) { - // Drop `generic parameter '...'` part of the locator to group all of the - // missing generic parameters related to the same path together. + // Drop `generic parameter` locator element so that all missing + // generic parameters related to the same path can be coalesced later. auto path = locator->getPath(); auto genericParam = locator->getGenericParameter(); - auto *fix = ExplicitlySpecifyGenericArguments::create(cs, {genericParam}, + auto *fix = DefaultGenericArgument::create(cs, genericParam, cs.getConstraintLocator(locator->getAnchor(), path.drop_back())); if (cs.recordFix(fix)) return true; diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 1fb5c64dce98e..ee46d2b50066c 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -702,39 +702,28 @@ CollectionElementContextualMismatch::create(ConstraintSystem &cs, Type srcType, CollectionElementContextualMismatch(cs, srcType, dstType, locator); } -bool ExplicitlySpecifyGenericArguments::diagnose(bool asNote) const { - auto &cs = getConstraintSystem(); - MissingGenericArgumentsFailure failure(cs, getParameters(), - getLocator()); - return failure.diagnose(asNote); -} +bool DefaultGenericArgument::coalesceAndDiagnose( + ArrayRef fixes, bool asNote) const { + llvm::SmallVector missingParams{Param}; -ConstraintFix * -ExplicitlySpecifyGenericArguments::coalescedWith(ArrayRef fixes) { - if (fixes.empty()) - return this; - - auto params = getParameters(); - llvm::SmallVector missingParams{params.begin(), - params.end()}; for (auto *otherFix : fixes) { - if (auto *fix = otherFix->getAs()) { - auto additionalParams = fix->getParameters(); - missingParams.append(additionalParams.begin(), additionalParams.end()); - } + if (auto *fix = otherFix->getAs()) + missingParams.push_back(fix->Param); } - return ExplicitlySpecifyGenericArguments::create(getConstraintSystem(), - missingParams, getLocator()); + auto &cs = getConstraintSystem(); + MissingGenericArgumentsFailure failure(cs, missingParams, getLocator()); + return failure.diagnose(asNote); } -ExplicitlySpecifyGenericArguments *ExplicitlySpecifyGenericArguments::create( - ConstraintSystem &cs, ArrayRef params, - ConstraintLocator *locator) { - unsigned size = totalSizeToAlloc(params.size()); - void *mem = cs.getAllocator().Allocate( - size, alignof(ExplicitlySpecifyGenericArguments)); - return new (mem) ExplicitlySpecifyGenericArguments(cs, params, locator); +bool DefaultGenericArgument::diagnose(bool asNote) const { + return coalesceAndDiagnose({}, asNote); +} + +DefaultGenericArgument * +DefaultGenericArgument::create(ConstraintSystem &cs, GenericTypeParamType *param, + ConstraintLocator *locator) { + return new (cs.getAllocator()) DefaultGenericArgument(cs, param, locator); } SkipUnhandledConstructInFunctionBuilder * diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 36ff2964b5b27..438e499ea87bd 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -183,10 +183,8 @@ enum class FixKind : uint8_t { /// function to `Void` to conform to expected result type. RemoveReturn, - /// Generic parameters could not be inferred and have to be explicitly - /// specified in the source. This fix groups all of the missing arguments - /// associated with single declaration. - ExplicitlySpecifyGenericArguments, + /// Default ambiguous generic arguments to \c Any + DefaultGenericArgument, /// Skip any unhandled constructs that occur within a closure argument that /// matches up with a @@ -254,14 +252,18 @@ class ConstraintFix { virtual std::string getName() const = 0; - /// Diagnose a failure associated with this fix given - /// root expression and information from constraint system. - virtual bool diagnose(bool asNote = false) const = 0; - - virtual ConstraintFix *coalescedWith(ArrayRef fixes) { - return this; + /// Coalesce this fix with the given secondary fixes and diagnose the failure. + /// + /// The default implementation ignores \c secondaryFixes and calls + /// \c diagnose. + virtual bool coalesceAndDiagnose(ArrayRef secondaryFixes, + bool asNote = false) const { + return diagnose(asNote); } + /// Diagnose a failure associated with this fix. + virtual bool diagnose(bool asNote = false) const = 0; + void print(llvm::raw_ostream &Out) const; SWIFT_DEBUG_DUMP; @@ -1251,49 +1253,32 @@ class CollectionElementContextualMismatch final : public ContextualMismatch { ConstraintLocator *locator); }; -class ExplicitlySpecifyGenericArguments final - : public ConstraintFix, - private llvm::TrailingObjects { - friend TrailingObjects; +class DefaultGenericArgument final : public ConstraintFix { + GenericTypeParamType *Param; - unsigned NumMissingParams; - - ExplicitlySpecifyGenericArguments(ConstraintSystem &cs, - ArrayRef params, - ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::ExplicitlySpecifyGenericArguments, locator), - NumMissingParams(params.size()) { - assert(!params.empty()); - std::uninitialized_copy(params.begin(), params.end(), - getParametersBuf().begin()); - } + DefaultGenericArgument(ConstraintSystem &cs, GenericTypeParamType *param, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::DefaultGenericArgument, locator), + Param(param) {} public: static bool classof(const ConstraintFix *fix) { - return fix->getKind() == FixKind::ExplicitlySpecifyGenericArguments; + return fix->getKind() == FixKind::DefaultGenericArgument; } std::string getName() const override { - return "default missing generic arguments to `Any`"; + auto paramName = Param->getString(); + return "default generic argument '" + paramName + "' to 'Any'"; } - ArrayRef getParameters() const { - return {getTrailingObjects(), NumMissingParams}; - } + bool coalesceAndDiagnose(ArrayRef secondaryFixes, + bool asNote = false) const override; bool diagnose(bool asNote = false) const override; - ConstraintFix *coalescedWith(ArrayRef fixes) override; - - static ExplicitlySpecifyGenericArguments * - create(ConstraintSystem &cs, ArrayRef params, - ConstraintLocator *locator); - -private: - MutableArrayRef getParametersBuf() { - return {getTrailingObjects(), NumMissingParams}; - } + static DefaultGenericArgument *create(ConstraintSystem &cs, + GenericTypeParamType *param, + ConstraintLocator *locator); }; class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 53447cb8c53d6..5e1249dd6d82c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -8390,7 +8390,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowAnyObjectKeyPathRoot: case FixKind::TreatKeyPathSubscriptIndexAsHashable: case FixKind::AllowInvalidRefInKeyPath: - case FixKind::ExplicitlySpecifyGenericArguments: + case FixKind::DefaultGenericArgument: case FixKind::GenericArgumentsMismatch: case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: From 9b6aa30f3e89d750a275cd5572bc31bb34f2ac7d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 12:13:51 -0800 Subject: [PATCH 156/283] Relax an assert in witness access diagnostics apple/swift#22235 added an assert that was a little overzealous about the structure of access control attributes in the AST. It used to always expect that we reached diagnoseWitnessFixAccessLevel when there was an explicit mismatch in user-written access markers. But declarations may also have their access levels rewritten and their access attributes stripped if they are invalid. If you can get all of these to occur by the time you diagnose the witness, you see no attribute for a witness with insufficient ACLs for a requirement and crash. Relax the assert by using the formal access level and resolve rdar://56818960 --- lib/Sema/TypeCheckProtocol.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 9fc4866fe1b56..7ba88dff46499 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2388,11 +2388,8 @@ static void diagnoseWitnessFixAccessLevel(DiagnosticEngine &diags, if (extAccess < requiredAccess) { shouldMoveToAnotherExtension = true; } else if (extAccess == requiredAccess) { - auto declAttr = decl->getAttrs().getAttribute(); - assert(declAttr && declAttr->getAccess() < requiredAccess && - "expect an explicitly specified access control level which is " - "less accessible than required."); - (void)declAttr; + assert(decl->getFormalAccess() < requiredAccess && + "witness is accessible?"); shouldUseDefaultAccess = true; } } From 88ee618a33fab7a68f461af3930fbab77d29e9f6 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 15:23:03 -0800 Subject: [PATCH 157/283] Move Vending Top-Level Autoclosure discriminators into ASTContext This bit has historically survived typechecking and parsing across source files. Stick it where we stick the other global state. This also means we don't have to thread TopLevelContext around anymore when invoking high-level typechecking entrypoints. --- include/swift/AST/ASTContext.h | 4 ++++ include/swift/Parse/LocalContext.h | 7 +------ lib/Sema/PCMacro.cpp | 2 +- lib/Sema/TypeCheckStmt.cpp | 10 +++++----- lib/Sema/TypeChecker.cpp | 2 +- lib/Sema/TypeChecker.h | 3 +-- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index e45eb30803d44..d105d67f1ba42 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -277,6 +277,10 @@ class ASTContext final { /// The # of times we have performed typo correction. unsigned NumTypoCorrections = 0; + /// The next auto-closure discriminator. This needs to be preserved + /// across invocations of both the parser and the type-checker. + unsigned NextAutoClosureDiscriminator = 0; + private: /// The current generation number, which reflects the number of /// times that external modules have been loaded. diff --git a/include/swift/Parse/LocalContext.h b/include/swift/Parse/LocalContext.h index 04af97d82394e..8e4b2b281f5a0 100644 --- a/include/swift/Parse/LocalContext.h +++ b/include/swift/Parse/LocalContext.h @@ -61,12 +61,7 @@ class LocalContext { }; /// Information associated with parsing the top-level context. -class TopLevelContext : public LocalContext { -public: - /// The next auto-closure discriminator. This needs to be preserved - /// across invocations of both the parser and the type-checker. - unsigned NextAutoClosureDiscriminator = 0; -}; +class TopLevelContext : public LocalContext {}; } // end namespace swift diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index 7cd40b46ca930..dda1b6c0c5aed 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -692,7 +692,7 @@ void swift::performPCMacro(SourceFile &SF, TopLevelContext &TLC) { if (NewBody != Body) { TLCD->setBody(NewBody); TypeChecker::checkTopLevelErrorHandling(TLCD); - TypeChecker::contextualizeTopLevelCode(TLC, TLCD); + TypeChecker::contextualizeTopLevelCode(TLCD); } return false; } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 1dd546540e0c0..12fd135018c3a 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -231,14 +231,14 @@ bool TypeChecker::contextualizeInitializer(Initializer *DC, Expr *E) { return CC.hasAutoClosures(); } -void TypeChecker::contextualizeTopLevelCode(TopLevelContext &TLC, - TopLevelCodeDecl *TLCD) { - unsigned nextDiscriminator = TLC.NextAutoClosureDiscriminator; +void TypeChecker::contextualizeTopLevelCode(TopLevelCodeDecl *TLCD) { + auto &Context = TLCD->DeclContext::getASTContext(); + unsigned nextDiscriminator = Context.NextAutoClosureDiscriminator; ContextualizeClosures CC(TLCD, nextDiscriminator); TLCD->getBody()->walk(CC); - assert(nextDiscriminator == TLC.NextAutoClosureDiscriminator && + assert(nextDiscriminator == Context.NextAutoClosureDiscriminator && "reentrant/concurrent invocation of contextualizeTopLevelCode?"); - TLC.NextAutoClosureDiscriminator = CC.NextDiscriminator; + Context.NextAutoClosureDiscriminator = CC.NextDiscriminator; } /// Emits an error with a fixit for the case of unnecessary cast over a diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 92c593b5c13c6..92c7238583f04 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -366,7 +366,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, if (auto *TLCD = dyn_cast(D)) { // Immediately perform global name-binding etc. TypeChecker::typeCheckTopLevelCodeDecl(TLCD); - TypeChecker::contextualizeTopLevelCode(TLC, TLCD); + TypeChecker::contextualizeTopLevelCode(TLCD); } else { TypeChecker::typeCheckDecl(D); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index f033eb9c90f5e..b74691cbebb07 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1159,8 +1159,7 @@ class TypeChecker final { /// /// \returns true if any closures were found static bool contextualizeInitializer(Initializer *DC, Expr *init); - static void contextualizeTopLevelCode(TopLevelContext &TLC, - TopLevelCodeDecl *TLCD); + static void contextualizeTopLevelCode(TopLevelCodeDecl *TLCD); /// Return the type-of-reference of the given value. /// From faa66f1205ea0b32f3552db86eaf6052f9429b50 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 15:25:13 -0800 Subject: [PATCH 158/283] Strip the REPLChecker of its TopLevelContext It's really not important where it manages to find this unique value, but it's gotta get it from somewhere. Shameless steal DiscriminatorFinder from DebuggerTransform to scrobble for the next available discriminator value. --- lib/Sema/TypeCheckREPL.cpp | 52 ++++++++++++++++++++++++++++++++------ lib/Sema/TypeChecker.cpp | 2 +- lib/Sema/TypeChecker.h | 4 +-- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index 9b83c0ddcad41..cc76489d074d9 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -17,6 +17,7 @@ #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/ASTWalker.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Expr.h" #include "swift/AST/NameLookup.h" @@ -30,6 +31,36 @@ using namespace swift; namespace { + +/// Find available closure discriminators. +/// +/// The parser typically takes care of assigning unique discriminators to +/// closures, but the parser is unavailable to this transform. +class DiscriminatorFinder : public ASTWalker { + unsigned NextDiscriminator = 0; + +public: + Expr *walkToExprPost(Expr *E) override { + auto *ACE = dyn_cast(E); + if (!ACE) + return E; + + unsigned Discriminator = ACE->getDiscriminator(); + assert(Discriminator != AbstractClosureExpr::InvalidDiscriminator && + "Existing closures should have valid discriminators"); + if (Discriminator >= NextDiscriminator) + NextDiscriminator = Discriminator + 1; + return E; + } + + // Get the next available closure discriminator. + unsigned getNextDiscriminator() { + if (NextDiscriminator == AbstractClosureExpr::InvalidDiscriminator) + llvm::report_fatal_error("Out of valid closure discriminators"); + return NextDiscriminator++; + } +}; + struct REPLContext { ASTContext &Context; SourceFile &SF; @@ -182,14 +213,14 @@ struct PatternBindingPrintLHS : public ASTVisitor { namespace { class REPLChecker : public REPLContext { - TopLevelContext &TLC; + DiscriminatorFinder &DF; /// The index of the next response metavariable to bind to a REPL result. unsigned NextResponseVariableIndex = 0; public: - REPLChecker(SourceFile &SF, TopLevelContext &TLC) - : REPLContext(SF), TLC(TLC) {} + REPLChecker(SourceFile &SF, DiscriminatorFinder &DF) + : REPLContext(SF), DF(DF) {} void processREPLTopLevelExpr(Expr *E); void processREPLTopLevelPatternBinding(PatternBindingDecl *PBD); @@ -222,7 +253,7 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { Arg->setSpecifier(ParamSpecifier::Default); auto params = ParameterList::createWithoutLoc(Arg); - unsigned discriminator = TLC.claimNextClosureDiscriminator(); + unsigned discriminator = DF.getNextDiscriminator(); ClosureExpr *CE = new (Context) ClosureExpr(params, SourceLoc(), SourceLoc(), SourceLoc(), @@ -429,13 +460,18 @@ Identifier REPLChecker::getNextResponseVariableName(DeclContext *DC) { /// processREPLTopLevel - This is called after we've parsed and typechecked some /// new decls at the top level. We inject code to print out expressions and /// pattern bindings the are evaluated. -void TypeChecker::processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC, - unsigned FirstDecl) { +void TypeChecker::processREPLTopLevel(SourceFile &SF, unsigned FirstDecl) { + // Walk over all decls in the file to find the next available closure + // discriminator. + DiscriminatorFinder DF; + for (Decl *D : SF.Decls) + D->walk(DF); + // Move new declarations out. std::vector NewDecls(SF.Decls.begin()+FirstDecl, SF.Decls.end()); SF.Decls.resize(FirstDecl); - REPLChecker RC(SF, TLC); + REPLChecker RC(SF, DF); // Loop over each of the new decls, processing them, adding them back to // the Decls list. @@ -455,7 +491,7 @@ void TypeChecker::processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC, if (auto *PBD = dyn_cast(D)) RC.processREPLTopLevelPatternBinding(PBD); - contextualizeTopLevelCode(TLC, TLCD); + TypeChecker::contextualizeTopLevelCode(TLCD); } SF.clearLookupCache(); diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 92c7238583f04..be9ac550497be 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -375,7 +375,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, // If we're in REPL mode, inject temporary result variables and other stuff // that the REPL needs to synthesize. if (SF.Kind == SourceFileKind::REPL && !Ctx.hadError()) - TypeChecker::processREPLTopLevel(SF, TLC, StartElem); + TypeChecker::processREPLTopLevel(SF, StartElem); typeCheckFunctionsAndExternalDecls(SF, TC); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b74691cbebb07..b877616ff8310 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -38,7 +38,6 @@ namespace swift { class GenericSignatureBuilder; class NominalTypeDecl; class NormalProtocolConformance; -class TopLevelContext; class TypeChecker; class TypeResolution; class TypeResolutionOptions; @@ -819,8 +818,7 @@ class TypeChecker final { static void typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD); - static void processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC, - unsigned StartElem); + static void processREPLTopLevel(SourceFile &SF, unsigned StartElem); static void typeCheckDecl(Decl *D); From fcf4703c1809ffeaaa9727d4d47fe6282affa35e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 15:26:25 -0800 Subject: [PATCH 159/283] Strip PCMacro of its TopLevelContext --- include/swift/Subsystems.h | 2 +- lib/Frontend/Frontend.cpp | 2 +- lib/Immediate/REPL.cpp | 2 +- lib/Sema/PCMacro.cpp | 7 +++---- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index f0be93228ee3c..ae5ab37ceab85 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -168,7 +168,7 @@ namespace swift { /// Once parsing and name-binding are complete this optionally walks the ASTs /// to add calls to externally provided functions that simulate /// "program counter"-like debugging events. - void performPCMacro(SourceFile &SF, TopLevelContext &TLC); + void performPCMacro(SourceFile &SF); /// Creates a type checker instance on the given AST context, if it /// doesn't already have one. diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 141d5d214664a..461716f25b901 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -861,7 +861,7 @@ void CompilerInstance::parseAndCheckTypesUpTo( performTypeChecking(SF, PersistentState->getTopLevelContext()); if (!Context->hadError() && Invocation.getFrontendOptions().PCMacro) { - performPCMacro(SF, PersistentState->getTopLevelContext()); + performPCMacro(SF); } // Playground transform knows to look out for PCMacro's changes and not diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 4de8035c504ec..4db9956c9c8c2 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -193,7 +193,7 @@ typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, &PersistentState); } while (!Done); - performTypeChecking(REPLInputFile, PersistentState.getTopLevelContext()); + performTypeChecking(REPLInputFile); return REPLModule; } diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index dda1b6c0c5aed..34750d7159f40 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -665,14 +665,13 @@ class Instrumenter : InstrumenterBase { } // end anonymous namespace -void swift::performPCMacro(SourceFile &SF, TopLevelContext &TLC) { +void swift::performPCMacro(SourceFile &SF) { class ExpressionFinder : public ASTWalker { private: unsigned TmpNameIndex = 0; - TopLevelContext &TLC; public: - ExpressionFinder(TopLevelContext &TLC) : TLC(TLC) {} + ExpressionFinder() = default; bool walkToDeclPre(Decl *D) override { ASTContext &ctx = D->getASTContext(); @@ -702,7 +701,7 @@ void swift::performPCMacro(SourceFile &SF, TopLevelContext &TLC) { } }; - ExpressionFinder EF(TLC); + ExpressionFinder EF; for (Decl *D : SF.Decls) { D->walk(EF); } From 09c7c74c1dfc8bded1a7af0c1ea4fb4542e0fc16 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 13 Nov 2019 15:26:46 -0800 Subject: [PATCH 160/283] Remove the context parameter from performTypeChecking --- include/swift/Subsystems.h | 3 +-- lib/Frontend/Frontend.cpp | 5 ++--- lib/IDE/REPLCodeCompletion.cpp | 3 +-- lib/Sema/TypeChecker.cpp | 3 +-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index ae5ab37ceab85..6175cc7615328 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -184,8 +184,7 @@ namespace swift { /// /// \param StartElem Where to start for incremental type-checking in the main /// source file. - void performTypeChecking(SourceFile &SF, TopLevelContext &TLC, - unsigned StartElem = 0); + void performTypeChecking(SourceFile &SF, unsigned StartElem = 0); /// Now that we have type-checked an entire module, perform any type /// checking that requires the full module, e.g., Objective-C method diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 461716f25b901..a3b97bda8d66f 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -858,7 +858,7 @@ void CompilerInstance::parseAndCheckTypesUpTo( return; } - performTypeChecking(SF, PersistentState->getTopLevelContext()); + performTypeChecking(SF); if (!Context->hadError() && Invocation.getFrontendOptions().PCMacro) { performPCMacro(SF); @@ -975,8 +975,7 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( performNameBinding(MainFile, CurTUElem); break; case SourceFile::TypeChecked: - performTypeChecking(MainFile, PersistentState->getTopLevelContext(), - CurTUElem); + performTypeChecking(MainFile, CurTUElem); break; } } diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 489413bcfdf75..4d3356c440687 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -212,8 +212,7 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, do { parseIntoSourceFile(SF, *BufferID, &Done, nullptr, &PersistentState); } while (!Done); - performTypeChecking(SF, PersistentState.getTopLevelContext(), - OriginalDeclCount); + performTypeChecking(SF, OriginalDeclCount); performCodeCompletionSecondPass(PersistentState, *CompletionCallbacksFactory); diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index be9ac550497be..e80b551022874 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -315,8 +315,7 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) TC.definedFunctions.clear(); } -void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, - unsigned StartElem) { +void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) { if (SF.ASTStage == SourceFile::TypeChecked) return; From c0312b9c0e8e57bee36b0bcc1ffee85b468cb900 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 11 Nov 2019 14:41:54 -0800 Subject: [PATCH 161/283] [ConstraintSystem] Record holes in the constraint system using a new flag in `TypeVariableOptions` rather than using a separate data structure in the constraint system. --- include/swift/AST/Types.h | 8 ++--- lib/Sema/CSBindings.cpp | 5 ++- lib/Sema/CSDiagnostics.cpp | 11 ++++++ lib/Sema/CSSimplify.cpp | 43 +++++++++------------- lib/Sema/CSSolver.cpp | 4 --- lib/Sema/ConstraintSystem.cpp | 3 +- lib/Sema/ConstraintSystem.h | 46 +++++++++++------------- lib/Sema/TypeCheckConstraints.cpp | 7 ++-- test/decl/init/nonnominal_init.swift | 7 ++-- test/expr/delayed-ident/static_var.swift | 3 +- 10 files changed, 65 insertions(+), 72 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index a37598ce3fe23..e8d8df1dad40b 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -347,12 +347,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { NumProtocols : 16 ); - SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 4+32, - : NumPadBits, - + SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 5+32, /// Type variable options. - Options : 4, - + Options : 5, + : NumPadBits, /// The unique number assigned to this type variable. ID : 32 ); diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 9b6de5a9131a1..0c4ceb8062b1a 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -734,10 +734,9 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { constraint->getLocator()}); } - // If we don't have any potential bindings, allow generic - // parameters and potential holes to default to `Unresolved`. + // If there are no bindings, typeVar may be a hole. if (shouldAttemptFixes() && result.Bindings.empty() && - (isPotentialHole(typeVar) || result.isGenericParameter())) { + typeVar->getImpl().canBindToHole()) { result.IsHole = true; result.addPotentialBinding({getASTContext().TheUnresolvedType, AllowedBindingKind::Exact, ConstraintKind::Defaultable, nullptr, diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index df9beb61fce20..527bb50b9154b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3161,6 +3161,17 @@ bool MissingMemberFailure::diagnoseAsError() { if (baseType->is()) diagnostic = diag::could_not_find_tuple_member; + bool hasUnresolvedPattern = false; + anchor->forEachChildExpr([&](Expr *expr) { + hasUnresolvedPattern |= isa(expr); + return hasUnresolvedPattern ? nullptr : expr; + }); + if (hasUnresolvedPattern && !baseType->getAs()) { + emitDiagnostic(anchor->getLoc(), + diag::cannot_match_unresolved_expr_pattern_with_value, baseType); + return; + } + emitDiagnostic(anchor->getLoc(), diagnostic, baseType, getName()) .highlight(baseExpr->getSourceRange()) .highlight(nameLoc.getSourceRange()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 5e1249dd6d82c..8cc28bfe04649 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -867,9 +867,7 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { auto *argType = CS.createTypeVariable(argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - - CS.recordPotentialHole(argType); + TVO_CanBindToNoEscape | TVO_CanBindToHole); Arguments.push_back(param.withType(argType)); ++NumSynthesizedArgs; @@ -4954,6 +4952,12 @@ ConstraintSystem::simplifyFunctionComponentConstraint( // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } + + if (simplified->isHole()) { + if (auto *typeVar = second->getAs()) + recordPotentialHole(typeVar); + return SolutionKind::Solved; + } } if (simplified->isTypeVariableOrMember()) { @@ -6743,9 +6747,12 @@ ConstraintSystem::simplifyKeyPathConstraint( } if (shouldAttemptFixes()) { - auto *componentLoc = getConstraintLocator( - keyPath, {LocatorPathElt::KeyPathComponent(i), - LocatorPathElt::KeyPathComponentResult()}); + auto typeVar = + llvm::find_if(componentTypeVars, [&](TypeVariableType *typeVar) { + auto *locator = typeVar->getImpl().getLocator(); + auto elt = locator->findLast(); + return elt && elt->getIndex() == i; + }); // If one of the components haven't been resolved, let's check // whether it has been determined to be a "hole" and if so, @@ -6753,7 +6760,8 @@ ConstraintSystem::simplifyKeyPathConstraint( // // This helps to, for example, diagnose problems with missing // members used as part of a key path. - if (isPotentialHoleAt(componentLoc)) { + if (typeVar != componentTypeVars.end() && + (*typeVar)->getImpl().canBindToHole()) { anyComponentsUnresolved = true; capability = ReadOnly; continue; @@ -7152,25 +7160,6 @@ ConstraintSystem::simplifyApplicableFnConstraint( // following: $T1 -> $T2. auto func1 = type1->castTo(); - // Let's check if this member couldn't be found and is fixed - // to exist based on its usage. - if (auto *memberTy = type2->getAs()) { - if (isPotentialHole(memberTy)) { - auto *funcTy = type1->castTo(); - auto *locator = memberTy->getImpl().getLocator(); - // Bind type variable associated with member to a type of argument - // application, which makes it seem like member exists with the - // types of the parameters matching argument types exactly. - addConstraint(ConstraintKind::Bind, memberTy, funcTy, locator); - // There might be no contextual type for result of the application, - // in cases like `let _ = x.foo()`, so let's record a potential hole. - auto resultTy = funcTy->getResult(); - if (auto *typeVar = resultTy->getAs()) - recordPotentialHole(typeVar); - return SolutionKind::Solved; - } - } - // Before stripping lvalue-ness and optional types, save the original second // type for handling `func callAsFunction` and `@dynamicCallable` // applications. This supports the following cases: @@ -8184,7 +8173,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { void ConstraintSystem::recordPotentialHole(TypeVariableType *typeVar) { assert(typeVar); - Holes.insert(typeVar->getImpl().getLocator()); + typeVar->getImpl().enableCanBindToHole(getSavedBindings()); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index f30fb4e32ace3..a3abc6771ef22 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -436,7 +436,6 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numSavedBindings = cs.solverState->savedBindings.size(); numConstraintRestrictions = cs.ConstraintRestrictions.size(); numFixes = cs.Fixes.size(); - numHoles = cs.Holes.size(); numFixedRequirements = cs.FixedRequirements.size(); numDisjunctionChoices = cs.DisjunctionChoices.size(); numOpenedTypes = cs.OpenedTypes.size(); @@ -484,9 +483,6 @@ ConstraintSystem::SolverScope::~SolverScope() { // Remove any fixes. truncate(cs.Fixes, numFixes); - // Remove any holes encountered along the current path. - truncate(cs.Holes, numHoles); - // Remove any disjunction choices. truncate(cs.DisjunctionChoices, numDisjunctionChoices); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index eadd085b89378..0dad16f6e37e8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1120,7 +1120,8 @@ void ConstraintSystem::openGenericParameters(DeclContext *outerDC, auto *paramLocator = getConstraintLocator( locator.withPathElement(LocatorPathElt::GenericParameter(gp))); - auto typeVar = createTypeVariable(paramLocator, TVO_PrefersSubtypeBinding); + auto typeVar = createTypeVariable(paramLocator, TVO_PrefersSubtypeBinding | + TVO_CanBindToHole); auto result = replacements.insert(std::make_pair( cast(gp->getCanonicalType()), typeVar)); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index b04e69f4a2d05..6c252673f51e7 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -71,9 +71,12 @@ namespace constraints { /// A handle that holds the saved state of a type variable, which /// can be restored. class SavedTypeVariableBinding { - /// The type variable and type variable options. - llvm::PointerIntPair TypeVarAndOptions; - + /// The type variable that we saved the state of. + TypeVariableType *TypeVar; + + /// The saved type variable options. + unsigned Options; + /// The parent or fixed type. llvm::PointerUnion ParentOrFixed; @@ -82,9 +85,6 @@ class SavedTypeVariableBinding { /// Restore the state of the type variable to the saved state. void restore(); - - TypeVariableType *getTypeVariable() { return TypeVarAndOptions.getPointer(); } - unsigned getOptions() { return TypeVarAndOptions.getInt(); } }; /// A set of saved type variable bindings. @@ -169,9 +169,12 @@ enum TypeVariableOptions { /// Whether the type variable can be bound to a non-escaping type or not. TVO_CanBindToNoEscape = 0x04, + /// Whether the type variable can be bound to a hole type or not. + TVO_CanBindToHole = 0x08, + /// Whether a more specific deduction for this type variable implies a /// better solution to the constraint system. - TVO_PrefersSubtypeBinding = 0x08, + TVO_PrefersSubtypeBinding = 0x10, }; /// The implementation object for a type variable used within the @@ -238,6 +241,9 @@ class TypeVariableType::Implementation { /// Whether this type variable can bind to an inout type. bool canBindToNoEscape() const { return getRawOptions() & TVO_CanBindToNoEscape; } + /// Whether this type variable can bind to a hole type. + bool canBindToHole() const { return getRawOptions() & TVO_CanBindToHole; } + /// Whether this type variable prefers a subtype binding over a supertype /// binding. bool prefersSubtypeBinding() const { @@ -427,6 +433,14 @@ class TypeVariableType::Implementation { ~TVO_CanBindToNoEscape; } + void enableCanBindToHole(constraints::SavedTypeVariableBindings *record) { + auto &impl = getRepresentative(record)->getImpl(); + if (record) + impl.recordBinding(*record); + + impl.getTypeVariable()->Bits.TypeVariableType.Options |= TVO_CanBindToHole; + } + /// Print the type variable to the given output stream. void print(llvm::raw_ostream &OS); }; @@ -1107,13 +1121,6 @@ class ConstraintSystem { /// The set of fixes applied to make the solution work. llvm::SmallVector Fixes; - /// The set of "holes" in the constraint system encountered - /// along the current path identified by locator. A "hole" is - /// a type variable which type couldn't be determined due to - /// an inference failure e.g. missing member, ambiguous generic - /// parameter which hasn't been explicitly specified. - llvm::SmallSetVector Holes; - /// The set of remembered disjunction choices used to reach /// the current constraint system. std::vector> @@ -1608,9 +1615,6 @@ class ConstraintSystem { /// The length of \c Fixes. unsigned numFixes; - /// The length of \c Holes. - unsigned numHoles; - /// The length of \c FixedRequirements. unsigned numFixedRequirements; @@ -2044,14 +2048,6 @@ class ConstraintSystem { void recordPotentialHole(TypeVariableType *typeVar); - bool isPotentialHole(TypeVariableType *typeVar) const { - return isPotentialHoleAt(typeVar->getImpl().getLocator()); - } - - bool isPotentialHoleAt(ConstraintLocator *locator) const { - return bool(Holes.count(locator)); - } - /// Determine whether constraint system already has a fix recorded /// for a particular location. bool hasFixFor(ConstraintLocator *locator, diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 79c4495d1acb3..81cb10b92dbd4 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -68,13 +68,12 @@ void TypeVariableType::Implementation::print(llvm::raw_ostream &OS) { } SavedTypeVariableBinding::SavedTypeVariableBinding(TypeVariableType *typeVar) - : TypeVarAndOptions(typeVar, typeVar->getImpl().getRawOptions()), + : TypeVar(typeVar), Options(typeVar->getImpl().getRawOptions()), ParentOrFixed(typeVar->getImpl().ParentOrFixed) { } void SavedTypeVariableBinding::restore() { - auto *typeVar = getTypeVariable(); - typeVar->getImpl().setRawOptions(getOptions()); - typeVar->getImpl().ParentOrFixed = ParentOrFixed; + TypeVar->getImpl().setRawOptions(Options); + TypeVar->getImpl().ParentOrFixed = ParentOrFixed; } GenericTypeParamType * diff --git a/test/decl/init/nonnominal_init.swift b/test/decl/init/nonnominal_init.swift index 96a906f14e9be..44f22b7f141d8 100644 --- a/test/decl/init/nonnominal_init.swift +++ b/test/decl/init/nonnominal_init.swift @@ -8,9 +8,12 @@ indirect enum Or { } func deMorgan(_ ne: Not>) -> And, Not> { + // FIXME(diagnostics): The error message about initialization here is confusing return And, Not>( - Not { a in ne(.left(a)) }, // expected-error {{non-nominal type 'Not' (aka '(A) -> Never') does not support explicit initialization}} - Not { a in ne(.right(a)) } + Not { a in ne(.left(a)) }, // expected-error {{type 'Not' (aka '(A) -> Never') has no member 'init'}} + // expected-error@-1 {{type 'Or' has no member 'left'}} + Not { a in ne(.right(a)) } // expected-error {{type 'Not' (aka '(B) -> Never') has no member 'init'}} + // expected-error@-1 {{type 'Or' has no member 'right'}} ) } diff --git a/test/expr/delayed-ident/static_var.swift b/test/expr/delayed-ident/static_var.swift index 02267a4f5dbeb..e1f47308def64 100644 --- a/test/expr/delayed-ident/static_var.swift +++ b/test/expr/delayed-ident/static_var.swift @@ -50,4 +50,5 @@ var _: HasClosure = .factoryOpt(3) // expected-error@-1 {{value of optional type '((Int) -> HasClosure)?' must be unwrapped to a value of type '(Int) -> HasClosure'}} // expected-note@-2 {{coalesce}} // expected-note@-3 {{force-unwrap}} -var _: HasClosure = .factoryOpt!(4) // expected-error {{type '((Int) -> HasClosure)?' has no member 'factoryOpt'}} +// FIXME: we should accept this +var _: HasClosure = .factoryOpt!(4) // expected-error {{type 'Optional<_>' has no member 'factoryOpt'}} From 5f3a6af0e54d71dcffd31e05b3b616d9bbff8685 Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Wed, 13 Nov 2019 15:55:25 -0800 Subject: [PATCH 162/283] Explicitly Specify a 64bit Shift to Fix Warning Explicitly doing a 64bit shift silences the otherwise noisy warning: ``` [1548/1735] Building CXX object tools\swift\lib\Sema\CMakeFiles\swiftSema.dir\NameBinding.cpp.obj S:\toolchain\swift\include\swift/AST/IndexSubset.h(96): warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) S:\toolchain\swift\include\swift/AST/IndexSubset.h(184): warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) ``` --- include/swift/AST/IndexSubset.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/IndexSubset.h b/include/swift/AST/IndexSubset.h index 0bead85fdc98c..4e33bae1d7c3d 100644 --- a/include/swift/AST/IndexSubset.h +++ b/include/swift/AST/IndexSubset.h @@ -93,7 +93,7 @@ class IndexSubset : public llvm::FoldingSetNode { for (auto i : indices.set_bits()) { unsigned bitWordIndex, offset; std::tie(bitWordIndex, offset) = getBitWordIndexAndOffset(i); - getBitWord(bitWordIndex) |= (1 << offset); + getBitWord(bitWordIndex) |= (1ull << offset); } } @@ -181,7 +181,7 @@ class IndexSubset : public llvm::FoldingSetNode { bool contains(unsigned index) const { unsigned bitWordIndex, offset; std::tie(bitWordIndex, offset) = getBitWordIndexAndOffset(index); - return getBitWord(bitWordIndex) & (1 << offset); + return getBitWord(bitWordIndex) & (1ull << offset); } bool isEmpty() const { From 71f5ceeaf8f93dafdbfbba9bcfc8b4a162ea6c68 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 13 Nov 2019 16:18:38 -0800 Subject: [PATCH 163/283] Fix closing braces lost during conflict resolution --- test/stdlib/Accelerate.swift | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/test/stdlib/Accelerate.swift b/test/stdlib/Accelerate.swift index 053dd8ffdf390..9f19817c9a7b7 100644 --- a/test/stdlib/Accelerate.swift +++ b/test/stdlib/Accelerate.swift @@ -115,6 +115,7 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { expectTrue(elementsAlmostEqual(destination, returnedResult)) } } +} //===----------------------------------------------------------------------===// // @@ -298,12 +299,13 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { expectTrue(elementsAlmostEqual(result, legacyResult)) expectTrue(elementsAlmostEqual(result, returnedResult)) } +} - //===----------------------------------------------------------------------===// - // - // vDSP difference equation - // - //===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +// +// vDSP difference equation +// +//===----------------------------------------------------------------------===// if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { @@ -382,12 +384,13 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { expectTrue(elementsAlmostEqual(result, legacyResult)) expectTrue(elementsAlmostEqual(result, returnedResult)) } +} - //===----------------------------------------------------------------------===// - // - // vDSP downsampling - // - //===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +// +// vDSP downsampling +// +//===----------------------------------------------------------------------===// if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { AccelerateTests.test("vDSP/DownsampleSinglePrecision") { @@ -469,12 +472,13 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { expectTrue(elementsAlmostEqual(result, legacyResult)) expectTrue(elementsAlmostEqual(result, returnedResult)) } +} - //===----------------------------------------------------------------------===// - // - // vDSP polynomial evaluation. - // - //===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +// +// vDSP polynomial evaluation. +// +//===----------------------------------------------------------------------===// if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { From 670ff7def1ad05a1ae30f011bad00a7c48cdfbaa Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 8 Nov 2019 23:02:22 -0800 Subject: [PATCH 164/283] [Constrant system] Drop expression from diagnoseAmbiguityWithFixes. It's unused now. --- lib/Sema/ConstraintSystem.cpp | 4 ++-- lib/Sema/ConstraintSystem.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 53395f6e4e7e6..b8c658435bef4 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2425,7 +2425,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { // Before removing any "fixed" solutions, let's check // if ambiguity is caused by fixes and diagnose if possible. - if (diagnoseAmbiguityWithFixes(expr, viable)) + if (diagnoseAmbiguityWithFixes(viable)) return true; // FIXME: If we were able to actually fix things along the way, @@ -2529,7 +2529,7 @@ static void diagnoseOperatorAmbiguity(ConstraintSystem &cs, } bool ConstraintSystem::diagnoseAmbiguityWithFixes( - Expr *expr, ArrayRef solutions) { + ArrayRef solutions) { if (solutions.empty()) return false; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c5aa1c80a2873..5ba8e83f2c095 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2108,7 +2108,7 @@ class ConstraintSystem { void diagnoseFailureForExpr(Expr *expr); bool diagnoseAmbiguity(Expr *expr, ArrayRef solutions); - bool diagnoseAmbiguityWithFixes(Expr *expr, ArrayRef solutions); + bool diagnoseAmbiguityWithFixes(ArrayRef solutions); /// Give the deprecation warning for referring to a global function /// when there's a method from a conditional conformance in a smaller/closer From 4c2a7bf1ac7a12b230e17ce83238fa7b7c9cbd0c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 10 Nov 2019 20:23:57 -0800 Subject: [PATCH 165/283] Revert "[Constraint solver] Be more careful about NULL parent expression." This reverts commit 0e269a512724a282512fc885be4806d71cd4d870. --- lib/Sema/CSDiagnostics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 3949ae2b4981b..7dfa649ff42ec 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -680,7 +680,7 @@ bool MissingConformanceFailure::diagnoseAsAmbiguousOperatorRef() { // about missing conformance just in case. auto operatorID = name.getIdentifier(); - auto *applyExpr = cast_or_null(findParentExpr(anchor)); + auto *applyExpr = cast(findParentExpr(anchor)); if (auto *binaryOp = dyn_cast(applyExpr)) { auto lhsType = getType(binaryOp->getArg()->getElement(0)); auto rhsType = getType(binaryOp->getArg()->getElement(1)); From 71523642ce704fb2be6860d61482f4b1ce257a19 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 13 Nov 2019 18:39:23 -0800 Subject: [PATCH 166/283] Fix logic related to isTriviallyDuplicatable. In SILInstruction::isTriviallyDuplicatable(): - Make deallocating instructions trivially duplicatable. They are by any useful definition--duplicating an instruction does not imply reordering it. Tail duplication was already treating deallocations as duplicatable, but doing it inconsistently. Sometimes it checks isTriviallyDuplicatable, and sometimes it doesn't, which appears to have been an accident. Disallowing duplication of deallocations will cause severe performance regressions. Instead, consistently allow them to be duplicated, making tail duplication more powerful, which could expose other bugs. - Do not duplicate on-stack AllocRefInst (without special consideration). This is a correctness fix that apparently was never exposed. Fix SILLoop::canDuplicate(): - Handle isDeallocatingStack. It's not clear how we were avoiding an assertion before when a stack allocatable reference was confined to a loop--probably just by luck. - Handle begin/end_access inside a loop. This is extremely important and probably prevented many loop optimizations from working with exclusivity. Update LoopRotate canDuplicateOrMoveToPreheader(). This is NFC. --- lib/SIL/LoopInfo.cpp | 34 +++++++++++-------- lib/SIL/SILInstruction.cpp | 7 ++-- .../LoopTransforms/LoopRotate.cpp | 4 +++ lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 2 +- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/SIL/LoopInfo.cpp b/lib/SIL/LoopInfo.cpp index 84f55ef1d4116..fd61dbe458242 100644 --- a/lib/SIL/LoopInfo.cpp +++ b/lib/SIL/LoopInfo.cpp @@ -47,6 +47,15 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { } return true; } + if (I->isDeallocatingStack()) { + SILInstruction *Alloc = nullptr; + if (auto *Dealloc = dyn_cast(I)) + Alloc = dyn_cast(Dealloc->getOperand()); + if (auto *Dealloc = dyn_cast(I)) + Alloc = dyn_cast(Dealloc->getOperand()); + // The matching alloc_stack must be in the loop. + return Alloc && contains(Alloc); + } // CodeGen can't build ssa for objc methods. if (auto *Method = dyn_cast(I)) { @@ -71,13 +80,17 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { return true; } - if (auto *Dealloc = dyn_cast(I)) { - // The matching alloc_stack must be in the loop. - if (auto *Alloc = dyn_cast(Dealloc->getOperand())) - return contains(Alloc->getParent()); + if (isa(I)) return false; - } + // The entire access must be within the loop. + if (auto BAI = dyn_cast(I)) { + for (auto *UI : BAI->getUses()) { + if (!contains(UI->getUser())) + return false; + } + return true; + } // The entire coroutine execution must be within the loop. // Note that we don't have to worry about the reverse --- a loop which // contains an end_apply or abort_apply of an external begin_apply --- @@ -92,18 +105,11 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { return true; } - if (isa(I)) - return false; - - if (isa(I)) - return false; - if (isa(I)) return false; - if (auto *PA = dyn_cast(I)) - return !PA->isOnStack(); - + // Some special cases above that aren't considered isTriviallyDuplicatable + // return true early. assert(I->isTriviallyDuplicatable() && "Code here must match isTriviallyDuplicatable in SILInstruction"); return true; diff --git a/lib/SIL/SILInstruction.cpp b/lib/SIL/SILInstruction.cpp index 4d73281ce1309..b1e14d28d4521 100644 --- a/lib/SIL/SILInstruction.cpp +++ b/lib/SIL/SILInstruction.cpp @@ -1172,9 +1172,9 @@ SILInstruction *SILInstruction::clone(SILInstruction *InsertPt) { /// additional handling. It is important to know this information when /// you perform such optimizations like e.g. jump-threading. bool SILInstruction::isTriviallyDuplicatable() const { - if (isa(this) || isa(this)) { + if (isAllocatingStack()) return false; - } + if (auto *ARI = dyn_cast(this)) { if (ARI->canAllocOnStack()) return false; @@ -1213,9 +1213,6 @@ bool SILInstruction::isTriviallyDuplicatable() const { if (isa(this)) return false; - if (auto *PA = dyn_cast(this)) - return !PA->isOnStack(); - // If you add more cases here, you should also update SILLoop:canDuplicate. return true; diff --git a/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp b/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp index 315007020431c..4625eb2d2b5a1 100644 --- a/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp +++ b/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp @@ -70,6 +70,10 @@ canDuplicateOrMoveToPreheader(SILLoop *loop, SILBasicBlock *preheader, invariants.insert(inst); } else if (!inst->isTriviallyDuplicatable()) return false; + // It wouldn't make sense to rotate dealloc_stack without also rotating the + // alloc_stack, which is covered by isTriviallyDuplicatable. + else if (isa(inst)) + return false; else if (isa(inst)) { moves.push_back(inst); invariants.insert(inst); diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 617456bf340a9..35e386a854dad 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -783,7 +783,7 @@ static NullablePtr getEnumCase(SILValue Val, } static int getThreadingCost(SILInstruction *I) { - if (!isa(I) && !I->isTriviallyDuplicatable()) + if (!I->isTriviallyDuplicatable()) return 1000; // Don't jumpthread function calls. From e35a068265f89c626a738a68a8896ea03b7abdee Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 8 Nov 2019 01:23:06 -0500 Subject: [PATCH 167/283] Sema: Add test case for https://bugs.swift.org/browse/SR-11392 --- .../compiler_crashers_2_fixed/sr11392.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/sr11392.swift diff --git a/validation-test/compiler_crashers_2_fixed/sr11392.swift b/validation-test/compiler_crashers_2_fixed/sr11392.swift new file mode 100644 index 0000000000000..449615a072272 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11392.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-frontend -verify -emit-ir %s + +// Ideally this would type check successfully; we should be able to +// infer that X == Int using the same-type constraint 'A.X == X'. + +protocol P1 { + associatedtype X + // expected-note@-1 {{protocol requires nested type 'X'; do you want to add it?}} + associatedtype A: P2 where A.X == X +} + +protocol P2 { + associatedtype X +} + +struct S {} + +extension S { + struct A: P2 { + typealias X = Int + } +} + + +extension S: P1 {} +// expected-error@-1 {{type 'S' does not conform to protocol 'P1'}} + +print(S.X.self) From aa62509d65d36f897aeb0ec39767d63f2afe8aea Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 8 Nov 2019 00:19:03 -0500 Subject: [PATCH 168/283] Sema: Remove an unnecessary call to isInvalid() This forces the interface type, which we don't want to do here. --- lib/Sema/TypeCheckType.cpp | 9 ++------- test/decl/nested/protocol.swift | 5 ++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index bc37804211e6a..629c730b7e45f 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1406,18 +1406,13 @@ static Type resolveNestedIdentTypeComponent( // Diagnose a bad conformance reference if we need to. if (!options.contains(TypeResolutionFlags::SilenceErrors) && - inferredAssocType && memberType && memberType->hasError()) { + inferredAssocType && memberType->hasError()) { maybeDiagnoseBadConformanceRef(DC, parentTy, comp->getLoc(), inferredAssocType); } - // If we found a reference to an associated type or other member type that - // was marked invalid, just return ErrorType to silence downstream errors. - if (member->isInvalid()) - return ErrorType::get(ctx); - // At this point, we need to have resolved the type of the member. - if (!memberType || memberType->hasError()) + if (memberType->hasError()) return memberType; // If there are generic arguments, apply them now. diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index 437a8d39432c6..ea5c973467b10 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -106,8 +106,7 @@ func testLookup(_ x: OuterForUFI.Inner) { x.extMethod() } -// N.B. Lookup fails here because OuterForUFI.Inner is marked invalid. func testLookup(_ x: T) { - x.req() // expected-error {{value of type 'T' has no member 'req'}} - x.extMethod() // expected-error {{value of type 'T' has no member 'extMethod'}} + x.req() + x.extMethod() } From 07d87a1224bf27b38b136971d3006eb6e5f835ea Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 8 Nov 2019 00:19:22 -0500 Subject: [PATCH 169/283] Sema: Simplify TypeResolver::resolveSILBoxType() --- lib/Sema/TypeCheckType.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 629c730b7e45f..6b8dd9d5e8633 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2587,26 +2587,11 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, auto argTy = resolveType(repr->getGenericArguments()[i], options); genericArgMap.insert({params[i], argTy->getCanonicalType()}); } - - bool ok = true; + subMap = SubstitutionMap::get( genericSig, QueryTypeSubstitutionMap{genericArgMap}, - [&](CanType depTy, Type replacement, ProtocolDecl *proto) - -> ProtocolConformanceRef { - auto result = TypeChecker::conformsToProtocol( - replacement, proto, DC, - ConformanceCheckOptions()); - if (result.isInvalid()) { - ok = false; - return ProtocolConformanceRef(proto); - } - - return result; - }); - - if (!ok) - return ErrorType::get(Context); + TypeChecker::LookUpConformance(DC)); } auto layout = SILLayout::get(Context, genericSig, fields); From 686ddd7cf7d03d46cc5a7166e7937d1196ffcb30 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 8 Nov 2019 00:19:53 -0500 Subject: [PATCH 170/283] Sema: Tighten up getReferencedAssociatedTypes() If a protocol requirement has a type that's a nested member type of another member type, eg, protocol P { associatedtype A : Q func f(_: A.B) } Then we don't actually want to use 'f()' to infer the witness for 'A'. By avoiding doing so, we eliminate some cycles which can allow some programs to type check that didn't before. --- lib/Sema/TypeCheckProtocol.cpp | 37 +++++++++++++++---- .../req/associated_type_inference_valid.swift | 24 ++++++++++++ .../sr11052-typealias.swift | 2 +- 3 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 test/decl/protocol/req/associated_type_inference_valid.swift diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 7ba88dff46499..8ed781786ad7f 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2215,16 +2215,39 @@ ConformanceChecker::getReferencedAssociatedTypes(ValueDecl *req) { return known->second; // Collect the set of associated types rooted on Self in the - // signature. + // signature. Note that for references to nested types, we only + // want to consider the outermost dependent member type. + // + // For example, a requirement typed '(Iterator.Element) -> ()' + // is not considered to reference the associated type 'Iterator'. auto &assocTypes = ReferencedAssociatedTypes[req]; - llvm::SmallPtrSet knownAssocTypes; - req->getInterfaceType()->getCanonicalType().visit([&](CanType type) { - if (auto assocType = getReferencedAssocTypeOfProtocol(type, Proto)) { - if (knownAssocTypes.insert(assocType).second) { - assocTypes.push_back(assocType); + + class Walker : public TypeWalker { + ProtocolDecl *Proto; + llvm::SmallVectorImpl &assocTypes; + llvm::SmallPtrSet knownAssocTypes; + + public: + Walker(ProtocolDecl *Proto, + llvm::SmallVectorImpl &assocTypes) + : Proto(Proto), assocTypes(assocTypes) {} + + Action walkToTypePre(Type type) override { + if (type->is()) { + if (auto assocType = getReferencedAssocTypeOfProtocol(type, Proto)) { + if (knownAssocTypes.insert(assocType).second) + assocTypes.push_back(assocType); } + + return Action::SkipChildren; } - }); + + return Action::Continue; + } + }; + + Walker walker(Proto, assocTypes); + req->getInterfaceType()->getCanonicalType().walk(walker); return assocTypes; } diff --git a/test/decl/protocol/req/associated_type_inference_valid.swift b/test/decl/protocol/req/associated_type_inference_valid.swift new file mode 100644 index 0000000000000..af92793e5a535 --- /dev/null +++ b/test/decl/protocol/req/associated_type_inference_valid.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend -emit-silgen %s + +// This is a SILGen test to ensure we can completely check these conformances +// and build valid AST. + +protocol P { + associatedtype T : Q = S + typealias Y = T.X + + func foo(_: T.X) +} + +protocol Q { + associatedtype X +} + +struct S : Q { + typealias X = () +} + +struct R : P { + let x: Y? = nil + func foo(_: Y) {} +} diff --git a/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift b/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift index 8460975c7d20e..352d50a754380 100644 --- a/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift +++ b/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift @@ -19,7 +19,7 @@ struct Concrete: ProtoB { fatalError() } - func protoFunc() -> Alias { // expected-error{{unsupported recursion for reference to type alias 'Alias' of type 'Concrete'}} + func protoFunc() -> Alias { fatalError() } } From 04fbcc01490646699883637abbda00546534a72c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 8 Nov 2019 00:48:21 -0500 Subject: [PATCH 171/283] Sema: Look for generic parameters first when inferring an associated type Previously we did this as a last resort if inference fails. The new behavior is technically source-breaking, but I suspect nobody relied on the old behavior. This can help avoid cycles by eliminating some unnecessary validation work. Fixes , . --- lib/Sema/TypeCheckProtocol.cpp | 23 +++++++++++-- lib/Sema/TypeCheckProtocolInference.cpp | 8 ----- stdlib/public/core/NativeSet.swift | 3 ++ .../req/associated_type_inference_valid.swift | 32 +++++++++++++++++++ test/decl/protocol/typealias_inference.swift | 18 ----------- 5 files changed, 55 insertions(+), 29 deletions(-) delete mode 100644 test/decl/protocol/typealias_inference.swift diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 8ed781786ad7f..c6f50fb49e605 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -3510,20 +3510,37 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( AssociatedTypeDecl *assocType) { // Conformances constructed by the ClangImporter should have explicit type // witnesses already. - if (isa(Conformance->getDeclContext()->getModuleScopeContext())) { + if (isa(DC->getModuleScopeContext())) { llvm::errs() << "Cannot look up associated type for imported conformance:\n"; Conformance->getType().dump(llvm::errs()); assocType->dump(llvm::errs()); abort(); } + // If we fail to find a witness via lookup, check for a generic parameter. + auto checkForGenericParameter = [&]() { + // If there is a generic parameter of the named type, use that. + if (auto genericSig = DC->getGenericSignatureOfContext()) { + for (auto gp : genericSig->getInnermostGenericParams()) { + if (gp->getName() == assocType->getName()) { + if (!checkTypeWitness(DC, Proto, assocType, gp)) { + recordTypeWitness(assocType, gp, nullptr); + return ResolveWitnessResult::Success; + } + } + } + } + + return ResolveWitnessResult::Missing; + }; + // Look for a member type with the same name as the associated type. auto candidates = TypeChecker::lookupMemberType( DC, Adoptee, assocType->getName(), NameLookupFlags::ProtocolMembers); // If there aren't any candidates, we're done. if (!candidates) { - return ResolveWitnessResult::Missing; + return checkForGenericParameter(); } // Determine which of the candidates is viable. @@ -3557,7 +3574,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( return x.first->getDeclContext() ->getSelfProtocolDecl() == nullptr; }) == nonViable.end()) - return ResolveWitnessResult::Missing; + return checkForGenericParameter(); // If there is a single viable candidate, form a substitution for it. if (viable.size() == 1) { diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index ffa58350ce96a..ebf92845dd7e9 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -918,14 +918,6 @@ AssociatedTypeInference::computeAbstractTypeWitness( return derivedType; } - // If there is a generic parameter of the named type, use that. - if (auto genericSig = dc->getGenericSignatureOfContext()) { - for (auto gp : genericSig->getInnermostGenericParams()) { - if (gp->getName() == assocType->getName()) - return dc->mapTypeIntoContext(gp); - } - } - return Type(); } diff --git a/stdlib/public/core/NativeSet.swift b/stdlib/public/core/NativeSet.swift index 093a9641ba318..143d7b87499b5 100644 --- a/stdlib/public/core/NativeSet.swift +++ b/stdlib/public/core/NativeSet.swift @@ -282,6 +282,9 @@ extension _NativeSet { } extension _NativeSet: _SetBuffer { + @usableFromInline + internal typealias Element = Element + @usableFromInline internal typealias Index = Set.Index diff --git a/test/decl/protocol/req/associated_type_inference_valid.swift b/test/decl/protocol/req/associated_type_inference_valid.swift index af92793e5a535..3b24d1ba42be7 100644 --- a/test/decl/protocol/req/associated_type_inference_valid.swift +++ b/test/decl/protocol/req/associated_type_inference_valid.swift @@ -22,3 +22,35 @@ struct R : P { let x: Y? = nil func foo(_: Y) {} } + +// SR-8813 +protocol BaseProtocol { + associatedtype Value + typealias Closure = () -> Value + + init(closure: Closure) +} + +struct Base: BaseProtocol { + private var closure: Closure? + + init(closure: Closure) { + withoutActuallyEscaping(closure) { new in + self.closure = new + } + } +} + +// SR-11407 +protocol _Drivable: AnyObject { + typealias Driver = Self +} +protocol Configurator { + associatedtype Drivable: _Drivable + typealias Driver = Drivable.Driver + func configure(driver: Driver) +} +struct AnyConfigurator: Configurator { + private let thing: Driver? + func configure(driver: AnyConfigurator.Driver) {} +} diff --git a/test/decl/protocol/typealias_inference.swift b/test/decl/protocol/typealias_inference.swift deleted file mode 100644 index 89b9c8dfaf3f2..0000000000000 --- a/test/decl/protocol/typealias_inference.swift +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -// SR-8813 -// By itself in this file because this particular expected error is omitted if there have been any other diagnostics. -protocol BaseProtocol { - associatedtype Value - typealias Closure = () -> Value - - init(closure: Closure) -} - -struct Base: BaseProtocol { - private let closure: Closure - - init(closure: Closure) { //expected-error {{reference to invalid type alias 'Closure' of type 'Base'}} - self.closure = closure - } -} From d60b97dcd14e1a91b16883aa286279346b3389b9 Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Thu, 14 Nov 2019 10:33:44 +0200 Subject: [PATCH 172/283] [Testing] Add missing REQUIRES: asan_runtime --- test/Driver/sanitize_recover.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Driver/sanitize_recover.swift b/test/Driver/sanitize_recover.swift index 8f81f0e19d421..ca7ca240acb41 100644 --- a/test/Driver/sanitize_recover.swift +++ b/test/Driver/sanitize_recover.swift @@ -3,6 +3,7 @@ // RUN: %swiftc_driver -v -sanitize-recover=address %s -o %t 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION %s // RUN: %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITH_RECOVER %s // RUN: %swiftc_driver -driver-print-jobs -sanitize=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITHOUT_RECOVER --implicit-check-not='-sanitize-recover=address' %s +// REQUIRES: asan_runtime // SAN_RECOVER_INVALID_ARG: unsupported argument 'foo' to option '-sanitize-recover=' // SAN_RECOVER_UNSUPPORTED_ARG: unsupported argument 'thread' to option '-sanitize-recover=' From 9963fe50c1222c1f35d44cc5bfb4c435e37665a6 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 14 Nov 2019 09:00:04 -0800 Subject: [PATCH 173/283] Add support in SILLoop::canDuplicate for PartialApplyInst. Which may also generate stack allocations. --- lib/SIL/LoopInfo.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/SIL/LoopInfo.cpp b/lib/SIL/LoopInfo.cpp index fd61dbe458242..72defd29eb7bc 100644 --- a/lib/SIL/LoopInfo.cpp +++ b/lib/SIL/LoopInfo.cpp @@ -48,13 +48,16 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { return true; } if (I->isDeallocatingStack()) { - SILInstruction *Alloc = nullptr; - if (auto *Dealloc = dyn_cast(I)) - Alloc = dyn_cast(Dealloc->getOperand()); - if (auto *Dealloc = dyn_cast(I)) - Alloc = dyn_cast(Dealloc->getOperand()); - // The matching alloc_stack must be in the loop. - return Alloc && contains(Alloc); + SILInstruction *alloc = nullptr; + if (auto *dealloc = dyn_cast(I)) { + SILValue address = dealloc->getOperand(); + if (isa(address) || isa(address)) + alloc = cast(address); + } + if (auto *dealloc = dyn_cast(I)) + alloc = dyn_cast(dealloc->getOperand()); + + return alloc && contains(alloc); } // CodeGen can't build ssa for objc methods. From ff601ad06d98d0ab15fb04439458c92a22c20c89 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Nov 2019 16:30:39 -0800 Subject: [PATCH 174/283] SourceKit: inline single-use variable (NFC) Inline the unnecessary use of the variable for creating the SourceKitSupport library. --- tools/SourceKit/lib/Support/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tools/SourceKit/lib/Support/CMakeLists.txt b/tools/SourceKit/lib/Support/CMakeLists.txt index 587f2b62395d6..e46b19f3378f4 100644 --- a/tools/SourceKit/lib/Support/CMakeLists.txt +++ b/tools/SourceKit/lib/Support/CMakeLists.txt @@ -1,4 +1,4 @@ -set(SourceKitSupport_sources +add_sourcekit_library(SourceKitSupport Concurrency-libdispatch.cpp FuzzyStringMatcher.cpp Logging.cpp @@ -6,10 +6,6 @@ set(SourceKitSupport_sources ThreadSafeRefCntPtr.cpp Tracing.cpp UIDRegistry.cpp -) - -add_sourcekit_library(SourceKitSupport - ${SourceKitSupport_sources} LINK_LIBS swiftBasic swiftSyntax clangBasic clangRewrite ) if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) From df27954b2a595ee54fc42cbf71510348e55effb3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Nov 2019 16:43:20 -0800 Subject: [PATCH 175/283] build: inline a number of single-use variables (NFC) Inline a number of single use variables in the swift-lang build. --- tools/SourceKit/tools/swift-lang/CMakeLists.txt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tools/SourceKit/tools/swift-lang/CMakeLists.txt b/tools/SourceKit/tools/swift-lang/CMakeLists.txt index 61824c85dac10..8db6dbe81a7d9 100644 --- a/tools/SourceKit/tools/swift-lang/CMakeLists.txt +++ b/tools/SourceKit/tools/swift-lang/CMakeLists.txt @@ -1,9 +1,4 @@ if(NOT SWIFT_SOURCEKIT_USE_INPROC_LIBRARY AND SWIFT_BUILD_STDLIB AND SWIFT_BUILD_SDK_OVERLAY) - set(EXTRA_COMPILE_FLAGS "-F" "${SWIFT_LIBRARY_OUTPUT_INTDIR}") - set(SOURCEKITD_LINK_LIBS sourcekitd) - set(INSTALLED_COMP sourcekit-xpc-service) - set(DEPENDS_LIST "sourcekitd-test") - # The build type of swiftlang should agree with stdlib # This setting could avoid us adding additional search paths when building # executables using SwiftLang. @@ -25,11 +20,11 @@ if(NOT SWIFT_SOURCEKIT_USE_INPROC_LIBRARY AND SWIFT_BUILD_STDLIB AND SWIFT_BUILD GYB_SOURCES UIDs.swift.gyb - DEPENDS ${DEPENDS_LIST} + DEPENDS sourcekitd-test SWIFT_MODULE_DEPENDS_OSX Darwin Foundation - PRIVATE_LINK_LIBRARIES ${SOURCEKITD_LINK_LIBS} - SWIFT_COMPILE_FLAGS ${EXTRA_COMPILE_FLAGS} - INSTALL_IN_COMPONENT ${INSTALLED_COMP} + PRIVATE_LINK_LIBRARIES sourcekitd + SWIFT_COMPILE_FLAGS -F${SWIFT_LIBRARY_OUTPUT_INTDIR} + INSTALL_IN_COMPONENT sourcekit-xpc-service DARWIN_INSTALL_NAME_DIR "@rpath" TARGET_SDKS ${SOURCEKIT_DEFAULT_TARGET_SDK} IS_STDLIB) From e6992d35cd498c89b89ec282418d16cf6b7862f0 Mon Sep 17 00:00:00 2001 From: Luca Torella Date: Thu, 29 Nov 2018 10:04:21 +0100 Subject: [PATCH 176/283] Fix reference to `range(of:)` in the contains doc Signed-off-by: Luca Torella --- stdlib/public/Darwin/Foundation/NSStringAPI.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Darwin/Foundation/NSStringAPI.swift b/stdlib/public/Darwin/Foundation/NSStringAPI.swift index 8aa1929268b92..3019b5190bae2 100644 --- a/stdlib/public/Darwin/Foundation/NSStringAPI.swift +++ b/stdlib/public/Darwin/Foundation/NSStringAPI.swift @@ -1671,7 +1671,7 @@ extension StringProtocol where Index == String.Index { /// Returns `true` if `other` is non-empty and contained within `self` by /// case-sensitive, non-literal search. Otherwise, returns `false`. /// - /// Equivalent to `self.rangeOfString(other) != nil` + /// Equivalent to `self.range(of: other) != nil` public func contains(_ other: T) -> Bool { let r = self.range(of: other) != nil if #available(macOS 10.10, iOS 8.0, *) { From ac99f189e811d2e1dc0cac8bac50b187e72198fa Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 14 Nov 2019 10:56:39 -0800 Subject: [PATCH 177/283] [Gardening] Clean up after #16227 Another contributor had gone ahead and added the leading Swifts because this wasn't here. Just drop them again. Supersedes #25211 --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 805b7714bfe54..f03ac4ca3911a 100644 --- a/README.md +++ b/README.md @@ -155,26 +155,26 @@ First, make sure that you're in the swift directory: To build using Ninja, run: - swift/utils/build-script --release-debuginfo + utils/build-script --release-debuginfo When developing Swift, it helps to build what you're working on in a debug configuration while building the rest of the project with optimizations. Below are some examples of using debug variants: - swift/utils/build-script --release-debuginfo --debug-swift # Swift frontend built in debug - swift/utils/build-script --release-debuginfo --debug-swift-stdlib # Standard library built in debug - swift/utils/build-script --release-debuginfo --debug-swift --force-optimized-typechecker # Swift frontend sans type checker built in debug + utils/build-script --release-debuginfo --debug-swift # Swift frontend built in debug + utils/build-script --release-debuginfo --debug-swift-stdlib # Standard library built in debug + utils/build-script --release-debuginfo --debug-swift --force-optimized-typechecker # Swift frontend sans type checker built in debug Limiting the amount of debug code in the compiler has a very large impact on Swift compile times, and in turn the test execution time. If you want to build the entire project in debug, you can run: - swift/utils/build-script --debug + utils/build-script --debug For documentation of all available arguments, as well as additional usage information, see the inline help: - swift/utils/build-script -h + utils/build-script -h #### Xcode From ae7723a6186f9577f5c56efcaa301d74f82e1a17 Mon Sep 17 00:00:00 2001 From: own2pwn <7850039+own2pwn@users.noreply.github.com> Date: Mon, 5 Aug 2019 00:26:02 +0300 Subject: [PATCH 178/283] [Docs] Fix typo in HighLevelSILOptimizations.rst. `nonelement` -> `non-element` --- docs/HighLevelSILOptimizations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HighLevelSILOptimizations.rst b/docs/HighLevelSILOptimizations.rst index 4b97b75705577..8646e741d9b61 100644 --- a/docs/HighLevelSILOptimizations.rst +++ b/docs/HighLevelSILOptimizations.rst @@ -135,7 +135,7 @@ Array, ContiguousArray, and ArraySlice data-structures. We consider the array state to consist of a set of disjoint elements -and a storage descriptor that encapsulates nonelement data such as the +and a storage descriptor that encapsulates non-element data such as the element count and capacity. Operations that semantically write state are always *control dependent*. A control dependent operation is one that may only be executed on the control flow paths in which the From 0718dc9f87f8636445ca12a7f269aa74d31fa048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Thu, 14 Nov 2019 12:41:25 -0800 Subject: [PATCH 179/283] [windows] Adapt pcm-emit-and-import for Windows separators. --- test/ClangImporter/pcm-emit-and-import.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ClangImporter/pcm-emit-and-import.swift b/test/ClangImporter/pcm-emit-and-import.swift index d2845aeccc5bb..c48ccb22eed30 100644 --- a/test/ClangImporter/pcm-emit-and-import.swift +++ b/test/ClangImporter/pcm-emit-and-import.swift @@ -6,7 +6,7 @@ // RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-DUMP // CHECK-DUMP: Information for module file '{{.*}}/script.pcm': // CHECK-DUMP: Module name: script -// CHECK-DUMP: Module map file: {{.*}}/Inputs/custom-modules/module.map +// CHECK-DUMP: Module map file: {{.*[/\\]}}Inputs{{/|\\}}custom-modules{{/|\\}}module.map // Compile a source file that imports the explicit module. // RUN: %target-swift-frontend -typecheck -verify -Xcc -fmodule-file=%t/script.pcm %s From 3c05acac1508bc3c2a32d2aa4436dc8117270fbd Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 14 Nov 2019 12:51:00 -0800 Subject: [PATCH 180/283] Address review feedback Rename getDefaultArgumentInitContextCached, and have it return an Optional. --- include/swift/AST/Decl.h | 2 +- lib/AST/Decl.cpp | 9 +++++---- lib/AST/TypeCheckRequests.cpp | 5 +---- lib/Sema/TypeCheckDecl.cpp | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 975b876dc53cc..c1cbba1c20596 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5198,7 +5198,7 @@ class ParamDecl : public VarDecl { /// Retrieve the cached initializer context for the parameter's default /// argument without triggering a request. - Initializer *getDefaultArgumentInitContextCached() const; + Optional getCachedDefaultArgumentInitContext() const; enum class Flags : uint8_t { /// Whether or not this parameter is vargs. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 67b9b7f2be5c7..3cba8c576f8a0 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6046,11 +6046,12 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { return AnyFunctionType::Param(type, label, flags); } -Initializer *ParamDecl::getDefaultArgumentInitContextCached() const { +Optional ParamDecl::getCachedDefaultArgumentInitContext() const { if (auto *defaultInfo = DefaultValueAndFlags.getPointer()) - return defaultInfo->InitContextAndIsTypeChecked.getPointer(); + if (auto *init = defaultInfo->InitContextAndIsTypeChecked.getPointer()) + return init; - return nullptr; + return None; } Initializer *ParamDecl::getDefaultArgumentInitContext() const { @@ -6160,7 +6161,7 @@ CustomAttr *ValueDecl::getAttachedFunctionBuilder() const { } void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) { - auto *oldContext = getDefaultArgumentInitContextCached(); + auto oldContext = getCachedDefaultArgumentInitContext(); assert((!oldContext || oldContext == initContext) && "Cannot change init context after setting"); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index c9b265958d340..38796bd8ae4ef 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1190,10 +1190,7 @@ void HasCircularRawValueRequest::noteCycleStep(DiagnosticEngine &diags) const { Optional DefaultArgumentInitContextRequest::getCachedResult() const { auto *param = std::get<0>(getStorage()); - if (auto *init = param->getDefaultArgumentInitContextCached()) - return init; - - return None; + return param->getCachedDefaultArgumentInitContext(); } void DefaultArgumentInitContextRequest::cacheResult(Initializer *init) const { diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 160d861143046..53aa04c639754 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2066,7 +2066,7 @@ DefaultArgumentInitContextRequest::evaluate(Evaluator &eval, continue; // If this param already has a context, continue using it. - if (otherParam->getDefaultArgumentInitContextCached()) + if (otherParam->getCachedDefaultArgumentInitContext()) continue; // Create a new initializer context. If this is for the parameter that From 1b8a723038d0e5c3b86549126c2613845b5ddcae Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Mon, 11 Nov 2019 11:28:13 -0500 Subject: [PATCH 181/283] [Reflection] Ignore BuiltinTypeDescriptors with zero size, alignment, or stride. rdar://problem/56784375 --- stdlib/public/Reflection/TypeRefBuilder.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index d16ad510bf3e5..586066bb4e5a7 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -253,9 +253,12 @@ TypeRefBuilder::getBuiltinTypeInfo(const TypeRef *TR) { for (auto Info : ReflectionInfos) { for (auto BuiltinTypeDescriptor : Info.Builtin) { - assert(BuiltinTypeDescriptor->Size > 0); - assert(BuiltinTypeDescriptor->getAlignment() > 0); - assert(BuiltinTypeDescriptor->Stride > 0); + if (BuiltinTypeDescriptor->Size <= 0) + continue; + if (BuiltinTypeDescriptor->getAlignment() <= 0) + continue; + if (BuiltinTypeDescriptor->Stride <= 0) + continue; if (!BuiltinTypeDescriptor->hasMangledTypeName()) continue; auto CandidateMangledName = From c81141af1db35885d4834565893d2db2ad8a83e7 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 14 Nov 2019 16:15:32 -0500 Subject: [PATCH 182/283] [Reflection] Add a test for reflecting empty structs. rdar://problem/56784375 --- .../Inputs/EmptyStruct/EmptyStruct.h | 3 + .../Reflection/Inputs/EmptyStruct/module.map | 3 + .../Reflection/reflect_empty_struct.swift | 74 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 validation-test/Reflection/Inputs/EmptyStruct/EmptyStruct.h create mode 100644 validation-test/Reflection/Inputs/EmptyStruct/module.map create mode 100644 validation-test/Reflection/reflect_empty_struct.swift diff --git a/validation-test/Reflection/Inputs/EmptyStruct/EmptyStruct.h b/validation-test/Reflection/Inputs/EmptyStruct/EmptyStruct.h new file mode 100644 index 0000000000000..580b9fb4bd762 --- /dev/null +++ b/validation-test/Reflection/Inputs/EmptyStruct/EmptyStruct.h @@ -0,0 +1,3 @@ +struct EmptyStructC { + int trailing[0]; +}; diff --git a/validation-test/Reflection/Inputs/EmptyStruct/module.map b/validation-test/Reflection/Inputs/EmptyStruct/module.map new file mode 100644 index 0000000000000..42b486a7c3670 --- /dev/null +++ b/validation-test/Reflection/Inputs/EmptyStruct/module.map @@ -0,0 +1,3 @@ +module EmptyStruct { + header "EmptyStruct.h" +} diff --git a/validation-test/Reflection/reflect_empty_struct.swift b/validation-test/Reflection/reflect_empty_struct.swift new file mode 100644 index 0000000000000..c207fbfa7362d --- /dev/null +++ b/validation-test/Reflection/reflect_empty_struct.swift @@ -0,0 +1,74 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct +// RUN: %target-codesign %t/reflect_empty_struct + +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import SwiftReflectionTest + +import EmptyStruct + +@_alignment(1) struct EmptyStruct { } +class Class { + var a = EmptyStruct() + var b: Any = EmptyStruct() + var c = EmptyStructC() + var d: Any = EmptyStructC() +} + +var obj = Class() + +reflect(object: obj) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_empty_struct.Class) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=a offset=16 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=b offset=16 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1)))) +// CHECK-64: (field name=c offset=48 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=trailing offset=0 +// CHECK-64: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (field name=d offset=48 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_empty_struct.Class) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=a offset=8 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=b offset=8 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)))) +// CHECK-32: (field name=c offset=24 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=trailing offset=0 +// CHECK-32: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (field name=d offset=24 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. From 7722d4329172fa421dbc3fecc1e3f79e1ee53887 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Nov 2019 16:47:28 -0800 Subject: [PATCH 183/283] build: simplify source file handling for sourcekitdAPI Avoid unnecessary list handling for the sources for sourcekitdAPI, inline the single use of the list and appending of the sources. --- .../tools/sourcekitd/lib/API/CMakeLists.txt | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt index c29f9d32ff208..923b87a6ebedd 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt @@ -1,5 +1,8 @@ +set(LLVM_OPTIONAL_SOURCES + sourcekitdAPI-XPC.cpp + sourcekitdAPI-InProc.cpp) -set(sourcekitdAPI_sources +add_sourcekit_library(sourcekitdAPI CodeCompletionResultsArray.cpp CompactArray.cpp DocStructureArray.cpp @@ -9,22 +12,14 @@ set(sourcekitdAPI_sources sourcekitdAPI-Common.cpp TokenAnnotationsArray.cpp ExpressionTypeArray.cpp + LINK_LIBS + SourceKitSupport SourceKitSwiftLang ) -set(sourcekitdAPI_Darwin_sources - sourcekitdAPI-XPC.cpp) -set(sourcekitdAPI_NonDarwin_InProc_sources - sourcekitdAPI-InProc.cpp) -set(LLVM_OPTIONAL_SOURCES ${sourcekitdAPI_Darwin_sources} ${sourcekitdAPI_NonDarwin_InProc_sources}) - if(APPLE AND HAVE_XPC_H) - list(APPEND sourcekitdAPI_sources ${sourcekitdAPI_Darwin_sources}) + target_sources(sourcekitdAPI PRIVATE + sourcekitdAPI-XPC.cpp) elseif(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - list(APPEND sourcekitdAPI_sources ${sourcekitdAPI_NonDarwin_InProc_sources}) + target_sources(sourcekitdAPI PRIVATE + sourcekitdAPI-InProc.cpp) endif() - -add_sourcekit_library(sourcekitdAPI - ${sourcekitdAPI_sources} - LINK_LIBS - SourceKitSupport SourceKitSwiftLang -) From db9c2ed3a46d4e759349bf61f639165a0c744027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Wed, 13 Nov 2019 14:08:44 -0800 Subject: [PATCH 184/283] [windows] Enable SourceKit tests in CI. --- utils/build-windows.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 1fbd9a14d2b78..7529f0609b8ae 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -238,7 +238,7 @@ cmake "%source_root%\swift"^ -DSWIFT_BUILD_STATIC_SDK_OVERLAY:BOOL=NO^ -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=YES^ -DSWIFT_BUILD_SOURCEKIT:BOOL=YES^ - -DSWIFT_ENABLE_SOURCEKIT_TESTS:BOOL=NO^ + -DSWIFT_ENABLE_SOURCEKIT_TESTS:BOOL=YES^ -DSWIFT_INSTALL_COMPONENTS="autolink-driver;compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;editor-integration;tools;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers"^ -DSWIFT_PARALLEL_LINK_JOBS=8^ -DPYTHON_EXECUTABLE:PATH=%PYTHON_HOME%\python.exe^ From 717c4d3ebf99d6dc592572c9d24d25a4c8005381 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 14 Nov 2019 09:22:31 -0800 Subject: [PATCH 185/283] SourceKit: remove `LINK_LIBS` (NFC) This removes the custom `LINK_LIBS` in favour of `target_link_libraries`. This simplifies the custom functions that we have for adding libraries, makes it easier to query the information from ninja and will allow us to slowly remove more of the custom logic for building the products. --- .../cmake/modules/AddSwiftSourceKit.cmake | 30 ++------- tools/SourceKit/lib/Core/CMakeLists.txt | 3 +- tools/SourceKit/lib/Support/CMakeLists.txt | 9 ++- tools/SourceKit/lib/SwiftLang/CMakeLists.txt | 65 +++++++++++-------- .../tools/complete-test/CMakeLists.txt | 14 ++-- .../tools/sourcekitd-repl/CMakeLists.txt | 15 ++--- .../tools/sourcekitd-test/CMakeLists.txt | 20 +++--- .../sourcekitd/bin/InProc/CMakeLists.txt | 7 +- .../sourcekitd/bin/XPC/Client/CMakeLists.txt | 2 +- .../sourcekitd/bin/XPC/Service/CMakeLists.txt | 4 +- .../tools/sourcekitd/lib/API/CMakeLists.txt | 5 +- 11 files changed, 88 insertions(+), 86 deletions(-) diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index 93a59786c57f7..ae92bb801b487 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -87,7 +87,6 @@ endfunction() # # Usage: # add_sourcekit_library(name # Name of the library -# [LINK_LIBS dep1 ...] # Libraries this library will be linked with # [DEPENDS dep1 ...] # Targets this library depends on # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this library depends on # [INSTALL_IN_COMPONENT comp] # The Swift installation component that this library belongs to. @@ -97,7 +96,7 @@ macro(add_sourcekit_library name) cmake_parse_arguments(SOURCEKITLIB "SHARED" "INSTALL_IN_COMPONENT" - "HEADERS;LINK_LIBS;DEPENDS;LLVM_LINK_COMPONENTS" + "HEADERS;DEPENDS;LLVM_LINK_COMPONENTS" ${ARGN}) set(srcs ${SOURCEKITLIB_UNPARSED_ARGUMENTS}) @@ -149,21 +148,6 @@ macro(add_sourcekit_library name) add_dependencies(${name} ${SOURCEKITLIB_DEPENDS}) endif(SOURCEKITLIB_DEPENDS) - set(prefixed_link_libraries) - foreach(dep ${SOURCEKITLIB_LINK_LIBS}) - if("${dep}" MATCHES "^clang") - set(dep "${LLVM_LIBRARY_OUTPUT_INTDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${dep}${CMAKE_STATIC_LIBRARY_SUFFIX}") - endif() - list(APPEND prefixed_link_libraries "${dep}") - endforeach() - set(SOURCEKITLIB_LINK_LIBS "${prefixed_link_libraries}") - - if("${libkind}" STREQUAL "SHARED") - target_link_libraries("${name}" PRIVATE ${SOURCEKITLIB_LINK_LIBS}) - else() - target_link_libraries("${name}" INTERFACE ${SOURCEKITLIB_LINK_LIBS}) - endif() - swift_common_llvm_config(${name} ${SOURCEKITLIB_LLVM_LINK_COMPONENTS}) if(SOURCEKITLIB_SHARED AND EXPORTED_SYMBOL_FILE) @@ -213,7 +197,6 @@ endmacro() # # Usage: # add_sourcekit_executable(name # Name of the executable -# [LINK_LIBS dep1 ...] # Libraries this executable depends on # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this executable # # depends on # [EXCLUDE_FROM_ALL] # Whether to exclude this executable from @@ -223,7 +206,7 @@ macro(add_sourcekit_executable name) cmake_parse_arguments(SOURCEKITEXE "EXCLUDE_FROM_ALL" "" - "LINK_LIBS;LLVM_LINK_COMPONENTS" + "LLVM_LINK_COMPONENTS" ${ARGN}) if (${SOURCEKITEXE_EXCLUDE_FROM_ALL}) @@ -244,7 +227,6 @@ macro(add_sourcekit_executable name) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif() - target_link_libraries(${name} PRIVATE ${SOURCEKITEXE_LINK_LIBS}) swift_common_llvm_config(${name} ${SOURCEKITEXE_LLVM_LINK_COMPONENTS}) target_link_libraries(${name} PRIVATE ${LLVM_COMMON_LIBS}) @@ -267,14 +249,13 @@ endmacro() # # Usage: # add_sourcekit_framework(name # Name of the framework -# [LINK_LIBS dep1 ...] # Libraries this framework will link with # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this framework depends on # [MODULEMAP modulemap] # Module map file for this framework # [INSTALL_IN_COMPONENT comp] # The Swift installation component that this framework belongs to. # source1 [source2 source3 ...]) # Sources to add into this framework macro(add_sourcekit_framework name) cmake_parse_arguments(SOURCEKITFW - "" "MODULEMAP;INSTALL_IN_COMPONENT" "LINK_LIBS;LLVM_LINK_COMPONENTS" ${ARGN}) + "" "MODULEMAP;INSTALL_IN_COMPONENT" "LLVM_LINK_COMPONENTS" ${ARGN}) set(srcs ${SOURCEKITFW_UNPARSED_ARGUMENTS}) set(lib_dir ${SOURCEKIT_LIBRARY_OUTPUT_INTDIR}) @@ -316,7 +297,6 @@ macro(add_sourcekit_framework name) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) - target_link_libraries(${name} PRIVATE ${SOURCEKITFW_LINK_LIBS}) swift_common_llvm_config(${name} ${SOURCEKITFW_LLVM_LINK_COMPONENTS}) if (EXPORTED_SYMBOL_FILE) @@ -394,11 +374,10 @@ endmacro(add_sourcekit_framework) # # Usage: # add_sourcekit_xpc_service(name # Name of the XPC service -# [LINK_LIBS dep1 ...] # Libraries this service will link with # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this service depends on # source1 [source2 source3 ...]) # Sources to add into this service macro(add_sourcekit_xpc_service name framework_target) - cmake_parse_arguments(SOURCEKITXPC "" "" "LINK_LIBS;LLVM_LINK_COMPONENTS" ${ARGN}) + cmake_parse_arguments(SOURCEKITXPC "" "" "LLVM_LINK_COMPONENTS" ${ARGN}) set(srcs ${SOURCEKITXPC_UNPARSED_ARGUMENTS}) set(lib_dir ${SOURCEKIT_LIBRARY_OUTPUT_INTDIR}) @@ -439,7 +418,6 @@ macro(add_sourcekit_xpc_service name framework_target) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) - target_link_libraries(${name} PRIVATE ${SOURCEKITXPC_LINK_LIBS}) swift_common_llvm_config(${name} ${SOURCEKITXPC_LLVM_LINK_COMPONENTS}) target_link_libraries(${name} PRIVATE ${LLVM_COMMON_LIBS}) diff --git a/tools/SourceKit/lib/Core/CMakeLists.txt b/tools/SourceKit/lib/Core/CMakeLists.txt index ce80d3bbea066..4416c61229f42 100644 --- a/tools/SourceKit/lib/Core/CMakeLists.txt +++ b/tools/SourceKit/lib/Core/CMakeLists.txt @@ -3,5 +3,6 @@ add_sourcekit_library(SourceKitCore Context.cpp LangSupport.cpp NotificationCenter.cpp - LINK_LIBS SourceKitSupport ) +target_link_libraries(SourceKitCore PRIVATE + SourceKitSupport) diff --git a/tools/SourceKit/lib/Support/CMakeLists.txt b/tools/SourceKit/lib/Support/CMakeLists.txt index e46b19f3378f4..568a9611a7957 100644 --- a/tools/SourceKit/lib/Support/CMakeLists.txt +++ b/tools/SourceKit/lib/Support/CMakeLists.txt @@ -5,9 +5,12 @@ add_sourcekit_library(SourceKitSupport ImmutableTextBuffer.cpp ThreadSafeRefCntPtr.cpp Tracing.cpp - UIDRegistry.cpp - LINK_LIBS swiftBasic swiftSyntax clangBasic clangRewrite -) + UIDRegistry.cpp) +target_link_libraries(SourceKitSupport PRIVATE + swiftBasic + swiftSyntax + clangBasic + clangRewrite) if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) target_link_libraries(SourceKitSupport INTERFACE dispatch BlocksRuntime) endif() diff --git a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt index 92fe7299dd8d5..de505e29847f7 100644 --- a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt +++ b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt @@ -10,31 +10,6 @@ add_sourcekit_library(SourceKitSwiftLang SwiftLangSupport.cpp SwiftSourceDocInfo.cpp SwiftTypeContextInfo.cpp - LINK_LIBS - SourceKitCore swiftDriver swiftFrontend - swiftClangImporter swiftIDE - swiftAST swiftMarkup swiftParse swiftParseSIL swiftSIL swiftSILGen - swiftSILOptimizer swiftIRGen swiftSema swiftBasic swiftSerialization - swiftSyntax swiftOption libcmark_static - # Clang dependencies. - clangIndex - clangFormat - clangToolingCore - clangFrontendTool - clangFrontend - clangDriver - clangCodeGen - clangSerialization - clangParse - clangSema - clangAnalysis - clangEdit - clangRewriteFrontend - clangRewrite - clangLex - clangAST - clangAPINotes - clangBasic LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader bitwriter @@ -49,5 +24,43 @@ add_sourcekit_library(SourceKitSwiftLang objcarcopts profiledata ) - +target_link_libraries(SourceKitSwiftLang PRIVATE + SourceKitCore + swiftDriver + swiftFrontend + swiftClangImporter + swiftIDE + swiftAST + swiftMarkup + swiftParse + swiftParseSIL + swiftSIL + swiftSILGen + swiftSILOptimizer + swiftIRGen + swiftSema + swiftBasic + swiftSerialization + swiftSyntax + swiftOption + libcmark_static + # Clang dependencies. + clangIndex + clangFormat + clangToolingCore + clangFrontendTool + clangFrontend + clangDriver + clangCodeGen + clangSerialization + clangParse + clangSema + clangAnalysis + clangEdit + clangRewriteFrontend + clangRewrite + clangLex + clangAST + clangAPINotes + clangBasic) add_dependencies(SourceKitSwiftLang clang-tablegen-targets) diff --git a/tools/SourceKit/tools/complete-test/CMakeLists.txt b/tools/SourceKit/tools/complete-test/CMakeLists.txt index f65a890c4c2b6..94c9bbf231e56 100644 --- a/tools/SourceKit/tools/complete-test/CMakeLists.txt +++ b/tools/SourceKit/tools/complete-test/CMakeLists.txt @@ -1,14 +1,12 @@ -if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_TEST_LINK_LIBS sourcekitdInProc) -else() - set(SOURCEKITD_TEST_LINK_LIBS sourcekitd) -endif() - add_sourcekit_executable(complete-test complete-test.cpp - LINK_LIBS ${SOURCEKITD_TEST_LINK_LIBS} - LLVM_LINK_COMPONENTS support option coverage lto + LLVM_LINK_COMPONENTS option coverage lto ) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(complete-test PRIVATE sourcekitdInProc) +else() + target_link_libraries(complete-test PRIVATE sourcekitd) +endif() if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) target_link_libraries(complete-test PRIVATE dispatch BlocksRuntime) endif() diff --git a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt index 32e49a17b6e7f..60aaa65c16c3e 100644 --- a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt @@ -2,17 +2,16 @@ set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} edit) check_symbol_exists(el_wgets "histedit.h" HAVE_UNICODE_LIBEDIT) if(HAVE_UNICODE_LIBEDIT) - if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_REPL_LINK_LIBS sourcekitdInProc) - else() - set(SOURCEKITD_REPL_LINK_LIBS sourcekitd) - endif() - add_sourcekit_executable(sourcekitd-repl sourcekitd-repl.cpp - LINK_LIBS ${SOURCEKITD_REPL_LINK_LIBS} edit - LLVM_LINK_COMPONENTS support coverage lto + LLVM_LINK_COMPONENTS coverage lto ) + target_link_libraries(sourcekitd-repl PRIVATE edit) + if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(sourcekitd-repl PRIVATE sourcekitdInProc) + else() + target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) + endif() if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) target_link_libraries(sourcekitd-repl PRIVATE dispatch BlocksRuntime) endif() diff --git a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt index 09f1106705a89..2febbf274b8ca 100644 --- a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt @@ -2,19 +2,21 @@ set(LLVM_TARGET_DEFINITIONS Options.td) swift_tablegen(Options.inc -gen-opt-parser-defs) swift_add_public_tablegen_target(sourcekitdTestOptionsTableGen) -if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_TEST_LINK_LIBS sourcekitdInProc) -else() - set(SOURCEKITD_TEST_LINK_LIBS sourcekitd) -endif() - add_sourcekit_executable(sourcekitd-test sourcekitd-test.cpp TestOptions.cpp - LINK_LIBS ${SOURCEKITD_TEST_LINK_LIBS} SourceKitSupport - clangRewrite clangLex clangBasic - LLVM_LINK_COMPONENTS core support option coverage lto + LLVM_LINK_COMPONENTS core option coverage lto ) +target_link_libraries(sourcekitd-test PRIVATE + SourceKitSupport + clangRewrite + clangLex + clangBasic) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(sourcekitd-test PRIVATE sourcekitdInProc) +else() + target_link_libraries(sourcekitd-test PRIVATE sourcekitd) +endif() if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) target_link_libraries(sourcekitd-test PRIVATE dispatch BlocksRuntime) endif() diff --git a/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt index fb59e74837520..8c410713d850a 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt @@ -5,7 +5,6 @@ option(SOURCEKITD_BUILD_STATIC_INPROC set(sourcekitdInProc_args sourcekitdInProc.cpp - LINK_LIBS SourceKitSwiftLang sourcekitdAPI LLVM_LINK_COMPONENTS support coverage ) @@ -36,6 +35,9 @@ else() SHARED ) endif() +target_link_libraries(sourcekitdInProc PRIVATE + SourceKitSwiftLang + sourcekitdAPI) # While it is possible to build this as a static library, # to get the runtime paths correct, it must be linked into a binary @@ -46,6 +48,9 @@ if (SOURCEKITD_BUILD_STATIC_INPROC) ${SOURCEKITD_SOURCE_DIR}/include/sourcekitd/sourcekitd.h ${sourcekitdInProc_args} ) + target_link_libraries(sourcekitdInProc_Static PRIVATE + SourceKitSwiftLang + sourcekitdAPI) endif() if (SOURCEKIT_BUILT_STANDALONE) diff --git a/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt index 638d3ac7e1561..fda4baa96d524 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt @@ -10,11 +10,11 @@ set(EXPORTED_SYMBOL_FILE "${SOURCEKITD_SOURCE_DIR}/bin/sourcekitd.exports") add_sourcekit_framework(sourcekitd ${public_headers} sourcekitd.cpp - LINK_LIBS sourcekitdAPI LLVM_LINK_COMPONENTS support MODULEMAP module.modulemap INSTALL_IN_COMPONENT sourcekit-xpc-service ) +target_link_libraries(sourcekitd PRIVATE sourcekitdAPI) add_definitions(-DSOURCEKIT_XPCSERVICE_IDENTIFIER="com.apple.SourceKitService.${SOURCEKIT_VERSION_STRING}_${SOURCEKIT_PLATFORM_NAME}") diff --git a/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt index 4a007bd7439e3..f7928fc5bc5e1 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt @@ -1,9 +1,11 @@ if (NOT SOURCEKIT_INSTALLING_INPROC) add_sourcekit_xpc_service(SourceKitService sourcekitd XPCService.cpp - LINK_LIBS SourceKitSwiftLang sourcekitdAPI LLVM_LINK_COMPONENTS support coverage ) + target_link_libraries(SourceKitService PRIVATE + SourceKitSwiftLang + sourcekitdAPI) endif() if (NOT SOURCEKIT_DEPLOYMENT_OS MATCHES "^macosx") diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt index 923b87a6ebedd..adb608642216e 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt @@ -12,9 +12,10 @@ add_sourcekit_library(sourcekitdAPI sourcekitdAPI-Common.cpp TokenAnnotationsArray.cpp ExpressionTypeArray.cpp - LINK_LIBS - SourceKitSupport SourceKitSwiftLang ) +target_link_libraries(sourcekitdAPI PRIVATE + SourceKitSupport + SourceKitSwiftLang) if(APPLE AND HAVE_XPC_H) target_sources(sourcekitdAPI PRIVATE From e7163006ee396757bd3b57eeefbc6ad90e602f58 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 14 Nov 2019 16:43:51 -0500 Subject: [PATCH 186/283] [Reflection] Accept BuiltinTypeDescriptors with zero size. rdar://problem/56784375 --- stdlib/public/Reflection/TypeRefBuilder.cpp | 2 -- .../Reflection/reflect_empty_struct.swift | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index 586066bb4e5a7..a052e61581b87 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -253,8 +253,6 @@ TypeRefBuilder::getBuiltinTypeInfo(const TypeRef *TR) { for (auto Info : ReflectionInfos) { for (auto BuiltinTypeDescriptor : Info.Builtin) { - if (BuiltinTypeDescriptor->Size <= 0) - continue; if (BuiltinTypeDescriptor->getAlignment() <= 0) continue; if (BuiltinTypeDescriptor->Stride <= 0) diff --git a/validation-test/Reflection/reflect_empty_struct.swift b/validation-test/Reflection/reflect_empty_struct.swift index c207fbfa7362d..32427344a3fc9 100644 --- a/validation-test/Reflection/reflect_empty_struct.swift +++ b/validation-test/Reflection/reflect_empty_struct.swift @@ -2,7 +2,7 @@ // RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct // RUN: %target-codesign %t/reflect_empty_struct -// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail // REQUIRES: objc_interop // REQUIRES: executable_test @@ -31,15 +31,13 @@ reflect(object: obj) // CHECK-64: Type info: // CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1 // CHECK-64: (field name=a offset=16 -// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (builtin size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-64: (field name=b offset=16 // CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 // CHECK-64: (field name=metadata offset=24 // CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1)))) // CHECK-64: (field name=c offset=48 -// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 -// CHECK-64: (field name=trailing offset=0 -// CHECK-64: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64: (builtin size=0 alignment=4 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-64: (field name=d offset=48 // CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 // CHECK-64: (field name=metadata offset=24 @@ -53,15 +51,13 @@ reflect(object: obj) // CHECK-32: Type info: // CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1 // CHECK-32: (field name=a offset=8 -// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (builtin size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-32: (field name=b offset=8 // CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 // CHECK-32: (field name=metadata offset=12 // CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)))) // CHECK-32: (field name=c offset=24 -// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 -// CHECK-32: (field name=trailing offset=0 -// CHECK-32: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32: (builtin size=0 alignment=4 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-32: (field name=d offset=24 // CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 // CHECK-32: (field name=metadata offset=12 From c8ce5b304c6f893929c2aead0ab40651e1a2b694 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 14 Nov 2019 13:45:23 -0800 Subject: [PATCH 187/283] ABI/API checker: don't complain about changing var to let or vice versa The tool diagnoses the removal of getter/setter for properties, so complaining about the keyword change can be redundant. rdar://problem/57201030 --- include/swift/AST/DiagnosticsModuleDiffer.def | 2 -- test/api-digester/Outputs/Cake-abi.txt | 2 -- tools/swift-api-digester/ModuleDiagsConsumer.cpp | 1 - tools/swift-api-digester/swift-api-digester.cpp | 3 --- 4 files changed, 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index 6ec0f78f191c8..df4ddf42b9a89 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -80,8 +80,6 @@ ERROR(decl_kind_changed,none,"%0 has been changed to a %1", (StringRef, StringRe ERROR(optional_req_changed,none,"%0 is %select{now|no longer}1 an optional requirement", (StringRef, bool)) -ERROR(var_let_changed,none,"%0 changes from %select{var|let}1 to %select{let|var}1", (StringRef, bool)) - ERROR(no_longer_open,none,"%0 is no longer open for subclassing", (StringRef)) ERROR(func_type_escaping_changed,none,"%0 has %select{removed|added}2 @escaping in %1", (StringRef, StringRef, bool)) diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index fa5be10f2f79a..f19c8589fdd62 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -76,8 +76,6 @@ cake: Protocol P4 is a new API without @available attribute cake: Struct C6 is now with @frozen cake: Var C1.CIIns1 changes from weak to strong cake: Var C1.CIIns2 changes from strong to weak -cake: Var GlobalLetChangedToVar changes from let to var -cake: Var GlobalVarChangedToLet changes from var to let cake: Var RequiementChanges.addedVar is a new API without @available attribute cake: Var fixedLayoutStruct.$__lazy_storage_$_lazy_d is a new API without @available attribute cake: Var fixedLayoutStruct.c is a new API without @available attribute diff --git a/tools/swift-api-digester/ModuleDiagsConsumer.cpp b/tools/swift-api-digester/ModuleDiagsConsumer.cpp index 43461821d1b57..dcb9902633de3 100644 --- a/tools/swift-api-digester/ModuleDiagsConsumer.cpp +++ b/tools/swift-api-digester/ModuleDiagsConsumer.cpp @@ -42,7 +42,6 @@ static StringRef getCategoryName(uint32_t ID) { return "/* Renamed Decls */"; case LocalDiagID::decl_attr_change: case LocalDiagID::decl_new_attr: - case LocalDiagID::var_let_changed: case LocalDiagID::func_self_access_change: case LocalDiagID::new_decl_without_intro: return "/* Decl Attribute changes */"; diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index e1c63597f87dc..dcc6e06131126 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -962,9 +962,6 @@ void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); } - if (isLet() != RV->isLet()) { - emitDiag(Loc, diag::var_let_changed, isLet()); - } } } From 9188dd6994911a1dfd1e6e06562b738f40fe8be2 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 14 Nov 2019 13:43:04 -0800 Subject: [PATCH 188/283] Add benchmarks for mutable copies of bridged Swift Strings --- .../single-source/NSStringConversion.swift | 94 ++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/benchmark/single-source/NSStringConversion.swift b/benchmark/single-source/NSStringConversion.swift index 40ef7319d18a1..9b51720861c3e 100644 --- a/benchmark/single-source/NSStringConversion.swift +++ b/benchmark/single-source/NSStringConversion.swift @@ -17,6 +17,7 @@ import TestsUtils import Foundation fileprivate var test:NSString = "" +fileprivate var mutableTest = "" public let NSStringConversion = [ BenchmarkInfo(name: "NSStringConversion", @@ -65,7 +66,44 @@ public let NSStringConversion = [ BenchmarkInfo(name: "NSStringConversion.Rebridge.LongUTF8", runFunction: run_NSStringConversion_longNonASCII_rebridge, tags: [.validation, .api, .String, .bridging], - setUpFunction: { test = NSString(cString: "Thë qüick bröwn föx jumps over the lazy dög", encoding: String.Encoding.utf8.rawValue)! })] + setUpFunction: { test = NSString(cString: "Thë qüick bröwn föx jumps over the lazy dög", encoding: String.Encoding.utf8.rawValue)! } ), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.UTF8", + runFunction: run_NSStringConversion_nonASCIIMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "tëst" }), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Medium", + runFunction: run_NSStringConversion_mediumMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "aaaaaaaaaaaaaaa" } ), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Long", + runFunction: run_NSStringConversion_longMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "The quick brown fox jumps over the lazy dog" } ), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.LongUTF8", + runFunction: run_NSStringConversion_longNonASCIIMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "Thë qüick bröwn föx jumps over the lazy dög" } ), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Rebridge", + runFunction: run_NSStringConversion_rebridgeMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "test" }), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Rebridge.UTF8", + runFunction: run_NSStringConversion_nonASCII_rebridgeMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "tëst" }), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Rebridge.Medium", + runFunction: run_NSStringConversion_medium_rebridgeMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "aaaaaaaaaaaaaaa" } ), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Rebridge.Long", + runFunction: run_NSStringConversion_long_rebridgeMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "The quick brown fox jumps over the lazy dog" } ), + BenchmarkInfo(name: "NSStringConversion.MutableCopy.Rebridge.LongUTF8", + runFunction: run_NSStringConversion_longNonASCII_rebridgeMutable, + tags: [.validation, .api, .String, .bridging], + setUpFunction: { mutableTest = "Thë qüick bröwn föx jumps over the lazy dög" } ) +] public func run_NSStringConversion(_ N: Int) { let test:NSString = NSString(cString: "test", encoding: String.Encoding.ascii.rawValue)! @@ -103,6 +141,31 @@ public func run_NSStringConversion_longNonASCII(_ N: Int) { innerLoop(test, N, 300) } +fileprivate func innerMutableLoop(_ str: String, _ N: Int, _ scale: Int = 5000) { + for _ in 1...N * scale { + let copy = (str as NSString).mutableCopy() as! NSMutableString + for char in (identity(copy) as String).utf8 { + blackHole(char) + } + } +} + +public func run_NSStringConversion_nonASCIIMutable(_ N: Int) { + innerMutableLoop(mutableTest, N, 500) +} + +public func run_NSStringConversion_mediumMutable(_ N: Int) { + innerMutableLoop(mutableTest, N, 500) +} + +public func run_NSStringConversion_longMutable(_ N: Int) { + innerMutableLoop(mutableTest, N, 250) +} + +public func run_NSStringConversion_longNonASCIIMutable(_ N: Int) { + innerMutableLoop(mutableTest, N, 150) +} + fileprivate func innerRebridge(_ str: NSString, _ N: Int, _ scale: Int = 5000) { for _ in 1...N * scale { let bridged = identity(str) as String @@ -135,4 +198,33 @@ public func run_NSStringConversion_longNonASCII_rebridge(_ N: Int) { innerRebridge(test, N, 300) } +fileprivate func innerMutableRebridge(_ str: String, _ N: Int, _ scale: Int = 5000) { + for _ in 1...N * scale { + let copy = (str as NSString).mutableCopy() as! NSMutableString + let bridged = identity(copy) as String + blackHole(bridged) + blackHole(bridged as NSString) + } +} + +public func run_NSStringConversion_rebridgeMutable(_ N: Int) { + innerMutableRebridge(mutableTest, N, 1000) +} + +public func run_NSStringConversion_nonASCII_rebridgeMutable(_ N: Int) { + innerMutableRebridge(mutableTest, N, 500) +} + +public func run_NSStringConversion_medium_rebridgeMutable(_ N: Int) { + innerMutableRebridge(mutableTest, N, 500) +} + +public func run_NSStringConversion_long_rebridgeMutable(_ N: Int) { + innerMutableRebridge(mutableTest, N, 500) +} + +public func run_NSStringConversion_longNonASCII_rebridgeMutable(_ N: Int) { + innerMutableRebridge(mutableTest, N, 300) +} + #endif From 01f203a54d1db2553c2892a38848de63dcfdfb38 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 13:21:13 -0800 Subject: [PATCH 189/283] [Constraint system] Sink Expr::getDepthMap() into its one client. NFC. It'll be easier to refactor this when it's not an API on Expr. --- include/swift/AST/Expr.h | 5 ----- lib/AST/Expr.cpp | 28 ---------------------------- lib/Sema/ConstraintSystem.cpp | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 5453d58a72a88..bce9b29b88ae4 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -530,11 +530,6 @@ class alignas(8) Expr { /// the parent map. llvm::DenseMap getParentMap(); - /// Produce a mapping from each subexpression to its depth and parent, - /// in the root expression. The root expression has depth 0, its children have - /// depth 1, etc. - llvm::DenseMap> getDepthMap(); - /// Produce a mapping from each expression to its index according to a /// preorder traversal of the expressions. The parent has index 0, its first /// child has index 1, its second child has index 2 if the first child is a diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 0d4e1dae3b07c..2bee677e3da31 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -721,34 +721,6 @@ llvm::DenseMap Expr::getParentMap() { return parentMap; } -llvm::DenseMap> Expr::getDepthMap() { - class RecordingTraversal : public ASTWalker { - public: - llvm::DenseMap> &DepthMap; - unsigned Depth = 0; - - explicit RecordingTraversal( - llvm::DenseMap> &depthMap) - : DepthMap(depthMap) {} - - std::pair walkToExprPre(Expr *E) override { - DepthMap[E] = {Depth, Parent.getAsExpr()}; - Depth++; - return { true, E }; - } - - Expr *walkToExprPost(Expr *E) override { - Depth--; - return E; - } - }; - - llvm::DenseMap> depthMap; - RecordingTraversal traversal(depthMap); - walk(traversal); - return depthMap; -} - llvm::DenseMap Expr::getPreorderIndexMap() { class RecordingTraversal : public ASTWalker { public: diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index b8c658435bef4..3eb6853060b05 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -73,6 +73,36 @@ ExpressionTimer::~ExpressionTimer() { .highlight(E->getSourceRange()); } +/// Extend the given depth map by adding depths for all of the subexpressions +/// of the given expression. +static void extendDepthMap( + Expr *expr, + llvm::DenseMap> &depthMap) { + class RecordingTraversal : public ASTWalker { + public: + llvm::DenseMap> &DepthMap; + unsigned Depth = 0; + + explicit RecordingTraversal( + llvm::DenseMap> &depthMap) + : DepthMap(depthMap) {} + + std::pair walkToExprPre(Expr *E) override { + DepthMap[E] = {Depth, Parent.getAsExpr()}; + Depth++; + return { true, E }; + } + + Expr *walkToExprPost(Expr *E) override { + Depth--; + return E; + } + }; + + RecordingTraversal traversal(depthMap); + expr->walk(traversal); +} + ConstraintSystem::ConstraintSystem(DeclContext *dc, ConstraintSystemOptions options, Expr *expr) @@ -80,8 +110,9 @@ ConstraintSystem::ConstraintSystem(DeclContext *dc, Arena(dc->getASTContext(), Allocator), CG(*new ConstraintGraph(*this)) { - if (expr) - ExprWeights = expr->getDepthMap(); + if (expr) { + extendDepthMap(expr, ExprWeights); + } assert(DC && "context required"); } From 2d5d8e397e1969d0867cdcf5e7a722e2b670e658 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 13 Nov 2019 23:40:57 -0500 Subject: [PATCH 190/283] Sema: Clean up applyGenericArguments() and friends --- lib/Sema/TypeCheckType.cpp | 116 +++++++++++++++++-------------------- lib/Sema/TypeChecker.h | 22 ------- 2 files changed, 54 insertions(+), 84 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 6b8dd9d5e8633..b1f07af3cb1f1 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -626,11 +626,43 @@ static bool isPointerToVoid(ASTContext &Ctx, Type Ty, bool &IsMutable) { return BGT->getGenericArgs().front()->isVoid(); } -Type TypeChecker::applyGenericArguments(Type type, - SourceLoc loc, - TypeResolution resolution, - GenericIdentTypeRepr *generic, - TypeResolutionOptions options) { +static void diagnoseUnboundGenericType(Type ty, SourceLoc loc); + +/// Apply generic arguments to the given type. +/// +/// If the type is itself not generic, this does nothing. +/// +/// This function emits diagnostics about an invalid type or the wrong number +/// of generic arguments, whereas applyUnboundGenericArguments requires this +/// to be in a correct and valid form. +/// +/// \param type The generic type to which to apply arguments. +/// \param resolution The type resolution to perform. +/// \param comp The arguments to apply with the angle bracket range for +/// diagnostics. +/// \param options The type resolution context. +/// +/// \returns A BoundGenericType bound to the given arguments, or null on +/// error. +/// +/// \see applyUnboundGenericArguments +static Type applyGenericArguments(Type type, + TypeResolution resolution, + ComponentIdentTypeRepr *comp, + TypeResolutionOptions options) { + auto loc = comp->getIdLoc(); + auto *generic = dyn_cast(comp); + if (!generic) { + if (type->is() && + !options.is(TypeResolverContext::TypeAliasDecl) && + !options.contains(TypeResolutionFlags::AllowUnboundGenerics)) { + diagnoseUnboundGenericType(type, loc); + return ErrorType::get(type->getASTContext()); + } + + return type; + } + if (type->hasError()) { generic->setInvalid(); return type; @@ -716,15 +748,14 @@ Type TypeChecker::applyGenericArguments(Type type, args.push_back(substTy); } - auto result = applyUnboundGenericArguments(unboundType, genericDecl, loc, - resolution, args); - if (!result) - return result; + auto result = TypeChecker::applyUnboundGenericArguments( + unboundType, genericDecl, loc, + resolution, args); if (!options.contains(TypeResolutionFlags::AllowUnavailable)) { if (options.isAnyExpr() || dc->getParent()->isLocalContext()) if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal) - diagnoseGenericTypeExportability(loc, result, dc); + TypeChecker::diagnoseGenericTypeExportability(loc, result, dc); } // Migration hack. @@ -908,41 +939,25 @@ static void maybeDiagnoseBadConformanceRef(DeclContext *dc, } /// Returns a valid type or ErrorType in case of an error. -static Type resolveTypeDecl(TypeDecl *typeDecl, SourceLoc loc, +static Type resolveTypeDecl(TypeDecl *typeDecl, DeclContext *foundDC, TypeResolution resolution, - GenericIdentTypeRepr *generic, + ComponentIdentTypeRepr *comp, TypeResolutionOptions options) { - // Resolve the type declaration to a specific type. How this occurs // depends on the current context and where the type was found. - Type type = TypeChecker::resolveTypeInContext(typeDecl, foundDC, resolution, - options, generic); - - if (type->is() && !generic && - !options.is(TypeResolverContext::TypeAliasDecl) && - !options.contains(TypeResolutionFlags::AllowUnboundGenerics)) { - diagnoseUnboundGenericType(type, loc); - return ErrorType::get(typeDecl->getASTContext()); - } + Type type = TypeChecker::resolveTypeInContext( + typeDecl, foundDC, resolution, options, + isa(comp)); if (type->hasError() && foundDC && (isa(typeDecl) || isa(typeDecl))) { auto fromDC = resolution.getDeclContext(); assert(fromDC && "No declaration context for type resolution?"); maybeDiagnoseBadConformanceRef(fromDC, foundDC->getDeclaredInterfaceType(), - loc, typeDecl); - } - - if (generic) { - // Apply the generic arguments to the type. - type = TypeChecker::applyGenericArguments(type, loc, resolution, generic, - options); - if (!type) - return nullptr; + comp->getIdLoc(), typeDecl); } - assert(type); - return type; + return applyGenericArguments(type, resolution, comp, options); } static std::string getDeclNameFromContext(DeclContext *dc, @@ -1217,9 +1232,8 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, // that now. if (auto *typeDecl = comp->getBoundDecl()) { // Resolve the type declaration within this context. - return resolveTypeDecl(typeDecl, comp->getIdLoc(), - comp->getDeclContext(), resolution, - dyn_cast(comp), options); + return resolveTypeDecl(typeDecl, comp->getDeclContext(), resolution, + comp, options); } // Resolve the first component, which is the only one that requires @@ -1265,13 +1279,8 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, auto *foundDC = entry.getDeclContext(); auto *typeDecl = cast(entry.getValueDecl()); - Type type = resolveTypeDecl(typeDecl, comp->getIdLoc(), - foundDC, resolution, - dyn_cast(comp), options); - - if (!type) - return type; - + Type type = resolveTypeDecl(typeDecl, foundDC, resolution, + comp, options); if (type->is()) return type; @@ -1355,23 +1364,6 @@ static Type resolveNestedIdentTypeComponent( auto &ctx = DC->getASTContext(); auto &diags = ctx.Diags; - auto maybeApplyGenericArgs = [&](Type memberType) { - // If there are generic arguments, apply them now. - if (auto genComp = dyn_cast(comp)) { - return TypeChecker::applyGenericArguments(memberType, comp->getIdLoc(), - resolution, genComp, options); - } - - if (memberType->is() && - !options.is(TypeResolverContext::TypeAliasDecl) && - !options.contains(TypeResolutionFlags::AllowUnboundGenerics)) { - diagnoseUnboundGenericType(memberType, comp->getLoc()); - return ErrorType::get(ctx); - } - - return memberType; - }; - auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType, AssociatedTypeDecl *inferredAssocType) { // Diagnose invalid cases. @@ -1416,7 +1408,7 @@ static Type resolveNestedIdentTypeComponent( return memberType; // If there are generic arguments, apply them now. - return maybeApplyGenericArgs(memberType); + return applyGenericArguments(memberType, resolution, comp, options); }; // Short-circuiting. @@ -1433,7 +1425,7 @@ static Type resolveNestedIdentTypeComponent( // type later on. if (!memberType->is() || memberType->castTo()->getAssocType()) { - return maybeApplyGenericArgs(memberType); + return applyGenericArguments(memberType, resolution, comp, options); } return memberType; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b877616ff8310..efc04df053a81 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -630,28 +630,6 @@ class TypeChecker final { TypeResolutionOptions options, bool isSpecialized); - /// Apply generic arguments to the given type. - /// - /// This function emits diagnostics about an invalid type or the wrong number - /// of generic arguments, whereas applyUnboundGenericArguments requires this - /// to be in a correct and valid form. - /// - /// \param type The generic type to which to apply arguments. - /// \param loc The source location for diagnostic reporting. - /// \param resolution The type resolution to perform. - /// \param generic The arguments to apply with the angle bracket range for - /// diagnostics. - /// \param options The type resolution context. - /// - /// \returns A BoundGenericType bound to the given arguments, or null on - /// error. - /// - /// \see applyUnboundGenericArguments - static Type applyGenericArguments(Type type, SourceLoc loc, - TypeResolution resolution, - GenericIdentTypeRepr *generic, - TypeResolutionOptions options); - /// Apply generic arguments to the given type. /// /// This function requires a valid unbound generic type with the correct From 54155fe29026b055d63aa765e8cff157a3f8925c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 14 Nov 2019 17:27:00 -0500 Subject: [PATCH 191/283] Sema: Check generic requirements of parent context when realizing non-generic types When realizing a type like Foo.Bar, we have to account for the possibility that Bar is defined in a constrained extension of Foo, and has generic requirements beyond those that Foo itself places on 'A'. Previously we only handled this for types referenced from the constraint system as part of openUnboundGenericType(), so we were allowing invalid types through in type context. Add the right checking to applyGenericArguments() to close the hole. Note that the old code path still exists in the constraint solver; it is used for member accesses on metatype bases only. Fixes . --- lib/Sema/ConstraintSystem.cpp | 7 +- lib/Sema/TypeCheckType.cpp | 62 ++++++++++- .../conditionally_defined_types.swift | 101 ++++++++---------- test/Constraints/rdar39931339.swift | 20 ++-- ...uirement_failures_in_contextual_type.swift | 10 +- test/decl/ext/extensions.swift | 8 ++ test/decl/var/property_wrappers.swift | 4 +- 7 files changed, 134 insertions(+), 78 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 075c4e7a2eb41..8d2077d66c534 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -629,8 +629,6 @@ Type ConstraintSystem::openUnboundGenericType( Type type, ConstraintLocatorBuilder locator) { assert(!type->getCanonicalType()->hasTypeParameter()); - checkNestedTypeConstraints(*this, type, locator); - if (!type->hasUnboundGenericType()) return type; @@ -988,6 +986,8 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value, TypeResolverContext::InExpression, /*isSpecialized=*/false); + checkNestedTypeConstraints(*this, type, locator); + // Open the type. type = openUnboundGenericType(type, locator); @@ -1241,6 +1241,9 @@ ConstraintSystem::getTypeOfMemberReference( auto memberTy = TypeChecker::substMemberTypeWithBase(DC->getParentModule(), typeDecl, baseObjTy); + + checkNestedTypeConstraints(*this, memberTy, locator); + // Open the type if it was a reference to a generic type. memberTy = openUnboundGenericType(memberTy, locator); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index b1f07af3cb1f1..ddcd749cbc010 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -626,6 +626,60 @@ static bool isPointerToVoid(ASTContext &Ctx, Type Ty, bool &IsMutable) { return BGT->getGenericArgs().front()->isVoid(); } +static Type checkConstrainedExtensionRequirements(Type type, + SourceLoc loc, + DeclContext *dc) { + // Even if the type is not generic, it might be inside of a generic + // context, so we need to check requirements. + GenericTypeDecl *decl; + Type parentTy; + if (auto *aliasTy = dyn_cast(type.getPointer())) { + decl = aliasTy->getDecl(); + parentTy = aliasTy->getParent(); + } else if (auto *nominalTy = type->getAs()) { + decl = nominalTy->getDecl(); + parentTy = nominalTy->getParent(); + } else { + return type; + } + + // FIXME: Some day the type might also have its own 'where' clause, even + // if its not generic. + + auto *ext = dyn_cast(decl->getDeclContext()); + if (!ext || !ext->isConstrainedExtension()) + return type; + + if (parentTy->hasUnboundGenericType() || + parentTy->hasTypeVariable()) { + return type; + } + + auto subMap = parentTy->getContextSubstitutions(ext); + + SourceLoc noteLoc = ext->getLoc(); + if (noteLoc.isInvalid()) + noteLoc = loc; + + auto genericSig = ext->getGenericSignature(); + auto result = + TypeChecker::checkGenericArguments( + dc, loc, noteLoc, type, + genericSig->getGenericParams(), + genericSig->getRequirements(), + QueryTypeSubstitutionMap{subMap}, + TypeChecker::LookUpConformance(dc), + None); + + switch (result) { + case RequirementCheckResult::Failure: + case RequirementCheckResult::SubstitutionFailure: + return ErrorType::get(dc->getASTContext()); + case RequirementCheckResult::Success: + return type; + } +} + static void diagnoseUnboundGenericType(Type ty, SourceLoc loc); /// Apply generic arguments to the given type. @@ -650,7 +704,9 @@ static Type applyGenericArguments(Type type, TypeResolution resolution, ComponentIdentTypeRepr *comp, TypeResolutionOptions options) { + auto dc = resolution.getDeclContext(); auto loc = comp->getIdLoc(); + auto *generic = dyn_cast(comp); if (!generic) { if (type->is() && @@ -660,7 +716,10 @@ static Type applyGenericArguments(Type type, return ErrorType::get(type->getASTContext()); } - return type; + if (resolution.getStage() == TypeResolutionStage::Structural) + return type; + + return checkConstrainedExtensionRequirements(type, loc, dc); } if (type->hasError()) { @@ -668,7 +727,6 @@ static Type applyGenericArguments(Type type, return type; } - auto dc = resolution.getDeclContext(); auto &ctx = dc->getASTContext(); auto &diags = ctx.Diags; diff --git a/test/Constraints/conditionally_defined_types.swift b/test/Constraints/conditionally_defined_types.swift index b1b51a6defae5..2107fa6e6faca 100644 --- a/test/Constraints/conditionally_defined_types.swift +++ b/test/Constraints/conditionally_defined_types.swift @@ -15,7 +15,7 @@ struct Z2: AssociatedType { } struct SameType {} -extension SameType where T == X { // expected-note 5 {{where 'T' = 'Y'}} +extension SameType where T == X { // expected-note 13{{requirement specified as 'T' == 'X' [with T = Y]}} typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U) // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} @@ -36,25 +36,24 @@ let _ = SameType.Decl3.self let _ = SameType.Decl4.self let _ = SameType.Decl5.self -let _ = SameType.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'SameType' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'SameType' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.TypeAlias1.self // expected-error {{'SameType.TypeAlias1' (aka 'X') requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.TypeAlias2.self // expected-error {{'SameType.TypeAlias2' (aka 'Y') requires the types 'Y' and 'X' be equivalent}} let _ = SameType.TypeAlias3.self // expected-error {{'SameType.TypeAlias3' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.self // expected-error {{referencing struct 'Decl1' on 'SameType' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl2.self // expected-error {{referencing enum 'Decl2' on 'SameType' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl3.self // expected-error {{referencing class 'Decl3' on 'SameType' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl2.self // expected-error {{'SameType.Decl2' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl3.self // expected-error {{'SameType.Decl3' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.self // expected-error {{'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl5.self // expected-error {{'SameType.Decl5' requires the types 'Y' and 'X' be equivalent}} -extension SameType: AssociatedType where T == X {} // expected-note {{where 'T' = 'Y'}} - -// (Y first here, because there were issues caused by running associated type -// inference for the first time) -let _ = SameType.T.self // expected-error {{referencing type alias 'T' on 'SameType' requires the types 'Y' and 'X' be equivalent}} +extension SameType: AssociatedType where T == X {} +// expected-note@-1 {{requirement specified as 'T' == 'X' [with T = Y]}} let _ = SameType.T.self +let _ = SameType.T.self // expected-error {{'SameType.T' (aka 'X') requires the types 'Y' and 'X' be equivalent}} + struct Conforms {} -extension Conforms where T: P { // expected-note 5 {{where 'T' = 'Y'}} +extension Conforms where T: P { typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U) @@ -75,33 +74,33 @@ let _ = Conforms.Decl3.self let _ = Conforms.Decl4.self let _ = Conforms.Decl5.self -let _ = Conforms.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'Conforms' requires that 'Y' conform to 'P'}} -let _ = Conforms.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'Conforms' requires that 'Y' conform to 'P'}} +let _ = Conforms.TypeAlias1.self // expected-error {{type 'Y' does not conform to protocol 'P'}} +let _ = Conforms.TypeAlias2.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.TypeAlias3.self // expected-error {{type 'Y' does not conform to protocol 'P'}} -let _ = Conforms.Decl1.self // expected-error {{referencing struct 'Decl1' on 'Conforms' requires that 'Y' conform to 'P'}} -let _ = Conforms.Decl2.self // expected-error {{referencing enum 'Decl2' on 'Conforms' requires that 'Y' conform to 'P'}} -let _ = Conforms.Decl3.self // expected-error {{referencing class 'Decl3' on 'Conforms' requires that 'Y' conform to 'P'}} +let _ = Conforms.Decl1.self // expected-error {{type 'Y' does not conform to protocol 'P'}} +let _ = Conforms.Decl2.self // expected-error {{type 'Y' does not conform to protocol 'P'}} +let _ = Conforms.Decl3.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.Decl4.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.Decl5.self // expected-error {{type 'Y' does not conform to protocol 'P'}} -extension Conforms: AssociatedType where T: P {} // expected-note {{where 'T' = 'Y'}} +extension Conforms: AssociatedType where T: P {} -let _ = Conforms.T.self // expected-error {{referencing type alias 'T' on 'Conforms' requires that 'Y' conform to 'P'}} +let _ = Conforms.T.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.T.self // Now, even more nesting! -extension SameType.Decl1 { // expected-note 5 {{where 'T' = 'Y'}} +extension SameType.Decl1 { typealias TypeAlias1 = T typealias TypeAlias2 = Y - typealias TypeAlias3 = (T, U) // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} + typealias TypeAlias3 = (T, U) struct Decl1 {} enum Decl2 {} class Decl3 {} - struct Decl4 {} // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} - enum Decl5 {} // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} + struct Decl4 {} + enum Decl5 {} } let _ = SameType.Decl1.TypeAlias1.self @@ -113,16 +112,16 @@ let _ = SameType.Decl1.Decl3.self let _ = SameType.Decl1.Decl4.self let _ = SameType.Decl1.Decl5.self -let _ = SameType.Decl1.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.TypeAlias3.self // expected-error {{'SameType.Decl1.TypeAlias3' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl1.self // expected-error {{referencing struct 'Decl1' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl2.self // expected-error {{referencing enum 'Decl2' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl3.self // expected-error {{referencing class 'Decl3' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl4.self // expected-error {{'SameType.Decl1.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl5.self // expected-error {{'SameType.Decl1.Decl5' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.TypeAlias1.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.TypeAlias2.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.TypeAlias3.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl1.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl2.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl3.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl4.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl5.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -extension SameType.Decl4 where U == X { // expected-note 5 {{where 'U' = 'Y'}} +extension SameType.Decl4 where U == X { // expected-note 5 {{requirement specified as 'U' == 'X' [with U = Y]}} typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U, V) // expected-note {{requirement specified as 'U' == 'X' [with U = Y]}} @@ -145,12 +144,12 @@ let _ = SameType.Decl4.Decl3.self let _ = SameType.Decl4.Decl4.self let _ = SameType.Decl4.Decl5.self -let _ = SameType.Decl4.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.TypeAlias1.self // expected-error {{'SameType.Decl4.TypeAlias1' (aka 'X') requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.TypeAlias2.self // expected-error {{'SameType.Decl4.TypeAlias2' (aka 'Y') requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.TypeAlias3.self // expected-error {{'SameType.Decl4.TypeAlias3' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.Decl1.self // expected-error {{referencing struct 'Decl1' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.Decl2.self // expected-error {{referencing enum 'Decl2' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.Decl3.self // expected-error {{referencing class 'Decl3' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.Decl1.self // expected-error {{'SameType.Decl4.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.Decl2.self // expected-error {{'SameType.Decl4.Decl2' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.Decl3.self // expected-error {{'SameType.Decl4.Decl3' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.Decl4.self // expected-error {{'SameType.Decl4.Decl4' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.Decl5.self // expected-error {{'SameType.Decl4.Decl5' requires the types 'Y' and 'X' be equivalent}} @@ -174,9 +173,6 @@ let _ = SameType.Decl4.Decl5.self // expected-error {{'SameType.Decl // Finally, extra complicated: extension Conforms.Decl4 where U: AssociatedType, U.T: P { -// expected-note@-1 5 {{where 'U' = 'Y'}} -// expected-note@-2 5 {{'U.T' = 'Z2.T' (aka 'Y')}} -// expected-note@-3 5 {{'U.T' = 'Y.T'}} typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U, V) @@ -200,34 +196,29 @@ let _ = Conforms.Decl4.Decl5.self // Two different forms of badness, corresponding to the two requirements: let _ = Conforms.Decl4.TypeAlias1.self -// expected-error@-1 {{referencing type alias 'TypeAlias1' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing type alias 'TypeAlias1' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.TypeAlias2.self -// expected-error@-1 {{referencing type alias 'TypeAlias2' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing type alias 'TypeAlias2' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.TypeAlias3.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl1.self -// expected-error@-1 {{referencing struct 'Decl1' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing struct 'Decl1' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl2.self -// expected-error@-1 {{referencing enum 'Decl2' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing enum 'Decl2' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl3.self -// expected-error@-1 {{referencing class 'Decl3' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing class 'Decl3' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl4.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl5.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}} -let _ = Conforms.Decl4.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} -let _ = Conforms.Decl4.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} +let _ = Conforms.Decl4.TypeAlias1.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} +let _ = Conforms.Decl4.TypeAlias2.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.TypeAlias3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} -let _ = Conforms.Decl4.Decl1.self // expected-error {{referencing struct 'Decl1' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} -let _ = Conforms.Decl4.Decl2.self // expected-error {{referencing enum 'Decl2' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} -let _ = Conforms.Decl4.Decl3.self // expected-error {{referencing class 'Decl3' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} +let _ = Conforms.Decl4.Decl1.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} +let _ = Conforms.Decl4.Decl2.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} +let _ = Conforms.Decl4.Decl3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl4.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl5.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} diff --git a/test/Constraints/rdar39931339.swift b/test/Constraints/rdar39931339.swift index b0c3e5d09b6bb..40add2eb11c18 100644 --- a/test/Constraints/rdar39931339.swift +++ b/test/Constraints/rdar39931339.swift @@ -6,26 +6,23 @@ struct S {} class C : P {} -extension S where T : P { -// expected-note@-1 {{where 'T' = 'T'}} -// expected-note@-2 {{where 'T' = 'Int'}} +extension S where T : P { // expected-note {{where 'T' = 'T'}} typealias A = Int typealias B = S } -extension S where T == Float { // expected-note {{where 'T' = 'Int'}} +extension S where T == Float { // expected-note {{requirement specified as 'T' == 'Float' [with T = Int]}} typealias C = Int } class A {} -extension A where T == [U], U: P { // expected-note {{where 'U' = 'Float'}} +extension A where T == [U], U: P { typealias S1 = Int } extension A where T == [U], U == Int { -// expected-note@-1 {{where 'U' = 'String'}} -// expected-note@-2 {{where 'T' = '[String]'}} +// expected-note@-1 {{requirement specified as 'T' == '[Int]' [with T = [String]]}} typealias S2 = Int } @@ -33,15 +30,14 @@ class B : A<[U], U> {} _ = B.S1() // Ok _ = B.S2() // Ok -_ = B.S1() // expected-error {{referencing type alias 'S1' on 'A' requires that 'Float' conform to 'P'}} +_ = B.S1() // expected-error {{type 'Float' does not conform to protocol 'P'}} _ = B.S2() -// expected-error@-1 {{referencing type alias 'S2' on 'A' requires the types '[String]' and '[Int]' be equivalent}} -// expected-error@-2 {{referencing type alias 'S2' on 'A' requires the types 'String' and 'Int' be equivalent}} +// expected-error@-1 {{'B.S2' (aka 'Int') requires the types '[String]' and '[Int]' be equivalent}} _ = S.A() // Ok -_ = S.A() // expected-error {{referencing type alias 'A' on 'S' requires that 'Int' conform to 'P'}} +_ = S.A() // expected-error {{type 'Int' does not conform to protocol 'P'}} _ = S.B() // expected-error {{type 'String' does not conform to protocol 'P'}} -_ = S.C() // expected-error {{referencing type alias 'C' on 'S' requires the types 'Int' and 'Float' be equivalent}} +_ = S.C() // expected-error {{'S.C' (aka 'Int') requires the types 'Int' and 'Float' be equivalent}} func foo(_ s: S.Type) { _ = s.A() // expected-error {{referencing type alias 'A' on 'S' requires that 'T' conform to 'P'}} diff --git a/test/Constraints/requirement_failures_in_contextual_type.swift b/test/Constraints/requirement_failures_in_contextual_type.swift index f3eeff105f65f..d7896b494bc93 100644 --- a/test/Constraints/requirement_failures_in_contextual_type.swift +++ b/test/Constraints/requirement_failures_in_contextual_type.swift @@ -2,8 +2,8 @@ struct A {} -extension A where T == Int32 { // expected-note 2 {{where 'T' = 'Int'}} - struct B : ExpressibleByIntegerLiteral { // expected-note {{where 'T' = 'Int'}} +extension A where T == Int32 { // expected-note 3{{requirement specified as 'T' == 'Int32' [with T = Int]}} + struct B : ExpressibleByIntegerLiteral { typealias E = Int typealias IntegerLiteralType = Int @@ -14,8 +14,8 @@ extension A where T == Int32 { // expected-note 2 {{where 'T' = 'Int'}} } let _: A.B = 0 -// expected-error@-1 {{referencing struct 'B' on 'A' requires the types 'Int' and 'Int32' be equivalent}} +// expected-error@-1 {{'A.B' requires the types 'Int' and 'Int32' be equivalent}} let _: A.C = 0 -// expected-error@-1 {{referencing type alias 'C' on 'A' requires the types 'Int' and 'Int32' be equivalent}} +// expected-error@-1 {{'A.C' (aka 'Int') requires the types 'Int' and 'Int32' be equivalent}} let _: A.B.E = 0 -// expected-error@-1 {{referencing type alias 'E' on 'A.B' requires the types 'Int' and 'Int32' be equivalent}} +// expected-error@-1 {{'A.B' requires the types 'Int' and 'Int32' be equivalent}} diff --git a/test/decl/ext/extensions.swift b/test/decl/ext/extensions.swift index c1cb32945f9e4..e62a20191479f 100644 --- a/test/decl/ext/extensions.swift +++ b/test/decl/ext/extensions.swift @@ -341,3 +341,11 @@ extension Tree.LimbContent.Contents { extension Tree.BoughPayload.Contents { // expected-error@-1 {{constrained extension must be declared on the unspecialized generic type 'Nest'}} } + +// SR-10466 Check 'where' clause when referencing type defined inside extension +struct SR_10466 { + var a : A // expected-error {{'SR_10466.A' (aka 'Int') requires the types 'T' and 'Never' be equivalent}} +} +extension SR_10466 where T == Never { // expected-note {{requirement specified as 'T' == 'Never' [with T = T]}} + typealias A = Int +} diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index d9c3b24c9d0b7..bf788e570cf9e 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -1584,12 +1584,12 @@ struct SR_11288_S3: SR_11288_P3 { // typealias as propertyWrapper in a constrained protocol extension // protocol SR_11288_P4 {} -extension SR_11288_P4 where Self: AnyObject { // expected-note 2 {{where 'Self' = 'SR_11288_S4'}} +extension SR_11288_P4 where Self: AnyObject { // expected-note {{requirement specified as 'Self' : 'AnyObject' [with Self = SR_11288_S4]}} typealias SR_11288_Wrapper4 = SR_11288_S0 } struct SR_11288_S4: SR_11288_P4 { - @SR_11288_Wrapper4 var answer = 42 // expected-error 2 {{referencing type alias 'SR_11288_Wrapper4' on 'SR_11288_P4' requires that 'SR_11288_S4' be a class type}} + @SR_11288_Wrapper4 var answer = 42 // expected-error {{'SR_11288_S4.SR_11288_Wrapper4' (aka 'SR_11288_S0') requires that 'SR_11288_S4' be a class type}} } class SR_11288_C0: SR_11288_P4 { From 0e06792642bf904a2f3bd2fb40992fa10d971198 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 14 Nov 2019 17:52:13 -0500 Subject: [PATCH 192/283] Add regression test for SR-10201 --- .../compiler_crashers_2_fixed/sr10201.swift | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/sr10201.swift diff --git a/validation-test/compiler_crashers_2_fixed/sr10201.swift b/validation-test/compiler_crashers_2_fixed/sr10201.swift new file mode 100644 index 0000000000000..863b4e5aea3d9 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr10201.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-frontend -typecheck -verify %s + +struct A { + typealias Value = Int +} + +protocol B { + typealias Value = A.Value + typealias T = String +} + +protocol NestedProtocol { + typealias _B = B +} + +struct Something: NestedProtocol { + + struct InnerTest: _B { + var value: Value = 42 + var t: T = "wait what?" + } +} + +protocol X {} + +protocol Y { + typealias _X = X + var x: _X { get } +} + +struct Struct: Y { + var x: _X = __X() +} + +extension Struct { + struct __X: _X {} +} From 9b9aa2377d551c0bb6f4dc5fa06f5d3d0f29b292 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 15:29:40 -0800 Subject: [PATCH 193/283] [Constraint solver] Lazily populate the expression depth/index map. --- lib/Sema/ConstraintSystem.cpp | 79 ++++++++++++++++++++++------------- lib/Sema/ConstraintSystem.h | 31 +++++++------- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 3eb6853060b05..9d0220958126d 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -73,35 +73,6 @@ ExpressionTimer::~ExpressionTimer() { .highlight(E->getSourceRange()); } -/// Extend the given depth map by adding depths for all of the subexpressions -/// of the given expression. -static void extendDepthMap( - Expr *expr, - llvm::DenseMap> &depthMap) { - class RecordingTraversal : public ASTWalker { - public: - llvm::DenseMap> &DepthMap; - unsigned Depth = 0; - - explicit RecordingTraversal( - llvm::DenseMap> &depthMap) - : DepthMap(depthMap) {} - - std::pair walkToExprPre(Expr *E) override { - DepthMap[E] = {Depth, Parent.getAsExpr()}; - Depth++; - return { true, E }; - } - - Expr *walkToExprPost(Expr *E) override { - Depth--; - return E; - } - }; - - RecordingTraversal traversal(depthMap); - expr->walk(traversal); -} ConstraintSystem::ConstraintSystem(DeclContext *dc, ConstraintSystemOptions options, @@ -111,7 +82,7 @@ ConstraintSystem::ConstraintSystem(DeclContext *dc, CG(*new ConstraintGraph(*this)) { if (expr) { - extendDepthMap(expr, ExprWeights); + InputExprs.insert(expr); } assert(DC && "context required"); @@ -530,6 +501,54 @@ ConstraintSystem::getCalleeLocator(ConstraintLocator *locator, return getConstraintLocator(anchor); } +/// Extend the given depth map by adding depths for all of the subexpressions +/// of the given expression. +static void extendDepthMap( + Expr *expr, + llvm::DenseMap> &depthMap) { + class RecordingTraversal : public ASTWalker { + public: + llvm::DenseMap> &DepthMap; + unsigned Depth = 0; + + explicit RecordingTraversal( + llvm::DenseMap> &depthMap) + : DepthMap(depthMap) {} + + std::pair walkToExprPre(Expr *E) override { + DepthMap[E] = {Depth, Parent.getAsExpr()}; + Depth++; + return { true, E }; + } + + Expr *walkToExprPost(Expr *E) override { + Depth--; + return E; + } + }; + + RecordingTraversal traversal(depthMap); + expr->walk(traversal); +} + +Optional> ConstraintSystem::getExprDepthAndParent( + Expr *expr) { + // Bring the set of expression weights up to date. + while (NumInputExprsInWeights < InputExprs.size()) { + extendDepthMap(InputExprs[NumInputExprsInWeights], ExprWeights); + ++NumInputExprsInWeights; + } + + auto e = ExprWeights.find(expr); + if (e != ExprWeights.end()) + return e->second; + + if (baseCS && baseCS != this) + return baseCS->getExprDepthAndParent(expr); + + return None; +} + Type ConstraintSystem::openUnboundGenericType(UnboundGenericType *unbound, ConstraintLocatorBuilder locator, OpenedTypeMap &replacements) { diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 5ba8e83f2c095..200873d6975d2 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1024,6 +1024,12 @@ class ConstraintSystem { unsigned CountDisjunctions = 0; private: + /// The set of expressions for which we have generated constraints. + llvm::SetVector InputExprs; + + /// The number of input expressions whose parents and depths have + /// been entered into \c ExprWeights. + unsigned NumInputExprsInWeights = 0; llvm::DenseMap> ExprWeights; @@ -1982,29 +1988,22 @@ class ConstraintSystem { getConstraintLocator(const ConstraintLocatorBuilder &builder); /// Lookup and return parent associated with given expression. - Expr *getParentExpr(Expr *expr) const { - auto e = ExprWeights.find(expr); - if (e != ExprWeights.end()) - return e->second.second; - - if (baseCS && baseCS != this) - return baseCS->getParentExpr(expr); - + Expr *getParentExpr(Expr *expr) { + if (auto result = getExprDepthAndParent(expr)) + return result->second; return nullptr; } /// Retrieve the depth of the given expression. - Optional getExprDepth(Expr *expr) const { - auto e = ExprWeights.find(expr); - if (e != ExprWeights.end()) - return e->second.first; - - if (baseCS && baseCS != this) - return baseCS->getExprDepth(expr); - + Optional getExprDepth(Expr *expr) { + if (auto result = getExprDepthAndParent(expr)) + return result->first; return None; } + /// Retrieve the depth and parent expression of the given expression. + Optional> getExprDepthAndParent(Expr *expr); + /// Returns a locator describing the callee for the anchor of a given locator. /// /// - For an unresolved dot/member anchor, this will be a locator describing From 2d915f60ababb4d60caf6f85c84e544d5acf9b31 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 15:41:38 -0800 Subject: [PATCH 194/283] [Constraint solver] Remove expression from the constructor. Rather than setting up the constraint solver with a single expression (that gets recorded for parents/depths), record each expression that goes through constraint generation. --- lib/Sema/CSGen.cpp | 2 ++ lib/Sema/CSSolver.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 7 +------ lib/Sema/ConstraintSystem.h | 3 +-- lib/Sema/TypeCheckConstraints.cpp | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index afdd347f35eb6..d2525513f9e8e 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3754,6 +3754,8 @@ namespace { } // end anonymous namespace Expr *ConstraintSystem::generateConstraints(Expr *expr, DeclContext *dc) { + InputExprs.insert(expr); + // Remove implicit conversions from the expression. expr = expr->walk(SanitizeExpr(*this)); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index fbffaa6456646..187f27a1aabfb 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -574,7 +574,7 @@ bool ConstraintSystem::Candidate::solve( }; // Allocate new constraint system for sub-expression. - ConstraintSystem cs(DC, None, E); + ConstraintSystem cs(DC, None); cs.baseCS = &BaseCS; // Set up expression type checker timer for the candidate. diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 9d0220958126d..09724d217f8b1 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -75,16 +75,11 @@ ExpressionTimer::~ExpressionTimer() { ConstraintSystem::ConstraintSystem(DeclContext *dc, - ConstraintSystemOptions options, - Expr *expr) + ConstraintSystemOptions options) : Context(dc->getASTContext()), DC(dc), Options(options), Arena(dc->getASTContext(), Allocator), CG(*new ConstraintGraph(*this)) { - if (expr) { - InputExprs.insert(expr); - } - assert(DC && "context required"); } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 200873d6975d2..5b48a5ada6074 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1664,8 +1664,7 @@ class ConstraintSystem { }; ConstraintSystem(DeclContext *dc, - ConstraintSystemOptions options, - Expr *expr = nullptr); + ConstraintSystemOptions options); ~ConstraintSystem(); /// Retrieve the type checker associated with this constraint system. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index c9baacd0c229b..946d9322e363e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2198,7 +2198,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, if (options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)) csOptions |= ConstraintSystemFlags::SubExpressionDiagnostics; - ConstraintSystem cs(dc, csOptions, expr); + ConstraintSystem cs(dc, csOptions); cs.baseCS = baseCS; // Verify that a purpose was specified if a convertType was. Note that it is From 38c29e231e7897c43e56e68d08bebbbacbb677ae Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 30 Sep 2019 13:56:17 -0700 Subject: [PATCH 195/283] Generalize and fix SinkAddressProjections. Fixes a potential real bug in the case that SinkAddressProjections moves projections without notifying SimplifyCFG of the change. This could fail to update Analyses (probably won't break anything in practice). Introduce SILInstruction::isPure. Among other things, this can tell you if it's safe to duplicate instructions at their uses. SinkAddressProjections should check this before sinking uses. I couldn't find a way to expose this as a real bug, but it is a theoretical bug. Add the SinkAddressProjections functionality to the BasicBlockCloner utility. Enable address projection sinking for all BasicBlockCloner clients (the four different kinds of jump-threading that use it). This brings the compiler much closer to banning all address phis. The "bugs" were originally introduced a week ago here: commit f22371bf0bd8ca5591d062d1a534a55331819dd9 (fork/fix-address-phi, fix-address-phi) Author: Andrew Trick Date: Tue Sep 17 16:45:51 2019 Add SIL SinkAddressProjections utility to avoid address phis. Enable this utility during jump-threading in SimplifyCFG. Ultimately, the SIL verifier should prevent all address-phis and we'll need to use this utility in a few more places. Fixes SIL verification failed: Unknown formal access pattern: storage --- include/swift/SIL/SILInstruction.h | 10 ++ .../SILOptimizer/Utils/BasicBlockOptUtils.h | 155 ++++++++++++------ .../swift/SILOptimizer/Utils/SILSSAUpdater.h | 4 +- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 81 ++++----- lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp | 78 +++++++-- .../Utils/CheckedCastBrJumpThreading.cpp | 44 ++--- lib/SILOptimizer/Utils/SILSSAUpdater.cpp | 2 +- test/DebugInfo/inlined-generics-basic.swift | 21 ++- .../SILOptimizer/simplify_cfg_address_phi.sil | 115 +++++++++++++ 9 files changed, 371 insertions(+), 139 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 3e64bce702fb4..d82949129dd8f 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -45,6 +45,7 @@ namespace swift { +class AllocationInst; class DeclRefExpr; class FloatLiteralExpr; class FuncDecl; @@ -618,6 +619,15 @@ class SILInstruction return getMemoryBehavior() != MemoryBehavior::None; } + /// Return true if the instruction is "pure" in the sense that it may execute + /// multiple times without affecting behavior. This implies that it can be + /// trivially cloned at multiple use sites without preserving path + /// equivalence. + bool isPure() const { + return !mayReadOrWriteMemory() && !mayTrap() && !isa(this) + && !isa(this); + } + /// Returns true if the result of this instruction is a pointer to stack /// allocated memory. In this case there must be an adjacent deallocating /// instruction. diff --git a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h index 2d0bb0e457892..77e5e4b0e2780 100644 --- a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h +++ b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h @@ -22,9 +22,10 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H #define SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H -#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILCloner.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" namespace swift { @@ -61,12 +62,65 @@ bool rotateLoop(SILLoop *loop, DominanceInfo *domInfo, SILLoopInfo *loopInfo, bool rotateSingleBlockLoops, SILBasicBlock *upToBB, bool shouldVerify); -/// Helper function to perform SSA updates in case of jump threading. -void updateSSAAfterCloning(BasicBlockCloner &cloner, SILBasicBlock *srcBB, - SILBasicBlock *destBB); +/// Sink address projections to their out-of-block uses. This is +/// required after cloning a block and before calling +/// updateSSAAfterCloning to avoid address-type phis. +/// +/// This clones address projections at their use points, but does not +/// mutate the block containing the projections. +/// +/// BasicBlockCloner handles this internally. +class SinkAddressProjections { + // Projections ordered from last to first in the chain. + SmallVector projections; + SmallSetVector inBlockDefs; + + // Transient per-projection data for use during cloning. + SmallVector usesToReplace; + llvm::SmallDenseMap firstBlockUse; + +public: + /// Check for an address projection chain ending at \p inst. Return true if + /// the given instruction is successfully analyzed. + /// + /// If \p inst does not produce an address, then return + /// true. getInBlockDefs() will contain \p inst if any of its + /// (non-address) values are used outside its block. + /// + /// If \p inst does produce an address, return true only of the + /// chain of address projections within this block is clonable at + /// their use sites. getInBlockDefs will return all non-address + /// operands in the chain that are also defined in this block. These + /// may require phis after cloning the projections. + bool analyzeAddressProjections(SILInstruction *inst); + + /// After analyzing projections, returns the list of (non-address) values + /// defined in the same block as the projections which will have uses outside + /// the block after cloning. + ArrayRef getInBlockDefs() const { + return inBlockDefs.getArrayRef(); + } + /// Clone the chain of projections at their use sites. + /// + /// Return true if anything was done. + /// + /// getInBlockProjectionOperandValues() can be called before or after cloning. + bool cloneProjections(); +}; /// Clone a single basic block and any required successor edges within the same /// function. +/// +/// Before cloning, call either canCloneBlock or call canCloneInstruction for +/// every instruction in the original block. +/// +/// To clone just the block, call cloneBlock. To also update the original +/// block's branch to jump to the newly cloned block, call cloneBranchTarget +/// instead. +/// +/// After cloning, call splitCriticalEdges, then updateSSAAfterCloning. This is +/// decoupled from cloning becaused some clients perform CFG edges updates after +/// cloning but before splitting CFG edges. class BasicBlockCloner : public SILCloner { using SuperTy = SILCloner; friend class SILCloner; @@ -75,18 +129,56 @@ class BasicBlockCloner : public SILCloner { /// The original block to be cloned. SILBasicBlock *origBB; + /// Will cloning require an SSA update? + bool needsSSAUpdate = false; + + /// Transient object for analyzing a single address projction chain. It's + /// state is reset each time analyzeAddressProjections is called. + SinkAddressProjections sinkProj; + public: /// An ordered list of old to new available value pairs. /// /// updateSSAAfterCloning() expects this public field to hold values that may /// be remapped in the cloned block and live out. - SmallVector, 16> AvailVals; + SmallVector, 16> availVals; // Clone blocks starting at `origBB`, within the same function. BasicBlockCloner(SILBasicBlock *origBB) : SILCloner(*origBB->getParent()), origBB(origBB) {} + bool canCloneBlock() { + for (auto &inst : *origBB) { + if (!canCloneInstruction(&inst)) + return false; + } + return true; + } + + /// Returns true if \p inst can be cloned. + /// + /// If canCloneBlock is not called, then this must be called for every + /// instruction in origBB, both to ensure clonability and to handle internal + /// book-keeping (needsSSAUpdate). + bool canCloneInstruction(SILInstruction *inst) { + assert(inst->getParent() == origBB); + + if (!inst->isTriviallyDuplicatable()) + return false; + + if (!sinkProj.analyzeAddressProjections(inst)) + return false; + + // Check if any of the non-address defs in the cloned block (including the + // current instruction) will still have uses outside the block after sinking + // address projections. + needsSSAUpdate |= !sinkProj.getInBlockDefs().empty(); + return true; + } + void cloneBlock(SILBasicBlock *insertAfterBB = nullptr) { + sinkAddressProjections(); + SmallVector successorBBs; successorBBs.reserve(origBB->getSuccessors().size()); llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs)); @@ -95,6 +187,9 @@ class BasicBlockCloner : public SILCloner { /// Clone the given branch instruction's destination block, splitting /// its successors, and rewrite the branch instruction. + /// + /// Return false if the branch's destination block cannot be cloned. When + /// false is returned, no changes have been made. void cloneBranchTarget(BranchInst *bi) { assert(origBB == bi->getDestBB()); @@ -110,10 +205,16 @@ class BasicBlockCloner : public SILCloner { return remapBasicBlock(origBB); } + bool wasCloned() { return isBlockCloned(origBB); } + /// Call this after processing all instructions to fix the control flow /// graph. The branch cloner may have left critical edges. bool splitCriticalEdges(DominanceInfo *domInfo, SILLoopInfo *loopInfo); + /// Helper function to perform SSA updates after calling both + /// cloneBranchTarget and splitCriticalEdges. + void updateSSAAfterCloning(); + protected: // MARK: CRTP overrides. @@ -137,8 +238,10 @@ class BasicBlockCloner : public SILCloner { void mapValue(SILValue origValue, SILValue mappedValue) { SuperTy::mapValue(origValue, mappedValue); - AvailVals.emplace_back(origValue, mappedValue); + availVals.emplace_back(origValue, mappedValue); } + + void sinkAddressProjections(); }; // Helper class that provides a callback that can be used in @@ -173,46 +276,6 @@ class CloneCollector { } }; -/// Sink address projections to their out-of-block uses. This is -/// required after cloning a block and before calling -/// updateSSAAfterCloning to avoid address-type phis. -/// -/// This clones address projections at their use points, but does not -/// mutate the block containing the projections. -class SinkAddressProjections { - // Projections ordered from last to first in the chain. - SmallVector projections; - SmallSetVector inBlockDefs; - -public: - /// Check for an address projection chain ending at \p inst. Return true if - /// the given instruction is successfully analyzed. - /// - /// If \p inst does not produce an address, then return - /// true. getInBlockDefs() will contain \p inst if any of its - /// (non-address) values are used outside its block. - /// - /// If \p inst does produce an address, return true only of the - /// chain of address projections within this block is clonable at - /// their use sites. getInBlockDefs will return all non-address - /// operands in the chain that are also defined in this block. These - /// may require phis after cloning the projections. - bool analyzeAddressProjections(SILInstruction *inst); - - /// After analyzing projections, returns the list of (non-address) values - /// defined in the same block as the projections which will have uses outside - /// the block after cloning. - ArrayRef getInBlockDefs() const { - return inBlockDefs.getArrayRef(); - } - /// Clone the chain of projections at their use sites. - /// - /// Return true if anything was done. - /// - /// getInBlockProjectionOperandValues() can be called before or after cloning. - bool cloneProjections(); -}; - /// Utility class for cloning init values into the static initializer of a /// SILGlobalVariable. class StaticInitCloner : public SILCloner { diff --git a/include/swift/SILOptimizer/Utils/SILSSAUpdater.h b/include/swift/SILOptimizer/Utils/SILSSAUpdater.h index 7d84920cffc01..f222f2309cc79 100644 --- a/include/swift/SILOptimizer/Utils/SILSSAUpdater.h +++ b/include/swift/SILOptimizer/Utils/SILSSAUpdater.h @@ -133,8 +133,10 @@ class UseWrapper { /// reconstruct the use. UseWrapper(Operand *Use); + Operand *getOperand(); + /// Return the operand we wrap. Reconstructing branch operands. - operator Operand*(); + operator Operand*() { return getOperand(); } }; } // end namespace swift diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 35e386a854dad..9fba73ceb7c3b 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -274,12 +274,15 @@ class ThreadInfo { ThreadInfo() = default; - void threadEdge() { + bool threadEdge() { LLVM_DEBUG(llvm::dbgs() << "thread edge from bb" << Src->getDebugID() << " to bb" << Dest->getDebugID() << '\n'); auto *SrcTerm = cast(Src->getTerminator()); BasicBlockCloner Cloner(SrcTerm->getDestBB()); + if (!Cloner.canCloneBlock()) + return false; + Cloner.cloneBranchTarget(SrcTerm); // We have copied the threaded block into the edge. @@ -329,7 +332,8 @@ class ThreadInfo { // After rewriting the cloned branch, split the critical edge. // This does not currently update DominanceInfo. Cloner.splitCriticalEdges(nullptr, nullptr); - updateSSAAfterCloning(Cloner, Src, Dest); + Cloner.updateSSAAfterCloning(); + return true; } }; @@ -551,8 +555,8 @@ bool SimplifyCFG::dominatorBasedSimplifications(SILFunction &Fn, return Changed; for (auto &ThreadInfo : JumpThreadableEdges) { - ThreadInfo.threadEdge(); - Changed = true; + if (ThreadInfo.threadEdge()) + Changed = true; } return Changed; @@ -922,16 +926,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { if (DestBB->getTerminator()->isFunctionExiting()) return false; - // We need to update SSA if a value duplicated is used outside of the - // duplicated block. - bool NeedToUpdateSSA = false; - - // Are the arguments to this block used outside of the block. - for (auto Arg : DestBB->getArguments()) - if ((NeedToUpdateSSA |= isUsedOutsideOfBlock(Arg))) { - break; - } - // We don't have a great cost model at the SIL level, so we don't want to // blissly duplicate tons of code with a goal of improved performance (we'll // leave that to LLVM). However, doing limited code duplication can lead to @@ -978,57 +972,39 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { if (ThreadingBudget <= 0) return false; + // Don't jump thread through a potential header - this can produce irreducible + // control flow. Still, we make an exception for switch_enum. + bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); + if (DestIsLoopHeader) { + if (!isa(DestBB->getTerminator())) + return false; + } + // If it looks potentially interesting, decide whether we *can* do the // operation and whether the block is small enough to be worth duplicating. int copyCosts = 0; - SinkAddressProjections sinkProj; - for (auto ii = DestBB->begin(), ie = DestBB->end(); ii != ie;) { - copyCosts += getThreadingCost(&*ii); + BasicBlockCloner Cloner(DestBB); + for (auto &inst : *DestBB) { + copyCosts += getThreadingCost(&inst); if (ThreadingBudget <= copyCosts) return false; // If this is an address projection with outside uses, sink it before // checking for SSA update. - if (!sinkProj.analyzeAddressProjections(&*ii)) - return false; - - sinkProj.cloneProjections(); - // After cloning check if any of the non-address defs in the cloned block - // (including the current instruction) now have uses outside the - // block. Do this even if nothing was cloned. - if (!sinkProj.getInBlockDefs().empty()) - NeedToUpdateSSA = true; - - auto nextII = std::next(ii); - recursivelyDeleteTriviallyDeadInstructions( - &*ii, false, [&nextII](SILInstruction *deadInst) { - if (deadInst->getIterator() == nextII) - ++nextII; - }); - ii = nextII; - } - - // Don't jump thread through a potential header - this can produce irreducible - // control flow. Still, we make an exception for switch_enum. - bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); - if (DestIsLoopHeader) { - if (!isa(DestBB->getTerminator())) + if (!Cloner.canCloneInstruction(&inst)) return false; } - LLVM_DEBUG(llvm::dbgs() << "jump thread from bb" << SrcBB->getDebugID() << " to bb" << DestBB->getDebugID() << '\n'); JumpThreadingCost[DestBB] += copyCosts; - // Okay, it looks like we want to do this and we can. Duplicate the - // destination block into this one, rewriting uses of the BBArgs to use the - // branch arguments as we go. - BasicBlockCloner Cloner(DestBB); + // Duplicate the destination block into this one, rewriting uses of the BBArgs + // to use the branch arguments as we go. Cloner.cloneBranchTarget(BI); - // Does not currently update DominanceInfo. Cloner.splitCriticalEdges(nullptr, nullptr); + Cloner.updateSSAAfterCloning(); // Once all the instructions are copied, we can nuke BI itself. We also add // the threaded and edge block to the worklist now that they (likely) can be @@ -1036,9 +1012,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { addToWorklist(SrcBB); addToWorklist(Cloner.getNewBB()); - if (NeedToUpdateSSA) - updateSSAAfterCloning(Cloner, Cloner.getNewBB(), DestBB); - // We may be able to simplify DestBB now that it has one fewer predecessor. simplifyAfterDroppingPredecessor(DestBB); @@ -2742,21 +2715,23 @@ bool SimplifyCFG::tailDuplicateObjCMethodCallSuccessorBlocks() { for (auto *BB : ObjCBlocks) { auto *Branch = cast(BB->getTerminator()); auto *DestBB = Branch->getDestBB(); - Changed = true; // Okay, it looks like we want to do this and we can. Duplicate the // destination block into this one, rewriting uses of the BBArgs to use the // branch arguments as we go. BasicBlockCloner Cloner(DestBB); + if (!Cloner.canCloneBlock()) + continue; + Cloner.cloneBranchTarget(Branch); // Does not currently update DominanceInfo. Cloner.splitCriticalEdges(nullptr, nullptr); + Cloner.updateSSAAfterCloning(); - updateSSAAfterCloning(Cloner, Cloner.getNewBB(), DestBB); + Changed = true; addToWorklist(Cloner.getNewBB()); } - return Changed; } diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 88f11083c49ee..526e2e6f07870 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -68,11 +68,19 @@ bool swift::removeUnreachableBlocks(SILFunction &f) { return changed; } -/// Helper function to perform SSA updates in case of jump threading. -void swift::updateSSAAfterCloning(BasicBlockCloner &cloner, - SILBasicBlock *srcBB, SILBasicBlock *destBB) { +void BasicBlockCloner::updateSSAAfterCloning() { + // All instructions should have been checked by canCloneInstruction. But we + // still need to check the arguments. + for (auto arg : origBB->getArguments()) { + if ((needsSSAUpdate |= isUsedOutsideOfBlock(arg))) { + break; + } + } + if (!needsSSAUpdate) + return; + SILSSAUpdater ssaUpdater; - for (auto availValPair : cloner.AvailVals) { + for (auto availValPair : availVals) { ValueBase *inst = availValPair.first; if (inst->use_empty()) continue; @@ -85,20 +93,20 @@ void swift::updateSSAAfterCloning(BasicBlockCloner &cloner, useList.push_back(UseWrapper(use)); ssaUpdater.Initialize(inst->getType()); - ssaUpdater.AddAvailableValue(destBB, inst); - ssaUpdater.AddAvailableValue(srcBB, newResult); + ssaUpdater.AddAvailableValue(origBB, inst); + ssaUpdater.AddAvailableValue(getNewBB(), newResult); if (useList.empty()) continue; // Update all the uses. for (auto useWrapper : useList) { - Operand *use = useWrapper; + Operand *use = useWrapper; // unwrap SILInstruction *user = use->getUser(); assert(user && "Missing user"); // Ignore uses in the same basic block. - if (user->getParent() == destBB) + if (user->getParent() == origBB) continue; ssaUpdater.RewriteUse(*use); @@ -128,6 +136,29 @@ bool BasicBlockCloner::splitCriticalEdges(DominanceInfo *domInfo, return changed; } +void BasicBlockCloner::sinkAddressProjections() { + // Because the address projections chains will be disjoint (an instruction + // in one chain cannot use the result of an instruction in another chain), + // the order they are sunk does not matter. + for (auto ii = origBB->begin(), ie = origBB->end(); ii != ie;) { + bool canSink = sinkProj.analyzeAddressProjections(&*ii); + (void)canSink; + assert(canSink && "canCloneInstruction should catch this."); + + sinkProj.cloneProjections(); + assert((sinkProj.getInBlockDefs().empty() || needsSSAUpdate) + && "canCloneInstruction should catch this."); + + auto nextII = std::next(ii); + recursivelyDeleteTriviallyDeadInstructions( + &*ii, false, [&nextII](SILInstruction *deadInst) { + if (deadInst->getIterator() == nextII) + ++nextII; + }); + ii = nextII; + } +} + // Populate 'projections' with the chain of address projections leading // to and including 'inst'. // @@ -149,7 +180,7 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) { return true; } if (auto *addressProj = dyn_cast(def)) { - if (addressProj->isTriviallyDuplicatable()) { + if (addressProj->isPure()) { projections.push_back(addressProj); return true; } @@ -183,19 +214,40 @@ bool SinkAddressProjections::cloneProjections() { return false; SILBasicBlock *bb = projections.front()->getParent(); - SmallVector usesToReplace; // Clone projections in last-to-first order. for (unsigned idx = 0; idx < projections.size(); ++idx) { auto *oldProj = projections[idx]; assert(oldProj->getParent() == bb); + // Reset transient per-projection sets. usesToReplace.clear(); + firstBlockUse.clear(); + // Gather uses. for (Operand *use : oldProj->getUses()) { - if (use->getUser()->getParent() != bb) + auto *useBB = use->getUser()->getParent(); + if (useBB != bb) { + firstBlockUse.try_emplace(useBB, use); usesToReplace.push_back(use); + } } + // Replace uses. Uses must be handled in the same order they were discovered + // above. + // + // Avoid cloning a projection multiple times per block. This avoids extra + // projections, but also prevents the removal of DebugValue. If a + // projection's only remaining is DebugValue, then it is deleted along with + // the DebugValue. for (Operand *use : usesToReplace) { - auto *newProj = oldProj->clone(use->getUser()); - use->set(cast(newProj)); + auto *useBB = use->getUser()->getParent(); + auto *firstUse = firstBlockUse.lookup(useBB); + SingleValueInstruction *newProj; + if (use == firstUse) + newProj = cast(oldProj->clone(use->getUser())); + else { + newProj = cast(firstUse->get()); + assert(newProj->getParent() == useBB); + newProj->moveFront(useBB); + } + use->set(newProj); } } return true; diff --git a/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp b/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp index 390e6e826a6be..e74d950a4b671 100644 --- a/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp +++ b/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp @@ -87,8 +87,8 @@ class CheckedCastBrJumpThreading { hasUnknownPreds(hasUnknownPreds) { } void modifyCFGForUnknownPreds(); - void modifyCFGForFailurePreds(Optional &Cloner); - void modifyCFGForSuccessPreds(Optional &Cloner); + void modifyCFGForFailurePreds(BasicBlockCloner &Cloner); + void modifyCFGForSuccessPreds(BasicBlockCloner &Cloner); }; // Contains an entry for each checked_cast_br to be optimized. @@ -243,15 +243,14 @@ void CheckedCastBrJumpThreading::Edit::modifyCFGForUnknownPreds() { /// Create a copy of the BB as a landing BB /// for all FailurePreds. -void CheckedCastBrJumpThreading::Edit:: -modifyCFGForFailurePreds(Optional &Cloner) { +void CheckedCastBrJumpThreading::Edit::modifyCFGForFailurePreds( + BasicBlockCloner &Cloner) { if (FailurePreds.empty()) return; - assert(!Cloner.hasValue()); - Cloner.emplace(CCBBlock); - Cloner->cloneBlock(); - SILBasicBlock *TargetFailureBB = Cloner->getNewBB(); + assert(!Cloner.wasCloned()); + Cloner.cloneBlock(); + SILBasicBlock *TargetFailureBB = Cloner.getNewBB(); auto *TI = TargetFailureBB->getTerminator(); SILBuilderWithScope Builder(TI); // This BB copy branches to a FailureBB. @@ -271,8 +270,8 @@ modifyCFGForFailurePreds(Optional &Cloner) { /// Create a copy of the BB or reuse BB as /// a landing basic block for all FailurePreds. -void CheckedCastBrJumpThreading::Edit:: -modifyCFGForSuccessPreds(Optional &Cloner) { +void CheckedCastBrJumpThreading::Edit::modifyCFGForSuccessPreds( + BasicBlockCloner &Cloner) { auto *CCBI = cast(CCBBlock->getTerminator()); if (InvertSuccess) { @@ -285,10 +284,9 @@ modifyCFGForSuccessPreds(Optional &Cloner) { if (!SuccessPreds.empty()) { // Create a copy of the BB as a landing BB. // for all SuccessPreds. - assert(!Cloner.hasValue()); - Cloner.emplace(CCBBlock); - Cloner->cloneBlock(); - SILBasicBlock *TargetSuccessBB = Cloner->getNewBB(); + assert(!Cloner.wasCloned()); + Cloner.cloneBlock(); + SILBasicBlock *TargetSuccessBB = Cloner.getNewBB(); auto *TI = TargetSuccessBB->getTerminator(); SILBuilderWithScope Builder(TI); // This BB copy branches to SuccessBB. @@ -343,6 +341,11 @@ bool CheckedCastBrJumpThreading::handleArgBBIsEntryBlock(SILBasicBlock *ArgBB, // Returns false if cloning required by jump threading cannot // be performed, because some of the constraints are violated. +// +// This does not check the constraint on address projections with out-of-block +// uses. Those are rare enough that they don't need to be checked first for +// efficiency, but they need to be gathered later, just before cloning, anyway +// in order to sink the projections. bool CheckedCastBrJumpThreading::checkCloningConstraints() { // Check some cloning related constraints. @@ -673,7 +676,9 @@ void CheckedCastBrJumpThreading::optimizeFunction() { Fn->verifyCriticalEdges(); for (Edit *edit : Edits) { - Optional Cloner; + BasicBlockCloner Cloner(edit->CCBBlock); + if (!Cloner.canCloneBlock()) + continue; // Create a copy of the BB as a landing BB // for all FailurePreds. @@ -684,12 +689,11 @@ void CheckedCastBrJumpThreading::optimizeFunction() { // Handle unknown preds. edit->modifyCFGForUnknownPreds(); - if (Cloner.hasValue()) { - updateSSAAfterCloning(*Cloner.getPointer(), Cloner->getNewBB(), - edit->CCBBlock); + if (Cloner.wasCloned()) { + Cloner.updateSSAAfterCloning(); - if (!Cloner->getNewBB()->pred_empty()) - BlocksForWorklist.push_back(Cloner->getNewBB()); + if (!Cloner.getNewBB()->pred_empty()) + BlocksForWorklist.push_back(Cloner.getNewBB()); } if (!edit->CCBBlock->pred_empty()) BlocksForWorklist.push_back(edit->CCBBlock); diff --git a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp index 9625114d8fa24..3936838c88e21 100644 --- a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp +++ b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp @@ -438,7 +438,7 @@ UseWrapper::UseWrapper(Operand *Use) { } /// Return the operand we wrap. Reconstructing branch operands. -UseWrapper::operator Operand *() { +Operand *UseWrapper::getOperand() { switch (Type) { case kRegularUse: return U; diff --git a/test/DebugInfo/inlined-generics-basic.swift b/test/DebugInfo/inlined-generics-basic.swift index d1f6fbb686973..93f13e5d05955 100644 --- a/test/DebugInfo/inlined-generics-basic.swift +++ b/test/DebugInfo/inlined-generics-basic.swift @@ -43,8 +43,8 @@ public class C { let r : R init(_ _r: R) { r = _r } - // SIL: // C.f(_:) - // IR: define {{.*}} @"$s1A1CC1fyyqd__lF" + // SIL-LABEL: // C.f(_:) + // IR-LABEL: define {{.*}} @"$s1A1CC1fyyqd__lF" #sourceLocation(file: "f.swift", line: 1) public func f(_ s: S) { // SIL: debug_value_addr %0 : $*S, let, name "s", argno 1,{{.*}} scope [[F]] @@ -57,7 +57,13 @@ public class C { // IR: call {{.*}}3use #sourceLocation(file: "f.swift", line: 2) g(s) - // IR: dbg.value({{.*}}, metadata ![[GR_T:[0-9]+]] + // Jump-threading removes the basic block containing debug_value + // "t" before the second call to `g(r)`. When this happens, the + // ref_element_addr in that removed block is left with a single + // debug_value use, so they are both deleted. This means we have + // no debug value for "t" in the call to `g(r)`. + // dbg.value({{.*}}, metadata ![[GR_T:[0-9]+]] + // IR: dbg.value({{.*}}, metadata ![[GR_U:[0-9]+]] // IR: call {{.*}}3use #sourceLocation(file: "f.swift", line: 3) @@ -81,6 +87,8 @@ public class C { g(false) } } +// SIL-LABEL: } // end sil function '$s1A1CC1fyyqd__lF' +// IR-LABEL: ret void // IR: ![[BOOL:[0-9]+]] = !DICompositeType({{.*}}name: "Bool" // IR: ![[LET_BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[BOOL]]) @@ -96,9 +104,12 @@ public class C { // IR: ![[SP_GS_T]] = {{.*}}linkageName: "$s1A1gyyxlFqd___Ti5" // IR: ![[GS_U]] = !DILocalVariable(name: "u", {{.*}} scope: ![[SP_GS_U:[0-9]+]], {{.*}} type: ![[LET_TAU_1_0]]) // IR: ![[SP_GS_U]] = {{.*}}linkageName: "$s1A1hyyxlFqd___Ti5" -// IR: ![[GR_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GR_T:[0-9]+]], {{.*}}type: ![[LET_TAU_0_0]]) + +// Debug info for this variable is removed. See the note above the call to g(r). +// ![[GR_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GR_T:[0-9]+]], {{.*}}type: ![[LET_TAU_0_0]]) // S has the same generic parameter numbering s T and U. -// IR: ![[SP_GR_T]] = {{.*}}linkageName: "$s1A1gyyxlF" +// ![[SP_GR_T]] = {{.*}}linkageName: "$s1A1gyyxlF" + // IR: ![[GR_U]] = !DILocalVariable(name: "u", {{.*}} scope: ![[SP_GR_U:[0-9]+]], {{.*}}type: ![[LET_TAU_0_0]]) // IR: ![[SP_GR_U]] = {{.*}}linkageName: "$s1A1hyyxlF" // IR: ![[GRS_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GRS_T:[0-9]+]], {{.*}}type: ![[LET_TUPLE:[0-9]+]] diff --git a/test/SILOptimizer/simplify_cfg_address_phi.sil b/test/SILOptimizer/simplify_cfg_address_phi.sil index 438ee2cbdf246..923ff8ba4a6cb 100644 --- a/test/SILOptimizer/simplify_cfg_address_phi.sil +++ b/test/SILOptimizer/simplify_cfg_address_phi.sil @@ -134,3 +134,118 @@ bb4: bb5: return %val : $Builtin.Int32 } + +// Test that debug_value_addr is not unnecessarilly lost during address projection sinking. +public class CC { + let r : R + init(_ _r: R) { r = _r } +} + +sil @useAny : $@convention(thin) (@in_guaranteed V) -> () + +// CHECK-LABEL: sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { +// CHECK: debug_value_addr %0 : $*S, let, name "u" +// CHECK: apply %{{.*}}(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// CHECK: [[FIELD:%.*]] = ref_element_addr %1 : $CC, #CC.r +// CHECK: debug_value_addr [[FIELD]] : $*R, let, name "u" +// CHECK: apply %10([[FIELD]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// CHECK-LABEL: } // end sil function 'testDebugValue' +sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { +bb0(%0 : $*S, %1 : $CC, %2 : $Bool): + %bool = struct_extract %2 : $Bool, #Bool._value + cond_br %bool, bb1, bb2 + +bb1: + debug_value_addr %0 : $*S, let, name "u" + %f1 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %call1 = apply %f1(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + br bb2 + +bb2: + %field = ref_element_addr %1 : $CC, #CC.r + debug_value_addr %field : $*R, let, name "t" + cond_br %bool, bb3, bb4 + +bb3: + debug_value_addr %field : $*R, let, name "u" + %f2 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %call2 = apply %f2(%field) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + br bb4 + +bb4: + %z = tuple () + return %z : $() +} + +// Test multiple uses and cloned allocation. +// +// project_box and struct_extract_addr will be sunk into three +// different blocks, but only once per block. +struct S { + @_hasStorage @_hasInitialValue var x: Int { get set } + init(x: Int = 0) + init() +} +sil @doNothing : $@convention(thin) (@inout Int) -> () + +// CHECK-LABEL: sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () { +// CHECK: bb0(%0 : $Bool, %1 : $*Int): +// CHECK: cond_br %{{.*}}, bb2, bb1 +// CHECK: bb1: +// CHECK: [[ALLOC1:%.*]] = alloc_box ${ var S }, var, name "s" +// CHECK: [[PROJ1:%.*]] = project_box [[ALLOC1]] : ${ var S }, 0 +// CHECK: [[ADR1:%.*]] = struct_element_addr [[PROJ1]] : $*S, #S.x +// CHECK: store %{{.*}} to [[ADR1]] : $*Int +// CHECK: br bb3([[ALLOC1]] : ${ var S }) +// CHECK: bb2: +// CHECK: apply %{{.*}}(%1) : $@convention(thin) (@inout Int) -> () +// CHECK: [[ALLOC2:%.*]] = alloc_box ${ var S }, var, name "s" +// CHECK: [[PROJ2:%.*]] = project_box [[ALLOC2]] : ${ var S }, 0 +// CHECK: [[ADR2:%.*]] = struct_element_addr [[PROJ2]] : $*S, #S.x +// CHECK: store %{{.*}} to [[ADR2]] : $*Int +// CHECK: apply %{{.*}}([[ADR2]]) : $@convention(thin) (@inout Int) -> () +// CHECK: br bb3([[ALLOC2]] : ${ var S }) +// CHECK: bb3([[BOXARG:%.*]] : ${ var S }): +// CHECK: [[PROJ3:%.*]] = project_box [[BOXARG]] : ${ var S }, 0 +// CHECK: [[ADR3:%.*]] = struct_element_addr [[PROJ3]] : $*S, #S.x +// CHECK: apply %{{.*}}([[ADR3]]) : $@convention(thin) (@inout Int) -> () +// CHECK: release_value [[BOXARG]] : ${ var S } +// CHECK-LABEL: } // end sil function 'testMultiUse' +sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () { +bb0(%0 : $Bool, %1 : $*Int): + %bool = struct_extract %0 : $Bool, #Bool._value + cond_br %bool, bb1, bb2 + +bb1: + %f1 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call1 = apply %f1(%1) : $@convention(thin) (@inout Int) -> () + br bb3 + +bb2: + br bb3 + +bb3: + %box3 = alloc_box ${ var S }, var, name "s" + %proj3 = project_box %box3 : ${ var S }, 0 + %adr3 = struct_element_addr %proj3 : $*S, #S.x + cond_br %bool, bb4, bb5 + +bb4: + %i4 = load %1 : $*Int + store %i4 to %adr3 : $*Int + %f2 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call2 = apply %f2(%adr3) : $@convention(thin) (@inout Int) -> () + br bb6 + +bb5: + %i5 = load %1 : $*Int + store %i5 to %adr3 : $*Int + br bb6 + +bb6: + %f6 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call6 = apply %f6(%adr3) : $@convention(thin) (@inout Int) -> () + release_value %box3 : ${ var S } + %z = tuple () + return %z : $() +} From 6db9a69e9417430ea9915d54a2c6116b2dd102a5 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 14 Nov 2019 16:24:46 -0800 Subject: [PATCH 196/283] [docs] Move all the futurey bits of LibraryEvolution.rst to a new file (#28007) It's not /exactly/ a manifesto but it's close enough. --- docs/LibraryEvolutionManifesto.md | 215 ++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 docs/LibraryEvolutionManifesto.md diff --git a/docs/LibraryEvolutionManifesto.md b/docs/LibraryEvolutionManifesto.md new file mode 100644 index 0000000000000..e2bad23a8a408 --- /dev/null +++ b/docs/LibraryEvolutionManifesto.md @@ -0,0 +1,215 @@ +# Introduction + +This document is intended to discuss current issues in and possible future expansions of Swift's model for ABI-stable _library evolution,_ described in [LibraryEvolution.rst][]. Like other "manifesto" documents in the Swift community, it sets out related tasks in service of a general goal, though this is still an exploration of the space more than a to-do list. + +[LibraryEvolution.rst]: ./LibraryEvolution.rst + + +# Open Issues + + +## Recompiling changes a protocol's implementation + +```swift +// Library, version 1 +protocol MagicType {} +protocol Wearable {} +func use(_ item: T) {} +``` + +```swift +// Client, version 1 +struct Amulet : MagicType, Wearable {} +use(Amulet()) +``` + +```swift +// Library, version 2 +protocol MagicType { + @available(dishwasherOS 2.0, *) + func equip() +} +extension MagicType { + @available(dishwasherOS 2.0, *) + func equip() { print("Equipped.") } +} + +protocol Wearable {} +extension Wearable where Self: MagicType { + @available(dishwasherOS 2.0, *) + func equip() { print("You put it on.") } +} + +func use(_ item: T) { item.equip() } +``` + +Let's say we're running dishwasherOS 2.0. Before the client is recompiled, the implementation of `equip()` used for `Amulet` instances can only be the default implementation, i.e. the one that prints "Equipped". However, recompiling the client will result in the constrained implementation being considered a "better" match for the protocol requirement, thus changing the behavior of the program. + +This should never change the *meaning* of a program, since the default implementation for a newly-added requirement should always be *correct.* However, it may have significantly different performance characteristics or side effects that would make the difference in behavior a surprise. + +This is similar to adding a new overload to an existing set of functions, which can also change the meaning of client code just by recompiling. However, the difference here is that the before-recompilation behavior was never requested or acknowledged by the client; it's just the best the library can do. + +A possible solution here is to require the client to acknowledge the added requirement in some way when it is recompiled. + +(We do not want to perform overload resolution at run time to find the best possible default implementation for a given type.) + + +# Evolving Attributes + +A number of annotations (attributes and modifiers) would benefit from being able to change between releases of a library. For example: + +- Making a `public` class `open`. +- Making a `public` class `final`. +- Making a struct or enum `@frozen`. +- Making a function `@inlinable`. + +However, all of these changes have to be associated with a specific release to be correct: + +- The newly-`open` class can't be subclassed if the client's deployment target is too old. +- Convenience initializers on a newly-`final` class can't satisfy protocol requirements if the client's deployment target is too old. +- A newly-`@frozen` struct may have had more private stored properties in the past. +- A newly-`@inlinable` function's body may not work correctly with older versions of the library. + +While solutions can be tailored to each of these problems, they have a common need to record the OS version when an annotation is added or changed. Without that, we're locked in to the way a declaration is originally published, or having to take correctness on faith. + +## Limitations + +For any annotation that affects the calling conventions of a function specifically, it becomes tricky to add or remove that annotation without breaking ABI. Consider the example of a struct becoming `@frozen`. Normally, functions that pass or return non-frozen structs have to do so indirectly (i.e. through a pointer), whereas frozen structs that are "small enough" (according to the ABI) can be passed in registers instead. So we get tables like this: + +Frozen struct | used in a module's ABI | used in non-public ways +---|---|--- +in its own module | direct | direct +in a client with library evolution | direct | direct +in a client without library evolution (like an app) | direct | direct + +Non-frozen struct | used in a module's ABI | used in non-public ways +---|---|--- +in its own module | indirect (no promises) | direct +in a client with library evolution | indirect (knows nothing) | indirect (knows nothing) +in a client without library evolution (like an app) | indirect (knows nothing) | indirect (knows nothing) + +However, for any library *or client* with library evolution enabled, the ABI columns must not change. That implies that marking a struct as frozen *after its initial release* is different from marking the struct frozen from the start. + +Frozen struct that used to be non-frozen | used in a module's ABI | used in non-public ways +---|---|--- +in its own module | indirect (ABI compat) | direct +in a client with library evolution | indirect (ABI compat) | direct +in a client without library evolution (like an app) | direct | direct + +There are a few ways to improve upon this, the most obvious being that any APIs with newer availability than when the struct became frozen can pass the struct around directly. It's worth noting that a library's minimum deployment target *cannot* be used for this purpose, since changing a minimum deployment target is not supposed to change a library's ABI. + +This isn't a *terrible* performance cost; remember that all C++ methods take `this` indirectly, and clients with new enough deployment targets can still *manipulate* the values directly even if the calling convention can't change. But it's something to think about for every annotation that can change after a type is introduced. + + +# Additional Annotations + +## Layout Constraints + +Developers with performance-sensitive use cases may want to promise more specific things about various types and have the compiler enforce them. + +- "trivial": Promises that assignment just requires a fixed-size bit-for-bit copy without any indirection or reference-counting operations. This may allow more efficient copying and destruction of values. + +- "maximum size N/alignment A": Promises that the type's size and required alignment are at most N bits and A bits, respectively. (Both may be smaller.) This would make it easier for the compiler to work with values on the stack. + +(Neither of these names / spellings are final. The name "trivial" comes from C++, though Swift's trivial is closer to C++'s "[trivially copyable][]".) + +Both of these annotations are things the compiler already knows when a type is declared, but writing them explicitly (a) allows the developer to check that they've made something properly optimizable, and (b) promises not to change that behavior between releases, even if, say, stored properties are added to or removed from a struct. + +[trivially copyable]: http://en.cppreference.com/w/cpp/types/is_trivially_copyable + + +## Frozen classes + +The compiler actually has basic support for frozen classes, which allow stored properties to be accessed directly by offset from the class reference. There's no real reason why this can't be supported more generally, but confusion around *what's* being frozen and the rarity of wanting to make this promise anyway kept it out of [SE-0260][]. + +[SE-0260]: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md + + +# Generalized Availability Model + +This section is focused on various ways to extend the OS-version-based availability model. + +## Per-library availability + +As a placeholder, imagine replacing OS versions with library versions in the various parts of Swift's availability checking syntax: + +```swift +// Client code +@available(Magician 1.5) +class CrystalBallView : MagicView { /*...*/ } + +func scareMySiblings() { + if #available(Magician 1.2) { + summonDemons() + } else { + print("BOO!!") + } +} +``` + +This sort of version checking is important for *backward*-deployment, where the developer has the interface available for a new version of a library (Magician 1.5), but might end up running the client against an old version (Magician 1.0). It's not interesting for *forward*-deployment, however—that's all up to the library to preserve ABI compatibility. + +Possible implementations for version checking include generating a hidden symbol into a library, or putting the version number in some kind of metadata, like the Info.plist in a framework bundle on Apple platforms. + + +### Publishing API with availability + +A library's API is already marked with the `public` modifier, but if a client wants to work with multiple releases of the library, the API needs availability information as well. Declaring availability on an entity using the *current* library's name specifies the oldest version in which that entity can be used. + +- Classes, structs, enums, and protocols may all have availability. +- Methods, properties, subscripts, and initializers may all have availability. +- Top-level functions, variables, and constants may have availability. +- Typealiases are treated as having availability for the purpose of availability checking, even though they have no run-time presence. + +In a versioned library, any top-level public entity from the list above may not be made ABI-public (`public`, `open`, `@usableFromInline`, or `@inlinable`) without availability. A public entity declared within a type (or an extension of a type) will default to having the same availability as the type. + +Code within a library may generally use all other entities declared within the library (barring their own availability checks), since the entire library is shipped as a unit. That is, even if a particular API was introduced in v1.0, its (non-public) implementation may refer to APIs introduced in later versions. Inlinable functions are the exception to this, since inlined code ends up outside the original module. + + +### Declaring library version dependencies + +Swift's current OS-based availability model includes the notion of a _minimum deployment target,_ the version of an OS that must be present for the program being compiled to run at all. For example, a program compiled with a minimum deployment target of iOS 9.2 will not launch on iOS 9.0. + +The generalized model suggests being able to make similar guarantees for individual libraries. For example, a client program may depend on version 1.1 of the "Magician" library; trying to run using version 1.0 will result in errors. By declaring this at compile-time, the client code can omit `@available` and `#available` checks that are satisfied by the minimum library version. + + +## Resilience domains + +In general, the features around library evolution for ABI stability's sake do not apply to clients that bundle their dependencies with them (such as an iOS app embedding third-party frameworks). In this case, a client can rely on all the current implementation details of its libraries when compiling, since the same version of the library is guaranteed to be present at run time. This allows more optimization than would otherwise be possible. + +In some cases, a collection of libraries may be built and delivered together, even though their clients may be packaged separately. (For example, the ICU project is usually built into several library binaries, but these libraries are always distributed together.) While the *clients* cannot rely on a particular version of any library being present, the various libraries in the collection should be able to take advantage of the implementations of their dependencies also in the collection---that is, it should treat types as frozen (except where it would affect public-facing ABI). We've used the term _resilience domain_ to describe modules in this sort of collection. + +There's no design yet for how resilience domains should be specified. In today's compiler, every library with library evolution enabled is in its own resilience domain, and every library that *doesn't* have library evolution enabled is in the "app" resilience domain. + +### Deployments + +Related to the concept of a resilience domain is a _deployment._ While a resilience domain allows related libraries to be compiled more efficiently, a deployment groups related libraries together to present semantic version information to clients. The simplest example of this might be an OS release: OS X 10.10.0 contains Foundation version 1151.16 and AppKit version 1343. A deployment thus acts as a "virtual dependency": clients that depend on OS X 10.10 can rely on the presence of both of the library versions above. + +The use of deployments allows clients to only have to think about aggregate dependencies, instead of listing every library they might depend on. It also allows library authors to build [many versions of a library][Foundation version numbers] within a larger release cycle, as well as allowing a vendor to bundle together many libraries with uncoordinated release schedules and release them as a logical unit. + +[Foundation version numbers]: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/doc/constant_group/Foundation_Framework_Version_Numbers + +There are lots of details to figure out here, including how to distribute this information. In particular, just like libraries publish the history of their own APIs, a deployment must publish the history of their included library versions, i.e. not just that OS X 10.10 contains Foundation 1151.16 and AppKit 1343, but also that OS X 10.9 contains Foundation 1056 and AppKit 1265, and that OS X 10.8 contains Foundation 945.0 and AppKit 1187, and so on, back to the earliest version of the deployment that is supported. + +Obviously, formalizing a model here is probably most useful for people distributing ABI-stable Swift libraries as part of an OS, i.e. Apple. + + +# Automated Tooling + +## ABI Checker + +The Swift repository has a basic ABI checker in the form of swift-api-digester. This tool looks at two versions of a library and determines if there are any changes which are known to be unsafe (say, changing the type of a function parameter). It would be nice™ to integrate this into SwiftPM and Xcode in some way. + +## Automatic Versioning + +A possible extension of the ABI checker would be a tool that *automatically* generates versioning information for entities in a library, given the previous public interface of the library. This would remove the need for versions on annotations, and declaring new public API would be as simple as marking an entity `public`. Obviously this would also remove the possibility of human error in managing library versions. + +However, making this tool has a number of additional difficulties beyond the simple checker tool: + +- The tool must be able to read past library interface formats. This is true for a validation tool as well, but the cost of failure is much higher. Similarly, the past version of a library *must* be available to correctly compile a new version. + +- Because the information goes into a library's public interface, the versioning tool must either be part of the compilation process, modify the interface generated by compilation, or produce a sidecar file that can be loaded when compiling the client. In any case, it must *produce* information in addition to *consuming* it. + +- Occasionally a library owner may want to override the inferred versions. This can be accomplished by providing explicit versioning information, as described above. + +- Bugs in the tool manifest as bugs in client programs. From b30efa75662a6f30098a46f9be79f7617646b289 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 13 Nov 2019 12:52:41 -0800 Subject: [PATCH 197/283] [pmo] Refactor addHandOffCopyDestroysForPhis to be able to take a load or load_borrow. Just some small changes to types. NFC. --- .../Mandatory/PredictableMemOpt.cpp | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index c4c292d678854..9304d9bd786df 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -492,8 +492,13 @@ class AvailableValueAggregator { /// ownership is enabled. void addMissingDestroysForCopiedValues(LoadBorrowInst *li, SILValue newVal, const SmallBitVector &instsToSkip); - void addHandOffCopyDestroysForPhis(LoadBorrowInst *li, SILValue newVal, - SmallBitVector &instsToSkipOut); + + /// As a result of us using the SSA updater, insert hand off copy/destroys at + /// each phi and make sure that intermediate phis do not leak by inserting + /// destroys along paths that go through the intermediate phi that do not also + /// go through the + void addHandOffCopyDestroysForPhis(SILInstruction *load, SILValue newVal, + SmallBitVector &instsToSkipOut); }; } // end anonymous namespace @@ -958,8 +963,10 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li, return nullptr; } -void AvailableValueAggregator::addHandOffCopyDestroysForPhis(LoadBorrowInst *lbi, SILValue newVal, - SmallBitVector &instsToSkip) { +void AvailableValueAggregator::addHandOffCopyDestroysForPhis( + SILInstruction *load, SILValue newVal, SmallBitVector &instsToSkip) { + assert(isa(load) || isa(load)); + ValueLifetimeAnalysis::Frontier lifetimeFrontier; SmallPtrSet visitedBlocks; SmallVector leakingBlocks; @@ -1124,14 +1131,14 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis(LoadBorrowInst *lbi auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - phiArg, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, + phiArg, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly // control equivalent as our load_borrow. So just insert a destroy_value // for the copy_value. - auto next = std::next(lbi->getIterator()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), phiArg); continue; @@ -1142,7 +1149,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis(LoadBorrowInst *lbi // this if we found a loop since our leaking blocks will lifetime extend the // value over the loop. if (!error.getFoundOverConsume()) { - auto next = std::next(lbi->getIterator()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), phiArg); } From 9243c61244cc666af8e8a0d55fe057bc44a99fc7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 14:59:58 -0800 Subject: [PATCH 198/283] Handle Boolean literals as enum raw values. Fixes rdar://problem/55384273. --- lib/Sema/DerivedConformanceRawRepresentable.cpp | 3 +++ lib/Sema/TypeCheckDecl.cpp | 14 +++++++++++++- test/decl/enum/bool_raw_value.swift | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test/decl/enum/bool_raw_value.swift diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index 91f3850508632..091da1f3e0fc0 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -43,6 +43,9 @@ static LiteralExpr *cloneRawLiteralExpr(ASTContext &C, LiteralExpr *expr) { /*implicit*/ true); if (floatLit->isNegative()) cast(clone)->setNegative(expr->getLoc()); + } else if (auto boolLit = dyn_cast(expr)) { + clone = new (C) BooleanLiteralExpr(boolLit->getValue(), expr->getLoc(), + /*implicit*/true); } else { llvm_unreachable("invalid raw literal expr"); } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 213f1c4a3fc63..9093087dfb0ba 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -75,7 +75,7 @@ namespace { /// Float and integer literals are additionally keyed by numeric equivalence. struct RawValueKey { enum class Kind : uint8_t { - String, Float, Int, Tombstone, Empty + String, Float, Int, Bool, Tombstone, Empty } kind; struct IntValueTy { @@ -101,6 +101,7 @@ struct RawValueKey { StringRef stringValue; IntValueTy intValue; FloatValueTy floatValue; + bool boolValue; }; explicit RawValueKey(LiteralExpr *expr) { @@ -136,6 +137,12 @@ struct RawValueKey { kind = Kind::String; stringValue = cast(expr)->getValue(); return; + + case ExprKind::BooleanLiteral: + kind = Kind::Bool; + boolValue = cast(expr)->getValue(); + return; + default: llvm_unreachable("not a valid literal expr for raw value"); } @@ -183,6 +190,8 @@ class DenseMapInfo { DenseMapInfo::getHashValue(k.intValue.v1); case RawValueKey::Kind::String: return DenseMapInfo::getHashValue(k.stringValue); + case RawValueKey::Kind::Bool: + return DenseMapInfo::getHashValue(k.boolValue); case RawValueKey::Kind::Empty: case RawValueKey::Kind::Tombstone: return 0; @@ -204,6 +213,8 @@ class DenseMapInfo { a.intValue.v1 == b.intValue.v1; case RawValueKey::Kind::String: return a.stringValue.equals(b.stringValue); + case RawValueKey::Kind::Bool: + return a.boolValue == b.boolValue; case RawValueKey::Kind::Empty: case RawValueKey::Kind::Tombstone: return true; @@ -1682,6 +1693,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, continue; } + // If the raw values of the enum case are fixed, then we trust our callers // to have set things up correctly. This comes up with imported enums // and deserialized @objc enums which always have their raw values setup diff --git a/test/decl/enum/bool_raw_value.swift b/test/decl/enum/bool_raw_value.swift new file mode 100644 index 0000000000000..26391db7d41bc --- /dev/null +++ b/test/decl/enum/bool_raw_value.swift @@ -0,0 +1,16 @@ +// RUN: %target-typecheck-verify-swift +extension Bool: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = value != 0 + } +} + +enum IsDefinitelyRecursive : Bool, Equatable, Hashable { + case recursive=false +} + +// expected-error@+1{{'IsRecursive' declares raw type 'Bool', but does not conform to RawRepresentable and conformance could not be synthesized}} +enum IsRecursive : Bool, Equatable, Hashable { + case recursive=false + case nonrecursive // expected-error{{enum case must declare a raw value when the preceding raw value is not an integer}} +} From 21a7fba89593a6d46c98f4064ce2df711fa6f12d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 17:21:44 -0800 Subject: [PATCH 199/283] More tests for Bool raw values of enums --- test/decl/enum/bool_raw_value.swift | 10 ++++++++++ test/decl/enum/objc_bool_raw_value.swift | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 test/decl/enum/objc_bool_raw_value.swift diff --git a/test/decl/enum/bool_raw_value.swift b/test/decl/enum/bool_raw_value.swift index 26391db7d41bc..88905f4f532ad 100644 --- a/test/decl/enum/bool_raw_value.swift +++ b/test/decl/enum/bool_raw_value.swift @@ -14,3 +14,13 @@ enum IsRecursive : Bool, Equatable, Hashable { case recursive=false case nonrecursive // expected-error{{enum case must declare a raw value when the preceding raw value is not an integer}} } + +enum IsRecursiveBad1Integral : Bool, Equatable, Hashable { + case recursive = 0 + case nonrecursive +} + +// expected-error @+1{{'IsRecursiveBad2' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} +enum IsRecursiveBad2 : Int, Equatable, Hashable { + case recursive = false // expected-error{{cannot convert value of type 'Bool' to raw type 'Int'}} +} diff --git a/test/decl/enum/objc_bool_raw_value.swift b/test/decl/enum/objc_bool_raw_value.swift new file mode 100644 index 0000000000000..5245ceac3e7f3 --- /dev/null +++ b/test/decl/enum/objc_bool_raw_value.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: objc_interop + +extension Bool: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = value != 0 + } +} + +@objc enum IsDefinitelyRecursive : Bool, Equatable, Hashable { // expected-error{{'@objc' enum raw type 'Bool' is not an integer type}} + case recursive=false +} From 62c0bb8258789bacfbc070884578298981135364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Thu, 14 Nov 2019 18:11:24 -0800 Subject: [PATCH 200/283] [msvc] std::move the pointer for the method that requires an rvalue. MSVC was chocking with the given syntax, saying that 'swift::DefaultArgumentInitializer *' wasn't the expected 'swift::Initializer *&&'. --- lib/Sema/TypeCheckDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 60121f22d07ca..fa1c39c3fb827 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2049,7 +2049,7 @@ DefaultArgumentInitContextRequest::evaluate(Evaluator &eval, if (param == otherParam) result = initDC; else - eval.cacheOutput(DefaultArgumentInitContextRequest{otherParam}, initDC); + eval.cacheOutput(DefaultArgumentInitContextRequest{otherParam}, std::move(initDC)); } assert(result && "Didn't create init context?"); return result; From 24b47b5673ccd9421365cfc76191fcc548a3d21a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 20:33:00 -0800 Subject: [PATCH 201/283] [Constraint solver] Sink Expr::getPreorderIndexMap() into its client. --- include/swift/AST/Expr.h | 6 ------ lib/AST/Expr.cpp | 22 ---------------------- lib/Sema/ConstraintSystem.cpp | 28 +++++++++++++++++++++++++++- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index bce9b29b88ae4..9a672524663ff 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -530,12 +530,6 @@ class alignas(8) Expr { /// the parent map. llvm::DenseMap getParentMap(); - /// Produce a mapping from each expression to its index according to a - /// preorder traversal of the expressions. The parent has index 0, its first - /// child has index 1, its second child has index 2 if the first child is a - /// leaf node, etc. - llvm::DenseMap getPreorderIndexMap(); - SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, unsigned Indent = 0) const; void dump(raw_ostream &OS, llvm::function_ref getType, diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 2bee677e3da31..8cd263494f86d 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -721,28 +721,6 @@ llvm::DenseMap Expr::getParentMap() { return parentMap; } -llvm::DenseMap Expr::getPreorderIndexMap() { - class RecordingTraversal : public ASTWalker { - public: - llvm::DenseMap &IndexMap; - unsigned Index = 0; - - explicit RecordingTraversal(llvm::DenseMap &indexMap) - : IndexMap(indexMap) { } - - std::pair walkToExprPre(Expr *E) override { - IndexMap[E] = Index; - Index++; - return { true, E }; - } - }; - - llvm::DenseMap indexMap; - RecordingTraversal traversal(indexMap); - walk(traversal); - return indexMap; -} - //===----------------------------------------------------------------------===// // Support methods for Exprs. //===----------------------------------------------------------------------===// diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 09724d217f8b1..18990a329fbcf 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2727,6 +2727,29 @@ static DeclName getOverloadChoiceName(ArrayRef choices) { return name; } +/// Extend the given index map with all of the subexpressions in the given +/// expression. +static void extendPreorderIndexMap( + Expr *expr, llvm::DenseMap &indexMap) { + class RecordingTraversal : public ASTWalker { + public: + llvm::DenseMap &IndexMap; + unsigned Index = 0; + + explicit RecordingTraversal(llvm::DenseMap &indexMap) + : IndexMap(indexMap) { } + + std::pair walkToExprPre(Expr *E) override { + IndexMap[E] = Index; + Index++; + return { true, E }; + } + }; + + RecordingTraversal traversal(indexMap); + expr->walk(traversal); +} + bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, ArrayRef solutions) { // Produce a diff of the solutions. @@ -2747,7 +2770,10 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, // Heuristically, all other things being equal, we should complain about the // ambiguous expression that (1) has the most overloads, (2) is deepest, or // (3) comes earliest in the expression. - auto indexMap = expr->getPreorderIndexMap(); + llvm::DenseMap indexMap; + for (auto expr : InputExprs) { + extendPreorderIndexMap(expr, indexMap); + } for (unsigned i = 0, n = diff.overloads.size(); i != n; ++i) { auto &overload = diff.overloads[i]; From abf41e73a809fe7607195cd77ff60f2f6c395517 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 14 Nov 2019 20:35:07 -0800 Subject: [PATCH 202/283] [Constraint system] Look for parent/depth info in base constraint system. Look first in the base constraint system for this information, so it only gets built once in the normal case. --- lib/Sema/ConstraintSystem.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 18990a329fbcf..3ba38205310d7 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -528,6 +528,12 @@ static void extendDepthMap( Optional> ConstraintSystem::getExprDepthAndParent( Expr *expr) { + // Check whether the parent has this information. + if (baseCS && baseCS != this) { + if (auto known = baseCS->getExprDepthAndParent(expr)) + return *known; + } + // Bring the set of expression weights up to date. while (NumInputExprsInWeights < InputExprs.size()) { extendDepthMap(InputExprs[NumInputExprsInWeights], ExprWeights); @@ -538,9 +544,6 @@ Optional> ConstraintSystem::getExprDepthAndParent( if (e != ExprWeights.end()) return e->second; - if (baseCS && baseCS != this) - return baseCS->getExprDepthAndParent(expr); - return None; } From 815bf31bda50e9cb19bc131b4938a2f876fee622 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 15 Nov 2019 15:45:47 +0900 Subject: [PATCH 203/283] [IDE] Enable 'IgnoreSwiftSourceInfo' in code completion Get source locations from '.swiftsourceinfo' is currently expensive. Since they are not necessary for code completion, set 'IgnoreSwiftSourceInfo' to 'true'. rdar://problem/57194358 https://bugs.swift.org/browse/SR-11767 --- tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp | 4 ++++ .../lib/SwiftLang/SwiftConformingMethodList.cpp | 4 ++++ .../SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp | 4 ++++ tools/swift-ide-test/swift-ide-test.cpp | 12 ++++++++++++ 4 files changed, 24 insertions(+) diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 5dc4aed03c39e..6a9ee7b491a1b 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -172,6 +172,10 @@ static bool swiftCodeCompleteImpl( return false; } + // Disable source location resolutions from .swiftsourceinfo file because + // they are somewhat heavy operations and are not needed for completions. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + const char *Position = InputFile->getBufferStart() + CodeCompletionOffset; std::unique_ptr NewBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 06f9bfd76e274..76c308d534136 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -67,6 +67,10 @@ static bool swiftConformingMethodListImpl( return false; } + // Disable source location resolutions from .swiftsourceinfo file because + // they are somewhat heavy operations and are not needed for completions. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); // Create a factory for code completion callbacks that will feed the diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index 3109a68689b08..285052d3a2895 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -67,6 +67,10 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, return false; } + // Disable source location resolutions from .swiftsourceinfo file because + // they are somewhat heavy operations and are not needed for completions. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); // Create a factory for code completion callbacks that will feed the diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 98862e524cad0..4266efaef387d 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -740,6 +740,10 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); + // Disable source location resolutions from .swiftsourceinfo file because + // they are somewhat heavy operations and are not needed for completions. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); // Create a CodeCompletionConsumer. @@ -801,6 +805,10 @@ doConformingMethodList(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); + // Disable source location resolutions from .swiftsourceinfo file because + // they are somewhat heavy operations and are not needed for completions. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); SmallVector typeNames; @@ -870,6 +878,10 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, Invocation.setCodeCompletionPoint(CleanFile.get(), CodeCompletionOffset); + // Disable source location resolutions from .swiftsourceinfo file because + // they are somewhat heavy operations and are not needed for completions. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + // Disable to build syntax tree because code-completion skips some portion of // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; From f2f521f8bf20957d323f7b5701f1d321bd3a5491 Mon Sep 17 00:00:00 2001 From: Tapan Thaker Date: Fri, 15 Nov 2019 07:28:50 -0800 Subject: [PATCH 204/283] [FrondEnd] Wrap RunInmediately() in a pretty stack trace indicating we are running user code --- lib/FrontendTool/FrontendTool.cpp | 6 ++++++ test/Frontend/crash-in-user-code.swift | 15 +++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 test/Frontend/crash-in-user-code.swift diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 44629ad48987c..85dba1f7a1fdd 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1337,6 +1337,12 @@ static bool processCommandLineAndRunImmediately(CompilerInvocation &Invocation, ProcessCmdLine(opts.ImmediateArgv.begin(), opts.ImmediateArgv.end()); Instance.setSILModule(std::move(SM)); + + PrettyStackTraceStringAction trace( + "running user code", + MSF.is() ? MSF.get()->getFilename() + : MSF.get()->getModuleFilename()); + ReturnValue = RunImmediately(Instance, CmdLine, IRGenOpts, Invocation.getSILOptions()); return Instance.getASTContext().hadError(); diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift new file mode 100644 index 0000000000000..5a2055e2c60f9 --- /dev/null +++ b/test/Frontend/crash-in-user-code.swift @@ -0,0 +1,15 @@ + +// RUN: echo %s > %t.filelist.txt +// RUN: not --crash %target-swift-frontend -interpret -filelist %t.filelist.txt 2>&1 | %FileCheck %s + +// CHECK: Stack dump: +// CHECK-NEXT: Program arguments: +// CHECK-NEXT: Swift version +// CHECK-NEXT: Contents of {{.*}}.filelist.txt: +// CHECK-NEXT: --- +// CHECK-NEXT: crash-in-user-code.swift +// CHECK-NEXT: --- +// CHECK-NEXT: While running user code "SOURCE_DIR/test/FrontEnd/crash-in-user-code.swift" + +let x: Int? = nil +x! From ee8447e7abf80b4407db643802b04b72bf3aa844 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Fri, 15 Nov 2019 10:50:51 -0500 Subject: [PATCH 205/283] [Reflection] Reject BuiltinTypeDescriptors with non-power-of-two alignments. rdar://problem/56784375 --- stdlib/public/Reflection/TypeRefBuilder.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index a052e61581b87..5452dd11cc008 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -253,12 +253,18 @@ TypeRefBuilder::getBuiltinTypeInfo(const TypeRef *TR) { for (auto Info : ReflectionInfos) { for (auto BuiltinTypeDescriptor : Info.Builtin) { - if (BuiltinTypeDescriptor->getAlignment() <= 0) - continue; if (BuiltinTypeDescriptor->Stride <= 0) continue; if (!BuiltinTypeDescriptor->hasMangledTypeName()) continue; + + auto Alignment = BuiltinTypeDescriptor->getAlignment(); + if (Alignment <= 0) + continue; + // Reject any alignment that's not a power of two. + if (Alignment & (Alignment - 1)) + continue; + auto CandidateMangledName = readTypeRef(BuiltinTypeDescriptor, BuiltinTypeDescriptor->TypeName); if (!reflectionNameMatches(CandidateMangledName, MangledName)) From 7c0ed24867d1746d2da288aea5526bdfc94700f9 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 15 Nov 2019 10:38:44 -0600 Subject: [PATCH 206/283] [stdlib] Document Windows implementation of SystemRandomNumberGenerator (#28268) --- stdlib/public/core/Random.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/core/Random.swift b/stdlib/public/core/Random.swift index 58bfeff2da169..1d32a55527cd6 100644 --- a/stdlib/public/core/Random.swift +++ b/stdlib/public/core/Random.swift @@ -144,6 +144,7 @@ extension RandomNumberGenerator { /// - Apple platforms use `arc4random_buf(3)`. /// - Linux platforms use `getrandom(2)` when available; otherwise, they read /// from `/dev/urandom`. +/// - Windows uses `BCryptGenRandom`. @frozen public struct SystemRandomNumberGenerator: RandomNumberGenerator { /// Creates a new instance of the system's default random number generator. From e66e033b00c3933a773b9e437c0a6f65c632432c Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 14 Nov 2019 14:28:42 -0800 Subject: [PATCH 207/283] Cleanup EscapeAnalysis::ConnectionGraph::initializePointsTo. Remove cruft that I added in the previous commit. This eliminates unnecessary cleverness so initializePointsTo is now very simple. Add hand-coded SIL test cases to somewhat verify that the new algorithm works. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 78 +++++------- test/SILOptimizer/escape_analysis.sil | 120 +++++++++++++++++++ 2 files changed, 147 insertions(+), 51 deletions(-) diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 10a7df0bfd737..168ccea435b3f 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -456,24 +456,18 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode, CGNode *newPointsTo, bool createEdge) { // Track nodes that require pointsTo edges. - llvm::SmallVector pointsToEdges; + llvm::SmallVector pointsToEdgeNodes; + if (createEdge) + pointsToEdgeNodes.push_back(initialNode); // Step 1: Visit each node that reaches or is reachable via defer edges until // reaching a node with the newPointsTo or with a proper pointsTo edge. // A worklist to gather updated nodes in the defer web. CGNodeWorklist updatedNodes(this); - updatedNodes.push(initialNode); - if (createEdge) - pointsToEdges.push_back(initialNode); - unsigned updateCount = 1; - assert(updateCount == updatedNodes.size()); - // Augment the worlist with the nodes that were reached via backward - // traversal. It's not as precise as DFS, but helps avoid redundant pointsTo - // edges in most cases. - llvm::SmallPtrSet backwardReachable; - - auto visitDeferTarget = [&](CGNode *node, bool isSuccessor) { + unsigned updateCount = 0; + + auto visitDeferTarget = [&](CGNode *node, bool /*isSuccessor*/) { if (updatedNodes.contains(node)) return true; @@ -484,7 +478,7 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode, // nodes are initialized one at a time, each time a new defer edge is // created. If this were not complete, then the backward traversal below // in Step 2 could reach uninitialized nodes not seen here in Step 1. - pointsToEdges.push_back(node); + pointsToEdgeNodes.push_back(node); return true; } ++updateCount; @@ -493,31 +487,29 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode, // edge. Create a "fake" pointsTo edge to maintain the graph invariant // (this changes the structure of the graph but adding this edge has no // effect on the process of merging nodes or creating new defer edges). - pointsToEdges.push_back(node); + pointsToEdgeNodes.push_back(node); } updatedNodes.push(node); - if (!isSuccessor) - backwardReachable.insert(node); - return true; }; + // Seed updatedNodes with initialNode. + visitDeferTarget(initialNode, true); // updatedNodes may grow during this loop. - unsigned nextUpdatedNodeIdx = 0; - for (; nextUpdatedNodeIdx < updatedNodes.size(); ++nextUpdatedNodeIdx) - updatedNodes[nextUpdatedNodeIdx]->visitDefers(visitDeferTarget); + for (unsigned idx = 0; idx < updatedNodes.size(); ++idx) + updatedNodes[idx]->visitDefers(visitDeferTarget); // Reset this worklist so others can be used, but updateNode.nodeVector still // holds all the nodes found by step 1. updatedNodes.reset(); // Step 2: Update pointsTo fields by propagating backward from nodes that // already have a pointsTo edge. - assert(nextUpdatedNodeIdx == updatedNodes.size()); - --nextUpdatedNodeIdx; - bool processBackwardReachable = false; do { - while (!pointsToEdges.empty()) { - CGNode *edgeNode = pointsToEdges.pop_back_val(); + while (!pointsToEdgeNodes.empty()) { + CGNode *edgeNode = pointsToEdgeNodes.pop_back_val(); if (!edgeNode->pointsTo) { + // This node is either (1) a leaf node in the defer web (identified in + // step 1) or (2) an arbitrary node in a defer-cycle (identified in a + // previous iteration of the outer loop). edgeNode->setPointsToEdge(newPointsTo); newPointsTo->mergeUsePoints(edgeNode); assert(updateCount--); @@ -542,35 +534,19 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode, return Traversal::Follow; }); } - // For all nodes visited in step 1, if any node was not backward-reachable - // from a pointsTo edge, create an edge for it and restart traversal. - // - // First process all forward-reachable nodes in backward order, then process - // all backwardReachable nodes in forward order. - while (nextUpdatedNodeIdx != updatedNodes.size()) { - CGNode *node = updatedNodes[nextUpdatedNodeIdx]; - // When processBackwardReachable == true, the backwardReachable set is - // empty and all forward reachable nodes already have a pointsTo edge. - if (!backwardReachable.count(node)) { - if (!node->pointsTo) { - pointsToEdges.push_back(node); - break; - } - } - if (processBackwardReachable) { - ++nextUpdatedNodeIdx; - continue; - } - if (nextUpdatedNodeIdx > 0) { - --nextUpdatedNodeIdx; - continue; + // For all nodes visited in step 1, pick a single node that was not + // backward-reachable from a pointsTo edge, create an edge for it and + // restart traversal. This only happens when step 1 fails to find leaves in + // the defer web because of defer edge cycles. + while (!updatedNodes.empty()) { + CGNode *node = updatedNodes.nodeVector.pop_back_val(); + if (!node->pointsTo) { + pointsToEdgeNodes.push_back(node); + break; } - // reverse direction - backwardReachable.clear(); - processBackwardReachable = true; } // This outer loop is exceedingly unlikely to execute more than twice. - } while (!pointsToEdges.empty()); + } while (!pointsToEdgeNodes.empty()); assert(updateCount == 0); } diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 72ae47bc383fb..3868f44f1ec7f 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -1535,3 +1535,123 @@ bb0: return %7 : $() } +// Test the absence of redundant pointsTo edges +// CHECK-LABEL: CG of testInitializePointsToLeaf +// CHECK: Arg %0 Esc: A, Succ: (%0.1) +// CHECK: Con %0.1 Esc: A, Succ: (%0.2) [rc] +// CHECK: Con %0.2 Esc: A, Succ: (%12.1) +// CHECK: Val %2 Esc: %4, Succ: %0.2 +// CHECK: Val %4 Esc: %4, Succ: %2 +// CHECK: Val %7 Esc: %12, Succ: (%12.1), %0.2 +// CHECK: Val %12 Esc: %12, Succ: (%12.1), %7 +// CHECK: Con %12.1 Esc: A, Succ: (%13) [rc] +// CHECK: Con %13 Esc: A, Succ: +// CHECK-LABEL: End +class C { + var c: C +} + +sil @testInitializePointsToWrapOptional : $@convention(method) (@guaranteed LinkedNode) -> Optional { +bb0(%0: $LinkedNode): + %adr = ref_element_addr %0 : $LinkedNode, #LinkedNode.next + %val = load %adr : $*LinkedNode + %optional = enum $Optional, #Optional.some!enumelt.1, %val : $LinkedNode + return %optional : $Optional +} + +sil @testInitializePointsToLeaf : $@convention(method) (@guaranteed LinkedNode) -> () { +bb0(%0 : $LinkedNode): + %f1 = function_ref @testInitializePointsToWrapOptional : $@convention(method) (@guaranteed LinkedNode) -> Optional + %call1 = apply %f1(%0) : $@convention(method) (@guaranteed LinkedNode) -> Optional + switch_enum %call1 : $Optional, case #Optional.some!enumelt.1: bb2, case #Optional.none!enumelt: bb3 + +bb2(%arg1 : $LinkedNode): + br bb4 + +bb3: + br bb4 + +bb4: + %call2 = apply %f1(%0) : $@convention(method) (@guaranteed LinkedNode) -> Optional + switch_enum %call2 : $Optional, case #Optional.some!enumelt.1: bb10, case #Optional.none!enumelt: bb9 + +bb9: + %37 = integer_literal $Builtin.Int1, -1 + cond_fail %37 : $Builtin.Int1, "Unexpectedly found nil while unwrapping an Optional value" + unreachable + +// %40 +bb10(%arg2 : $LinkedNode): + %adr = ref_element_addr %arg2 : $LinkedNode, #LinkedNode.next + %val = load %adr : $*LinkedNode + %66 = tuple () + return %66 : $() +} + +// Another test for redundant pointsTo edges. In the original +// implementation, redundant points edges were created whenever adding +// a defer edge from a node with uninitialized pointsTo to a node with +// already-initialized pointsTo. +// CHECK-LABEL: CG of testInitializePointsToRedundant +// CHECK: Arg %0 Esc: A, Succ: (%0.1) +// CHECK: Con %0.1 Esc: A, Succ: (%2) [rc] +// CHECK: Arg %1 Esc: A, Succ: (%0.1) +// CHECK: Con %2 Esc: A, Succ: +// CHECK: Val %7 Esc: %7,%18, Succ: %0 +// CHECK: Val %12 Esc: %12,%14,%18, Succ: %1 +// CHECK: Val %14 Esc: %18, Succ: (%0.1), %1, %12 +// CHECK: Val %18 Esc: %18, Succ: %7, %14 +// CHECK-LABEL: End +sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C { +bb0(%0: $C, %1 : $C): + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $C) + +bb2: + br bb3(%1 : $C) + +bb3(%arg : $C): + return %arg : $C +} + +sil @testInitializePointsToRedundant : $@convention(method) (@guaranteed C, @guaranteed C) -> () { +bb0(%0 : $C, %1 : $C): + %adr0 = ref_element_addr %0 : $C, #C.c + %val0 = load %adr0 : $*C + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $C) + +bb2: + br bb3(%0 : $C) + +bb3(%arg1 : $C): + br bb4 + +bb4: + cond_br undef, bb5, bb6 + +bb5: + br bb7(%1 : $C) + +bb6: + br bb7(%1 : $C) + +bb7(%arg2 : $C): + %f1 = function_ref @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C + %call1 = apply %f1(%arg2, %1) : $@convention(method) (@guaranteed C, @guaranteed C) -> C + cond_br undef, bb8, bb9 + +bb8: + br bb10(%call1 : $C) + +bb9: + br bb10(%arg1 : $C) + +bb10(%arg3 : $C): + %66 = tuple () + return %66 : $() +} From 7d4bf753f49d2f6a150868429d8a6fce27627f16 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Fri, 15 Nov 2019 12:56:24 -0500 Subject: [PATCH 208/283] [Test] Don't test demangling of SwiftObject's name, as it's subject to change. rdar://problem/57163094 --- test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m | 2 -- test/stdlib/SwiftObjectNSObject.swift | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m b/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m index dc97c4221c718..700756926f15c 100644 --- a/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m +++ b/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m @@ -412,7 +412,6 @@ void TestSwiftObjectNSObject(id c, id d) expectTrue ([[c description] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[D description] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C description] isEqual:@"SwiftObjectNSObject.C"]); - expectTrue ([[S description] isEqual:@(SwiftObjectDemangledName)]); expectTrue ([[D_meta description] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C_meta description] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[S_meta description] isEqual:@(SwiftObjectDemangledName)]); @@ -430,7 +429,6 @@ void TestSwiftObjectNSObject(id c, id d) expectTrue ([[c debugDescription] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[D debugDescription] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C debugDescription] isEqual:@"SwiftObjectNSObject.C"]); - expectTrue ([[S debugDescription] isEqual:@(SwiftObjectDemangledName)]); expectTrue ([[D_meta debugDescription] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C_meta debugDescription] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[S_meta debugDescription] isEqual:@(SwiftObjectDemangledName)]); diff --git a/test/stdlib/SwiftObjectNSObject.swift b/test/stdlib/SwiftObjectNSObject.swift index c46b184c64dc6..695a357427a0d 100644 --- a/test/stdlib/SwiftObjectNSObject.swift +++ b/test/stdlib/SwiftObjectNSObject.swift @@ -45,7 +45,7 @@ func TestSwiftObjectNSObject(_ c: C, _ d: D) // This check is for NSLog() output from TestSwiftObjectNSObject(). // CHECK: c ##SwiftObjectNSObject.C## // CHECK-NEXT: d ##SwiftObjectNSObject.D## -// CHECK-NEXT: S ##{{(Swift._)?}}SwiftObject## +// CHECK-NEXT: S ##{{.*}}SwiftObject## // Temporarily disable this test on older OSes until we have time to // look into why it's failing there. rdar://problem/47870743 From 5eb1df532b75a04d5060c1ac4945fbae7a156d2f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 15 Nov 2019 08:35:52 -0800 Subject: [PATCH 209/283] [CSDiagnostics] NFC: Use std::tie to call resolveImmutableBase --- lib/Sema/CSDiagnostics.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 40d7642971064..f2eef8758a2f0 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1555,9 +1555,9 @@ bool AssignmentFailure::diagnoseAsError() { // Walk through the destination expression, resolving what the problem is. If // we find a node in the lvalue path that is problematic, this returns it. - auto immInfo = resolveImmutableBase(DestExpr); - - Optional choice = immInfo.second; + Expr *immutableExpr; + Optional choice; + std::tie(immutableExpr, choice) = resolveImmutableBase(DestExpr); // Attempt diagnostics based on the overload choice. if (choice.hasValue()) { @@ -1571,9 +1571,9 @@ bool AssignmentFailure::diagnoseAsError() { if (!choice->isDecl()) { if (choice->getKind() == OverloadChoiceKind::KeyPathApplication && - !isa(immInfo.first)) { + !isa(immutableExpr)) { std::string message = "key path is read-only"; - if (auto *SE = dyn_cast(immInfo.first)) { + if (auto *SE = dyn_cast(immutableExpr)) { if (auto *DRE = dyn_cast(getKeyPathArgument(SE))) { auto identifier = DRE->getDecl()->getBaseName().getIdentifier(); message = @@ -1581,7 +1581,7 @@ bool AssignmentFailure::diagnoseAsError() { } } emitDiagnostic(Loc, DeclDiagnostic, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } return false; @@ -1595,7 +1595,7 @@ bool AssignmentFailure::diagnoseAsError() { message += VD->getName().str().str(); message += "'"; - auto type = getType(immInfo.first); + auto type = getType(immutableExpr); if (isKnownKeyPathType(type)) message += " is read-only"; @@ -1614,7 +1614,7 @@ bool AssignmentFailure::diagnoseAsError() { } emitDiagnostic(Loc, DeclDiagnostic, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); // If there is a masked instance variable of the same type, emit a // note to fixit prepend a 'self.'. @@ -1654,7 +1654,7 @@ bool AssignmentFailure::diagnoseAsError() { message = "subscript is immutable"; emitDiagnostic(Loc, DeclDiagnostic, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } @@ -1676,7 +1676,7 @@ bool AssignmentFailure::diagnoseAsError() { message += " is not settable"; emitDiagnostic(Loc, diagID, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } } @@ -1686,13 +1686,13 @@ bool AssignmentFailure::diagnoseAsError() { // If a keypath was the problem but wasn't resolved into a vardecl // it is ambiguous or unable to be used for setting. - if (auto *KPE = dyn_cast_or_null(immInfo.first)) { + if (auto *KPE = dyn_cast_or_null(immutableExpr)) { emitDiagnostic(Loc, DeclDiagnostic, "immutable key path") .highlight(KPE->getSourceRange()); return true; } - if (auto LE = dyn_cast(immInfo.first)) { + if (auto LE = dyn_cast(immutableExpr)) { emitDiagnostic(Loc, DeclDiagnostic, "literals are not mutable") .highlight(LE->getSourceRange()); return true; @@ -1700,7 +1700,7 @@ bool AssignmentFailure::diagnoseAsError() { // If the expression is the result of a call, it is an rvalue, not a mutable // lvalue. - if (auto *AE = dyn_cast(immInfo.first)) { + if (auto *AE = dyn_cast(immutableExpr)) { // Handle literals, which are a call to the conversion function. auto argsTuple = dyn_cast(AE->getArg()->getSemanticsProvidingExpr()); @@ -1733,22 +1733,22 @@ bool AssignmentFailure::diagnoseAsError() { return true; } - if (auto contextualType = cs.getContextualType(immInfo.first)) { + if (auto contextualType = cs.getContextualType(immutableExpr)) { Type neededType = contextualType->getInOutObjectType(); - Type actualType = getType(immInfo.first)->getInOutObjectType(); + Type actualType = getType(immutableExpr)->getInOutObjectType(); if (!neededType->isEqual(actualType)) { if (DeclDiagnostic.ID != diag::cannot_pass_rvalue_inout_subelement.ID) { emitDiagnostic(Loc, DeclDiagnostic, "implicit conversion from '" + actualType->getString() + "' to '" + neededType->getString() + "' requires a temporary") - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); } return true; } } - if (auto IE = dyn_cast(immInfo.first)) { + if (auto IE = dyn_cast(immutableExpr)) { emitDiagnostic(Loc, DeclDiagnostic, "result of conditional operator '? :' is never mutable") .highlight(IE->getQuestionLoc()) @@ -1757,7 +1757,7 @@ bool AssignmentFailure::diagnoseAsError() { } emitDiagnostic(Loc, TypeDiagnostic, getType(DestExpr)) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } From 8d208be40a81a512ad9aa3449cb3e2ef57b5331a Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 15 Nov 2019 08:35:52 -0800 Subject: [PATCH 210/283] [CSDiagnostics] Use qualified lookup for 'self.' fix-it We only want to look for properties on the type which the method is a member of. Resolves SR-11786. --- lib/Sema/CSDiagnostics.cpp | 46 ++++++++++++++++++++++++------------ test/Sema/immutability.swift | 12 ++++++++++ 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index f2eef8758a2f0..fd6e4bc0a9124 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1619,21 +1619,37 @@ bool AssignmentFailure::diagnoseAsError() { // If there is a masked instance variable of the same type, emit a // note to fixit prepend a 'self.'. if (auto typeContext = DC->getInnermostTypeContext()) { - UnqualifiedLookup lookup(VD->getFullName(), typeContext); - for (auto &result : lookup.Results) { - const VarDecl *typeVar = dyn_cast(result.getValueDecl()); - if (typeVar && typeVar != VD && typeVar->isSettable(DC) && - typeVar->isSetterAccessibleFrom(DC) && - typeVar->getType()->isEqual(VD->getType())) { - // But not in its own accessor. - auto AD = - dyn_cast_or_null(DC->getInnermostMethodContext()); - if (!AD || AD->getStorage() != typeVar) { - emitDiagnostic(Loc, diag::masked_instance_variable, - typeContext->getSelfTypeInContext()) - .fixItInsert(Loc, "self."); - } - } + SmallVector results; + DC->lookupQualified(typeContext->getSelfNominalTypeDecl(), + VD->getFullName(), + NL_QualifiedDefault | NL_RemoveNonVisible, results); + + auto foundProperty = llvm::find_if(results, [&](ValueDecl *decl) { + // We're looking for a settable property that is the same type as the + // var we found. + auto *var = dyn_cast(decl); + if (!var || var == VD) + return false; + + if (!var->isSettable(DC) || !var->isSetterAccessibleFrom(DC)) + return false; + + if (!var->getType()->isEqual(VD->getType())) + return false; + + // Don't suggest a property if we're in one of its accessors. + auto *methodDC = DC->getInnermostMethodContext(); + if (auto *AD = dyn_cast_or_null(methodDC)) + if (AD->getStorage() == var) + return false; + + return true; + }); + + if (foundProperty != results.end()) { + emitDiagnostic(Loc, diag::masked_instance_variable, + typeContext->getSelfTypeInContext()) + .fixItInsert(Loc, "self."); } } diff --git a/test/Sema/immutability.swift b/test/Sema/immutability.swift index 999c9eb8fdad6..8386d74215619 100644 --- a/test/Sema/immutability.swift +++ b/test/Sema/immutability.swift @@ -701,3 +701,15 @@ extension JustAProtocol { name = "World" // expected-error {{cannot assign to property: 'self' is immutable}} } } + +struct S { + var x = 0 + + struct Nested { + func foo() { + // SR-11786: Make sure we don't offer the 'self.' fix-it here. + let x = 0 // expected-note {{change 'let' to 'var' to make it mutable}} + x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} + } + } +} From 37e15dbf442f01255e1377eecf85e010452cf7a5 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 15 Nov 2019 09:44:34 -0800 Subject: [PATCH 211/283] [CSDiagnostics] Use expr's start loc for 'self.' fix-it Resolves SR-11787. --- lib/Sema/CSDiagnostics.cpp | 5 +++-- test/Sema/immutability.swift | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index fd6e4bc0a9124..01a2dc7692432 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1647,9 +1647,10 @@ bool AssignmentFailure::diagnoseAsError() { }); if (foundProperty != results.end()) { - emitDiagnostic(Loc, diag::masked_instance_variable, + auto startLoc = immutableExpr->getStartLoc(); + emitDiagnostic(startLoc, diag::masked_instance_variable, typeContext->getSelfTypeInContext()) - .fixItInsert(Loc, "self."); + .fixItInsert(startLoc, "self."); } } diff --git a/test/Sema/immutability.swift b/test/Sema/immutability.swift index 8386d74215619..adb8ba8800569 100644 --- a/test/Sema/immutability.swift +++ b/test/Sema/immutability.swift @@ -712,4 +712,17 @@ struct S { x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} } } + + func bar() { + // SR-11787: Make sure we insert "self." in the right location. + let x = 0 // expected-note 3{{change 'let' to 'var' to make it mutable}} + x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} + // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}} + + (try x) += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} + // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{10-10=self.}} + + x = 1 // expected-error {{cannot assign to value: 'x' is a 'let' constant}} + // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}} + } } From 6d6feb60de548d1f4dea112b7f014497f06522f2 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 15 Nov 2019 11:06:57 -0800 Subject: [PATCH 212/283] [CSDiagnostics] Support static "Type." fix-it Check whether we found a static property, and if so, suggest inserting "Type." instead of "self.". Resolves SR-11788. --- include/swift/AST/DiagnosticsSema.def | 6 +++--- lib/Sema/CSDiagnostics.cpp | 21 ++++++++++++++++----- test/Sema/immutability.swift | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index df60139eea9e4..8cec44ca5dfc6 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3424,9 +3424,9 @@ ERROR(assignment_bang_has_immutable_subcomponent,none, NOTE(change_to_mutating,none, "mark %select{method|accessor}0 'mutating' to make 'self' mutable", (bool)) -NOTE(masked_instance_variable,none, - "add explicit 'self.' to refer to mutable property of %0", - (Type)) +NOTE(masked_mutable_property,none, + "add explicit '%0' to refer to mutable %1 of %2", + (StringRef, DescriptiveDeclKind, Type)) ERROR(assignment_let_property_delegating_init,none, "'let' property %0 may not be initialized directly; use " diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 01a2dc7692432..72da9a9b08b58 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1616,8 +1616,8 @@ bool AssignmentFailure::diagnoseAsError() { emitDiagnostic(Loc, DeclDiagnostic, message) .highlight(immutableExpr->getSourceRange()); - // If there is a masked instance variable of the same type, emit a - // note to fixit prepend a 'self.'. + // If there is a masked property of the same type, emit a + // note to fixit prepend a 'self.' or 'Type.'. if (auto typeContext = DC->getInnermostTypeContext()) { SmallVector results; DC->lookupQualified(typeContext->getSelfNominalTypeDecl(), @@ -1648,9 +1648,20 @@ bool AssignmentFailure::diagnoseAsError() { if (foundProperty != results.end()) { auto startLoc = immutableExpr->getStartLoc(); - emitDiagnostic(startLoc, diag::masked_instance_variable, - typeContext->getSelfTypeInContext()) - .fixItInsert(startLoc, "self."); + auto *property = *foundProperty; + auto selfTy = typeContext->getSelfTypeInContext(); + + // If we found an instance property, suggest inserting "self.", + // otherwise suggest "Type." for a static property. + std::string fixItText; + if (property->isInstanceMember()) { + fixItText = "self."; + } else { + fixItText = selfTy->getString() + "."; + } + emitDiagnostic(startLoc, diag::masked_mutable_property, + fixItText, property->getDescriptiveKind(), selfTy) + .fixItInsert(startLoc, fixItText); } } diff --git a/test/Sema/immutability.swift b/test/Sema/immutability.swift index adb8ba8800569..af74e64f54a88 100644 --- a/test/Sema/immutability.swift +++ b/test/Sema/immutability.swift @@ -704,6 +704,7 @@ extension JustAProtocol { struct S { var x = 0 + static var y = 0 struct Nested { func foo() { @@ -724,5 +725,19 @@ struct S { x = 1 // expected-error {{cannot assign to value: 'x' is a 'let' constant}} // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}} + + // SR-11788: Insert "Type." for a static property. + let y = 0 // expected-note {{change 'let' to 'var' to make it mutable}} + y += 1 // expected-error {{left side of mutating operator isn't mutable: 'y' is a 'let' constant}} + // expected-note@-1 {{add explicit 'S.' to refer to mutable static property of 'S'}} {{5-5=S.}} + } +} + +struct S2 { + static var y: Int { get { 0 } set {} } + func foo() { + let y = 0 // expected-note {{change 'let' to 'var' to make it mutable}} + y += 1 // expected-error {{left side of mutating operator isn't mutable: 'y' is a 'let' constant}} + // expected-note@-1 {{add explicit 'S2.' to refer to mutable static property of 'S2'}} {{5-5=S2.}} } } From 55e3797471e32317ab3a8f791700d4b260b88ea1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 15 Nov 2019 14:32:13 -0500 Subject: [PATCH 213/283] Revert "Sema: Look for generic parameters first when inferring an associated type" This breaks source compatibility a little bit more than we'd like, so reverting it for now. Fixes . This reverts commit 04fbcc01490646699883637abbda00546534a72c. --- lib/Sema/TypeCheckProtocol.cpp | 23 ++----------- lib/Sema/TypeCheckProtocolInference.cpp | 8 +++++ stdlib/public/core/NativeSet.swift | 3 -- .../req/associated_type_inference_valid.swift | 32 ------------------- test/decl/protocol/typealias_inference.swift | 18 +++++++++++ 5 files changed, 29 insertions(+), 55 deletions(-) create mode 100644 test/decl/protocol/typealias_inference.swift diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index fd228d5b87056..4a2971a307ae2 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -3502,37 +3502,20 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( AssociatedTypeDecl *assocType) { // Conformances constructed by the ClangImporter should have explicit type // witnesses already. - if (isa(DC->getModuleScopeContext())) { + if (isa(Conformance->getDeclContext()->getModuleScopeContext())) { llvm::errs() << "Cannot look up associated type for imported conformance:\n"; Conformance->getType().dump(llvm::errs()); assocType->dump(llvm::errs()); abort(); } - // If we fail to find a witness via lookup, check for a generic parameter. - auto checkForGenericParameter = [&]() { - // If there is a generic parameter of the named type, use that. - if (auto genericSig = DC->getGenericSignatureOfContext()) { - for (auto gp : genericSig->getInnermostGenericParams()) { - if (gp->getName() == assocType->getName()) { - if (!checkTypeWitness(DC, Proto, assocType, gp)) { - recordTypeWitness(assocType, gp, nullptr); - return ResolveWitnessResult::Success; - } - } - } - } - - return ResolveWitnessResult::Missing; - }; - // Look for a member type with the same name as the associated type. auto candidates = TypeChecker::lookupMemberType( DC, Adoptee, assocType->getName(), NameLookupFlags::ProtocolMembers); // If there aren't any candidates, we're done. if (!candidates) { - return checkForGenericParameter(); + return ResolveWitnessResult::Missing; } // Determine which of the candidates is viable. @@ -3566,7 +3549,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( return x.first->getDeclContext() ->getSelfProtocolDecl() == nullptr; }) == nonViable.end()) - return checkForGenericParameter(); + return ResolveWitnessResult::Missing; // If there is a single viable candidate, form a substitution for it. if (viable.size() == 1) { diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index ebf92845dd7e9..ffa58350ce96a 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -918,6 +918,14 @@ AssociatedTypeInference::computeAbstractTypeWitness( return derivedType; } + // If there is a generic parameter of the named type, use that. + if (auto genericSig = dc->getGenericSignatureOfContext()) { + for (auto gp : genericSig->getInnermostGenericParams()) { + if (gp->getName() == assocType->getName()) + return dc->mapTypeIntoContext(gp); + } + } + return Type(); } diff --git a/stdlib/public/core/NativeSet.swift b/stdlib/public/core/NativeSet.swift index 143d7b87499b5..093a9641ba318 100644 --- a/stdlib/public/core/NativeSet.swift +++ b/stdlib/public/core/NativeSet.swift @@ -282,9 +282,6 @@ extension _NativeSet { } extension _NativeSet: _SetBuffer { - @usableFromInline - internal typealias Element = Element - @usableFromInline internal typealias Index = Set.Index diff --git a/test/decl/protocol/req/associated_type_inference_valid.swift b/test/decl/protocol/req/associated_type_inference_valid.swift index 3b24d1ba42be7..af92793e5a535 100644 --- a/test/decl/protocol/req/associated_type_inference_valid.swift +++ b/test/decl/protocol/req/associated_type_inference_valid.swift @@ -22,35 +22,3 @@ struct R : P { let x: Y? = nil func foo(_: Y) {} } - -// SR-8813 -protocol BaseProtocol { - associatedtype Value - typealias Closure = () -> Value - - init(closure: Closure) -} - -struct Base: BaseProtocol { - private var closure: Closure? - - init(closure: Closure) { - withoutActuallyEscaping(closure) { new in - self.closure = new - } - } -} - -// SR-11407 -protocol _Drivable: AnyObject { - typealias Driver = Self -} -protocol Configurator { - associatedtype Drivable: _Drivable - typealias Driver = Drivable.Driver - func configure(driver: Driver) -} -struct AnyConfigurator: Configurator { - private let thing: Driver? - func configure(driver: AnyConfigurator.Driver) {} -} diff --git a/test/decl/protocol/typealias_inference.swift b/test/decl/protocol/typealias_inference.swift new file mode 100644 index 0000000000000..89b9c8dfaf3f2 --- /dev/null +++ b/test/decl/protocol/typealias_inference.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +// SR-8813 +// By itself in this file because this particular expected error is omitted if there have been any other diagnostics. +protocol BaseProtocol { + associatedtype Value + typealias Closure = () -> Value + + init(closure: Closure) +} + +struct Base: BaseProtocol { + private let closure: Closure + + init(closure: Closure) { //expected-error {{reference to invalid type alias 'Closure' of type 'Base'}} + self.closure = closure + } +} From d0219b6f7a95b51ccde3ba33eb96435f4393c0e4 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 15 Nov 2019 12:55:41 -0800 Subject: [PATCH 214/283] [stlextras] Add overloads for binary_search that take containers. --- include/swift/Basic/STLExtras.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index 5c143c1b6f397..a0025dfb4b917 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -638,6 +638,16 @@ inline T accumulate(const Container &C, T init, BinaryOperation op) { return std::accumulate(C.begin(), C.end(), init, op); } +template +inline bool binary_search(const Container &C, T value) { + return std::binary_search(C.begin(), C.end(), value); +} + +template +inline bool binary_search(const Container &C, T value, BinaryOperation op) { + return std::binary_search(C.begin(), C.end(), value, op); +} + /// Returns true if the range defined by \p mainBegin ..< \p mainEnd starts with /// the same elements as the range defined by \p prefixBegin ..< \p prefixEnd. /// From da6991537fc117bd76234cb13570eb954e76893b Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Fri, 15 Nov 2019 22:11:43 +0000 Subject: [PATCH 215/283] [Linux] Build and install Foundation static libraries. - Builds libFoundation.a, libFoundationNetworking.a and libFoundationXML.a and installs them in usr/lib/swift_static/linux - Note this does NOT make -static-stdlib work for Foundation at this time. --- utils/build-script-impl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/build-script-impl b/utils/build-script-impl index 569ec7780d04d..f3e19b5f22ba0 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1847,6 +1847,9 @@ for host in "${ALL_HOSTS[@]}"; do tmp_product=libdispatch LIBDISPATCH_STATIC_CMAKE_OPTIONS=${LIBDISPATCH_CMAKE_OPTIONS[@]} fi + if [[ ${tmp_product} == "foundation_static" ]]; then + tmp_product=foundation + fi if ! [[ $(should_execute_action "${host}-${tmp_product}-build") ]]; then continue fi @@ -3093,6 +3096,9 @@ for host in "${ALL_HOSTS[@]}"; do if [[ ${tmp_product} == "libdispatch_static" ]]; then tmp_product=libdispatch fi + if [[ ${tmp_product} == "foundation_static" ]]; then + tmp_product=foundation + fi if ! [[ $(should_execute_action "${host}-${tmp_product}-install") ]]; then continue fi From 564e377b8ea9ad3c10c417609434c93ca8252392 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 15 Nov 2019 14:24:24 -0800 Subject: [PATCH 216/283] setInvalid on attr --- lib/Sema/TypeCheckAttr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 0d188bacbac77..ad2747d4b70a9 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2464,6 +2464,7 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) { if (auto *ED = dyn_cast(D)) { if (!ED->getModuleContext()->isResilient()) { + attr->setInvalid(); return; } From 185c265ef662b216b7fcf0cde6d076324ae704d2 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 13 Nov 2019 16:11:37 -0800 Subject: [PATCH 217/283] NFC: Move LookupResult from Sema to Name Lookup This will be used as the result type for the unqualified lookup request. --- include/swift/AST/NameLookup.h | 75 ++++++++++++++++++++++++++++++++ lib/AST/NameLookup.cpp | 41 +++++++++++++++++ lib/Sema/TypeCheckNameLookup.cpp | 41 ----------------- lib/Sema/TypeChecker.h | 75 -------------------------------- 4 files changed, 116 insertions(+), 116 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 05edf1a7f940a..ff57a3e36dc43 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -120,6 +120,81 @@ struct LookupResultEntry { void print(llvm::raw_ostream &) const; }; +/// The result of name lookup. +class LookupResult { +private: + /// The set of results found. + SmallVector Results; + size_t IndexOfFirstOuterResult = 0; + +public: + LookupResult() {} + + explicit LookupResult(const SmallVectorImpl &Results, + size_t indexOfFirstOuterResult) + : Results(Results.begin(), Results.end()), + IndexOfFirstOuterResult(indexOfFirstOuterResult) {} + + using iterator = SmallVectorImpl::iterator; + iterator begin() { return Results.begin(); } + iterator end() { + return Results.begin() + IndexOfFirstOuterResult; + } + unsigned size() const { return innerResults().size(); } + bool empty() const { return innerResults().empty(); } + + ArrayRef innerResults() const { + return llvm::makeArrayRef(Results).take_front(IndexOfFirstOuterResult); + } + + ArrayRef outerResults() const { + return llvm::makeArrayRef(Results).drop_front(IndexOfFirstOuterResult); + } + + const LookupResultEntry& operator[](unsigned index) const { + return Results[index]; + } + + LookupResultEntry front() const { return innerResults().front(); } + LookupResultEntry back() const { return innerResults().back(); } + + /// Add a result to the set of results. + void add(LookupResultEntry result, bool isOuter) { + Results.push_back(result); + if (!isOuter) { + IndexOfFirstOuterResult++; + assert(IndexOfFirstOuterResult == Results.size() && + "found an outer result before an inner one"); + } else { + assert(IndexOfFirstOuterResult > 0 && + "found outer results without an inner one"); + } + } + + void clear() { Results.clear(); } + + /// Determine whether the result set is nonempty. + explicit operator bool() const { + return !empty(); + } + + TypeDecl *getSingleTypeResult() const { + if (size() != 1) + return nullptr; + + return dyn_cast(front().getValueDecl()); + } + + /// Filter out any results that aren't accepted by the given predicate. + void + filter(llvm::function_ref pred); + + /// Shift down results by dropping inner results while keeping outer + /// results (if any), the innermost of which are recogized as inner + /// results afterwards. + void shiftDownResults(); +}; + /// This class implements and represents the result of performing /// unqualified lookup (i.e. lookup for a plain identifier). class UnqualifiedLookup { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index cb4ba6cb5f774..7124c4a876a17 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -65,6 +65,47 @@ ValueDecl *LookupResultEntry::getBaseDecl() const { return nominalDecl; } +void LookupResult::filter( + llvm::function_ref pred) { + size_t index = 0; + size_t originalFirstOuter = IndexOfFirstOuterResult; + Results.erase(std::remove_if(Results.begin(), Results.end(), + [&](LookupResultEntry result) -> bool { + auto isInner = index < originalFirstOuter; + index++; + if (pred(result, !isInner)) + return false; + + // Need to remove this, which means, if it is + // an inner result, the outer results need to + // shift down. + if (isInner) + IndexOfFirstOuterResult--; + return true; + }), + Results.end()); +} + +void LookupResult::shiftDownResults() { + // Remove inner results. + Results.erase(Results.begin(), Results.begin() + IndexOfFirstOuterResult); + IndexOfFirstOuterResult = 0; + + if (Results.empty()) + return; + + // Compute IndexOfFirstOuterResult. + const DeclContext *dcInner = Results.front().getValueDecl()->getDeclContext(); + for (auto &&result : Results) { + const DeclContext *dc = result.getValueDecl()->getDeclContext(); + if (dc == dcInner || + (dc->isModuleScopeContext() && dcInner->isModuleScopeContext())) + ++IndexOfFirstOuterResult; + else + break; + } +} + void DebuggerClient::anchor() {} void AccessFilteringDeclConsumer::foundDecl( diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 95e0b7c3fab82..43e0134c35d83 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -26,47 +26,6 @@ using namespace swift; -void LookupResult::filter( - llvm::function_ref pred) { - size_t index = 0; - size_t originalFirstOuter = IndexOfFirstOuterResult; - Results.erase(std::remove_if(Results.begin(), Results.end(), - [&](LookupResultEntry result) -> bool { - auto isInner = index < originalFirstOuter; - index++; - if (pred(result, !isInner)) - return false; - - // Need to remove this, which means, if it is - // an inner result, the outer results need to - // shift down. - if (isInner) - IndexOfFirstOuterResult--; - return true; - }), - Results.end()); -} - -void LookupResult::shiftDownResults() { - // Remove inner results. - Results.erase(Results.begin(), Results.begin() + IndexOfFirstOuterResult); - IndexOfFirstOuterResult = 0; - - if (Results.empty()) - return; - - // Compute IndexOfFirstOuterResult. - const DeclContext *dcInner = Results.front().getValueDecl()->getDeclContext(); - for (auto &&result : Results) { - const DeclContext *dc = result.getValueDecl()->getDeclContext(); - if (dc == dcInner || - (dc->isModuleScopeContext() && dcInner->isModuleScopeContext())) - ++IndexOfFirstOuterResult; - else - break; - } -} - namespace { /// Builder that helps construct a lookup result from the raw lookup /// data. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index efc04df053a81..c078728ac0dae 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -75,81 +75,6 @@ enum class DeclTypeCheckingSemantics { OpenExistential, }; -/// The result of name lookup. -class LookupResult { -private: - /// The set of results found. - SmallVector Results; - size_t IndexOfFirstOuterResult = 0; - -public: - LookupResult() {} - - explicit LookupResult(const SmallVectorImpl &Results, - size_t indexOfFirstOuterResult) - : Results(Results.begin(), Results.end()), - IndexOfFirstOuterResult(indexOfFirstOuterResult) {} - - using iterator = SmallVectorImpl::iterator; - iterator begin() { return Results.begin(); } - iterator end() { - return Results.begin() + IndexOfFirstOuterResult; - } - unsigned size() const { return innerResults().size(); } - bool empty() const { return innerResults().empty(); } - - ArrayRef innerResults() const { - return llvm::makeArrayRef(Results).take_front(IndexOfFirstOuterResult); - } - - ArrayRef outerResults() const { - return llvm::makeArrayRef(Results).drop_front(IndexOfFirstOuterResult); - } - - const LookupResultEntry& operator[](unsigned index) const { - return Results[index]; - } - - LookupResultEntry front() const { return innerResults().front(); } - LookupResultEntry back() const { return innerResults().back(); } - - /// Add a result to the set of results. - void add(LookupResultEntry result, bool isOuter) { - Results.push_back(result); - if (!isOuter) { - IndexOfFirstOuterResult++; - assert(IndexOfFirstOuterResult == Results.size() && - "found an outer result before an inner one"); - } else { - assert(IndexOfFirstOuterResult > 0 && - "found outer results without an inner one"); - } - } - - void clear() { Results.clear(); } - - /// Determine whether the result set is nonempty. - explicit operator bool() const { - return !empty(); - } - - TypeDecl *getSingleTypeResult() const { - if (size() != 1) - return nullptr; - - return dyn_cast(front().getValueDecl()); - } - - /// Filter out any results that aren't accepted by the given predicate. - void - filter(llvm::function_ref pred); - - /// Shift down results by dropping inner results while keeping outer - /// results (if any), the innermost of which are recogized as inner - /// results afterwards. - void shiftDownResults(); -}; - /// An individual result of a name lookup for a type. struct LookupTypeResultEntry { TypeDecl *Member; From 633de0241b38291eccbeb53ff1cec8a7d69909d9 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 13 Nov 2019 16:06:54 -0800 Subject: [PATCH 218/283] Add UnqualifiedLookupRequest This request performs raw unqualified lookup, and will be used to replace the UnqualifiedLookup type. --- include/swift/AST/Identifier.h | 9 ++++- include/swift/AST/NameLookup.h | 42 ++++++++++++++-------- include/swift/AST/NameLookupRequests.h | 26 ++++++++++++++ include/swift/AST/NameLookupTypeIDZone.def | 4 +++ lib/AST/Identifier.cpp | 4 +++ lib/AST/NameLookup.cpp | 22 ++++++++++++ lib/AST/UnqualifiedLookup.cpp | 8 +++++ 7 files changed, 100 insertions(+), 15 deletions(-) diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 924ce17f04b6c..4d5cc01853c12 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -428,7 +428,7 @@ class DeclName { // it is simple or compound), or a reference to a compound declaration name. llvm::PointerUnion SimpleOrCompound; - DeclName(void *Opaque) + explicit DeclName(void *Opaque) : SimpleOrCompound(decltype(SimpleOrCompound)::getFromOpaqueValue(Opaque)) {} @@ -571,6 +571,11 @@ class DeclName { return !(lhs == rhs); } + friend llvm::hash_code hash_value(DeclName name) { + using llvm::hash_value; + return hash_value(name.getOpaqueValue()); + } + friend bool operator<(DeclName lhs, DeclName rhs) { return lhs.compare(rhs) < 0; } @@ -615,6 +620,8 @@ class DeclName { SWIFT_DEBUG_DUMP; }; +void simple_display(llvm::raw_ostream &out, DeclName name); + enum class ObjCSelectorFamily : unsigned { None, #define OBJC_SELECTOR_FAMILY(LABEL, PREFIX) LABEL, diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index ff57a3e36dc43..e373f2896e24f 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -117,6 +117,11 @@ struct LookupResultEntry { ValueDecl *getBaseDecl() const; + friend bool operator ==(const LookupResultEntry &lhs, + const LookupResultEntry &rhs) { + return lhs.BaseDC == rhs.BaseDC && lhs.Value == rhs.Value; + } + void print(llvm::raw_ostream &) const; }; @@ -185,6 +190,11 @@ class LookupResult { return dyn_cast(front().getValueDecl()); } + friend bool operator ==(const LookupResult &lhs, const LookupResult &rhs) { + return lhs.Results == rhs.Results && + lhs.IndexOfFirstOuterResult == rhs.IndexOfFirstOuterResult; + } + /// Filter out any results that aren't accepted by the given predicate. void filter(llvm::function_ref pred); @@ -195,24 +205,28 @@ class LookupResult { void shiftDownResults(); }; +enum class UnqualifiedLookupFlags { + /// This lookup is known to not affect downstream files. + KnownPrivate = 0x01, + /// This lookup should only return types. + TypeLookup = 0x02, + /// This lookup should consider declarations within protocols to which the + /// context type conforms. + AllowProtocolMembers = 0x04, + /// Don't check access when doing lookup into a type. + IgnoreAccessControl = 0x08, + /// This lookup should include results from outside the innermost scope with + /// results. + IncludeOuterResults = 0x10, +}; + +void simple_display(llvm::raw_ostream &out, UnqualifiedLookupFlags flags); + /// This class implements and represents the result of performing /// unqualified lookup (i.e. lookup for a plain identifier). class UnqualifiedLookup { public: - enum class Flags { - /// This lookup is known to not affect downstream files. - KnownPrivate = 0x01, - /// This lookup should only return types. - TypeLookup = 0x02, - /// This lookup should consider declarations within protocols to which the - /// context type conforms. - AllowProtocolMembers = 0x04, - /// Don't check access when doing lookup into a type. - IgnoreAccessControl = 0x08, - /// This lookup should include results from outside the innermost scope with - /// results. - IncludeOuterResults = 0x10, - }; + using Flags = UnqualifiedLookupFlags; using Options = OptionSet; /// Lookup an unqualified identifier \p Name in the context. diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 614603c9ef9d6..a96953da2e37b 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -25,11 +25,16 @@ namespace swift { class ClassDecl; +class DeclContext; +class DeclName; class DestructorDecl; class GenericContext; class GenericParamList; +class LookupResult; +class SourceLoc; class TypeAliasDecl; class TypeDecl; +enum class UnqualifiedLookupFlags; namespace ast_scope { class ASTScopeImpl; class ScopeCreator; @@ -302,6 +307,27 @@ class ExpandASTScopeRequest void cacheResult(ast_scope::ASTScopeImpl *) const {} }; +/// Performs unqualified lookup for a DeclName from a given context. +class UnqualifiedLookupRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + // FIXME: We should be taking an OptionSet instead of the raw + // UnqualifiedLookupFlags, but we don't want to define == for OptionSet. We + // should find a way to define custom equality specifically for requests. + llvm::Expected evaluate(Evaluator &evaluator, DeclName name, + DeclContext *dc, SourceLoc loc, + UnqualifiedLookupFlags flags) const; +}; + #define SWIFT_TYPEID_ZONE NameLookup #define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index 5548a96d2aca3..c9c8cdbdf8ae5 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -45,3 +45,7 @@ SWIFT_REQUEST(NameLookup, TypeDeclsFromWhereClauseRequest, SWIFT_REQUEST(NameLookup, UnderlyingTypeDeclsReferencedRequest, DirectlyReferencedTypeDecls(TypeAliasDecl *), Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, UnqualifiedLookupRequest, + LookupResult(DeclName, DeclContext *, SourceLoc, + UnqualifiedLookupFlags), + Uncached, NoLocationInfo) diff --git a/lib/AST/Identifier.cpp b/lib/AST/Identifier.cpp index 8fbf7e20416ea..ba41df96b45fa 100644 --- a/lib/AST/Identifier.cpp +++ b/lib/AST/Identifier.cpp @@ -47,6 +47,10 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, DeclName I) { return OS; } +void swift::simple_display(llvm::raw_ostream &out, DeclName name) { + out << name; +} + raw_ostream &llvm::operator<<(raw_ostream &OS, swift::ObjCSelector S) { unsigned n = S.getNumArgs(); if (n == 0) { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 7124c4a876a17..81c442069757c 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -106,6 +106,28 @@ void LookupResult::shiftDownResults() { } } +void swift::simple_display(llvm::raw_ostream &out, + UnqualifiedLookupFlags flags) { + using Flag = std::pair; + Flag possibleFlags[] = { + {UnqualifiedLookupFlags::AllowProtocolMembers, "AllowProtocolMembers"}, + {UnqualifiedLookupFlags::IgnoreAccessControl, "IgnoreAccessControl"}, + {UnqualifiedLookupFlags::IncludeOuterResults, "IncludeOuterResults"}, + {UnqualifiedLookupFlags::KnownPrivate, "KnownPrivate"}, + {UnqualifiedLookupFlags::TypeLookup, "TypeLookup"}, + }; + + UnqualifiedLookup::Options options(flags); + auto flagsToPrint = llvm::make_filter_range( + possibleFlags, [&](Flag flag) { return options.contains(flag.first); }); + + out << "{ "; + interleave( + flagsToPrint, [&](Flag flag) { out << flag.second; }, + [&] { out << ", "; }); + out << " }"; +} + void DebuggerClient::anchor() {} void AccessFilteringDeclConsumer::foundDecl( diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 60c2964d8adbf..d9a5981453075 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -1228,6 +1228,14 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, factory.performUnqualifiedLookup(); } +llvm::Expected +UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, DeclName name, + DeclContext *dc, SourceLoc loc, + UnqualifiedLookupFlags flags) const { + UnqualifiedLookup lookup(name, dc, loc, UnqualifiedLookup::Options(flags)); + return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); +} + TypeDecl *UnqualifiedLookup::getSingleTypeResult() const { if (Results.size() != 1) return nullptr; From 3aa7158d6cce365d9a21d788382e178f9806c50f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 13 Nov 2019 16:06:54 -0800 Subject: [PATCH 219/283] Switch UnqualifiedLookup callers over to UnqualifiedLookupRequest --- include/swift/AST/NameLookup.h | 8 ++++ lib/AST/NameLookup.cpp | 7 +++- lib/Immediate/REPL.cpp | 9 ++++- lib/ParseSIL/ParseSIL.cpp | 12 ++++-- lib/Sema/MiscDiagnostics.cpp | 10 +++-- lib/Sema/TypeCheckAttr.cpp | 13 +++--- lib/Sema/TypeCheckNameLookup.cpp | 40 ++++++++++--------- .../lib/SwiftLang/SwiftSourceDocInfo.cpp | 10 +++-- tools/swift-ast-script/ASTScriptEvaluator.cpp | 5 ++- tools/swift-ide-test/swift-ide-test.cpp | 9 +++-- 10 files changed, 80 insertions(+), 43 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index e373f2896e24f..085ceb9c5fc06 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -156,6 +156,11 @@ class LookupResult { return llvm::makeArrayRef(Results).drop_front(IndexOfFirstOuterResult); } + /// \returns An array of both the inner and outer results. + ArrayRef allResults() const { + return llvm::makeArrayRef(Results); + } + const LookupResultEntry& operator[](unsigned index) const { return Results[index]; } @@ -163,6 +168,9 @@ class LookupResult { LookupResultEntry front() const { return innerResults().front(); } LookupResultEntry back() const { return innerResults().back(); } + /// \returns The index of the first outer result within \c allResults(). + size_t getIndexOfFirstOuterResult() const { return IndexOfFirstOuterResult; } + /// Add a result to the set of results. void add(LookupResultEntry result, bool isOuter) { Results.push_back(result); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 81c442069757c..f4e62543271d5 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1982,8 +1982,11 @@ directReferencesForUnqualifiedTypeLookup(DeclName name, if (lookupOuter == LookupOuterResults::Included) options |= UnqualifiedLookup::Flags::IncludeOuterResults; - UnqualifiedLookup lookup(name, dc, loc, options); - for (const auto &result : lookup.Results) { + auto &ctx = dc->getASTContext(); + auto flags = UnqualifiedLookupFlags(options.toRaw()); + auto lookup = evaluateOrDefault( + ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); + for (const auto &result : lookup.allResults()) { if (auto typeDecl = dyn_cast(result.getValueDecl())) results.push_back(typeDecl); } diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 4db9956c9c8c2..5a95395544496 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -20,6 +20,7 @@ #include "swift/AST/IRGenOptions.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/Basic/LLVMContext.h" #include "swift/Frontend/Frontend.h" #include "swift/IDE/REPLCodeCompletion.h" @@ -1091,8 +1092,12 @@ class REPLEnvironment { ASTContext &ctx = CI.getASTContext(); SourceFile &SF = MostRecentModule->getMainSourceFile(SourceFileKind::REPL); - UnqualifiedLookup lookup(ctx.getIdentifier(Tok.getText()), &SF); - for (auto result : lookup.Results) { + auto name = ctx.getIdentifier(Tok.getText()); + auto flags = UnqualifiedLookupFlags(); + auto lookup = evaluateOrDefault( + ctx.evaluator, + UnqualifiedLookupRequest{name, &SF, SourceLoc(), flags}, {}); + for (auto result : lookup) { printOrDumpDecl(result.getValueDecl(), doPrint); if (auto typeDecl = dyn_cast(result.getValueDecl())) { diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 178da31ae7a18..331be6b2613ed 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -15,6 +15,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" @@ -1185,10 +1186,13 @@ lookupTopDecl(Parser &P, DeclBaseName Name, bool typeLookup) { if (typeLookup) options |= UnqualifiedLookup::Flags::TypeLookup; - UnqualifiedLookup DeclLookup(Name, &P.SF, SourceLoc(), options); - assert(DeclLookup.isSuccess() && DeclLookup.Results.size() == 1); - ValueDecl *VD = DeclLookup.Results.back().getValueDecl(); - return VD; + auto &ctx = P.SF.getASTContext(); + auto flags = UnqualifiedLookupFlags(options.toRaw()); + auto lookup = evaluateOrDefault( + ctx.evaluator, + UnqualifiedLookupRequest{Name, &P.SF, SourceLoc(), flags}, {}); + assert(lookup.size() == 1); + return lookup.back().getValueDecl(); } /// Find the ValueDecl given an interface type and a member name. diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 7efca03fd8931..87a1ff349c6d6 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -19,6 +19,7 @@ #include "TypeCheckAvailability.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/Pattern.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" @@ -531,13 +532,14 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, } DeclContext *topLevelContext = DC->getModuleScopeContext(); - UnqualifiedLookup lookup(VD->getBaseName(), topLevelContext, - /*Loc=*/SourceLoc(), - UnqualifiedLookup::Flags::KnownPrivate); + auto req = UnqualifiedLookupRequest{VD->getBaseName(), topLevelContext, + SourceLoc(), + UnqualifiedLookupFlags::KnownPrivate}; + auto lookup = evaluateOrDefault(Ctx.evaluator, req, {}); // Group results by module. Pick an arbitrary result from each module. llvm::SmallDenseMap resultsByModule; - for (auto &result : lookup.Results) { + for (auto &result : lookup) { const ValueDecl *value = result.getValueDecl(); resultsByModule.insert(std::make_pair(value->getModuleContext(),value)); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index acf1b4882c8cb..bcb188b7cc129 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2073,12 +2073,13 @@ void lookupReplacedDecl(DeclName replacedDeclName, auto *moduleScopeCtxt = declCtxt->getModuleScopeContext(); if (isa(declCtxt)) { - UnqualifiedLookup lookup(replacedDeclName, moduleScopeCtxt, - attr->getLocation()); - if (lookup.isSuccess()) { - for (auto entry : lookup.Results) { - results.push_back(entry.getValueDecl()); - } + auto &ctx = declCtxt->getASTContext(); + auto req = + UnqualifiedLookupRequest{replacedDeclName, moduleScopeCtxt, + attr->getLocation(), UnqualifiedLookupFlags()}; + auto lookup = evaluateOrDefault(ctx.evaluator, req, {}); + for (auto entry : lookup) { + results.push_back(entry.getValueDecl()); } return; } diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 43e0134c35d83..4f6501d4ac1a0 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/TopCollection.h" #include @@ -225,13 +226,17 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, SourceLoc loc, NameLookupOptions options) { - UnqualifiedLookup lookup(name, dc, loc, - convertToUnqualifiedLookupOptions(options)); + auto ulOptions = convertToUnqualifiedLookupOptions(options); + + auto &ctx = dc->getASTContext(); + auto flags = UnqualifiedLookupFlags(ulOptions.toRaw()); + auto lookup = evaluateOrDefault( + ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); LookupResult result; LookupResultBuilder builder(result, dc, options); - for (auto idx : indices(lookup.Results)) { - const auto &found = lookup.Results[idx]; + for (auto idx : indices(lookup.allResults())) { + const auto &found = lookup[idx]; // Determine which type we looked through to find this result. Type foundInType; @@ -246,7 +251,7 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, } builder.add(found.getValueDecl(), found.getDeclContext(), foundInType, - /*isOuter=*/idx >= lookup.IndexOfFirstOuterResult); + /*isOuter=*/idx >= lookup.getIndexOfFirstOuterResult()); } return result; } @@ -255,18 +260,18 @@ LookupResult TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, SourceLoc loc, NameLookupOptions options) { + auto &ctx = dc->getASTContext(); auto ulOptions = convertToUnqualifiedLookupOptions(options) | UnqualifiedLookup::Flags::TypeLookup; { // Try lookup without ProtocolMembers first. - UnqualifiedLookup lookup( - name, dc, loc, - ulOptions - UnqualifiedLookup::Flags::AllowProtocolMembers); - - if (!lookup.Results.empty() || - !options.contains(NameLookupFlags::ProtocolMembers)) { - return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); - } + ulOptions -= UnqualifiedLookupFlags::AllowProtocolMembers; + auto flags = UnqualifiedLookupFlags(ulOptions.toRaw()); + auto lookup = evaluateOrDefault( + ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); + if (!lookup.allResults().empty() || + !options.contains(NameLookupFlags::ProtocolMembers)) + return lookup; } { @@ -275,11 +280,10 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, // FIXME: Fix the problem where if NominalTypeDecl::getAllProtocols() // is called too early, we start resolving extensions -- even those // which do provide not conformances. - UnqualifiedLookup lookup( - name, dc, loc, - ulOptions | UnqualifiedLookup::Flags::AllowProtocolMembers); - - return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); + ulOptions |= UnqualifiedLookupFlags::AllowProtocolMembers; + auto flags = UnqualifiedLookupFlags(ulOptions.toRaw()); + return evaluateOrDefault( + ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 4fb215165d9c5..07b23383bc093 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/GenericSignature.h" #include "swift/Basic/SourceManager.h" @@ -495,10 +496,13 @@ void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) { // FIXME: Extract useful related declarations, overloaded functions, // if VD is an initializer, we should extract other initializers etc. - // For now we use UnqualifiedLookup to fetch other declarations with the same + // For now we use unqualified lookup to fetch other declarations with the same // base name. - UnqualifiedLookup Lookup(VD->getBaseName(), VD->getDeclContext()); - for (auto result : Lookup.Results) { + auto &ctx = VD->getASTContext(); + auto req = UnqualifiedLookupRequest{VD->getBaseName(), VD->getDeclContext(), + SourceLoc(), UnqualifiedLookupFlags()}; + auto lookup = evaluateOrDefault(ctx.evaluator, req, {}); + for (auto result : lookup) { ValueDecl *RelatedVD = result.getValueDecl(); if (RelatedVD->getAttrs().isUnavailable(VD->getASTContext())) continue; diff --git a/tools/swift-ast-script/ASTScriptEvaluator.cpp b/tools/swift-ast-script/ASTScriptEvaluator.cpp index 7731d5976d94f..c49b75c7cf1c6 100644 --- a/tools/swift-ast-script/ASTScriptEvaluator.cpp +++ b/tools/swift-ast-script/ASTScriptEvaluator.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/Frontend/Frontend.h" using namespace swift; @@ -114,7 +115,9 @@ bool ASTScript::execute() const { return true; } - UnqualifiedLookup viewLookup(ctx.getIdentifier("View"), swiftUI); + auto req = UnqualifiedLookupRequest{ctx.getIdentifier("View"), swiftUI, + SourceLoc(), UnqualifiedLookupFlags()}; + auto viewLookup = evaluateOrDefault(ctx.evaluator, req, {}); auto viewProtocol = dyn_cast_or_null(viewLookup.getSingleTypeResult()); if (!viewProtocol) { diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 98862e524cad0..d4e4282709d1e 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -22,6 +22,7 @@ #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/ImportCache.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/PrintOptions.h" #include "swift/AST/RawComment.h" #include "swift/AST/USRGeneration.h" @@ -2233,9 +2234,11 @@ static int doPrintDecls(const CompilerInvocation &InitInvok, for (const auto &name : DeclsToPrint) { ASTContext &ctx = CI.getASTContext(); - UnqualifiedLookup lookup(ctx.getIdentifier(name), - CI.getPrimarySourceFile()); - for (auto result : lookup.Results) { + auto req = UnqualifiedLookupRequest{ctx.getIdentifier(name), + CI.getPrimarySourceFile(), SourceLoc(), + UnqualifiedLookupFlags()}; + auto lookup = evaluateOrDefault(ctx.evaluator, req, {}); + for (auto result : lookup) { result.getValueDecl()->print(*Printer, Options); if (auto typeDecl = dyn_cast(result.getValueDecl())) { From e8c30ca1b710742958b818066d5f90d054812f90 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 14 Nov 2019 18:53:22 -0800 Subject: [PATCH 220/283] Remove UnqualifiedLookup Now that we have UnqualifiedLookupRequest, this class no longer serves much of a purpose. Inline its constructor logic into the request. --- include/swift/AST/NameLookup.h | 42 ++----------------- lib/AST/NameLookup.cpp | 10 ++--- lib/AST/UnqualifiedLookup.cpp | 72 ++++++++------------------------ lib/ParseSIL/ParseSIL.cpp | 4 +- lib/Sema/TypeCheckNameLookup.cpp | 14 +++---- 5 files changed, 35 insertions(+), 107 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 085ceb9c5fc06..19ba526df99a1 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -230,45 +230,11 @@ enum class UnqualifiedLookupFlags { void simple_display(llvm::raw_ostream &out, UnqualifiedLookupFlags flags); -/// This class implements and represents the result of performing -/// unqualified lookup (i.e. lookup for a plain identifier). -class UnqualifiedLookup { -public: - using Flags = UnqualifiedLookupFlags; - using Options = OptionSet; - - /// Lookup an unqualified identifier \p Name in the context. - /// - /// If the current DeclContext is nested in a function body, the SourceLoc - /// is used to determine which declarations in that body are visible. - UnqualifiedLookup(DeclName Name, DeclContext *DC, - SourceLoc Loc = SourceLoc(), Options options = Options()); - - using ResultsVector = SmallVector; - ResultsVector Results; - - /// The index of the first result that isn't from the innermost scope - /// with results. - /// - /// That is, \c makeArrayRef(Results).take_front(IndexOfFirstOuterResults) - /// will be \c Results from the innermost scope that had results, and the - /// remaining elements of Results will be from parent scopes of this one. - /// - /// Allows unqualified name lookup to return results from outer scopes. - /// This is necessary for disambiguating calls to functions like `min` and - /// `max`. - size_t IndexOfFirstOuterResult; - - /// Return true if anything was found by the name lookup. - bool isSuccess() const { return !Results.empty(); } - - /// Get the result as a single type, or a null type if that fails. - TypeDecl *getSingleTypeResult() const; -}; +using UnqualifiedLookupOptions = OptionSet; -inline UnqualifiedLookup::Options operator|(UnqualifiedLookup::Flags flag1, - UnqualifiedLookup::Flags flag2) { - return UnqualifiedLookup::Options(flag1) | flag2; +inline UnqualifiedLookupOptions operator|(UnqualifiedLookupFlags flag1, + UnqualifiedLookupFlags flag2) { + return UnqualifiedLookupOptions(flag1) | flag2; } /// Describes the reason why a certain declaration is visible. diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index f4e62543271d5..8c6cb62b9f138 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -117,7 +117,7 @@ void swift::simple_display(llvm::raw_ostream &out, {UnqualifiedLookupFlags::TypeLookup, "TypeLookup"}, }; - UnqualifiedLookup::Options options(flags); + UnqualifiedLookupOptions options(flags); auto flagsToPrint = llvm::make_filter_range( possibleFlags, [&](Flag flag) { return options.contains(flag.first); }); @@ -1976,11 +1976,11 @@ directReferencesForUnqualifiedTypeLookup(DeclName name, SourceLoc loc, DeclContext *dc, LookupOuterResults lookupOuter) { DirectlyReferencedTypeDecls results; - UnqualifiedLookup::Options options = - UnqualifiedLookup::Flags::TypeLookup | - UnqualifiedLookup::Flags::AllowProtocolMembers; + UnqualifiedLookupOptions options = + UnqualifiedLookupFlags::TypeLookup | + UnqualifiedLookupFlags::AllowProtocolMembers; if (lookupOuter == LookupOuterResults::Included) - options |= UnqualifiedLookup::Flags::IncludeOuterResults; + options |= UnqualifiedLookupFlags::IncludeOuterResults; auto &ctx = dc->getASTContext(); auto flags = UnqualifiedLookupFlags(options.toRaw()); diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index d9a5981453075..d8420ebbf5ef2 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// /// -/// This file implements the construction of an UnqualifiedLookup, which entails -/// performing the lookup. +/// This file implements unqualified lookup, which searches for an identifier +/// from a given context. /// //===----------------------------------------------------------------------===// @@ -81,18 +81,14 @@ namespace { } // end anonymous namespace namespace { - /// Because UnqualifiedLookup does all of its work in the constructor, - /// a factory class is needed to hold all of the inputs and outputs so - /// that the construction code can be decomposed into bite-sized pieces. - class UnqualifiedLookupFactory { friend class ASTScopeDeclConsumerForUnqualifiedLookup; public: - using Flags = UnqualifiedLookup::Flags; - using Options = UnqualifiedLookup::Options; - using ResultsVector = UnqualifiedLookup::ResultsVector; + using Flags = UnqualifiedLookupFlags; + using Options = UnqualifiedLookupOptions; + using ResultsVector = SmallVector; private: struct ContextAndResolvedIsCascadingUse { @@ -203,12 +199,6 @@ namespace { public: // clang-format off - UnqualifiedLookupFactory(DeclName Name, - DeclContext *const DC, - SourceLoc Loc, - Options options, - UnqualifiedLookup &lookupToBeCreated); - UnqualifiedLookupFactory(DeclName Name, DeclContext *const DC, SourceLoc Loc, @@ -342,7 +332,7 @@ namespace { #pragma mark common helper declarations static NLOptions - computeBaseNLOptions(const UnqualifiedLookup::Options options, + computeBaseNLOptions(const UnqualifiedLookupOptions options, const bool isOriginallyTypeLookup); Optional getInitialIsCascadingUse() const { @@ -433,18 +423,6 @@ class ASTScopeDeclConsumerForUnqualifiedLookup #pragma mark UnqualifiedLookupFactory functions // clang-format off -UnqualifiedLookupFactory::UnqualifiedLookupFactory( - DeclName Name, - DeclContext *const DC, - SourceLoc Loc, - Options options, - UnqualifiedLookup &lookupToBeCreated) -: UnqualifiedLookupFactory(Name, DC, Loc, options, - lookupToBeCreated.Results, - lookupToBeCreated.IndexOfFirstOuterResult) - -{} - UnqualifiedLookupFactory::UnqualifiedLookupFactory( DeclName Name, DeclContext *const DC, @@ -1065,7 +1043,7 @@ void UnqualifiedLookupFactory::findResultsAndSaveUnavailables( NLOptions UnqualifiedLookupFactory::computeBaseNLOptions( - const UnqualifiedLookup::Options options, + const UnqualifiedLookupOptions options, const bool isOriginallyTypeLookup) { NLOptions baseNLOptions = NL_UnqualifiedDefault; if (options.contains(Flags::AllowProtocolMembers)) @@ -1209,37 +1187,21 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( return factory.isFirstResultEnough(); } - -#pragma mark UnqualifiedLookup functions - -// clang-format off -UnqualifiedLookup::UnqualifiedLookup(DeclName Name, - DeclContext *const DC, - SourceLoc Loc, - Options options) - // clang-format on - : IndexOfFirstOuterResult(0) { - - auto *stats = DC->getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumUnqualifiedLookup++; - - UnqualifiedLookupFactory factory(Name, DC, Loc, options, *this); - factory.performUnqualifiedLookup(); -} - llvm::Expected UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, DeclName name, DeclContext *dc, SourceLoc loc, UnqualifiedLookupFlags flags) const { - UnqualifiedLookup lookup(name, dc, loc, UnqualifiedLookup::Options(flags)); - return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); -} + auto *stats = dc->getASTContext().Stats; + if (stats) + stats->getFrontendCounters().NumUnqualifiedLookup++; -TypeDecl *UnqualifiedLookup::getSingleTypeResult() const { - if (Results.size() != 1) - return nullptr; - return dyn_cast(Results.back().getValueDecl()); + SmallVector results; + size_t indexOfFirstOuterResult = 0; + UnqualifiedLookupFactory factory(name, dc, loc, + UnqualifiedLookupOptions(flags), results, + indexOfFirstOuterResult); + factory.performUnqualifiedLookup(); + return LookupResult(results, indexOfFirstOuterResult); } #pragma mark debugging diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 331be6b2613ed..102996ef1aaa3 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -1182,9 +1182,9 @@ lookupTopDecl(Parser &P, DeclBaseName Name, bool typeLookup) { llvm::SaveAndRestore ASTStage(P.SF.ASTStage, SourceFile::Parsed); - UnqualifiedLookup::Options options; + UnqualifiedLookupOptions options; if (typeLookup) - options |= UnqualifiedLookup::Flags::TypeLookup; + options |= UnqualifiedLookupFlags::TypeLookup; auto &ctx = P.SF.getASTContext(); auto flags = UnqualifiedLookupFlags(options.toRaw()); diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 4f6501d4ac1a0..c8df79a5533fc 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -208,17 +208,17 @@ namespace { }; } // end anonymous namespace -static UnqualifiedLookup::Options +static UnqualifiedLookupOptions convertToUnqualifiedLookupOptions(NameLookupOptions options) { - UnqualifiedLookup::Options newOptions; + UnqualifiedLookupOptions newOptions; if (options.contains(NameLookupFlags::KnownPrivate)) - newOptions |= UnqualifiedLookup::Flags::KnownPrivate; + newOptions |= UnqualifiedLookupFlags::KnownPrivate; if (options.contains(NameLookupFlags::ProtocolMembers)) - newOptions |= UnqualifiedLookup::Flags::AllowProtocolMembers; + newOptions |= UnqualifiedLookupFlags::AllowProtocolMembers; if (options.contains(NameLookupFlags::IgnoreAccessControl)) - newOptions |= UnqualifiedLookup::Flags::IgnoreAccessControl; + newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) - newOptions |= UnqualifiedLookup::Flags::IncludeOuterResults; + newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; return newOptions; } @@ -262,7 +262,7 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, NameLookupOptions options) { auto &ctx = dc->getASTContext(); auto ulOptions = convertToUnqualifiedLookupOptions(options) | - UnqualifiedLookup::Flags::TypeLookup; + UnqualifiedLookupFlags::TypeLookup; { // Try lookup without ProtocolMembers first. ulOptions -= UnqualifiedLookupFlags::AllowProtocolMembers; From acbf0b264c3b561bc7a12abf5b780296bc173357 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 14 Nov 2019 11:27:47 -0800 Subject: [PATCH 221/283] Remove NumUnqualifiedLookup counter We now have an equivalent counter for UnqualifiedLookupRequest. --- include/swift/Basic/Statistics.def | 3 --- lib/AST/UnqualifiedLookup.cpp | 4 ---- 2 files changed, 7 deletions(-) diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index 134811bbbf39c..f6a074855e9e6 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -153,9 +153,6 @@ FRONTEND_STATISTIC(AST, NumLookupInModule) /// Number of local lookups into a module. FRONTEND_STATISTIC(AST, NumModuleLookupValue) -/// Number of unqualified lookups. -FRONTEND_STATISTIC(AST, NumUnqualifiedLookup) - /// Number of local lookups into a module's class members, for /// AnyObject lookup. FRONTEND_STATISTIC(AST, NumModuleLookupClassMember) diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index d8420ebbf5ef2..85aa84bc89593 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -1191,10 +1191,6 @@ llvm::Expected UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, DeclName name, DeclContext *dc, SourceLoc loc, UnqualifiedLookupFlags flags) const { - auto *stats = dc->getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumUnqualifiedLookup++; - SmallVector results; size_t indexOfFirstOuterResult = 0; UnqualifiedLookupFactory factory(name, dc, loc, From 25949c2903cf9955d39ca32cc43301b24a8d27d7 Mon Sep 17 00:00:00 2001 From: Dan Liew <36706441+danliew-apple@users.noreply.github.com> Date: Fri, 15 Nov 2019 14:26:41 -0800 Subject: [PATCH 222/283] [Sanitizer] Don't depend on symbolication in `asan_recover.swift`. (#28290) Relying on symbolication via `atos` unfortunately does not seem to be a good idea because there are several testing scenarios where this doesn't work and we fallback to `dladdr()` for Darwin platforms. To workaround this, the `asan_recover.swift` test has been changed to just test for the function name which covers the fully-symbolicated and partially symbolicated case (`dladdr()` fallback). To make sure that the two errors come from different locations (the original intention of examining source locations) the second error has been moved into a different function called `foo`. Unfortunately in some testing scenarios `foo` isn't always demangled correctly so we have to make the FileCheck pattern quite liberal. rdar://problem/57198494 --- test/Sanitizers/asan_recover.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/Sanitizers/asan_recover.swift b/test/Sanitizers/asan_recover.swift index 4853001268ad1..dbb8bd7662b68 100644 --- a/test/Sanitizers/asan_recover.swift +++ b/test/Sanitizers/asan_recover.swift @@ -42,6 +42,11 @@ fflush(stdout) let size:Int = 128; +func foo(_ rawptr:UnsafeMutablePointer) { + print("Read second element:\(rawptr.advanced(by: 1).pointee)") + fflush(stdout) +} + // In this test we need multiple issues to occur that ASan can detect. // Allocating a buffer and artificially poisoning it seems like the best way to // test this because there's no undefined behavior happening. Hopefully this @@ -77,18 +82,18 @@ __asan_poison_memory_region(UnsafeMutableRawPointer(x), size) // NOTE: Testing for stackframe `#0` should ensure that the poison read // happened in instrumentation and not in an interceptor. // CHECK-COMMON-STDERR: AddressSanitizer: use-after-poison -// CHECK-COMMON-STDERR: #0 0x{{.+}} in main {{.*}}asan_recover.swift:[[@LINE+1]] +// CHECK-COMMON-STDERR: #0 0x{{.+}} in main{{.*}} print("Read first element:\(x.advanced(by: 0).pointee)") fflush(stdout) // CHECK-RECOVER-STDOUT: Read first element:0 // Second error +// NOTE: Very loose regex is to accomodate if name demangling +// fails. rdar://problem/57235673 // CHECK-RECOVER-STDERR: AddressSanitizer: use-after-poison -// CHECK-RECOVER-STDERR: #0 0x{{.+}} in main {{.*}}asan_recover.swift:[[@LINE+1]] -print("Read second element:\(x.advanced(by: 1).pointee)") -fflush(stdout) +// CHECK-RECOVER-STDERR: #0 0x{{.+}} in {{.*}}foo{{.*}} // CHECK-RECOVER-STDOUT: Read second element:0 - +foo(x) __asan_unpoison_memory_region(UnsafeMutableRawPointer(x), size) x.deallocate(); From b8a1ecdb3ff38b33720ccc214f393d226a0eb551 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 15 Nov 2019 12:55:41 -0800 Subject: [PATCH 223/283] [stlextras] Add overloads for binary_search that take containers. --- include/swift/Basic/STLExtras.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index a0025dfb4b917..96a7c9e6a6334 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -639,12 +639,12 @@ inline T accumulate(const Container &C, T init, BinaryOperation op) { } template -inline bool binary_search(const Container &C, T value) { +inline bool binary_search(const Container &C, const T &value) { return std::binary_search(C.begin(), C.end(), value); } template -inline bool binary_search(const Container &C, T value, BinaryOperation op) { +inline bool binary_search(const Container &C, const T &value, BinaryOperation op) { return std::binary_search(C.begin(), C.end(), value, op); } From 95de4d2929bbcf6bce09a318cc2e1bb58e60e44b Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 15 Nov 2019 14:04:19 -0800 Subject: [PATCH 224/283] [pmo] Fix load [copy] like I fixed load_borrow. This involved fixing a few issues exposed by trying to use the load_borrow code with load [copy] that were caught in our tests by the ownership verifier. Specifically: 1. In getUnderlyingBorrowIntroducingValues we were first checking if a user was a guaranteed forwarding instruction and then doing a check if our value was a value with None ownership that we want to skip. This caused weird behavior where one could get the wrong borrow introducers if that trivial value came from a different guaranteed value. 2. I found via a test case in predictable_memaccess_opts that we needed to insert compensating destroys for phi nodes after we process all of the incoming values. The reason why this is needed is that multiple phi nodes could use the same incoming value. In such a case, we need to treat all of the copy_values inserted for each phi node as users of that incoming value to prevent us from inserting too many destroy_value. Consider: ``` bb0: %0 = copy_value cond_br ..., bb1, bb2 bb1: br bb3(%0) bb2: br bb4(%0) ``` If we processed the phi args in bb3, bb4 separately, we would insert an extra 2 destroys in bb1, bb2. The implementation works by processing each phi and for each incoming value of the phi appending the value, copy we made for the value to an array. We then stable sort the array only by value. This then allows us to process ranges of copies with the same underlying incoming value in a 2nd loop taking advantage of all of the copies for an incoming value being contiguous in said array. We still lifetime extend to the load/insert destroy_value for the actual phi (not its incoming values) in that first loop. 3. I tightened up the invariant in the AvailableValueAggregator that it always returns values that are newly copied unless we are performing a take. This involved changing the code in handlePrimitiveValues to always insert copies when ownership is enabled. 4. I tightened up the identification of intermediate phi nodes by instead of just checking if the phi node has a single terminator user to instead it having a single terminator user that has a successor with an inserted phi node that we know about. --- lib/SIL/OwnershipUtils.cpp | 14 +- .../Mandatory/PredictableMemOpt.cpp | 361 +++++----- .../predictable_memaccess_opts.sil | 621 +++++++++++++++++- .../predictable_memopt_ownership.sil | 59 +- 4 files changed, 867 insertions(+), 188 deletions(-) diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index bbb30c28a500d..feeab6da18e6f 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -163,6 +163,13 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } + // If v produces .none ownership, then we can ignore it. It is important + // that we put this before checking for guaranteed forwarding instructions, + // since we want to ignore guaranteed forwarding instructions that in this + // specific case produce a .none value. + if (v.getOwnershipKind() == ValueOwnershipKind::None) + continue; + // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isGuaranteedForwardingValue(v)) { @@ -173,10 +180,9 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } - // If v produces any ownership, then we can ignore it. Otherwise, we need to - // return false since this is an introducer we do not understand. - if (v.getOwnershipKind() != ValueOwnershipKind::None) - return false; + // Otherwise, this is an introducer we do not understand. Bail and return + // false. + return false; } return true; diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 9304d9bd786df..0e30b247206ec 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "predictable-memopt" #include "PMOMemoryUseCollector.h" +#include "swift/Basic/STLExtras.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/BranchPropagatedUser.h" #include "swift/SIL/OwnershipUtils.h" @@ -445,9 +446,6 @@ class AvailableValueAggregator { bool isTopLevel = true); bool canTake(SILType loadTy, unsigned firstElt) const; - SingleValueInstruction *addMissingDestroysForCopiedValues(LoadInst *li, - SILValue newVal); - void print(llvm::raw_ostream &os) const; void dump() const LLVM_ATTRIBUTE_USED; @@ -467,13 +465,19 @@ class AvailableValueAggregator { /// reference counts of the intermediate copies and phis to ensure that all /// forwarding operations in the CFG are strongly control equivalent (i.e. run /// the same number of times). - void fixupOwnership(LoadBorrowInst *lbi, SILValue newVal) { + void fixupOwnership(SILInstruction *load, SILValue newVal) { + assert(isa(load) || isa(load)); + + // Sort phi nodes so we can use it for bisection operations. + sort(insertedPhiNodes); + // Sort inserted insts so we can bisect upon it and mark copy_value as needing // to be skipped. sort(insertedInsts); + SmallBitVector instsToSkip(insertedInsts.size()); - addHandOffCopyDestroysForPhis(lbi, newVal, instsToSkip); - addMissingDestroysForCopiedValues(lbi, newVal, instsToSkip); + addHandOffCopyDestroysForPhis(load, newVal, instsToSkip); + addMissingDestroysForCopiedValues(load, newVal, instsToSkip); } private: @@ -490,8 +494,8 @@ class AvailableValueAggregator { /// If as a result of us copying values, we may have unconsumed destroys, find /// the appropriate location and place the values there. Only used when /// ownership is enabled. - void addMissingDestroysForCopiedValues(LoadBorrowInst *li, SILValue newVal, - const SmallBitVector &instsToSkip); + void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal, + const SmallBitVector &instsToSkip); /// As a result of us using the SSA updater, insert hand off copy/destroys at /// each phi and make sure that intermediate phis do not leak by inserting @@ -726,9 +730,15 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // Finally, grab the value from the SSA updater. SILValue result = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); - assert(result.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); - return result; + if (isTake() || !B.hasOwnership()) { + return result; + } + + // Be careful with this value and insert a copy in our load block to prevent + // any weird control equivalence issues. + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.emitCopyValueOperation(Loc, result); } SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, @@ -759,7 +769,7 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, // If we are going to use this to promote a borrowed value, insert borrow // operations. Eventually I am going to do this for everything, but this // should make it easier to bring up. - if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { + if (!isTake()) { for (unsigned i : indices(ResultElts)) { ResultElts[i] = B.emitBeginBorrowOperation(Loc, ResultElts[i]); } @@ -793,7 +803,7 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, firstElt += numSubElt; } - if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { + if (!isTake()) { for (unsigned i : indices(resultElts)) { resultElts[i] = B.emitBeginBorrowOperation(Loc, resultElts[i]); } @@ -840,7 +850,13 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, !builder.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - return eltVal; + + if (!builder.hasOwnership()) { + return eltVal; + } + + SILBuilderWithScope builder2(&*B.getInsertionPoint(), &insertedInsts); + return builder2.emitCopyValueOperation(Loc, eltVal); } // If we have an available value, then we want to extract the subelement from @@ -892,75 +908,50 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, assert(!B.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - return eltVal; + if (!B.hasOwnership()) + return eltVal; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.emitCopyValueOperation(Loc, eltVal); } -SingleValueInstruction * -AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li, - SILValue newVal) { - assert(B.hasOwnership() && - "We assume this is only called if we have ownership"); +namespace { - SmallPtrSet visitedBlocks; - SmallVector leakingBlocks; - bool foundLoop = false; - auto loc = RegularLocation::getAutoGeneratedLocation(); - while (!insertedInsts.empty()) { - auto *cvi = dyn_cast(insertedInsts.pop_back_val()); - if (!cvi) - continue; +struct PhiNodeCleanupState { + /// The incoming value that we need to cleanup. + SILValue incomingValue; - // Clear our state. - visitedBlocks.clear(); - leakingBlocks.clear(); - // The linear lifetime checker doesn't care if the passed in load is - // actually a user of our copy_value. What we care about is that the load is - // guaranteed to be in the block where we have reformed the tuple in a - // consuming manner. This means if we add it as the consuming use of the - // copy, we can find the leaking places if any exist. - // - // Then perform the linear lifetime check. If we succeed, continue. We have - // no further work to do. - auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; - LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); - auto error = checker.checkValue( - cvi, {BranchPropagatedUser(&li->getAllOperands()[0])}, {}, errorKind, - &leakingBlocks); - if (!error.getFoundError()) - continue; + /// The copy that we inserted right before the phi that will be fed into the + /// phi. + CopyValueInst *phiCopy; - // Ok, we found some leaking blocks. Since we are using the linear lifetime - // checker with memory, we do not have any guarantees that the store is out - // side of a loop and a load is in a loop. In such a case, we want to - // replace the load with a copy_value. - foundLoop |= error.getFoundOverConsume(); + PhiNodeCleanupState(SILValue incomingValue, CopyValueInst *phiCopy) + : incomingValue(incomingValue), phiCopy(phiCopy) {} - // Ok, we found some leaking blocks. Insert destroys at the - // beginning of these blocks for our copy_value. - for (auto *bb : leakingBlocks) { - SILBuilderWithScope b(bb->begin()); - b.emitDestroyValueOperation(loc, cvi); - } - } + /// If our incoming value is not defined in the block in our phi node's block, + /// return the insertion point to use to insert destroy_value for the incoming + /// value. Otherwise, return nullptr. + SILInstruction *getNonPhiBlockIncomingValueDef() const; +}; + +} // end anonymous namespace - // If we didn't find a loop, we are done, just return svi to get RAUWed. - if (!foundLoop) { - return li; +SILInstruction *PhiNodeCleanupState::getNonPhiBlockIncomingValueDef() const { + auto *phiBlock = phiCopy->getParent(); + if (phiBlock == incomingValue->getParentBlock()) { + return nullptr; } - // If we found a loop, then we know that our leaking blocks are the exiting - // blocks of the loop and the value has been lifetime extended over the loop. + if (auto *cvi = dyn_cast(incomingValue)) { + return cvi; + } - // If we have a load, we need to put in a copy so that the destroys within - // the loop are properly balanced. - newVal = SILBuilderWithScope(li).emitCopyValueOperation(loc, newVal); + assert(isa(incomingValue)); - li->replaceAllUsesWith(newVal); - SILValue addr = li->getOperand(); - li->eraseFromParent(); - if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); - return nullptr; + // Otherwise, our copy_value may not be post-dominated by our phi. To + // work around that, we need to insert destroys along the other + // paths. So set base to the first instruction in our argument's block, + // so we can insert destroys for our base. + return &*incomingValue->getParentBlock()->begin(); } void AvailableValueAggregator::addHandOffCopyDestroysForPhis( @@ -973,45 +964,72 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( SmallVector, 8> incomingValues; auto loc = RegularLocation::getAutoGeneratedLocation(); - LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); #ifndef NDEBUG + LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); for (auto *phi : insertedPhiNodes) { LLVM_DEBUG(llvm::dbgs() << "Phi: " << *phi); } #endif // Before we begin, identify the offset for all phis that are intermediate - // phis inserted by the SSA updater. + // phis inserted by the SSA updater. We are taking advantage of the fact that + // the SSA updater just constructs the web without knowledge of ownership. So + // if a phi node is only used by another phi node that we inserted, then we + // have an intermediate phi node. SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); for (unsigned i : indices(insertedPhiNodes)) { - if (insertedPhiNodes[i]->getSingleUserOfType()) { - intermediatePhiOffsets.set(i); + if (auto *termInst = insertedPhiNodes[i]->getSingleUserOfType()) { + // Only set the value if we find termInst has a successor with a phi node + // in our insertedPhiNodes. + for (auto succBBArgs : termInst->getSuccessorBlockArguments()) { + if (any_of(succBBArgs, [&](SILPhiArgument *arg) { + return binary_search(insertedPhiNodes, arg); + })) { + intermediatePhiOffsets.set(i); + break; + } + } } } // First go through all of our phi nodes doing the following: // // 1. If any of the phi node have a copy_value as an operand, we know that the - // copy_value does not dominate our final definition. In such a case since - // we may not have that the copy_value is post-dominated by the phi, we - // need to insert a copy_value at the phi to allow for post-domination and - // then use the ValueLifetimeChecker to determine the rest of the frontier - // for the value. + // copy_value does not dominate our final definition since otherwise the + // SSA updater would not have inserted a phi node here. In such a case + // since we may not have that the copy_value is post-dominated by the phi, + // we need to insert a copy_value at the phi to allow for post-domination + // and then use the ValueLifetimeChecker to determine the rest of the + // frontier for the base value. // // 2. If our phi node is used by another phi node, we run into a similar // problem where we could have that our original phi node does not dominate - // our final definition and may not be strongly control dependent on our - // phi. To work around this problem, we insert at the phi a copy_value to - // allow for the phi to post_dominate its copy and then extend the lifetime - // of the phied value over that copy. + // our final definition (since the SSA updater would not have inserted the + // phi) and may not be strongly control dependent on our phi. To work + // around this problem, we insert at the phi a copy_value to allow for the + // phi to post_dominate its copy and then extend the lifetime of the phied + // value over that copy. + // + // As an extra complication to this, when we insert compensating releases for + // any copy_values from (1), we need to insert the destroy_value on "base + // values" (either a copy_value or the first instruction of a phi argument's + // block) /after/ we have found all of the base_values to ensure that if the + // same base value is used by multiple phis, we do not insert too many destroy + // value. + // + // NOTE: At first glance one may think that such a problem could not occur + // with phi nodes as well. Sadly if we allow for double backedge loops, it is + // possible (there may be more cases). + llvm::SmallVector phiNodeCleanupState; + for (unsigned i : indices(insertedPhiNodes)) { - auto *phiArg = insertedPhiNodes[i]; + auto *phi = insertedPhiNodes[i]; - // If our phiArg is not owned, continue. No fixes are needed. - if (phiArg->getOwnershipKind() != ValueOwnershipKind::Owned) + // If our phi is not owned, continue. No fixes are needed. + if (phi->getOwnershipKind() != ValueOwnershipKind::Owned) continue; - LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phiArg); + LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phi); // Otherwise, we have a copy_value that may not be strongly control // equivalent with our phi node. In such a case, we need to use // ValueLifetimeAnalysis to lifetime extend the copy such that we can @@ -1021,8 +1039,8 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( leakingBlocks.clear(); incomingValues.clear(); - phiArg->getIncomingPhiValues(incomingValues); - unsigned phiIndex = phiArg->getIndex(); + phi->getIncomingPhiValues(incomingValues); + unsigned phiIndex = phi->getIndex(); for (auto pair : incomingValues) { SILValue value = pair.second; @@ -1055,54 +1073,13 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // that for our actual phi. auto *termInst = pair.first->getTerminator(); SILBuilderWithScope builder(termInst); - auto *phiCopy = builder.createCopyValue(loc, value); + CopyValueInst *phiCopy = builder.createCopyValue(loc, value); termInst->setOperand(phiIndex, phiCopy); - // Normalize on our base now that we have inserted the copy_value into the - // terminator block. If we have a copy_value, just use it directly as our - // base. We know it isn't in the block of our phiCopy due to a check - // above. - SILInstruction *base = nullptr; - if (auto *cvi = dyn_cast(value)) { - assert(cvi->getParent() != phiCopy->getParent() && - "Just to check invariant from above"); - base = cvi; - } else { - assert(isa(value)); - // If we have a phi argument and our incoming value block is the same as - // our phi block, we know that the copy_value we inserted will only be - // used by the phi. So insert a destroy_value in the incoming value - // block after the copy_value that we inserted and then continue. - if (pair.first == value->getParentBlock()) { - builder.createDestroyValue(loc, value); - continue; - } - - // Otherwise, our copy_value may not be post-dominated by our phi. To - // work around that, we need to insert destroys along the other - // paths. So set base to the first instruction in our argument's block, - // so we can insert destroys for our base. - base = &*value->getParentBlock()->begin(); - } - assert(base && "Should have been assigned"); - - // Then lifetime extend our base over the copy_value. - assert(lifetimeFrontier.empty()); - ValueLifetimeAnalysis analysis(base, phiCopy); - bool foundCriticalEdges = !analysis.computeFrontier( - lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, - &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - - while (!lifetimeFrontier.empty()) { - auto *insertPoint = lifetimeFrontier.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createDestroyValue(loc, value); - } - - visitedBlocks.clear(); - leakingBlocks.clear(); + // Now that we know our base, phi, phiCopy for this specific incoming + // value, append it to the phiNodeClenaupState so we can insert + // destroy_values late after we visit all insertedPhiNodes. + phiNodeCleanupState.emplace_back(value, phiCopy); } // Then see if our phi is an intermediate phi. If it is an intermediate phi, @@ -1131,8 +1108,8 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - phiArg, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, - errorKind, &leakingBlocks); + phi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, + &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly @@ -1140,7 +1117,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // for the copy_value. auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation(next->getLoc(), phiArg); + builder.emitDestroyValueOperation(next->getLoc(), phi); continue; } @@ -1151,23 +1128,96 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( if (!error.getFoundOverConsume()) { auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation(next->getLoc(), phiArg); + builder.emitDestroyValueOperation(next->getLoc(), phi); } // Ok, we found some leaking blocks. Insert destroys at the beginning of // these blocks for our copy_value. for (auto *bb : leakingBlocks) { SILBuilderWithScope b(bb->begin()); - b.emitDestroyValueOperation(loc, phiArg); + b.emitDestroyValueOperation(loc, phi); + } + } + + // At this point, we have visited all of our phis, lifetime extended them to + // the the load block, and inserted phi copies at all of our intermediate phi + // nodes. Now we need to cleanup and insert all of the compensating + // destroy_value that we need. We do this by sorting our phiNodeCleanupState + // just by baseValue. This will ensure that all values with the same base + // value are able to have all of their phiCopies passed at the same time to + // the ValueLifetimeAnalysis. + stable_sort(phiNodeCleanupState, [](const PhiNodeCleanupState &lhs, + const PhiNodeCleanupState &rhs) { + return lhs.incomingValue < rhs.incomingValue; + }); + + for (auto ii = phiNodeCleanupState.begin(), ie = phiNodeCleanupState.end(); + ii != ie;) { + SILValue incomingValue = ii->incomingValue; + + // First find the end of the values for which ii does not equal baseValue. + auto rangeEnd = std::find_if_not( + std::next(ii), ie, [&](const PhiNodeCleanupState &next) { + return incomingValue == next.incomingValue; + }); + + SWIFT_DEFER { + // Once we have finished processing, set ii to rangeEnd. This ensures that + // the code below does not need to worry about updating the iterator. + ii = rangeEnd; + }; + + // Before we do anything, see if we have a single cleanup state. In such a + // case, we could have that we have a phi node as an incoming value and a + // copy_value in that same block. In such a case, we want to just insert the + // copy and continue. This means that + // cleanupState.getNonPhiBlockIncomingValueDef() should always return a + // non-null value in the code below. + if (std::next(ii) == rangeEnd && isa(ii->incomingValue)) { + auto *insertPt = ii->getNonPhiBlockIncomingValueDef(); + if (!insertPt) { + CopyValueInst *phiCopy = ii->phiCopy; + SILBasicBlock *phiBlock = phiCopy->getParent(); + SILBuilderWithScope builder(phiBlock->getTerminator()); + builder.createDestroyValue(loc, incomingValue); + continue; + } + } + + // Otherwise, we know that we have for this incomingValue, multiple + // potential insert pts that we need to handle at the same time with our + // lifetime query. Gather up those uses. + SmallVector users; + transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users), + [](const PhiNodeCleanupState &value) { return value.phiCopy; }); + + // Then lifetime extend our base over the copy_value. + assert(lifetimeFrontier.empty()); + auto *def = ii->getNonPhiBlockIncomingValueDef(); + assert(def && "Should never have a nullptr here since we handled all of " + "the single block cases earlier"); + ValueLifetimeAnalysis analysis(def, users); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createDestroyValue(loc, incomingValue); } + + visitedBlocks.clear(); + leakingBlocks.clear(); } + // Clear the phi node array now that we are done. insertedPhiNodes.clear(); } void AvailableValueAggregator::addMissingDestroysForCopiedValues( - LoadBorrowInst *lbi, SILValue newVal, - const SmallBitVector &instsToSkip) { + SILInstruction *load, SILValue newVal, const SmallBitVector &instsToSkip) { assert(B.hasOwnership() && "We assume this is only called if we have ownership"); @@ -1188,8 +1238,8 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // begin_borrow. if (auto *li = dyn_cast(insertedInsts[i])) { if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { - assert(li->getParent() == lbi->getParent()); - auto next = std::next(lbi->getIterator()); + assert(li->getParent() == load->getParent()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), li); continue; @@ -1217,14 +1267,14 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - cvi, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, errorKind, + cvi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly // control equivalent as our load_borrow. So just insert a destroy_value // for the copy_value. - auto next = std::next(lbi->getIterator()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), cvi); continue; @@ -1235,7 +1285,7 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // this if we found a loop since our leaking blocks will lifetime extend the // value over the loop. if (!error.getFoundOverConsume()) { - auto next = std::next(lbi->getIterator()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), cvi); } @@ -2031,16 +2081,15 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { // blocks that we may have can be found by performing a linear lifetime check // over all copies that we found using the load as the "consuming uses" (just // for the purposes of identifying the consuming block). - auto *oldLoad = agg.addMissingDestroysForCopiedValues(li, newVal); + agg.fixupOwnership(li, newVal); - // If we are returned the load, eliminate it. Otherwise, it was already - // handled for us... so return true. - if (!oldLoad) - return true; + // Now that we have fixed up all of our missing destroys, insert the copy + // value for our actual load and RAUW. + newVal = SILBuilderWithScope(li).emitCopyValueOperation(li->getLoc(), newVal); - oldLoad->replaceAllUsesWith(newVal); - SILValue addr = oldLoad->getOperand(0); - oldLoad->eraseFromParent(); + li->replaceAllUsesWith(newVal); + SILValue addr = li->getOperand(); + li->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) recursivelyDeleteTriviallyDeadInstructions(addrI); return true; diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index 192723cfbd731..8fa9df6db1b1e 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -60,9 +60,11 @@ bb0(%0 : $Builtin.Int32): // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] +// CHECK: destroy_value [[ARG_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG_COPY]] +// CHECK: return [[ARG_COPY_2]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion' sil [ossa] @simple_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -83,10 +85,14 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY]] +// CHECK: [[ARG2_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_BORROW:%.*]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion' sil [ossa] @struct_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -144,9 +150,11 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // CHECK: br bb3([[ARG_COPY]] : // // CHECK: bb3([[RESULT:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion_multi_insertpt' sil [ossa] @simple_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -192,10 +200,16 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] +// CHECK: [[ARG2_COPY_COPY:%.*]] = copy_value [[ARG2_COPY]] +// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] +// CHECK: [[ARG2_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -306,12 +320,17 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] // CHECK: [[SECOND_ADDR:%.*]] = struct_element_addr [[STACK]] // CHECK: [[SECOND_VAL_COPY:%.*]] = load [copy] [[SECOND_ADDR]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] +// CHECK: [[SECOND_VAL_COPY_BORROW:%.*]] = begin_borrow [[SECOND_VAL_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY_BORROW]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %arg2 : @owned $Builtin.NativeObject): @@ -413,9 +432,11 @@ bb3: // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] +// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD]] +// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] // CHECK: } // end sil function 'simple_partialstructuse_load_promotion' sil [ossa] @simple_partialstructuse_load_promotion : $@convention(thin) (@owned NativeObjectPair) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectPair): @@ -437,9 +458,11 @@ bb0(%0 : @owned $NativeObjectPair): // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD_2]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] +// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD]] +// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] // CHECK: } // end sil function 'simple_partialtupleuse_load_promotion' sil [ossa] @simple_partialtupleuse_load_promotion : $@convention(thin) (@owned NativeObjectAndTuple) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectAndTuple): @@ -459,9 +482,10 @@ bb0(%0 : @owned $NativeObjectAndTuple): // CHECK: store [[ARG0]] to [init] [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [assign] [[STACK]] +// CHECK: [[ARG1_COPY_1:%.*]] = copy_value [[ARG1_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG1_COPY]] +// CHECK: return [[ARG1_COPY_1]] // CHECK: } // end sil function 'simple_assignstore' sil [ossa] @simple_assignstore : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -488,11 +512,15 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: br bb3([[LHS2_COPY]] : +// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] +// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] +// CHECK: br bb3([[LHS2_COPY_2]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] -// CHECK: br bb3([[LHS1_COPY]] : +// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] +// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] +// CHECK: br bb3([[LHS1_COPY_2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_addr [[STACK]] @@ -1340,3 +1368,570 @@ bbEnd: return %9999 : $() } +//--- + +// CHECK-LABEL: sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'load_copy_promote_with_loop_1' +sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { +bb0(%0 : @owned $NativeObjectPair): + %1 = alloc_stack $NativeObjectPair + store %0 to [init] %1 : $*NativeObjectPair + %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + br bb2 + +bb2: + %3 = load [copy] %2 : $*Builtin.NativeObject + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + br bb2 +} + +// CHECK-LABEL: sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_promote_with_loop_2' +sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { +bb0(%0 : @owned $NativeObjectPair): + %1 = alloc_stack $NativeObjectPair + store %0 to [init] %1 : $*NativeObjectPair + %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + br bb2 + +bb2: + %3 = load [copy] %2 : $*Builtin.NativeObject + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + cond_br undef, bb3, bb4 + +bb3: + br bb2 + +bb4: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_promote_two_backedge_loop' +sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + br bb1 + +bb1: + br bb2 + +bb2: + cond_br undef, bb3, bb4 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + destroy_value %2 : $Builtin.NativeObject + cond_br undef, bb5, bb6 + +bb4: + %3 = load [copy] %1 : $*Builtin.NativeObject + destroy_value %3 : $Builtin.NativeObject + cond_br undef, bb7, bb8 + +bb5: + br bb2 + +bb6: + br bb9 + +bb7: + br bb2 + +bb8: + br bb9 + +bb9: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK: bb0( +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_reload' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0c to [init] %1b : $*Builtin.NativeObject + destroy_value %0b : $Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + destroy_value %0c : $Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + %0bhat = copy_value %0b : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1b : $*Builtin.NativeObject + %0bhat2 = copy_value %0bhat : $Builtin.NativeObject + store %0bhat2 to [init] %1b : $*Builtin.NativeObject + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_value %0bhat : $Builtin.NativeObject + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy' +sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_2' +sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_3' +sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + %1 = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) + %1a = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 0 + %1b = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 1 + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load [copy] %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + dealloc_stack %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_4' +sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent' +sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8 + +bb6: + br bb8 + +bb7: + br bbPreLoopHeader + +bb8: + br bbPreLoopHeader + +bbPreLoopHeader: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load [copy] %0 : $*Builtin.NativeObject + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// In this case, we will have that we need to separately lifetime extend our phi +// node's copy to prevent leaks along the edge skipping the loop. +// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent_2' +sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8a + +bb6: + br bb8a + +bb7: + br bbPreLoopHeader + +bb8a: + br bb8 + +bb8: + cond_br undef, bbPreLoopHeader1, bbSkipLoop + +bbPreLoopHeader: + br bbLoop + +bbPreLoopHeader1: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load [copy] %0 : $*Builtin.NativeObject + br bbLoop6 + +bbLoop6: + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbLoop5 + +bbLoop5: + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbEnd + +bbSkipLoop: + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index ebb893ac4b803..e18ace13123ac 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -211,8 +211,12 @@ bb0(%0 : @owned $ContainsNativeObject): // CHECK: [[f3:%.*]] = struct_extract [[BORROWED_ARG]] : $ComplexStruct, #ComplexStruct.f1 // CHECK: [[f3_copy:%.*]] = copy_value [[f3]] // CHECK: end_borrow [[BORROWED_ARG]] +// CHECK: [[f3_copy_1:%.*]] = copy_value [[f3_copy]] +// CHECK: [[f3_copy_2:%.*]] = copy_value [[f3_copy_1]] +// CHECK: [[f2_x_copy_1:%.*]] = copy_value [[f2_x_copy]] +// CHECK: [[f2_x_copy_2:%.*]] = copy_value [[f2_x_copy_1]] // CHECK: destroy_value [[ARG]] -// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy]] : $Builtin.NativeObject, [[f2_x_copy]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) +// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy_2]] : $Builtin.NativeObject, [[f2_x_copy_2]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) // CHECK: return [[RESULT]] // CHECK: } // end sil function 'multiple_level_extract_2' sil [ossa] @multiple_level_extract_2 : $@convention(thin) (@owned ComplexStruct) -> (@owned Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32) { @@ -559,11 +563,15 @@ bb3: // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: br bb3([[LHS2_COPY]] : +// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] +// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] +// CHECK: br bb3([[LHS2_COPY_2]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] : $Builtin.NativeObject -// CHECK: br bb3([[LHS1_COPY]] : +// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] +// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] +// CHECK: br bb3([[LHS1_COPY_2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_value [[ARG]] @@ -649,9 +657,11 @@ struct NativeObjectTriple { // CHECK-NEXT: br bb3([[PAIR_LHS_COPY]] : // // CHECK: bb3([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK-NEXT: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) -// CHECK-NEXT: destroy_value [[REFORMED]] -// CHECK-NEXT: return [[PHI]] +// CHECK: [[PHI_COPY_1:%.*]] = copy_value [[PHI]] +// CHECK: [[PHI_COPY_2:%.*]] = copy_value [[PHI_COPY_1]] +// CHECK: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) +// CHECK: destroy_value [[REFORMED]] +// CHECK: return [[PHI_COPY_2]] // CHECK: } // end sil function 'diamond_test_4' sil [ossa] @diamond_test_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair): @@ -710,9 +720,14 @@ bb3: // CHECK: bb4: // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL]] : {{.*}}) +// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] +// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_RHS_VAL_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL_COPY_BORROW]] : {{.*}}) +// CHECK: [[STRUCT_COPY:%.*]] = copy_value [[STRUCT]] +// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY]] // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT]] +// CHECK: return [[STRUCT_COPY_2]] // CHECK: } // end sil function 'diamond_test_5' sil [ossa] @diamond_test_5 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): @@ -758,10 +773,14 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[FALSE_BB]]: // CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 @@ -774,10 +793,14 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[SUCC_2]]([[PHI1:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 @@ -786,15 +809,21 @@ bb4: // CHECK: br [[EXIT_BB:bb[0-9]+]]([[PHI1:%.*]] : $Builtin.NativeObject) // // CHECK: [[SUCC_1]]([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: br [[EXIT_BB]]([[PHI]] : {{.*}}) +// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] +// CHECK: br [[EXIT_BB]]([[PHI_COPY]] : {{.*}}) // // CHECK: [[EXIT_BB]]([[PHI:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[PHI]] : {{.*}}) +// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] +// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] +// CHECK: [[PHI_COPY_BORROW:%.*]] = begin_borrow [[PHI_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[PHI_COPY_BORROW]] : {{.*}}) +// CHECK: [[STRUCT_COPY_1:%.*]] = copy_value [[STRUCT]] +// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY_1]] // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT]] +// CHECK: return [[STRUCT_COPY_2]] // CHECK: } // end sil function 'diamond_test_6' sil [ossa] @diamond_test_6 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): From a1a891e47aed49aa41d821634eeae89c4117ace0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Wed, 13 Nov 2019 17:45:38 -0800 Subject: [PATCH 225/283] [windows] Use the temporal directory for root of VFS tests. The VFS tests were using Unix absolute paths, which does not play well when Windows see them as relative to the current drive letter. By using the temporal directory, both Windows and Unix can use the same paths and avoid the problem. Additionally, a couple of inputs have to be transformed into the native path format, because sourcekitd-test compares the inputs as strings, and they need to match exactly. So the source file and the name of the VFS entries are transformed into native using the helper from LLVM support. --- .../SourceKit/CodeComplete/injected_vfs.swift | 6 ++-- .../injected_vfs_complete_open.swift | 22 +++++++-------- .../injected_vfs_swiftinterface.swift | 2 +- test/SourceKit/CursorInfo/injected_vfs.swift | 22 +++++++-------- test/SourceKit/InterfaceGen/gen_stdlib.swift | 2 +- test/SourceKit/Sema/injected_vfs.swift | 28 +++++++++---------- .../Sema/injected_vfs_after_edit.swift | 2 +- .../Sema/injected_vfs_sourcetext.swift | 4 +-- .../lib/SwiftLang/SwiftASTManager.cpp | 1 + .../tools/sourcekitd-test/TestOptions.cpp | 5 +++- .../tools/sourcekitd-test/sourcekitd-test.cpp | 3 ++ .../SourceKit/SwiftLang/CursorInfoTest.cpp | 16 +++++------ unittests/SourceKit/SwiftLang/EditingTest.cpp | 4 +-- 13 files changed, 62 insertions(+), 55 deletions(-) diff --git a/test/SourceKit/CodeComplete/injected_vfs.swift b/test/SourceKit/CodeComplete/injected_vfs.swift index e4b5f71ff45ed..b6eb657337790 100644 --- a/test/SourceKit/CodeComplete/injected_vfs.swift +++ b/test/SourceKit/CodeComplete/injected_vfs.swift @@ -19,9 +19,9 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=complete -pos=9:27 -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s -// RUN: %sourcekitd-test -req=complete -pos=10:31 -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s -// RUN: %sourcekitd-test -req=complete -pos=11:30 -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s +// RUN: %sourcekitd-test -req=complete -pos=9:27 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: %sourcekitd-test -req=complete -pos=10:31 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s +// RUN: %sourcekitd-test -req=complete -pos=11:30 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s // RUN: not %sourcekitd-test -req=complete -vfs-name nope %s -pass-as-sourcetext -dont-print-request -pos=9:27 2>&1 | %FileCheck %s -check-prefix=NONEXISTENT_VFS_ERROR // NONEXISTENT_VFS_ERROR: error response (Request Failed): unknown virtual filesystem 'nope' diff --git a/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift b/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift index 9f6df7bd69ffa..167fea6c7855e 100644 --- a/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift +++ b/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift @@ -19,32 +19,32 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=complete.open -pos=9:28 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s -// RUN: %sourcekitd-test -req=complete.open -pos=10:32 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s -// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s +// RUN: %sourcekitd-test -req=complete.open -pos=9:28 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: %sourcekitd-test -req=complete.open -pos=10:32 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s +// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s // RUN: not %sourcekitd-test -req=complete.open -vfs-name nope %s -pass-as-sourcetext -dont-print-request -pos=9:27 2>&1 | %FileCheck %s -check-prefix=NONEXISTENT_VFS_ERROR // NONEXISTENT_VFS_ERROR: error response (Request Failed): unknown virtual filesystem 'nope' -// RUN: not %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ -// RUN: == -req=complete.update -pos=11:31 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift 2>&1 | %FileCheck --check-prefix=UNSUPPORTED_REQ %s +// RUN: not %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ +// RUN: == -req=complete.update -pos=11:31 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift 2>&1 | %FileCheck --check-prefix=UNSUPPORTED_REQ %s // UNSUPPORTED_REQ: error response (Request Invalid): This request does not support custom filesystems -// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=11:31 %s | %FileCheck --check-prefix=CHECK-SAMETARGET %s -// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=11:31 -req-opts=filtertext=method %s | %FileCheck --check-prefix=CHECK-SAMETARGET %s // Inner completion. -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInSameTarget -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_SAMETARGET %s -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInSameTarget -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_SAMETARGET %s +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=9:1 -req-opts=filtertext=StructDefinedInSameTarget %s | %FileCheck --check-prefix=INNER_SAMETARGET %s // INNER_SAMETARGET: key.name: "StructDefinedInSameTarget" // INNER_SAMETARGET: key.name: "StructDefinedInSameTarget." -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInCModule -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_CMODULE %s -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInCModule -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_CMODULE %s +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=9:1 -req-opts=filtertext=StructDefinedInCModule %s | %FileCheck --check-prefix=INNER_CMODULE %s // INNER_CMODULE: key.name: "StructDefinedInCModule" // INNER_CMODULE: key.name: "StructDefinedInCModule." diff --git a/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift b/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift index 572de65437b03..1088f98e3a45d 100644 --- a/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift +++ b/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift @@ -10,4 +10,4 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module-interface-path %t/SwiftModule.swiftinterface -module-name SwiftModule -emit-module -o /dev/null %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=complete -pos=6:31 -vfs-files=/target_file1.swift=%s,/SwiftModule/SwiftModule.swiftinterface=%t/SwiftModule.swiftinterface /target_file1.swift -pass-as-sourcetext -- /target_file1.swift -I /SwiftModule -target %target-triple | %FileCheck %s +// RUN: %sourcekitd-test -req=complete -pos=6:31 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/SwiftModule/SwiftModule.swiftinterface=%t/SwiftModule.swiftinterface %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift -I %t/VFS/SwiftModule -target %target-triple | %FileCheck %s diff --git a/test/SourceKit/CursorInfo/injected_vfs.swift b/test/SourceKit/CursorInfo/injected_vfs.swift index fd9348ee46082..44e3a461c9c50 100644 --- a/test/SourceKit/CursorInfo/injected_vfs.swift +++ b/test/SourceKit/CursorInfo/injected_vfs.swift @@ -10,7 +10,7 @@ func foo( // CHECK-CMODULE: key.kind: source.lang.swift.ref.struct // CHECK-CMODULE: key.name: "StructDefinedInCModule" -// CHECK-CMODULE: key.filepath: "/CModule/CModule.h" +// CHECK-CMODULE: key.filepath: "{{.*}}/CModule{{/|\\\\}}CModule.h" // CHECK-SWIFTMODULE-REF: key.kind: source.lang.swift.ref.struct // CHECK-SWIFTMODULE-REF: key.name: "StructDefinedInSwiftModule" @@ -32,28 +32,28 @@ func foo( // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift // == CursorInfo works for struct defined in CModule == -// RUN: %sourcekitd-test -req=cursor -pos=5:43 -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: %sourcekitd-test -req=cursor -pos=5:43 -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s // USR test intentionally omitted for CModule, because SourceKit does not support clang USRs. // == CursorInfo works for struct defined in SwiftModule == -// RUN: %sourcekitd-test -req=cursor -pos=6:43 -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-REF %s -// RUN: %sourcekitd-test -req=cursor -usr "s:11SwiftModule015StructDefinedInaB0V" -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=6:43 -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-REF %s +// RUN: %sourcekitd-test -req=cursor -usr "s:11SwiftModule015StructDefinedInaB0V" -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-DECL %s // == CursorInfo works for struct defined in same target as primary file == -// RUN: %sourcekitd-test -req=cursor -pos=7:43 -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-REF %s -// RUN: %sourcekitd-test -req=cursor -usr "s:4main25StructDefinedInSameTargetV" -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=7:43 -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-REF %s +// RUN: %sourcekitd-test -req=cursor -usr "s:4main25StructDefinedInSameTargetV" -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-DECL %s // == Using an open document == // RUN: %sourcekitd-test \ -// RUN: -req=open -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: -req=open -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s // == Using an open document without semantic info == // RUN: %sourcekitd-test \ -// RUN: -req=syntax-map -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext == \ -// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: -req=syntax-map -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext == \ +// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s // == Overriding an open document == // RUN: %sourcekitd-test \ // RUN: -req=syntax-map %s -pass-as-sourcetext == \ -// RUN: -req=cursor -pos=5:43 %s -print-raw-response -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: -req=cursor -pos=5:43 %s -print-raw-response -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s diff --git a/test/SourceKit/InterfaceGen/gen_stdlib.swift b/test/SourceKit/InterfaceGen/gen_stdlib.swift index e8adf40a43696..b4a8320608cce 100644 --- a/test/SourceKit/InterfaceGen/gen_stdlib.swift +++ b/test/SourceKit/InterfaceGen/gen_stdlib.swift @@ -36,7 +36,7 @@ var x: Int // CHECK1-NEXT: $s // CHECK1-NEXT: Swift{{$}} // CHECK1-NEXT: Math/Integers -// CHECK1-NEXT: /{{$}} +// CHECK1-NEXT: {{[A-Za-z]:\\|/}}{{$}} // CHECK1-NEXT: SYSTEM // CHECK1-NEXT: @frozen struct Int : FixedWidthInteger{{.*}}SignedInteger{{.*}} diff --git a/test/SourceKit/Sema/injected_vfs.swift b/test/SourceKit/Sema/injected_vfs.swift index af9d3cecd1970..b3254c41f0992 100644 --- a/test/SourceKit/Sema/injected_vfs.swift +++ b/test/SourceKit/Sema/injected_vfs.swift @@ -17,32 +17,32 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift | %FileCheck %s +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift | %FileCheck %s -// RUN: not %sourcekitd-test -req=syntax-map -vfs-files=/target_file1.swift=%s /target_file1.swift -dont-print-request 2>&1 | %FileCheck %s -check-prefix=SOURCEFILE_ERROR +// RUN: not %sourcekitd-test -req=syntax-map -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift -dont-print-request 2>&1 | %FileCheck %s -check-prefix=SOURCEFILE_ERROR // SOURCEFILE_ERROR: error response (Request Failed): using 'key.sourcefile' to read source text from the filesystem // RUN: not %sourcekitd-test -req=syntax-map -vfs-name nope %s -pass-as-sourcetext -dont-print-request 2>&1 | %FileCheck %s -check-prefix=NONEXISTENT_VFS_ERROR // NONEXISTENT_VFS_ERROR: error response (Request Failed): unknown virtual filesystem 'nope' // == Close the document and reopen with a new VFS (modules) == -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=close -name /target_file1.swift == \ -// RUN: -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s /target_file1.swift /target_file1.swift | %FileCheck %s -check-prefix=NO_MODULES_VFS +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=close -name %t/VFS/target_file1.swift == \ +// RUN: -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift %t/VFS/target_file1.swift | %FileCheck %s -check-prefix=NO_MODULES_VFS // NO_MODULES_VFS: no such module 'CModule' // == Close the document and reopen with a new VFS (inputs) == -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=close -name /target_file1.swift == \ -// RUN: -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s /target_file1.swift /target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=close -name %t/VFS/target_file1.swift == \ +// RUN: -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift %t/VFS/target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD // TARGET_FILE_2_MOD: cannot convert value of type 'Void' to specified type 'String' // TARGET_FILE_2_MOD: cannot convert value of type '()' to specified type 'Float' // TARGET_FILE_2_MOD: cannot convert value of type 'Int' to specified type 'Double' // == Reopen with a new VFS without closing == -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s /target_file1.swift /target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift %t/VFS/target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD diff --git a/test/SourceKit/Sema/injected_vfs_after_edit.swift b/test/SourceKit/Sema/injected_vfs_after_edit.swift index 2c004254f0921..a233e125091e4 100644 --- a/test/SourceKit/Sema/injected_vfs_after_edit.swift +++ b/test/SourceKit/Sema/injected_vfs_after_edit.swift @@ -4,7 +4,7 @@ func foo(_ structDefinedInSameTarget: StructDefinedInSameTarget) { // CHECK: cannot convert value of type '()' to specified type 'Int' } -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift %s -pass-as-sourcetext -- %s /target_file2.swift -target %target-triple == \ +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -target %target-triple == \ // RUN: -req=print-diags %s == \ // RUN: -req=edit %s -pos=2:12 -length=6 -replace='Int' == \ // RUN: -req=print-diags %s | %FileCheck %s diff --git a/test/SourceKit/Sema/injected_vfs_sourcetext.swift b/test/SourceKit/Sema/injected_vfs_sourcetext.swift index 66161c8db5ba1..6ef1e46a82dfa 100644 --- a/test/SourceKit/Sema/injected_vfs_sourcetext.swift +++ b/test/SourceKit/Sema/injected_vfs_sourcetext.swift @@ -3,5 +3,5 @@ func foo(_ structDefinedInSameTarget: StructDefinedInSameTarget) { // CHECK: cannot convert value of type '()' to specified type 'Double' } -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift /target_file1.swift | %FileCheck %s +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift %t/VFS/target_file1.swift | %FileCheck %s diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index dc58ad2fabc31..58663bd93b6a1 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -439,6 +439,7 @@ static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( llvm::SmallString<128> newFilename; if (auto err = FileSystem->getRealPath(input.file(), newFilename)) newFilename = input.file(); + llvm::sys::path::native(newFilename); bool newIsPrimary = input.isPrimary() || (!PrimaryFile.empty() && PrimaryFile == newFilename); if (newIsPrimary) { diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index be2d900e842a6..1ecae811b9f96 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -16,6 +16,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace sourcekitd_test; @@ -358,8 +359,10 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { for (const char *vfsFile : InputArg->getValues()) { StringRef name, target; std::tie(name, target) = StringRef(vfsFile).split('='); + llvm::SmallString<64> nativeName; + llvm::sys::path::native(name, nativeName); bool passAsSourceText = target.consume_front("@"); - VFSFiles.try_emplace(name, VFSFile(target.str(), passAsSourceText)); + VFSFiles.try_emplace(nativeName.str(), VFSFile(target.str(), passAsSourceText)); } break; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 1e565e54a553c..23ce67ffce814 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Threading.h" @@ -283,6 +284,7 @@ static inline std::string getInterfaceGenDocumentName() { // "Absolute path" on all platforms since handleTestInvocation will attempt to make this absolute llvm::SmallString<64> path = llvm::StringRef("/"); llvm::sys::fs::make_absolute(path); + llvm::sys::path::native(path); return path.str(); } @@ -446,6 +448,7 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { llvm::SmallString<64> AbsSourceFile; AbsSourceFile += SourceFile; llvm::sys::fs::make_absolute(AbsSourceFile); + llvm::sys::path::native(AbsSourceFile); SourceFile = AbsSourceFile.str(); } std::string SemaName = !Opts.Name.empty() ? Opts.Name : SourceFile; diff --git a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp index 004b9ad94e4c4..193c8fcd4fad9 100644 --- a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp +++ b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp @@ -189,10 +189,10 @@ class CursorInfoTest : public ::testing::Test { } // anonymous namespace TEST_F(CursorInfoTest, FileNotExist) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let foo = 0\n"; - const char *Args[] = { "/" }; + const char *Args[] = { "" }; open(DocName, Contents); auto FooOffs = findOffset("foo =", Contents); @@ -205,7 +205,7 @@ static const char *ExpensiveInit = "[0:0,0:0,0:0,0:0,0:0,0:0,0:0]"; TEST_F(CursorInfoTest, EditAfter) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -240,7 +240,7 @@ TEST_F(CursorInfoTest, EditAfter) { } TEST_F(CursorInfoTest, EditBefore) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let foo = 0\n" "let value = foo;\n"; @@ -277,7 +277,7 @@ TEST_F(CursorInfoTest, EditBefore) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -307,7 +307,7 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -337,7 +337,7 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -368,7 +368,7 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; const char *Args[] = {"-parse-as-library"}; diff --git a/unittests/SourceKit/SwiftLang/EditingTest.cpp b/unittests/SourceKit/SwiftLang/EditingTest.cpp index 85b62da5f2487..b383a133982bf 100644 --- a/unittests/SourceKit/SwiftLang/EditingTest.cpp +++ b/unittests/SourceKit/SwiftLang/EditingTest.cpp @@ -197,7 +197,7 @@ static UIdent SemaDiagStage("source.diagnostic.stage.swift.sema"); static UIdent ParseDiagStage("source.diagnostic.stage.swift.parse"); TEST_F(EditTest, DiagsAfterEdit) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "func foo {}\n" "let v = 0\n"; @@ -234,7 +234,7 @@ TEST_F(EditTest, DiagsAfterEdit) { void EditTest::doubleOpenWithDelay(std::chrono::microseconds delay, bool closeDoc) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "func foo() { _ = unknown_name }\n"; const char *Args[] = { "-parse-as-library" }; From 2978884fc0e375d3252f4dd1cb5df70a4b1c9618 Mon Sep 17 00:00:00 2001 From: Alex Langford Date: Fri, 15 Nov 2019 15:30:00 -0800 Subject: [PATCH 226/283] [build] Refer to projects through the monorepo directly Why am I doing this? Because we have now switched to the monorepo, we no longer need to have logic to detect whether or not a project is present. Any project under the llvm project is guaranteed to be available if you're building llvm. I hope this is the first change of many that can clean up some of the logic in build-script-impl. Some simplifications and cleanups that I think can be made include: - Adding the option to build swift as a part of the llvm build. (Or even making this the default behavior) - Taking advantage of `LLVM_ENABLE_PROJECTS` to simplify some of the logic to set up clang, clang-tools-extra, and other projects. --- utils/build-script-impl | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 569ec7780d04d..f283f09086174 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1166,9 +1166,9 @@ function should_build_stdlib_target() { # NINJA_SOURCE_DIR="${WORKSPACE}/ninja" SWIFT_SOURCE_DIR="${WORKSPACE}/swift" -LLVM_SOURCE_DIR="${WORKSPACE}/llvm" +LLVM_SOURCE_DIR="${WORKSPACE}/llvm-project/llvm" CMARK_SOURCE_DIR="${WORKSPACE}/cmark" -LLDB_SOURCE_DIR="${WORKSPACE}/lldb" +LLDB_SOURCE_DIR="${WORKSPACE}/llvm-project/lldb" LLBUILD_SOURCE_DIR="${WORKSPACE}/llbuild" STRESSTEST_PACKAGE_DIR="${WORKSPACE}/swift-stress-tester" XCTEST_SOURCE_DIR="${WORKSPACE}/swift-corelibs-xctest" @@ -1177,7 +1177,7 @@ FOUNDATION_STATIC_SOURCE_DIR="${WORKSPACE}/swift-corelibs-foundation" LIBDISPATCH_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBDISPATCH_STATIC_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBICU_SOURCE_DIR="${WORKSPACE}/icu" -LIBCXX_SOURCE_DIR="${WORKSPACE}/libcxx" +LIBCXX_SOURCE_DIR="${WORKSPACE}/llvm-project/libcxx" PLAYGROUNDSUPPORT_SOURCE_DIR="${WORKSPACE}/swift-xcode-playground-support" if [[ ! "${SKIP_BUILD_PLAYGROUNDSUPPORT}" && ! -d ${PLAYGROUNDSUPPORT_SOURCE_DIR} ]]; then @@ -1187,29 +1187,18 @@ fi # Symlink clang into the llvm tree. CLANG_SOURCE_DIR="${LLVM_SOURCE_DIR}/tools/clang" -if [ ! -e "${WORKSPACE}/clang" ] ; then - # If llvm/tools/clang is already a directory, use that and skip the symlink. - if [ ! -d "${CLANG_SOURCE_DIR}" ] ; then - echo "Can't find source directory for clang (tried ${WORKSPACE}/clang and ${CLANG_SOURCE_DIR})" - exit 1 - fi -fi if [ ! -d "${CLANG_SOURCE_DIR}" ] ; then - make_relative_symlink "${WORKSPACE}/clang" "${CLANG_SOURCE_DIR}" + make_relative_symlink "${WORKSPACE}/llvm-project/clang" "${CLANG_SOURCE_DIR}" fi # Don't symlink clang-tools-extra into the tree as the 'clang' symlink prevents # make_relative_symlink from working correctly. -if [ -e "${WORKSPACE}/clang-tools-extra" ] ; then - CLANG_TOOLS_EXTRA_SOURCE_DIR="${WORKSPACE}/clang-tools-extra" -fi +CLANG_TOOLS_EXTRA_SOURCE_DIR="${WORKSPACE}/llvm-project/clang-tools-extra" # Symlink compiler-rt into the llvm tree, if it exists. COMPILER_RT_SOURCE_DIR="${LLVM_SOURCE_DIR}/projects/compiler-rt" -if [ -e "${WORKSPACE}/compiler-rt" ] ; then - if [ ! -d "${COMPILER_RT_SOURCE_DIR}" ] ; then - make_relative_symlink "${WORKSPACE}/compiler-rt" "${COMPILER_RT_SOURCE_DIR}" - fi +if [ ! -d "${COMPILER_RT_SOURCE_DIR}" ] ; then + make_relative_symlink "${WORKSPACE}/llvm-project/compiler-rt" "${COMPILER_RT_SOURCE_DIR}" fi PRODUCTS=(cmark llvm) @@ -1930,11 +1919,9 @@ for host in "${ALL_HOSTS[@]}"; do -DLLVM_TOOL_LLD_BUILD:BOOL=TRUE ) - if [[ ! -z "${CLANG_TOOLS_EXTRA_SOURCE_DIR}" ]] ; then - cmake_options+=( - -DLLVM_EXTERNAL_CLANG_TOOLS_EXTRA_SOURCE_DIR="${CLANG_TOOLS_EXTRA_SOURCE_DIR}" - ) - fi + cmake_options+=( + -DLLVM_EXTERNAL_CLANG_TOOLS_EXTRA_SOURCE_DIR="${CLANG_TOOLS_EXTRA_SOURCE_DIR}" + ) if [[ "${BUILD_TOOLCHAIN_ONLY}" ]]; then cmake_options+=( From cf4fc068dda0b22d4db2100ba8ba8ed41ecd8adb Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Nov 2019 15:53:38 -0800 Subject: [PATCH 227/283] build: require libdispatch on non-Darwin targets libdispatch is already required for Foundation. At this point libdispatch is available on all the supported platforms. We simply enable SyntaxParserLib and SourceKit on all targets, allowing users to disable them. If enabled on non-Darwin platforms, the user must specify `SWIFT_PATH_TO_LIBDISPATCH_SOURCE` to indicate where to find libdispatch. This is done implicitly by build-script, resulting in no change for users. --- CMakeLists.txt | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 66ba9a5be2e5c..dba786165495e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -459,28 +459,15 @@ if(CMAKE_C_COMPILER_ID MATCHES Clang) add_compile_options($<$,$>:-Werror=gnu>) endif() -if(CMAKE_SYSTEM_NAME STREQUAL Darwin OR - EXISTS "${SWIFT_PATH_TO_LIBDISPATCH_SOURCE}") - set(SWIFT_BUILD_SYNTAXPARSERLIB_default TRUE) - set(SWIFT_BUILD_SOURCEKIT_default TRUE) -else() - set(SWIFT_BUILD_SYNTAXPARSERLIB_default FALSE) - set(SWIFT_BUILD_SOURCEKIT_default FALSE) -endif() -option(SWIFT_BUILD_SYNTAXPARSERLIB - "Build the Swift Syntax Parser library" - ${SWIFT_BUILD_SYNTAXPARSERLIB_default}) -option(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB - "Only build the Swift Syntax Parser library" FALSE) -option(SWIFT_BUILD_SOURCEKIT - "Build SourceKit" - ${SWIFT_BUILD_SOURCEKIT_default}) -option(SWIFT_ENABLE_SOURCEKIT_TESTS - "Enable running SourceKit tests" - ${SWIFT_BUILD_SOURCEKIT_default}) - -if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin" AND - (SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT)) +option(SWIFT_BUILD_SYNTAXPARSERLIB "Build the Swift Syntax Parser library" TRUE) +option(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB "Only build the Swift Syntax Parser library" FALSE) +option(SWIFT_BUILD_SOURCEKIT "Build SourceKit" TRUE) +option(SWIFT_ENABLE_SOURCEKIT_TESTS "Enable running SourceKit tests" TRUE) + +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + if(NOT EXISTS "${SWIFT_PATH_TO_LIBDISPATCH_SOURCE}") + message(SEND_ERROR "SyntaxParserLib and SourceKit require libdispatch on non-Darwin hosts. Please specify SWIFT_PATH_TO_LIBDISPATCH_SOURCE") + endif() set(SWIFT_NEED_EXPLICIT_LIBDISPATCH TRUE) else() set(SWIFT_NEED_EXPLICIT_LIBDISPATCH FALSE) From 2853615880fedb67ce4900bea98bff0627d4d654 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 15 Nov 2019 16:09:38 -0800 Subject: [PATCH 228/283] Add type to package inputs to UnqualifiedLookupRequest This allows us use an OptionSet parameter for the request (as currently we can't directly use it as a parameter due to not having an == definition for it). It also allows us to regain default arguments for the source loc and flag parameters. --- include/swift/AST/NameLookup.h | 4 +- include/swift/AST/NameLookupRequests.h | 48 +++++++++++++++---- include/swift/AST/NameLookupTypeIDZone.def | 5 +- lib/AST/Identifier.cpp | 2 +- lib/AST/NameLookup.cpp | 9 ++-- lib/AST/NameLookupRequests.cpp | 19 ++++++++ lib/AST/UnqualifiedLookup.cpp | 10 ++-- lib/Immediate/REPL.cpp | 5 +- lib/ParseSIL/ParseSIL.cpp | 7 ++- lib/Sema/MiscDiagnostics.cpp | 9 ++-- lib/Sema/TypeCheckAttr.cpp | 8 ++-- lib/Sema/TypeCheckNameLookup.cpp | 24 +++++----- .../lib/SwiftLang/SwiftSourceDocInfo.cpp | 7 +-- tools/swift-ast-script/ASTScriptEvaluator.cpp | 7 +-- tools/swift-ide-test/swift-ide-test.cpp | 8 ++-- 15 files changed, 111 insertions(+), 61 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 19ba526df99a1..a73ef38940d5f 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -228,10 +228,10 @@ enum class UnqualifiedLookupFlags { IncludeOuterResults = 0x10, }; -void simple_display(llvm::raw_ostream &out, UnqualifiedLookupFlags flags); - using UnqualifiedLookupOptions = OptionSet; +void simple_display(llvm::raw_ostream &out, UnqualifiedLookupOptions options); + inline UnqualifiedLookupOptions operator|(UnqualifiedLookupFlags flag1, UnqualifiedLookupFlags flag2) { return UnqualifiedLookupOptions(flag1) | flag2; diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index a96953da2e37b..bf71eec9152a0 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -307,11 +307,47 @@ class ExpandASTScopeRequest void cacheResult(ast_scope::ASTScopeImpl *) const {} }; +/// The input type for an unqualified lookup request. +class UnqualifiedLookupDescriptor { + using LookupOptions = OptionSet; + +public: + DeclName Name; + DeclContext *DC; + SourceLoc Loc; + LookupOptions Options; + + UnqualifiedLookupDescriptor(DeclName name, DeclContext *dc, + SourceLoc loc = SourceLoc(), + LookupOptions options = {}) + : Name(name), DC(dc), Loc(loc), Options(options) {} + + friend llvm::hash_code hash_value(const UnqualifiedLookupDescriptor &desc) { + return llvm::hash_combine(desc.Name, desc.DC, desc.Loc, + desc.Options.toRaw()); + } + + friend bool operator==(const UnqualifiedLookupDescriptor &lhs, + const UnqualifiedLookupDescriptor &rhs) { + return lhs.Name == rhs.Name && lhs.DC == rhs.DC && lhs.Loc == rhs.Loc && + lhs.Options.toRaw() == rhs.Options.toRaw(); + } + + friend bool operator!=(const UnqualifiedLookupDescriptor &lhs, + const UnqualifiedLookupDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, + const UnqualifiedLookupDescriptor &desc); + +SourceLoc extractNearestSourceLoc(const UnqualifiedLookupDescriptor &desc); + /// Performs unqualified lookup for a DeclName from a given context. class UnqualifiedLookupRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -320,12 +356,8 @@ class UnqualifiedLookupRequest friend SimpleRequest; // Evaluation. - // FIXME: We should be taking an OptionSet instead of the raw - // UnqualifiedLookupFlags, but we don't want to define == for OptionSet. We - // should find a way to define custom equality specifically for requests. - llvm::Expected evaluate(Evaluator &evaluator, DeclName name, - DeclContext *dc, SourceLoc loc, - UnqualifiedLookupFlags flags) const; + llvm::Expected evaluate(Evaluator &evaluator, + UnqualifiedLookupDescriptor desc) const; }; #define SWIFT_TYPEID_ZONE NameLookup diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index c9c8cdbdf8ae5..16a4b35ed5646 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -46,6 +46,5 @@ SWIFT_REQUEST(NameLookup, UnderlyingTypeDeclsReferencedRequest, DirectlyReferencedTypeDecls(TypeAliasDecl *), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, UnqualifiedLookupRequest, - LookupResult(DeclName, DeclContext *, SourceLoc, - UnqualifiedLookupFlags), - Uncached, NoLocationInfo) + LookupResult(UnqualifiedLookupDescriptor), Uncached, + NoLocationInfo) diff --git a/lib/AST/Identifier.cpp b/lib/AST/Identifier.cpp index ba41df96b45fa..880ab05241ce9 100644 --- a/lib/AST/Identifier.cpp +++ b/lib/AST/Identifier.cpp @@ -48,7 +48,7 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, DeclName I) { } void swift::simple_display(llvm::raw_ostream &out, DeclName name) { - out << name; + out << "'" << name << "'"; } raw_ostream &llvm::operator<<(raw_ostream &OS, swift::ObjCSelector S) { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 8c6cb62b9f138..1384e778aeb51 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -107,7 +107,7 @@ void LookupResult::shiftDownResults() { } void swift::simple_display(llvm::raw_ostream &out, - UnqualifiedLookupFlags flags) { + UnqualifiedLookupOptions options) { using Flag = std::pair; Flag possibleFlags[] = { {UnqualifiedLookupFlags::AllowProtocolMembers, "AllowProtocolMembers"}, @@ -117,7 +117,6 @@ void swift::simple_display(llvm::raw_ostream &out, {UnqualifiedLookupFlags::TypeLookup, "TypeLookup"}, }; - UnqualifiedLookupOptions options(flags); auto flagsToPrint = llvm::make_filter_range( possibleFlags, [&](Flag flag) { return options.contains(flag.first); }); @@ -1983,9 +1982,9 @@ directReferencesForUnqualifiedTypeLookup(DeclName name, options |= UnqualifiedLookupFlags::IncludeOuterResults; auto &ctx = dc->getASTContext(); - auto flags = UnqualifiedLookupFlags(options.toRaw()); - auto lookup = evaluateOrDefault( - ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); + auto descriptor = UnqualifiedLookupDescriptor(name, dc, loc, options); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); for (const auto &result : lookup.allResults()) { if (auto typeDecl = dyn_cast(result.getValueDecl())) results.push_back(typeDecl); diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 6641e32797777..5eb06e0bafa6d 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -128,6 +128,25 @@ void GenericParamListRequest::cacheResult(GenericParamList *params) const { context->GenericParamsAndBit.setPointerAndInt(params, true); } +//----------------------------------------------------------------------------// +// UnqualifiedLookupRequest computation. +//----------------------------------------------------------------------------// + +void swift::simple_display(llvm::raw_ostream &out, + const UnqualifiedLookupDescriptor &desc) { + out << "looking up "; + simple_display(out, desc.Name); + out << " from "; + simple_display(out, desc.DC); + out << " with options "; + simple_display(out, desc.Options); +} + +SourceLoc +swift::extractNearestSourceLoc(const UnqualifiedLookupDescriptor &desc) { + return extractNearestSourceLoc(desc.DC); +} + // Define request evaluation functions for each of the name lookup requests. static AbstractRequestFunction *nameLookupRequestFunctions[] = { #define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \ diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 85aa84bc89593..057b86335b8d4 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -1188,14 +1188,12 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( } llvm::Expected -UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, DeclName name, - DeclContext *dc, SourceLoc loc, - UnqualifiedLookupFlags flags) const { +UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, + UnqualifiedLookupDescriptor desc) const { SmallVector results; size_t indexOfFirstOuterResult = 0; - UnqualifiedLookupFactory factory(name, dc, loc, - UnqualifiedLookupOptions(flags), results, - indexOfFirstOuterResult); + UnqualifiedLookupFactory factory(desc.Name, desc.DC, desc.Loc, desc.Options, + results, indexOfFirstOuterResult); factory.performUnqualifiedLookup(); return LookupResult(results, indexOfFirstOuterResult); } diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 5a95395544496..b973616a09f74 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -1093,10 +1093,9 @@ class REPLEnvironment { SourceFile &SF = MostRecentModule->getMainSourceFile(SourceFileKind::REPL); auto name = ctx.getIdentifier(Tok.getText()); - auto flags = UnqualifiedLookupFlags(); + auto descriptor = UnqualifiedLookupDescriptor(name, &SF); auto lookup = evaluateOrDefault( - ctx.evaluator, - UnqualifiedLookupRequest{name, &SF, SourceLoc(), flags}, {}); + ctx.evaluator, UnqualifiedLookupRequest{descriptor}, {}); for (auto result : lookup) { printOrDumpDecl(result.getValueDecl(), doPrint); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 102996ef1aaa3..6e075807f0845 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -1187,10 +1187,9 @@ lookupTopDecl(Parser &P, DeclBaseName Name, bool typeLookup) { options |= UnqualifiedLookupFlags::TypeLookup; auto &ctx = P.SF.getASTContext(); - auto flags = UnqualifiedLookupFlags(options.toRaw()); - auto lookup = evaluateOrDefault( - ctx.evaluator, - UnqualifiedLookupRequest{Name, &P.SF, SourceLoc(), flags}, {}); + auto descriptor = UnqualifiedLookupDescriptor(Name, &P.SF); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); assert(lookup.size() == 1); return lookup.back().getValueDecl(); } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 87a1ff349c6d6..8d88c70164ea7 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -532,10 +532,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, } DeclContext *topLevelContext = DC->getModuleScopeContext(); - auto req = UnqualifiedLookupRequest{VD->getBaseName(), topLevelContext, - SourceLoc(), - UnqualifiedLookupFlags::KnownPrivate}; - auto lookup = evaluateOrDefault(Ctx.evaluator, req, {}); + auto descriptor = UnqualifiedLookupDescriptor( + VD->getBaseName(), topLevelContext, SourceLoc(), + UnqualifiedLookupFlags::KnownPrivate); + auto lookup = evaluateOrDefault(Ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); // Group results by module. Pick an arbitrary result from each module. llvm::SmallDenseMap resultsByModule; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index bcb188b7cc129..0b00003b5b226 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2074,10 +2074,10 @@ void lookupReplacedDecl(DeclName replacedDeclName, auto *moduleScopeCtxt = declCtxt->getModuleScopeContext(); if (isa(declCtxt)) { auto &ctx = declCtxt->getASTContext(); - auto req = - UnqualifiedLookupRequest{replacedDeclName, moduleScopeCtxt, - attr->getLocation(), UnqualifiedLookupFlags()}; - auto lookup = evaluateOrDefault(ctx.evaluator, req, {}); + auto descriptor = UnqualifiedLookupDescriptor( + replacedDeclName, moduleScopeCtxt, attr->getLocation()); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); for (auto entry : lookup) { results.push_back(entry.getValueDecl()); } diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index c8df79a5533fc..e701a30ecbaa5 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -229,9 +229,9 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, auto ulOptions = convertToUnqualifiedLookupOptions(options); auto &ctx = dc->getASTContext(); - auto flags = UnqualifiedLookupFlags(ulOptions.toRaw()); - auto lookup = evaluateOrDefault( - ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); + auto descriptor = UnqualifiedLookupDescriptor(name, dc, loc, ulOptions); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); LookupResult result; LookupResultBuilder builder(result, dc, options); @@ -265,10 +265,12 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, UnqualifiedLookupFlags::TypeLookup; { // Try lookup without ProtocolMembers first. - ulOptions -= UnqualifiedLookupFlags::AllowProtocolMembers; - auto flags = UnqualifiedLookupFlags(ulOptions.toRaw()); - auto lookup = evaluateOrDefault( - ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); + auto desc = UnqualifiedLookupDescriptor( + name, dc, loc, + ulOptions - UnqualifiedLookupFlags::AllowProtocolMembers); + + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); if (!lookup.allResults().empty() || !options.contains(NameLookupFlags::ProtocolMembers)) return lookup; @@ -280,10 +282,10 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, // FIXME: Fix the problem where if NominalTypeDecl::getAllProtocols() // is called too early, we start resolving extensions -- even those // which do provide not conformances. - ulOptions |= UnqualifiedLookupFlags::AllowProtocolMembers; - auto flags = UnqualifiedLookupFlags(ulOptions.toRaw()); - return evaluateOrDefault( - ctx.evaluator, UnqualifiedLookupRequest{name, dc, loc, flags}, {}); + auto desc = UnqualifiedLookupDescriptor( + name, dc, loc, + ulOptions | UnqualifiedLookupFlags::AllowProtocolMembers); + return evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 07b23383bc093..63710d6f8d6c6 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -499,9 +499,10 @@ void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) { // For now we use unqualified lookup to fetch other declarations with the same // base name. auto &ctx = VD->getASTContext(); - auto req = UnqualifiedLookupRequest{VD->getBaseName(), VD->getDeclContext(), - SourceLoc(), UnqualifiedLookupFlags()}; - auto lookup = evaluateOrDefault(ctx.evaluator, req, {}); + auto descriptor = UnqualifiedLookupDescriptor(VD->getBaseName(), + VD->getDeclContext()); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); for (auto result : lookup) { ValueDecl *RelatedVD = result.getValueDecl(); if (RelatedVD->getAttrs().isUnavailable(VD->getASTContext())) diff --git a/tools/swift-ast-script/ASTScriptEvaluator.cpp b/tools/swift-ast-script/ASTScriptEvaluator.cpp index c49b75c7cf1c6..8158783563e67 100644 --- a/tools/swift-ast-script/ASTScriptEvaluator.cpp +++ b/tools/swift-ast-script/ASTScriptEvaluator.cpp @@ -115,9 +115,10 @@ bool ASTScript::execute() const { return true; } - auto req = UnqualifiedLookupRequest{ctx.getIdentifier("View"), swiftUI, - SourceLoc(), UnqualifiedLookupFlags()}; - auto viewLookup = evaluateOrDefault(ctx.evaluator, req, {}); + auto descriptor = UnqualifiedLookupDescriptor(ctx.getIdentifier("View"), + swiftUI); + auto viewLookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); auto viewProtocol = dyn_cast_or_null(viewLookup.getSingleTypeResult()); if (!viewProtocol) { diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index d4e4282709d1e..029f51f51ec74 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -2234,10 +2234,10 @@ static int doPrintDecls(const CompilerInvocation &InitInvok, for (const auto &name : DeclsToPrint) { ASTContext &ctx = CI.getASTContext(); - auto req = UnqualifiedLookupRequest{ctx.getIdentifier(name), - CI.getPrimarySourceFile(), SourceLoc(), - UnqualifiedLookupFlags()}; - auto lookup = evaluateOrDefault(ctx.evaluator, req, {}); + auto descriptor = UnqualifiedLookupDescriptor(ctx.getIdentifier(name), + CI.getPrimarySourceFile()); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); for (auto result : lookup) { result.getValueDecl()->print(*Printer, Options); From 125c1fb03eb65b1d8b6de878fdf219b46f8d028f Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Mon, 11 Nov 2019 16:24:58 -0800 Subject: [PATCH 229/283] [Sema] Avoid needing the typechecker for the IsStaticRequest request It was being used purely to get the name of the type context for a diagnostic message. SourceKit's syntactic-only requests were hitting an assertion when this diagnostic was triggered because they don't set up a type checker. --- include/swift/AST/DiagnosticsSema.def | 8 ++- lib/Sema/TypeCheckDecl.cpp | 16 +++-- .../DocumentStructure/Inputs/main.swift | 4 ++ .../structure.swift.empty.response | 65 ++++++++++++++++++- .../structure.swift.foobar.response | 65 ++++++++++++++++++- .../structure.swift.response | 65 ++++++++++++++++++- test/decl/func/operator.swift | 2 +- 7 files changed, 215 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index df60139eea9e4..e3cdeb70b8aad 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -911,8 +911,12 @@ ERROR(invalid_arg_count_for_operator,none, "operators must have one or two arguments", ()) ERROR(operator_in_local_scope,none, "operator functions can only be declared at global or in type scope", ()) -ERROR(nonstatic_operator_in_type,none, - "operator %0 declared in type %1 must be 'static'", (Identifier, Type)) +ERROR(nonstatic_operator_in_nominal,none, + "operator %0 declared in type %1 must be 'static'", + (Identifier, DeclName)) +ERROR(nonstatic_operator_in_extension,none, + "operator %0 declared in extension of %1 must be 'static'", + (Identifier, TypeRepr*)) ERROR(nonfinal_operator_in_class,none, "operator %0 declared in non-final class %1 must be 'final'", (Identifier, Type)) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 258a650c7646d..a3f0d10ddd250 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1258,10 +1258,18 @@ IsStaticRequest::evaluate(Evaluator &evaluator, FuncDecl *decl) const { decl->isOperator() && dc->isTypeContext()) { auto operatorName = decl->getFullName().getBaseIdentifier(); - decl->diagnose(diag::nonstatic_operator_in_type, - operatorName, dc->getDeclaredInterfaceType()) - .fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/true), - "static "); + if (auto ED = dyn_cast(dc->getAsDecl())) { + decl->diagnose(diag::nonstatic_operator_in_extension, + operatorName, ED->getExtendedTypeRepr()) + .fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/true), + "static "); + } else { + auto *NTD = cast(dc->getAsDecl()); + decl->diagnose(diag::nonstatic_operator_in_nominal, operatorName, + NTD->getName()) + .fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/true), + "static "); + } result = true; } diff --git a/test/SourceKit/DocumentStructure/Inputs/main.swift b/test/SourceKit/DocumentStructure/Inputs/main.swift index 1825f3a070c83..cb11ce01e9f3f 100644 --- a/test/SourceKit/DocumentStructure/Inputs/main.swift +++ b/test/SourceKit/DocumentStructure/Inputs/main.swift @@ -135,3 +135,7 @@ class OneMore { fatalError() } } + +class Chain { + func + (lhs: Chain, rhs: Chain) -> Chain { fatalError() } +} diff --git a/test/SourceKit/DocumentStructure/structure.swift.empty.response b/test/SourceKit/DocumentStructure/structure.swift.empty.response index 43b0be09bfa2a..3225f13f78e1e 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.empty.response +++ b/test/SourceKit/DocumentStructure/structure.swift.empty.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2259, + key.length: 2348, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -1403,6 +1403,69 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Chain", + key.offset: 2260, + key.length: 87, + key.nameoffset: 2266, + key.namelength: 5, + key.bodyoffset: 2276, + key.bodylength: 70, + key.substructure: [ + { + key.kind: source.lang.swift.decl.generic_type_param, + key.name: "A", + key.offset: 2272, + key.length: 1, + key.nameoffset: 2272, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.function.method.static, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 2279, + key.length: 66, + key.typename: "Chain", + key.nameoffset: 2284, + key.namelength: 32, + key.bodyoffset: 2330, + key.bodylength: 14, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 2287, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 2302, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2331, + key.length: 12, + key.nameoffset: 2331, + key.namelength: 10, + key.bodyoffset: 2342, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ diff --git a/test/SourceKit/DocumentStructure/structure.swift.foobar.response b/test/SourceKit/DocumentStructure/structure.swift.foobar.response index 3ecd5555db9e2..56938d04e5765 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.foobar.response +++ b/test/SourceKit/DocumentStructure/structure.swift.foobar.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2259, + key.length: 2348, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -1403,6 +1403,69 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Chain", + key.offset: 2260, + key.length: 87, + key.nameoffset: 2266, + key.namelength: 5, + key.bodyoffset: 2276, + key.bodylength: 70, + key.substructure: [ + { + key.kind: source.lang.swift.decl.generic_type_param, + key.name: "A", + key.offset: 2272, + key.length: 1, + key.nameoffset: 2272, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.function.method.static, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 2279, + key.length: 66, + key.typename: "Chain", + key.nameoffset: 2284, + key.namelength: 32, + key.bodyoffset: 2330, + key.bodylength: 14, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 2287, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 2302, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2331, + key.length: 12, + key.nameoffset: 2331, + key.namelength: 10, + key.bodyoffset: 2342, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ diff --git a/test/SourceKit/DocumentStructure/structure.swift.response b/test/SourceKit/DocumentStructure/structure.swift.response index 83d66b8c72def..74afaac5d23ea 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.response +++ b/test/SourceKit/DocumentStructure/structure.swift.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2259, + key.length: 2348, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -1403,6 +1403,69 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Chain", + key.offset: 2260, + key.length: 87, + key.nameoffset: 2266, + key.namelength: 5, + key.bodyoffset: 2276, + key.bodylength: 70, + key.substructure: [ + { + key.kind: source.lang.swift.decl.generic_type_param, + key.name: "A", + key.offset: 2272, + key.length: 1, + key.nameoffset: 2272, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.function.method.static, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 2279, + key.length: 66, + key.typename: "Chain", + key.nameoffset: 2284, + key.namelength: 32, + key.bodyoffset: 2330, + key.bodylength: 14, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 2287, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 2302, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2331, + key.length: 12, + key.nameoffset: 2331, + key.namelength: 10, + key.bodyoffset: 2342, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ diff --git a/test/decl/func/operator.swift b/test/decl/func/operator.swift index 8cc6904ba3290..c9c2d5da25a37 100644 --- a/test/decl/func/operator.swift +++ b/test/decl/func/operator.swift @@ -243,7 +243,7 @@ struct S1 { } extension S1 { - func %%%%(lhs: S1, rhs: S1) -> S1 { return lhs } // expected-error{{operator '%%%%' declared in type 'S1' must be 'static'}}{{3-3=static }} + func %%%%(lhs: S1, rhs: S1) -> S1 { return lhs } // expected-error{{operator '%%%%' declared in extension of 'S1' must be 'static'}}{{3-3=static }} } class C0 { From 77daaf3338a7676843e1c17e04d15c45e3e9f832 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Tue, 12 Nov 2019 12:49:31 -0800 Subject: [PATCH 230/283] [ASTVerifier] Don't perform semantic requests in verifyParsed() SourceKit doesn't install a typechecker for its syntactic requests so was hitting assertions in these methods if any typechecking was triggered. --- lib/AST/ASTVerifier.cpp | 18 ------------------ .../DocumentStructure/Inputs/main.swift | 8 ++++++++ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 6b2ff5d2976e2..2fe478a3ecfc4 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -2866,15 +2866,6 @@ class Verifier : public ASTWalker { void verifyParsed(ConstructorDecl *CD) { PrettyStackTraceDecl debugStack("verifying ConstructorDecl", CD); - - auto *DC = CD->getDeclContext(); - if (!isa(DC) && !isa(DC) && - !CD->isInvalid()) { - Out << "ConstructorDecls outside nominal types and extensions " - "should be marked invalid"; - abort(); - } - verifyParsedBase(CD); } @@ -2955,15 +2946,6 @@ class Verifier : public ASTWalker { Out << "DestructorDecl cannot be generic"; abort(); } - - auto *DC = DD->getDeclContext(); - if (!isa(DC) && !isa(DC) && - !DD->isInvalid()) { - Out << "DestructorDecls outside nominal types and extensions " - "should be marked invalid"; - abort(); - } - verifyParsedBase(DD); } diff --git a/test/SourceKit/DocumentStructure/Inputs/main.swift b/test/SourceKit/DocumentStructure/Inputs/main.swift index cb11ce01e9f3f..9e6f9e23bd8cc 100644 --- a/test/SourceKit/DocumentStructure/Inputs/main.swift +++ b/test/SourceKit/DocumentStructure/Inputs/main.swift @@ -139,3 +139,11 @@ class OneMore { class Chain { func + (lhs: Chain, rhs: Chain) -> Chain { fatalError() } } + +public init() { + fatalError() +} + +deinit { + fatalError() +} From 196ee01c9f85e484580bfc9dee2edf7e372c7c6f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Nov 2019 19:40:01 -0800 Subject: [PATCH 231/283] build: relax SWIFT_PATH_TO_LIBDISPATCH_SOURCE requirement This is only needed for SourceKit or SyntaxParserLib. Relax the check with the condition that was accidentally dropped. --- CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dba786165495e..a847f530fa7ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -464,13 +464,15 @@ option(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB "Only build the Swift Syntax Parser libr option(SWIFT_BUILD_SOURCEKIT "Build SourceKit" TRUE) option(SWIFT_ENABLE_SOURCEKIT_TESTS "Enable running SourceKit tests" TRUE) -if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) - if(NOT EXISTS "${SWIFT_PATH_TO_LIBDISPATCH_SOURCE}") - message(SEND_ERROR "SyntaxParserLib and SourceKit require libdispatch on non-Darwin hosts. Please specify SWIFT_PATH_TO_LIBDISPATCH_SOURCE") +if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) + if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(SWIFT_NEED_EXPLICIT_LIBDISPATCH FALSE) + else() + set(SWIFT_NEED_EXPLICIT_LIBDISPATCH TRUE) + if(NOT EXISTS "${SWIFT_PATH_TO_LIBDISPATCH_SOURCE}") + message(SEND_ERROR "SyntaxParserLib and SourceKit require libdispatch on non-Darwin hosts. Please specify SWIFT_PATH_TO_LIBDISPATCH_SOURCE") + endif() endif() - set(SWIFT_NEED_EXPLICIT_LIBDISPATCH TRUE) -else() - set(SWIFT_NEED_EXPLICIT_LIBDISPATCH FALSE) endif() # From 6273941034cce59bcc26a5e84626f4785ba64b74 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 15 Nov 2019 14:09:27 -0800 Subject: [PATCH 232/283] [Constraint solver] Centralize solution-application logic somewhat. In anticipation of eliminating this solution-application logic, centralize to application of solutions back to the constraint system for the purpose of applying the solution to update ASTs. --- lib/Sema/CSApply.cpp | 9 --------- lib/Sema/ConstraintSystem.h | 3 +++ lib/Sema/TypeCheckConstraints.cpp | 3 +++ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 803843e50a8e8..a6621983de558 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7334,10 +7334,6 @@ namespace { /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. bool ConstraintSystem::applySolutionFixes(const Solution &solution) { - // First transfer all of the deduced information back - // to the constraint system. - applySolution(solution); - /// Collect the fixes on a per-expression basis. llvm::SmallDenseMap> fixesPerExpr; for (auto *fix : solution.Fixes) { @@ -7392,11 +7388,6 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, Type convertType, bool discardedExpr, bool skipClosures) { - // Add the node types back. - for (auto &nodeType : solution.addedNodeTypes) { - setType(nodeType.first, nodeType.second); - } - // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c87a5235b71cf..ba3647619f6b3 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1707,6 +1707,9 @@ class ConstraintSystem { /// constraint system for further exploration. void applySolution(const Solution &solution); + // FIXME: Allows the type checker to apply solutions. + friend class swift::TypeChecker; + /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. bool applySolutionFixes(const Solution &solution); diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5bebe709b4950..4bbf6ea8954e0 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2261,6 +2261,9 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, if (!result) return Type(); + // Apply this solution to the constraint system. + cs.applySolution(solution); + // Apply the solution to the expression. result = cs.applySolution( solution, result, convertType.getType(), From c63076e3a1b744e43025605d7799fe7110169ecd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 15 Nov 2019 13:31:08 -0800 Subject: [PATCH 233/283] [Constraint system] Apply fixes without relying on a root expression. When applying the set of fixes introduced by a solution, don't rely on a walk from the "root" expression of the constraint system to attribute the fixes to expressions. Rather, collect the fixes and emit diagnostics in source order of the expressions that pertain to. --- lib/Sema/CSApply.cpp | 142 ++++++++++++++++------------------- lib/Sema/ConstraintSystem.h | 2 +- test/Constraints/fixes.swift | 8 +- 3 files changed, 71 insertions(+), 81 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 3215bb72e56ba..803843e50a8e8 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7305,95 +7305,85 @@ Expr *ConstraintSystem::coerceToRValue(Expr *expr) { [&](Expr *expr, Type type) { setType(expr, type); }); } -/// Emit the fixes computed as part of the solution, returning true if we were -/// able to emit an error message, or false if none of the fixits worked out. -bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) { - // First transfer all of the deduced information back - // to the constraint system. - applySolution(solution); - - class DiagnosticWalker : public ASTWalker { - Expr *root; - const Solution &solution; - llvm::SmallDenseMap> fixesPerExpr; - - /// Determines whether any error have been diagnosed while - /// trying to apply fixes associated with a given solution. - bool DiagnosedAnyErrors = false; +namespace { + /// Function object to compare source locations, putting invalid + /// locations at the end. + class CompareExprSourceLocs { + SourceManager &sourceMgr; public: - DiagnosticWalker(Expr *expr, const Solution &solution) - : root(expr), solution(solution) { - for (auto *fix : solution.Fixes) - fixesPerExpr[fix->getAnchor()].push_back(fix); - } - - std::pair walkToExprPre(Expr *E) override { - // Diagnose root expression last. - if (E == root) - return {true, E}; - - if (auto *closure = dyn_cast(E)) { - auto result = solution.builderTransformedClosures.find(closure); - if (result != solution.builderTransformedClosures.end()) { - auto *transformedExpr = result->second.singleExpr; - // Since this closure has been transformed into something - // else let's look inside transformed expression instead. - transformedExpr->walk(*this); - return {false, E}; - } + explicit CompareExprSourceLocs(SourceManager &sourceMgr) + : sourceMgr(sourceMgr) { } + + bool operator()(Expr *lhs, Expr *rhs) const { + if (static_cast(lhs) != static_cast(rhs)) { + return static_cast(lhs); } - diagnose(E); - return {true, E}; - } + auto lhsLoc = lhs->getLoc(); + auto rhsLoc = rhs->getLoc(); + if (lhsLoc.isValid() != rhsLoc.isValid()) + return lhsLoc.isValid(); - Expr *walkToExprPost(Expr *E) override { - if (E == root) - diagnose(E); - return E; + return sourceMgr.isBeforeInBuffer(lhsLoc, rhsLoc); } + }; - std::pair walkToStmtPre(Stmt *S) override { - return {true, S}; - } +} - bool hadErrors() const { return DiagnosedAnyErrors; } +/// Emit the fixes computed as part of the solution, returning true if we were +/// able to emit an error message, or false if none of the fixits worked out. +bool ConstraintSystem::applySolutionFixes(const Solution &solution) { + // First transfer all of the deduced information back + // to the constraint system. + applySolution(solution); - private: - void diagnose(Expr *E) { - auto fixes = fixesPerExpr.find(E); - if (fixes == fixesPerExpr.end()) - return; + /// Collect the fixes on a per-expression basis. + llvm::SmallDenseMap> fixesPerExpr; + for (auto *fix : solution.Fixes) { + fixesPerExpr[fix->getAnchor()].push_back(fix); + } - // Coalesce fixes with the same locator to avoid duplicating notes. - using ConstraintFixVector = llvm::SmallVector; - llvm::SmallMapVector, 4> aggregatedFixes; - for (auto *fix : fixes->second) - aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix); - - for (auto fixesPerLocator : aggregatedFixes) { - for (auto fixesPerKind : fixesPerLocator.second) { - auto fixes = fixesPerKind.second; - auto *primaryFix = fixes[0]; - ArrayRef secondaryFixes{fixes.begin() + 1, fixes.end()}; - - auto diagnosed = primaryFix->coalesceAndDiagnose(secondaryFixes); - if (primaryFix->isWarning()) { - assert(diagnosed && "warnings should always be diagnosed"); - (void)diagnosed; - } else { - DiagnosedAnyErrors |= diagnosed; - } + // Collect all of the expressions that have fixes, and sort them by + // source ordering. + SmallVector exprsWithFixes; + for (const auto &fix : fixesPerExpr) { + exprsWithFixes.push_back(fix.getFirst()); + } + std::sort(exprsWithFixes.begin(), exprsWithFixes.end(), + CompareExprSourceLocs(Context.SourceMgr)); + + // Walk over each of the expressions, diagnosing fixes. + bool diagnosedAnyErrors = false; + + for (auto expr : exprsWithFixes) { + // Coalesce fixes with the same locator to avoid duplicating notes. + auto fixes = fixesPerExpr[expr]; + + using ConstraintFixVector = llvm::SmallVector; + llvm::SmallMapVector, 4> aggregatedFixes; + for (auto *fix : fixes) + aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix); + + for (auto fixesPerLocator : aggregatedFixes) { + for (auto fixesPerKind : fixesPerLocator.second) { + auto fixes = fixesPerKind.second; + auto *primaryFix = fixes[0]; + ArrayRef secondaryFixes{fixes.begin() + 1, fixes.end()}; + + auto diagnosed = primaryFix->coalesceAndDiagnose(secondaryFixes); + if (primaryFix->isWarning()) { + assert(diagnosed && "warnings should always be diagnosed"); + (void)diagnosed; + } else { + diagnosedAnyErrors |= diagnosed; } } } - }; + } - DiagnosticWalker diagnostics(E, solution); - E->walk(diagnostics); - return diagnostics.hadErrors(); + return diagnosedAnyErrors; } /// Apply a given solution to the expression, producing a fully @@ -7413,7 +7403,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, if (shouldSuppressDiagnostics()) return nullptr; - bool diagnosedErrorsViaFixes = applySolutionFixes(expr, solution); + bool diagnosedErrorsViaFixes = applySolutionFixes(solution); // If all of the available fixes would result in a warning, // we can go ahead and apply this solution to AST. if (!llvm::all_of(solution.Fixes, [](const ConstraintFix *fix) { diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c3f690d360737..c87a5235b71cf 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1709,7 +1709,7 @@ class ConstraintSystem { /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. - bool applySolutionFixes(Expr *E, const Solution &solution); + bool applySolutionFixes(const Solution &solution); /// If there is more than one viable solution, /// attempt to pick the best solution and remove all of the rest. diff --git a/test/Constraints/fixes.swift b/test/Constraints/fixes.swift index 028977eeafde7..9cb8e4aded51d 100644 --- a/test/Constraints/fixes.swift +++ b/test/Constraints/fixes.swift @@ -198,14 +198,14 @@ func moreComplexUnwrapFixes() { // expected-note@-2{{force-unwrap using '!'}}{{12-12=!}} takeOpt(t.optS.value) // expected-error{{value of optional type 'T?' must be unwrapped to refer to member 'optS' of wrapped base type 'T'}} - // expected-note@-1{{chain the optional using '?'}}{{17-17=?}} + // expected-note@-1{{chain the optional using '?'}}{{12-12=?}} // expected-error@-2{{value of optional type 'S?' must be unwrapped to refer to member 'value' of wrapped base type 'S'}} - // expected-note@-3{{chain the optional using '?'}}{{12-12=?}} + // expected-note@-3{{chain the optional using '?'}}{{17-17=?}} takeNon(t.optS.value) // expected-error{{value of optional type 'T?' must be unwrapped to refer to member 'optS' of wrapped base type 'T'}} - // expected-note@-1{{chain the optional using '?'}}{{17-17=?}} + // expected-note@-1{{chain the optional using '?'}}{{12-12=?}} // expected-error@-2{{value of optional type 'S?' must be unwrapped to refer to member 'value' of wrapped base type 'S'}} - // expected-note@-3{{chain the optional using '?'}}{{12-12=?}} + // expected-note@-3{{chain the optional using '?'}}{{17-17=?}} // expected-note@-4{{force-unwrap using '!'}}{{17-17=!}} takeNon(os?.value) // expected-error{{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} From 78e360a67dfbe6f47c19d23873eea1da8f9b7659 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 15 Nov 2019 21:09:27 -0800 Subject: [PATCH 234/283] [Constraint solver] Drop unused Expr* from diagnoseAmbiguity(). We've made this unnecessary. --- lib/Sema/ConstraintSystem.cpp | 5 ++--- lib/Sema/ConstraintSystem.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index e2579eae5e0a3..656d2c43c7b38 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2503,7 +2503,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { } } - if (diagnoseAmbiguity(expr, viable)) { + if (diagnoseAmbiguity(viable)) { return true; } } @@ -2757,8 +2757,7 @@ static void extendPreorderIndexMap( expr->walk(traversal); } -bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, - ArrayRef solutions) { +bool ConstraintSystem::diagnoseAmbiguity(ArrayRef solutions) { // Produce a diff of the solutions. SolutionDiff diff(solutions); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index ba3647619f6b3..80aaba2865e64 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2104,7 +2104,7 @@ class ConstraintSystem { /// emits an error message. void diagnoseFailureForExpr(Expr *expr); - bool diagnoseAmbiguity(Expr *expr, ArrayRef solutions); + bool diagnoseAmbiguity(ArrayRef solutions); bool diagnoseAmbiguityWithFixes(ArrayRef solutions); /// Give the deprecation warning for referring to a global function From bcb45468e29c4e074b5440bb8bf7c0194905b6f4 Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Sat, 16 Nov 2019 08:14:07 +0200 Subject: [PATCH 235/283] [CMake] SWIFT_ENABLE_SOURCEKIT_TESTS depends on SWIFT_BUILD_SOURCEKIT --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a847f530fa7ed..2a38a7e591da7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -462,7 +462,7 @@ endif() option(SWIFT_BUILD_SYNTAXPARSERLIB "Build the Swift Syntax Parser library" TRUE) option(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB "Only build the Swift Syntax Parser library" FALSE) option(SWIFT_BUILD_SOURCEKIT "Build SourceKit" TRUE) -option(SWIFT_ENABLE_SOURCEKIT_TESTS "Enable running SourceKit tests" TRUE) +option(SWIFT_ENABLE_SOURCEKIT_TESTS "Enable running SourceKit tests" ${SWIFT_BUILD_SOURCEKIT}) if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) if(CMAKE_SYSTEM_NAME STREQUAL Darwin) From 5c53756ef6642a0cbcf2e40b3f034320a0ff6eb2 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sat, 16 Nov 2019 12:16:28 -0800 Subject: [PATCH 236/283] [update-checkout] Remove the symlink from update-checkout because we are using llvm projects directly --- .../update_checkout/update_checkout.py | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/utils/update_checkout/update_checkout/update_checkout.py b/utils/update_checkout/update_checkout/update_checkout.py index 68810069810cd..4357c2d11721b 100755 --- a/utils/update_checkout/update_checkout/update_checkout.py +++ b/utils/update_checkout/update_checkout/update_checkout.py @@ -400,53 +400,6 @@ def skip_list_for_platform(config): return skip_list -# Python 2.7 in Windows doesn't support os.symlink -os_symlink = getattr(os, "symlink", None) -if callable(os_symlink): - pass -else: - def symlink_ms(source, link_name): - source = os.path.normpath(source) - link_name = os.path.normpath(link_name) - if os.path.isdir(link_name): - os.rmdir(link_name) - elif os.exists(link_name): - os.remove(link_name) - import ctypes - csl = ctypes.windll.kernel32.CreateSymbolicLinkW - csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32) - csl.restype = ctypes.c_ubyte - flags = 1 if os.path.isdir(source) else 0 - if csl(link_name, source, flags) == 0: - raise ctypes.WinError() - os.symlink = symlink_ms - - -def symlink_llvm_monorepo(args): - print("Create symlink for LLVM Project") - llvm_projects = ['clang', - 'llvm', - 'lldb', - 'compiler-rt', - 'libcxx', - 'clang-tools-extra'] - for project in llvm_projects: - src_path = os.path.join(args.source_root, - 'llvm-project', - project) - dst_path = os.path.join(args.source_root, project) - if not os.path.islink(dst_path): - try: - os.symlink(src_path, dst_path) - except OSError as e: - if e.errno == errno.EEXIST: - print("File '%s' already exists. Remove it, so " - "update-checkout can create the symlink to the " - "llvm-monorepo." % dst_path) - else: - raise e - - def main(): freeze_support() parser = argparse.ArgumentParser( @@ -599,7 +552,6 @@ def main(): if fail_count > 0: print("update-checkout failed, fix errors and try again") else: - symlink_llvm_monorepo(args) print("update-checkout succeeded") print_repo_hashes(args, config) sys.exit(fail_count) From 11d20b8c922cf0b03a2c6909c69f28f003a951da Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Fri, 15 Nov 2019 11:44:54 -0800 Subject: [PATCH 237/283] [IDE] Avoid name binding in sourcekitd's syntactic requests It looks like we recently started binding extensions to their nominals in order to continue to compute access levels via ValueDecl::getFormalAccess() after an assertion was added to enforce that bindExtensions had been called before anything tried to call ExtensionDecl::getBoundNominal() - which getFormalAccess() depends on. Sourcekitd's syntactic requests are made on every keypress in the editor though, so we shouldn't do any name binding (which may require module loading) to keep them as fast as possible. This patch restores the old inferAccessLevel() functions we used prior to the switch to ValueDecl::getFormalAccess() (plus a few fixes) that does as much as it can syntactically, without any name binding, and simply doesn't report the access level in cases where it couldn't be computed without name-binding. This also fixes an assertion hit we were getting trying to bind extensions in inactive ifconfig clauses, which ASTScope doesn't support. Resolves rdar://problem/57202584 --- lib/IDE/SyntaxModel.cpp | 4 - .../DocumentStructure/Inputs/main.swift | 6 ++ .../access_parse.swift.response | 8 -- .../structure.swift.empty.response | 95 +++++++++++++++++- .../structure.swift.foobar.response | 97 ++++++++++++++++++- .../structure.swift.invalid.response | 2 - .../structure.swift.response | 97 ++++++++++++++++++- ...lang_module.swift.apinotes_swift3.response | 14 --- ...lang_module.swift.apinotes_swift4.response | 8 -- .../gen_clang_module.swift.response | 3 - tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp | 80 +++++++++++++-- 11 files changed, 353 insertions(+), 61 deletions(-) diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index 37fa1f0051c22..c3f7aad6a8a31 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -904,10 +904,6 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) { pushStructureNode(SN, NTD); } else if (auto *ED = dyn_cast(D)) { - // Normally bindExtension() would take care of computing the extended - // nominal. It must be done before asking for generic parameters. - if (!inInactiveClause || ASTScope::areInactiveIfConfigClausesSupported()) - ED->computeExtendedNominal(); SyntaxStructureNode SN; setDecl(SN, D); SN.Kind = SyntaxStructureKind::Extension; diff --git a/test/SourceKit/DocumentStructure/Inputs/main.swift b/test/SourceKit/DocumentStructure/Inputs/main.swift index 9e6f9e23bd8cc..bba0489d7a99b 100644 --- a/test/SourceKit/DocumentStructure/Inputs/main.swift +++ b/test/SourceKit/DocumentStructure/Inputs/main.swift @@ -147,3 +147,9 @@ public init() { deinit { fatalError() } + +#if false + extension Result { + func foo() {} + } + #endif diff --git a/test/SourceKit/DocumentStructure/access_parse.swift.response b/test/SourceKit/DocumentStructure/access_parse.swift.response index 7ca60e870922f..0e8c673b3761f 100644 --- a/test/SourceKit/DocumentStructure/access_parse.swift.response +++ b/test/SourceKit/DocumentStructure/access_parse.swift.response @@ -777,7 +777,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "DefAccess", key.offset: 1399, key.length: 43, @@ -788,7 +787,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1423, key.length: 17, @@ -801,7 +799,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "PubAccess", key.offset: 1443, key.length: 43, @@ -812,7 +809,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1467, key.length: 17, @@ -825,7 +821,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "IntAccess", key.offset: 1487, key.length: 43, @@ -836,7 +831,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1511, key.length: 17, @@ -849,7 +843,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "PrivAccess", key.offset: 1531, key.length: 44, @@ -860,7 +853,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1556, key.length: 17, diff --git a/test/SourceKit/DocumentStructure/structure.swift.empty.response b/test/SourceKit/DocumentStructure/structure.swift.empty.response index 3225f13f78e1e..6cade62798a42 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.empty.response +++ b/test/SourceKit/DocumentStructure/structure.swift.empty.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2348, + key.length: 2472, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -223,7 +223,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 377, key.length: 45, @@ -234,7 +233,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls2", key.offset: 402, key.length: 18, @@ -558,7 +556,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "Foo", key.offset: 999, key.length: 58, @@ -569,7 +566,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "anExtendedFooFunction()", key.offset: 1019, key.length: 36, @@ -1466,6 +1462,81 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "init()", + key.offset: 2356, + key.length: 27, + key.nameoffset: 2356, + key.namelength: 6, + key.bodyoffset: 2364, + key.bodylength: 18, + key.attributes: [ + { + key.offset: 2349, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2369, + key.length: 12, + key.nameoffset: 2369, + key.namelength: 10, + key.bodyoffset: 2380, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.private, + key.name: "deinit", + key.offset: 2385, + key.length: 27, + key.nameoffset: 2385, + key.namelength: 6, + key.bodyoffset: 2393, + key.bodylength: 18, + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2398, + key.length: 12, + key.nameoffset: 2398, + key.namelength: 10, + key.bodyoffset: 2409, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Result", + key.offset: 2425, + key.length: 38, + key.nameoffset: 2435, + key.namelength: 6, + key.bodyoffset: 2443, + key.bodylength: 19, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "foo()", + key.offset: 2447, + key.length: 13, + key.nameoffset: 2452, + key.namelength: 5, + key.bodyoffset: 2459, + key.bodylength: 0 + } + ] } ], key.diagnostics: [ @@ -1502,6 +1573,20 @@ key.sourcetext: "func " } ] + }, + { + key.line: 143, + key.column: 12, + key.severity: source.diagnostic.severity.error, + key.description: "initializers may only be declared within a type", + key.diagnostic_stage: source.diagnostic.stage.swift.parse + }, + { + key.line: 147, + key.column: 1, + key.severity: source.diagnostic.severity.error, + key.description: "deinitializers may only be declared within a class", + key.diagnostic_stage: source.diagnostic.stage.swift.parse } ] } diff --git a/test/SourceKit/DocumentStructure/structure.swift.foobar.response b/test/SourceKit/DocumentStructure/structure.swift.foobar.response index 56938d04e5765..f8b89e0089b25 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.foobar.response +++ b/test/SourceKit/DocumentStructure/structure.swift.foobar.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2348, + key.length: 2472, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -223,7 +223,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 377, key.length: 45, @@ -234,7 +233,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls2", key.offset: 402, key.length: 18, @@ -558,7 +556,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "Foo", key.offset: 999, key.length: 58, @@ -569,7 +566,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "anExtendedFooFunction()", key.offset: 1019, key.length: 36, @@ -1466,6 +1462,81 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "init()", + key.offset: 2356, + key.length: 27, + key.nameoffset: 2356, + key.namelength: 6, + key.bodyoffset: 2364, + key.bodylength: 18, + key.attributes: [ + { + key.offset: 2349, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2369, + key.length: 12, + key.nameoffset: 2369, + key.namelength: 10, + key.bodyoffset: 2380, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.private, + key.name: "deinit", + key.offset: 2385, + key.length: 27, + key.nameoffset: 2385, + key.namelength: 6, + key.bodyoffset: 2393, + key.bodylength: 18, + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2398, + key.length: 12, + key.nameoffset: 2398, + key.namelength: 10, + key.bodyoffset: 2409, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Result", + key.offset: 2425, + key.length: 38, + key.nameoffset: 2435, + key.namelength: 6, + key.bodyoffset: 2443, + key.bodylength: 19, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "foo()", + key.offset: 2447, + key.length: 13, + key.nameoffset: 2452, + key.namelength: 5, + key.bodyoffset: 2459, + key.bodylength: 0 + } + ] } ], key.diagnostics: [ @@ -1505,6 +1576,22 @@ key.sourcetext: "func " } ] + }, + { + key.line: 143, + key.column: 12, + key.filepath: "-foobar", + key.severity: source.diagnostic.severity.error, + key.description: "initializers may only be declared within a type", + key.diagnostic_stage: source.diagnostic.stage.swift.parse + }, + { + key.line: 147, + key.column: 1, + key.filepath: "-foobar", + key.severity: source.diagnostic.severity.error, + key.description: "deinitializers may only be declared within a class", + key.diagnostic_stage: source.diagnostic.stage.swift.parse } ] } diff --git a/test/SourceKit/DocumentStructure/structure.swift.invalid.response b/test/SourceKit/DocumentStructure/structure.swift.invalid.response index 9ee32104bfeb2..56ddd30872a9c 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.invalid.response +++ b/test/SourceKit/DocumentStructure/structure.swift.invalid.response @@ -16,7 +16,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 12, key.length: 43, @@ -27,7 +26,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls1", key.offset: 35, key.length: 18, diff --git a/test/SourceKit/DocumentStructure/structure.swift.response b/test/SourceKit/DocumentStructure/structure.swift.response index 74afaac5d23ea..8e6f1a01d121b 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.response +++ b/test/SourceKit/DocumentStructure/structure.swift.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2348, + key.length: 2472, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -223,7 +223,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 377, key.length: 45, @@ -234,7 +233,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls2", key.offset: 402, key.length: 18, @@ -558,7 +556,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "Foo", key.offset: 999, key.length: 58, @@ -569,7 +566,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "anExtendedFooFunction()", key.offset: 1019, key.length: 36, @@ -1466,6 +1462,81 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "init()", + key.offset: 2356, + key.length: 27, + key.nameoffset: 2356, + key.namelength: 6, + key.bodyoffset: 2364, + key.bodylength: 18, + key.attributes: [ + { + key.offset: 2349, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2369, + key.length: 12, + key.nameoffset: 2369, + key.namelength: 10, + key.bodyoffset: 2380, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.private, + key.name: "deinit", + key.offset: 2385, + key.length: 27, + key.nameoffset: 2385, + key.namelength: 6, + key.bodyoffset: 2393, + key.bodylength: 18, + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2398, + key.length: 12, + key.nameoffset: 2398, + key.namelength: 10, + key.bodyoffset: 2409, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Result", + key.offset: 2425, + key.length: 38, + key.nameoffset: 2435, + key.namelength: 6, + key.bodyoffset: 2443, + key.bodylength: 19, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "foo()", + key.offset: 2447, + key.length: 13, + key.nameoffset: 2452, + key.namelength: 5, + key.bodyoffset: 2459, + key.bodylength: 0 + } + ] } ], key.diagnostics: [ @@ -1505,6 +1576,22 @@ key.sourcetext: "func " } ] + }, + { + key.line: 143, + key.column: 12, + key.filepath: main.swift, + key.severity: source.diagnostic.severity.error, + key.description: "initializers may only be declared within a type", + key.diagnostic_stage: source.diagnostic.stage.swift.parse + }, + { + key.line: 147, + key.column: 1, + key.filepath: main.swift, + key.severity: source.diagnostic.severity.error, + key.description: "deinitializers may only be declared within a class", + key.diagnostic_stage: source.diagnostic.stage.swift.parse } ] } diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response index a904d1ab08d7f..190980edf2204 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response @@ -1554,7 +1554,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Class_Container", key.offset: 410, key.length: 87, @@ -1659,7 +1658,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToGlobal_Class_Container", key.offset: 649, key.length: 105, @@ -1750,7 +1748,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Class_Swift3", key.offset: 934, key.length: 117, @@ -1779,7 +1776,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Class_Swift4", key.offset: 1052, key.length: 88, @@ -1853,7 +1849,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Class_Container", key.offset: 1269, key.length: 198, @@ -1974,7 +1969,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Class_Swift3", key.offset: 1657, key.length: 127, @@ -2003,7 +1997,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Class_Swift4", key.offset: 1785, key.length: 93, @@ -2093,7 +2086,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Typedef_Container", key.offset: 2250, key.length: 82, @@ -2169,7 +2161,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToGlobal_Typedef_Container", key.offset: 2485, key.length: 109, @@ -2260,7 +2251,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Typedef_Swift3", key.offset: 2778, key.length: 121, @@ -2289,7 +2279,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Typedef_Swift4", key.offset: 2900, key.length: 83, @@ -2349,7 +2338,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Typedef_Container", key.offset: 3114, key.length: 195, @@ -2456,7 +2444,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Typedef_Swift3", key.offset: 3503, key.length: 131, @@ -2485,7 +2472,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Typedef_Swift4", key.offset: 3635, key.length: 88, diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response index 942911aa898a2..9d8951bb7351b 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response @@ -1088,7 +1088,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Class_Container", key.offset: 323, key.length: 87, @@ -1255,7 +1254,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Class_Swift4", key.offset: 741, key.length: 88, @@ -1329,7 +1327,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Class_Container", key.offset: 958, key.length: 105, @@ -1434,7 +1431,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Class_Swift4", key.offset: 1253, key.length: 93, @@ -1508,7 +1504,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Typedef_Container", key.offset: 1627, key.length: 82, @@ -1646,7 +1641,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Typedef_Swift4", key.offset: 2045, key.length: 83, @@ -1706,7 +1700,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Typedef_Container", key.offset: 2259, key.length: 100, @@ -1797,7 +1790,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Typedef_Swift4", key.offset: 2553, key.length: 88, diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.response index 2fe21a6d289f5..429e2c8b7095a 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.response @@ -6535,7 +6535,6 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "FooClassBase", key.offset: 5587, key.length: 66, @@ -6565,7 +6564,6 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "FooClassBase", key.offset: 5700, key.length: 107, @@ -6612,7 +6610,6 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "FooClassBase", key.offset: 5809, key.length: 66, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index 922c8e54ebd3c..ce2f229b7eae6 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -1120,6 +1120,73 @@ static UIdent getAccessLevelUID(AccessLevel Access) { llvm_unreachable("Unhandled access level in switch."); } +static Optional +inferDefaultAccessSyntactically(const ExtensionDecl *ED) { + // Check if the extension has an explicit access control attribute. + if (auto *AA = ED->getAttrs().getAttribute()) + return std::min(std::max(AA->getAccess(), AccessLevel::FilePrivate), + AccessLevel::Public); + return None; +} + +/// Document structure is a purely syntactic request that shouldn't require name lookup +/// or type-checking, so this is a best-effort computation, particularly where extensions +/// are concerned. +static Optional inferAccessSyntactically(const ValueDecl *D) { + assert(D); + + // Check if the decl has an explicit access control attribute. + if (auto *AA = D->getAttrs().getAttribute()) + return AA->getAccess(); + + DeclContext *DC = D->getDeclContext(); + + if (D->getKind() == DeclKind::Destructor || + D->getKind() == DeclKind::EnumElement) { + if (auto container = dyn_cast(D->getDeclContext())) + return std::max(container->getFormalAccess(), AccessLevel::Internal); + return AccessLevel::Private; + } + + switch (DC->getContextKind()) { + case DeclContextKind::TopLevelCodeDecl: + return AccessLevel::FilePrivate; + case DeclContextKind::SerializedLocal: + case DeclContextKind::AbstractClosureExpr: + case DeclContextKind::EnumElementDecl: + case DeclContextKind::Initializer: + case DeclContextKind::AbstractFunctionDecl: + case DeclContextKind::SubscriptDecl: + return AccessLevel::Private; + case DeclContextKind::Module: + case DeclContextKind::FileUnit: + return AccessLevel::Internal; + case DeclContextKind::GenericTypeDecl: { + auto generic = cast(DC); + AccessLevel access = AccessLevel::Internal; + if (isa(generic)) { + if (auto protoAccess = inferAccessSyntactically(generic)) + access = std::max(AccessLevel::FilePrivate, protoAccess.getValue()); + } + return access; + } + case DeclContextKind::ExtensionDecl: + auto *ED = cast(DC); + return inferDefaultAccessSyntactically(ED); + } + + llvm_unreachable("Unhandled DeclContextKind in switch."); +} + +static Optional +inferSetterAccessSyntactically(const AbstractStorageDecl *D) { + if (!D->isSettable(/*UseDC=*/nullptr)) + return None; + if (auto *AA = D->getAttrs().getAttribute()) + return AA->getAccess(); + return inferAccessSyntactically(D); +} + class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { SourceManager &SrcManager; EditorConsumer &Consumer; @@ -1176,16 +1243,15 @@ class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { Node.Kind != SyntaxStructureKind::LocalVariable && Node.Kind != SyntaxStructureKind::GenericTypeParam) { if (auto *VD = dyn_cast_or_null(Node.Dcl)) { - AccessLevel = getAccessLevelUID(VD->getFormalAccess()); + if (auto Access = inferAccessSyntactically(VD)) + AccessLevel = getAccessLevelUID(Access.getValue()); } else if (auto *ED = dyn_cast_or_null(Node.Dcl)) { - auto StrictAccess = ED->getDefaultAccessLevel(); - AccessLevel = getAccessLevelUID(StrictAccess); + if (auto DefaultAccess = inferDefaultAccessSyntactically(ED)) + AccessLevel = getAccessLevelUID(DefaultAccess.getValue()); } if (auto *ASD = dyn_cast_or_null(Node.Dcl)) { - if (ASD->isSettable(/*UseDC=*/nullptr)) { - swift::AccessLevel SetAccess = ASD->getSetterFormalAccess(); - SetterAccessLevel = getAccessLevelUID(SetAccess); - } + if (auto SetAccess = inferSetterAccessSyntactically(ASD)) + SetterAccessLevel = getAccessLevelUID(SetAccess.getValue()); } } From b530e87737bfcec56a4c02b8fc6aa3b7e207a6d3 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 16 Nov 2019 14:51:32 -0800 Subject: [PATCH 238/283] [AutoDiff upstream] Organize AutoDiff tests. Move all tests into appropriate subdirectories. `test/AutoDiff` subdirectory naming matches `test/` subdirectory name. --- test/AutoDiff/{ => Parse}/differentiable_attr_parse.swift | 0 test/AutoDiff/{ => stdlib}/differentiable_protocol.swift | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/AutoDiff/{ => Parse}/differentiable_attr_parse.swift (100%) rename test/AutoDiff/{ => stdlib}/differentiable_protocol.swift (100%) diff --git a/test/AutoDiff/differentiable_attr_parse.swift b/test/AutoDiff/Parse/differentiable_attr_parse.swift similarity index 100% rename from test/AutoDiff/differentiable_attr_parse.swift rename to test/AutoDiff/Parse/differentiable_attr_parse.swift diff --git a/test/AutoDiff/differentiable_protocol.swift b/test/AutoDiff/stdlib/differentiable_protocol.swift similarity index 100% rename from test/AutoDiff/differentiable_protocol.swift rename to test/AutoDiff/stdlib/differentiable_protocol.swift From 2d2a5ded9d8803775505259b120beeaf13c0b49e Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 16 Nov 2019 15:07:57 -0800 Subject: [PATCH 239/283] [AutoDiff upstream] Minor cleanup for `@differentiable` attribute. Add TODO comments referencing JIRA issues. Add disabled `@differentiable` attribute serialization test. TF-836 tracks enabling the test. Blocked by TF-828: `@differentiable` attribute type-checking. --- lib/AST/Attr.cpp | 7 + lib/Sema/TypeCheckAttr.cpp | 3 +- lib/Serialization/Serialization.cpp | 5 + .../Serialization/differentiable_attr.swift | 129 ++++++++++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 test/AutoDiff/Serialization/differentiable_attr.swift diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 0600d3591d06b..87c5bca84dce2 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -857,6 +857,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer << ")"; break; + case DAK_Differentiable: { + Printer.printAttrName("@differentiable"); + auto *attr = cast(this); + printDifferentiableAttrArguments(attr, Printer, Options, D); + break; + } + case DAK_Count: llvm_unreachable("exceed declaration attribute kinds"); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index acf1b4882c8cb..b3e20dc595d21 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -112,7 +112,8 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(ProjectedValueProperty) IGNORED_ATTR(ReferenceOwnership) - // TODO: Changes are yet to be upstreamed from apple/tensorflow branch. + // TODO(TF-828): Upstream `@differentiable` attribute type-checking from + // tensorflow branch. IGNORED_ATTR(Differentiable) #undef IGNORED_ATTR diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index fbdb17f2d83cb..f5c908eb8eae8 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2291,6 +2291,11 @@ class Serializer::DeclSerializer : public DeclVisitor { vjpRef = S.addDeclRef(vjpFunction); auto paramIndices = attr->getParameterIndices(); + // TODO(TF-837): Implement `@differentiable` attribute serialization. + // Blocked by TF-828: `@differentiable` attribute type-checking, which + // resolves parameter indices (`IndexSubset *`). + if (!paramIndices) + return; assert(paramIndices && "Checked parameter indices must be resolved"); SmallVector indices; for (unsigned i : range(paramIndices->getCapacity())) diff --git a/test/AutoDiff/Serialization/differentiable_attr.swift b/test/AutoDiff/Serialization/differentiable_attr.swift new file mode 100644 index 0000000000000..88b0f0bb844c7 --- /dev/null +++ b/test/AutoDiff/Serialization/differentiable_attr.swift @@ -0,0 +1,129 @@ +// SWIFT_ENABLE_TENSORFLOW + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -emit-module -parse-as-library -o %t +// RUN: llvm-bcanalyzer %t/differentiable_attr.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -disable-sil-linking -enable-sil-verify-all %t/differentiable_attr.swiftmodule -o - | %FileCheck %s +// REQUIRES: differentiable_programming + +// TODO(TF-836): Enable this test. +// Blocked by TF-828: `@differentiating` attribute type-checking. +// XFAIL: * + +// BCANALYZER-NOT: UnknownCode + +import _Differentiation + +// CHECK: @differentiable(wrt: x, jvp: jvpSimple, vjp: vjpSimple) +// CHECK-NEXT: func simple(x: Float) -> Float +@differentiable(jvp: jvpSimple, vjp: vjpSimple) +func simple(x: Float) -> Float { + return x +} + +// CHECK: @differentiable(linear, wrt: x) +// CHECK-NEXT: func simple2(x: Float) -> Float +@differentiable(linear) +func simple2(x: Float) -> Float { + return x +} + +// CHECK: @differentiable(linear, wrt: x) +// CHECK-NEXT: func simple4(x: Float) -> Float +@differentiable(linear, wrt: x) +func simple4(x: Float) -> Float { + return x +} + +func jvpSimple(x: Float) -> (Float, (Float) -> Float) { + return (x, { v in v }) +} + +func vjpSimple(x: Float) -> (Float, (Float) -> Float) { + return (x, { v in v }) +} + +// CHECK: @differentiable(wrt: x) +// CHECK-NEXT: func testWrtClause(x: Float, y: Float) -> Float +@differentiable(wrt: x) +func testWrtClause(x: Float, y: Float) -> Float { + return x + y +} + +struct InstanceMethod : Differentiable { + // CHECK: @differentiable(wrt: (self, y)) + // CHECK-NEXT: func testWrtClause(x: Float, y: Float) -> Float + @differentiable(wrt: (self, y)) + func testWrtClause(x: Float, y: Float) -> Float { + return x + y + } + + struct TangentVector: Differentiable, AdditiveArithmetic { + typealias TangentVector = Self + static func ==(_: Self, _: Self) -> Bool { fatalError() } + static var zero: Self { fatalError() } + static func +(_: Self, _: Self) -> Self { fatalError() } + static func -(_: Self, _: Self) -> Self { fatalError() } + } + mutating func move(along direction: TangentVector) {} +} + +// CHECK: @differentiable(wrt: x where T : Differentiable) +// CHECK-NEXT: func testOnlyWhereClause(x: T) -> T where T : Numeric +@differentiable(where T : Differentiable) +func testOnlyWhereClause(x: T) -> T { + return x +} + +// CHECK: @differentiable(wrt: x, vjp: vjpTestWhereClause where T : Differentiable) +// CHECK-NEXT: func testWhereClause(x: T) -> T where T : Numeric +@differentiable(vjp: vjpTestWhereClause where T : Differentiable) +func testWhereClause(x: T) -> T { + return x +} +func vjpTestWhereClause(x: T) -> (T, (T.TangentVector) -> T.TangentVector) + where T : Numeric, T : Differentiable +{ + return (x, { v in v }) +} + +protocol P {} +extension P { + // CHECK: @differentiable(wrt: self, vjp: vjpTestWhereClauseMethod where Self : Differentiable) + // CHECK-NEXT: func testWhereClauseMethod() -> Self + @differentiable(wrt: self, vjp: vjpTestWhereClauseMethod where Self : Differentiable) + func testWhereClauseMethod() -> Self { + return self + } +} +extension P where Self : Differentiable { + func vjpTestWhereClauseMethod() -> (Self, (Self.TangentVector) -> Self.TangentVector) { + return (self, { v in v }) + } +} + +// CHECK: @differentiable(wrt: x, vjp: vjpTestWhereClauseMethodTypeConstraint where T : Differentiable, T == T.TangentVector) +// CHECK-NEXT: func testWhereClauseMethodTypeConstraint(x: T) -> T where T : Numeric +@differentiable(vjp: vjpTestWhereClauseMethodTypeConstraint where T : Differentiable, T == T.TangentVector) +func testWhereClauseMethodTypeConstraint(x: T) -> T { + return x +} +func vjpTestWhereClauseMethodTypeConstraint(x: T) -> (T, (T) -> T) + where T : Numeric, T : Differentiable, T == T.TangentVector +{ + return (x, { v in v }) +} + +extension P { + // CHECK: @differentiable(wrt: self, vjp: vjpTestWhereClauseMethodTypeConstraint where Self : Differentiable, Self == Self.TangentVector) + // CHECK-NEXT: func testWhereClauseMethodTypeConstraint() -> Self + @differentiable(wrt: self, vjp: vjpTestWhereClauseMethodTypeConstraint where Self.TangentVector == Self, Self : Differentiable) + func testWhereClauseMethodTypeConstraint() -> Self { + return self + } +} +extension P where Self : Differentiable, Self == Self.TangentVector { + func vjpTestWhereClauseMethodTypeConstraint() -> (Self, (Self.TangentVector) -> Self.TangentVector) { + return (self, { v in v }) + } +} From 2b8e266694dbdb03cd7d5d5c06c3e368a1ff6b09 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 16 Nov 2019 15:54:12 -0800 Subject: [PATCH 240/283] Revert "[pmo] Fix load [copy] like I fixed load_borrow." --- include/swift/Basic/STLExtras.h | 4 +- lib/SIL/OwnershipUtils.cpp | 14 +- .../Mandatory/PredictableMemOpt.cpp | 361 +++++----- .../predictable_memaccess_opts.sil | 621 +----------------- .../predictable_memopt_ownership.sil | 59 +- 5 files changed, 190 insertions(+), 869 deletions(-) diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index 96a7c9e6a6334..a0025dfb4b917 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -639,12 +639,12 @@ inline T accumulate(const Container &C, T init, BinaryOperation op) { } template -inline bool binary_search(const Container &C, const T &value) { +inline bool binary_search(const Container &C, T value) { return std::binary_search(C.begin(), C.end(), value); } template -inline bool binary_search(const Container &C, const T &value, BinaryOperation op) { +inline bool binary_search(const Container &C, T value, BinaryOperation op) { return std::binary_search(C.begin(), C.end(), value, op); } diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index feeab6da18e6f..bbb30c28a500d 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -163,13 +163,6 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } - // If v produces .none ownership, then we can ignore it. It is important - // that we put this before checking for guaranteed forwarding instructions, - // since we want to ignore guaranteed forwarding instructions that in this - // specific case produce a .none value. - if (v.getOwnershipKind() == ValueOwnershipKind::None) - continue; - // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isGuaranteedForwardingValue(v)) { @@ -180,9 +173,10 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } - // Otherwise, this is an introducer we do not understand. Bail and return - // false. - return false; + // If v produces any ownership, then we can ignore it. Otherwise, we need to + // return false since this is an introducer we do not understand. + if (v.getOwnershipKind() != ValueOwnershipKind::None) + return false; } return true; diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 0e30b247206ec..9304d9bd786df 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -13,7 +13,6 @@ #define DEBUG_TYPE "predictable-memopt" #include "PMOMemoryUseCollector.h" -#include "swift/Basic/STLExtras.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/BranchPropagatedUser.h" #include "swift/SIL/OwnershipUtils.h" @@ -446,6 +445,9 @@ class AvailableValueAggregator { bool isTopLevel = true); bool canTake(SILType loadTy, unsigned firstElt) const; + SingleValueInstruction *addMissingDestroysForCopiedValues(LoadInst *li, + SILValue newVal); + void print(llvm::raw_ostream &os) const; void dump() const LLVM_ATTRIBUTE_USED; @@ -465,19 +467,13 @@ class AvailableValueAggregator { /// reference counts of the intermediate copies and phis to ensure that all /// forwarding operations in the CFG are strongly control equivalent (i.e. run /// the same number of times). - void fixupOwnership(SILInstruction *load, SILValue newVal) { - assert(isa(load) || isa(load)); - - // Sort phi nodes so we can use it for bisection operations. - sort(insertedPhiNodes); - + void fixupOwnership(LoadBorrowInst *lbi, SILValue newVal) { // Sort inserted insts so we can bisect upon it and mark copy_value as needing // to be skipped. sort(insertedInsts); - SmallBitVector instsToSkip(insertedInsts.size()); - addHandOffCopyDestroysForPhis(load, newVal, instsToSkip); - addMissingDestroysForCopiedValues(load, newVal, instsToSkip); + addHandOffCopyDestroysForPhis(lbi, newVal, instsToSkip); + addMissingDestroysForCopiedValues(lbi, newVal, instsToSkip); } private: @@ -494,8 +490,8 @@ class AvailableValueAggregator { /// If as a result of us copying values, we may have unconsumed destroys, find /// the appropriate location and place the values there. Only used when /// ownership is enabled. - void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal, - const SmallBitVector &instsToSkip); + void addMissingDestroysForCopiedValues(LoadBorrowInst *li, SILValue newVal, + const SmallBitVector &instsToSkip); /// As a result of us using the SSA updater, insert hand off copy/destroys at /// each phi and make sure that intermediate phis do not leak by inserting @@ -730,15 +726,9 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // Finally, grab the value from the SSA updater. SILValue result = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); - assert(result.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); - if (isTake() || !B.hasOwnership()) { - return result; - } - // Be careful with this value and insert a copy in our load block to prevent - // any weird control equivalence issues. - SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); - return builder.emitCopyValueOperation(Loc, result); + assert(result.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); + return result; } SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, @@ -769,7 +759,7 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, // If we are going to use this to promote a borrowed value, insert borrow // operations. Eventually I am going to do this for everything, but this // should make it easier to bring up. - if (!isTake()) { + if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { for (unsigned i : indices(ResultElts)) { ResultElts[i] = B.emitBeginBorrowOperation(Loc, ResultElts[i]); } @@ -803,7 +793,7 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, firstElt += numSubElt; } - if (!isTake()) { + if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { for (unsigned i : indices(resultElts)) { resultElts[i] = B.emitBeginBorrowOperation(Loc, resultElts[i]); } @@ -850,13 +840,7 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, !builder.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - - if (!builder.hasOwnership()) { - return eltVal; - } - - SILBuilderWithScope builder2(&*B.getInsertionPoint(), &insertedInsts); - return builder2.emitCopyValueOperation(Loc, eltVal); + return eltVal; } // If we have an available value, then we want to extract the subelement from @@ -908,50 +892,75 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, assert(!B.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - if (!B.hasOwnership()) - return eltVal; - SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); - return builder.emitCopyValueOperation(Loc, eltVal); + return eltVal; } -namespace { - -struct PhiNodeCleanupState { - /// The incoming value that we need to cleanup. - SILValue incomingValue; - - /// The copy that we inserted right before the phi that will be fed into the - /// phi. - CopyValueInst *phiCopy; +SingleValueInstruction * +AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li, + SILValue newVal) { + assert(B.hasOwnership() && + "We assume this is only called if we have ownership"); - PhiNodeCleanupState(SILValue incomingValue, CopyValueInst *phiCopy) - : incomingValue(incomingValue), phiCopy(phiCopy) {} + SmallPtrSet visitedBlocks; + SmallVector leakingBlocks; + bool foundLoop = false; + auto loc = RegularLocation::getAutoGeneratedLocation(); + while (!insertedInsts.empty()) { + auto *cvi = dyn_cast(insertedInsts.pop_back_val()); + if (!cvi) + continue; - /// If our incoming value is not defined in the block in our phi node's block, - /// return the insertion point to use to insert destroy_value for the incoming - /// value. Otherwise, return nullptr. - SILInstruction *getNonPhiBlockIncomingValueDef() const; -}; + // Clear our state. + visitedBlocks.clear(); + leakingBlocks.clear(); + // The linear lifetime checker doesn't care if the passed in load is + // actually a user of our copy_value. What we care about is that the load is + // guaranteed to be in the block where we have reformed the tuple in a + // consuming manner. This means if we add it as the consuming use of the + // copy, we can find the leaking places if any exist. + // + // Then perform the linear lifetime check. If we succeed, continue. We have + // no further work to do. + auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; + LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); + auto error = checker.checkValue( + cvi, {BranchPropagatedUser(&li->getAllOperands()[0])}, {}, errorKind, + &leakingBlocks); + if (!error.getFoundError()) + continue; -} // end anonymous namespace + // Ok, we found some leaking blocks. Since we are using the linear lifetime + // checker with memory, we do not have any guarantees that the store is out + // side of a loop and a load is in a loop. In such a case, we want to + // replace the load with a copy_value. + foundLoop |= error.getFoundOverConsume(); -SILInstruction *PhiNodeCleanupState::getNonPhiBlockIncomingValueDef() const { - auto *phiBlock = phiCopy->getParent(); - if (phiBlock == incomingValue->getParentBlock()) { - return nullptr; + // Ok, we found some leaking blocks. Insert destroys at the + // beginning of these blocks for our copy_value. + for (auto *bb : leakingBlocks) { + SILBuilderWithScope b(bb->begin()); + b.emitDestroyValueOperation(loc, cvi); + } } - if (auto *cvi = dyn_cast(incomingValue)) { - return cvi; + // If we didn't find a loop, we are done, just return svi to get RAUWed. + if (!foundLoop) { + return li; } - assert(isa(incomingValue)); + // If we found a loop, then we know that our leaking blocks are the exiting + // blocks of the loop and the value has been lifetime extended over the loop. + + // If we have a load, we need to put in a copy so that the destroys within + // the loop are properly balanced. + newVal = SILBuilderWithScope(li).emitCopyValueOperation(loc, newVal); - // Otherwise, our copy_value may not be post-dominated by our phi. To - // work around that, we need to insert destroys along the other - // paths. So set base to the first instruction in our argument's block, - // so we can insert destroys for our base. - return &*incomingValue->getParentBlock()->begin(); + li->replaceAllUsesWith(newVal); + SILValue addr = li->getOperand(); + li->eraseFromParent(); + if (auto *addrI = addr->getDefiningInstruction()) + recursivelyDeleteTriviallyDeadInstructions(addrI); + return nullptr; } void AvailableValueAggregator::addHandOffCopyDestroysForPhis( @@ -964,72 +973,45 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( SmallVector, 8> incomingValues; auto loc = RegularLocation::getAutoGeneratedLocation(); -#ifndef NDEBUG LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); +#ifndef NDEBUG for (auto *phi : insertedPhiNodes) { LLVM_DEBUG(llvm::dbgs() << "Phi: " << *phi); } #endif // Before we begin, identify the offset for all phis that are intermediate - // phis inserted by the SSA updater. We are taking advantage of the fact that - // the SSA updater just constructs the web without knowledge of ownership. So - // if a phi node is only used by another phi node that we inserted, then we - // have an intermediate phi node. + // phis inserted by the SSA updater. SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); for (unsigned i : indices(insertedPhiNodes)) { - if (auto *termInst = insertedPhiNodes[i]->getSingleUserOfType()) { - // Only set the value if we find termInst has a successor with a phi node - // in our insertedPhiNodes. - for (auto succBBArgs : termInst->getSuccessorBlockArguments()) { - if (any_of(succBBArgs, [&](SILPhiArgument *arg) { - return binary_search(insertedPhiNodes, arg); - })) { - intermediatePhiOffsets.set(i); - break; - } - } + if (insertedPhiNodes[i]->getSingleUserOfType()) { + intermediatePhiOffsets.set(i); } } // First go through all of our phi nodes doing the following: // // 1. If any of the phi node have a copy_value as an operand, we know that the - // copy_value does not dominate our final definition since otherwise the - // SSA updater would not have inserted a phi node here. In such a case - // since we may not have that the copy_value is post-dominated by the phi, - // we need to insert a copy_value at the phi to allow for post-domination - // and then use the ValueLifetimeChecker to determine the rest of the - // frontier for the base value. + // copy_value does not dominate our final definition. In such a case since + // we may not have that the copy_value is post-dominated by the phi, we + // need to insert a copy_value at the phi to allow for post-domination and + // then use the ValueLifetimeChecker to determine the rest of the frontier + // for the value. // // 2. If our phi node is used by another phi node, we run into a similar // problem where we could have that our original phi node does not dominate - // our final definition (since the SSA updater would not have inserted the - // phi) and may not be strongly control dependent on our phi. To work - // around this problem, we insert at the phi a copy_value to allow for the - // phi to post_dominate its copy and then extend the lifetime of the phied - // value over that copy. - // - // As an extra complication to this, when we insert compensating releases for - // any copy_values from (1), we need to insert the destroy_value on "base - // values" (either a copy_value or the first instruction of a phi argument's - // block) /after/ we have found all of the base_values to ensure that if the - // same base value is used by multiple phis, we do not insert too many destroy - // value. - // - // NOTE: At first glance one may think that such a problem could not occur - // with phi nodes as well. Sadly if we allow for double backedge loops, it is - // possible (there may be more cases). - llvm::SmallVector phiNodeCleanupState; - + // our final definition and may not be strongly control dependent on our + // phi. To work around this problem, we insert at the phi a copy_value to + // allow for the phi to post_dominate its copy and then extend the lifetime + // of the phied value over that copy. for (unsigned i : indices(insertedPhiNodes)) { - auto *phi = insertedPhiNodes[i]; + auto *phiArg = insertedPhiNodes[i]; - // If our phi is not owned, continue. No fixes are needed. - if (phi->getOwnershipKind() != ValueOwnershipKind::Owned) + // If our phiArg is not owned, continue. No fixes are needed. + if (phiArg->getOwnershipKind() != ValueOwnershipKind::Owned) continue; - LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phi); + LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phiArg); // Otherwise, we have a copy_value that may not be strongly control // equivalent with our phi node. In such a case, we need to use // ValueLifetimeAnalysis to lifetime extend the copy such that we can @@ -1039,8 +1021,8 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( leakingBlocks.clear(); incomingValues.clear(); - phi->getIncomingPhiValues(incomingValues); - unsigned phiIndex = phi->getIndex(); + phiArg->getIncomingPhiValues(incomingValues); + unsigned phiIndex = phiArg->getIndex(); for (auto pair : incomingValues) { SILValue value = pair.second; @@ -1073,13 +1055,54 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // that for our actual phi. auto *termInst = pair.first->getTerminator(); SILBuilderWithScope builder(termInst); - CopyValueInst *phiCopy = builder.createCopyValue(loc, value); + auto *phiCopy = builder.createCopyValue(loc, value); termInst->setOperand(phiIndex, phiCopy); - // Now that we know our base, phi, phiCopy for this specific incoming - // value, append it to the phiNodeClenaupState so we can insert - // destroy_values late after we visit all insertedPhiNodes. - phiNodeCleanupState.emplace_back(value, phiCopy); + // Normalize on our base now that we have inserted the copy_value into the + // terminator block. If we have a copy_value, just use it directly as our + // base. We know it isn't in the block of our phiCopy due to a check + // above. + SILInstruction *base = nullptr; + if (auto *cvi = dyn_cast(value)) { + assert(cvi->getParent() != phiCopy->getParent() && + "Just to check invariant from above"); + base = cvi; + } else { + assert(isa(value)); + // If we have a phi argument and our incoming value block is the same as + // our phi block, we know that the copy_value we inserted will only be + // used by the phi. So insert a destroy_value in the incoming value + // block after the copy_value that we inserted and then continue. + if (pair.first == value->getParentBlock()) { + builder.createDestroyValue(loc, value); + continue; + } + + // Otherwise, our copy_value may not be post-dominated by our phi. To + // work around that, we need to insert destroys along the other + // paths. So set base to the first instruction in our argument's block, + // so we can insert destroys for our base. + base = &*value->getParentBlock()->begin(); + } + assert(base && "Should have been assigned"); + + // Then lifetime extend our base over the copy_value. + assert(lifetimeFrontier.empty()); + ValueLifetimeAnalysis analysis(base, phiCopy); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, + &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createDestroyValue(loc, value); + } + + visitedBlocks.clear(); + leakingBlocks.clear(); } // Then see if our phi is an intermediate phi. If it is an intermediate phi, @@ -1108,8 +1131,8 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - phi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, - &leakingBlocks); + phiArg, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, + errorKind, &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly @@ -1117,7 +1140,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // for the copy_value. auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation(next->getLoc(), phi); + builder.emitDestroyValueOperation(next->getLoc(), phiArg); continue; } @@ -1128,96 +1151,23 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( if (!error.getFoundOverConsume()) { auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation(next->getLoc(), phi); + builder.emitDestroyValueOperation(next->getLoc(), phiArg); } // Ok, we found some leaking blocks. Insert destroys at the beginning of // these blocks for our copy_value. for (auto *bb : leakingBlocks) { SILBuilderWithScope b(bb->begin()); - b.emitDestroyValueOperation(loc, phi); - } - } - - // At this point, we have visited all of our phis, lifetime extended them to - // the the load block, and inserted phi copies at all of our intermediate phi - // nodes. Now we need to cleanup and insert all of the compensating - // destroy_value that we need. We do this by sorting our phiNodeCleanupState - // just by baseValue. This will ensure that all values with the same base - // value are able to have all of their phiCopies passed at the same time to - // the ValueLifetimeAnalysis. - stable_sort(phiNodeCleanupState, [](const PhiNodeCleanupState &lhs, - const PhiNodeCleanupState &rhs) { - return lhs.incomingValue < rhs.incomingValue; - }); - - for (auto ii = phiNodeCleanupState.begin(), ie = phiNodeCleanupState.end(); - ii != ie;) { - SILValue incomingValue = ii->incomingValue; - - // First find the end of the values for which ii does not equal baseValue. - auto rangeEnd = std::find_if_not( - std::next(ii), ie, [&](const PhiNodeCleanupState &next) { - return incomingValue == next.incomingValue; - }); - - SWIFT_DEFER { - // Once we have finished processing, set ii to rangeEnd. This ensures that - // the code below does not need to worry about updating the iterator. - ii = rangeEnd; - }; - - // Before we do anything, see if we have a single cleanup state. In such a - // case, we could have that we have a phi node as an incoming value and a - // copy_value in that same block. In such a case, we want to just insert the - // copy and continue. This means that - // cleanupState.getNonPhiBlockIncomingValueDef() should always return a - // non-null value in the code below. - if (std::next(ii) == rangeEnd && isa(ii->incomingValue)) { - auto *insertPt = ii->getNonPhiBlockIncomingValueDef(); - if (!insertPt) { - CopyValueInst *phiCopy = ii->phiCopy; - SILBasicBlock *phiBlock = phiCopy->getParent(); - SILBuilderWithScope builder(phiBlock->getTerminator()); - builder.createDestroyValue(loc, incomingValue); - continue; - } - } - - // Otherwise, we know that we have for this incomingValue, multiple - // potential insert pts that we need to handle at the same time with our - // lifetime query. Gather up those uses. - SmallVector users; - transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users), - [](const PhiNodeCleanupState &value) { return value.phiCopy; }); - - // Then lifetime extend our base over the copy_value. - assert(lifetimeFrontier.empty()); - auto *def = ii->getNonPhiBlockIncomingValueDef(); - assert(def && "Should never have a nullptr here since we handled all of " - "the single block cases earlier"); - ValueLifetimeAnalysis analysis(def, users); - bool foundCriticalEdges = !analysis.computeFrontier( - lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - - while (!lifetimeFrontier.empty()) { - auto *insertPoint = lifetimeFrontier.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createDestroyValue(loc, incomingValue); + b.emitDestroyValueOperation(loc, phiArg); } - - visitedBlocks.clear(); - leakingBlocks.clear(); } - // Clear the phi node array now that we are done. insertedPhiNodes.clear(); } void AvailableValueAggregator::addMissingDestroysForCopiedValues( - SILInstruction *load, SILValue newVal, const SmallBitVector &instsToSkip) { + LoadBorrowInst *lbi, SILValue newVal, + const SmallBitVector &instsToSkip) { assert(B.hasOwnership() && "We assume this is only called if we have ownership"); @@ -1238,8 +1188,8 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // begin_borrow. if (auto *li = dyn_cast(insertedInsts[i])) { if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { - assert(li->getParent() == load->getParent()); - auto next = std::next(load->getIterator()); + assert(li->getParent() == lbi->getParent()); + auto next = std::next(lbi->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), li); continue; @@ -1267,14 +1217,14 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - cvi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, + cvi, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, errorKind, &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly // control equivalent as our load_borrow. So just insert a destroy_value // for the copy_value. - auto next = std::next(load->getIterator()); + auto next = std::next(lbi->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), cvi); continue; @@ -1285,7 +1235,7 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // this if we found a loop since our leaking blocks will lifetime extend the // value over the loop. if (!error.getFoundOverConsume()) { - auto next = std::next(load->getIterator()); + auto next = std::next(lbi->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), cvi); } @@ -2081,15 +2031,16 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { // blocks that we may have can be found by performing a linear lifetime check // over all copies that we found using the load as the "consuming uses" (just // for the purposes of identifying the consuming block). - agg.fixupOwnership(li, newVal); + auto *oldLoad = agg.addMissingDestroysForCopiedValues(li, newVal); - // Now that we have fixed up all of our missing destroys, insert the copy - // value for our actual load and RAUW. - newVal = SILBuilderWithScope(li).emitCopyValueOperation(li->getLoc(), newVal); + // If we are returned the load, eliminate it. Otherwise, it was already + // handled for us... so return true. + if (!oldLoad) + return true; - li->replaceAllUsesWith(newVal); - SILValue addr = li->getOperand(); - li->eraseFromParent(); + oldLoad->replaceAllUsesWith(newVal); + SILValue addr = oldLoad->getOperand(0); + oldLoad->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) recursivelyDeleteTriviallyDeadInstructions(addrI); return true; diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index 8fa9df6db1b1e..192723cfbd731 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -60,11 +60,9 @@ bb0(%0 : $Builtin.Int32): // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] -// CHECK: destroy_value [[ARG_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG_COPY_2]] +// CHECK: return [[ARG_COPY]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion' sil [ossa] @simple_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -85,14 +83,10 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: [[ARG1_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY]] -// CHECK: [[ARG2_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_BORROW:%.*]] : $Builtin.NativeObject) -// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion' sil [ossa] @struct_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -150,11 +144,9 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // CHECK: br bb3([[ARG_COPY]] : // // CHECK: bb3([[RESULT:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion_multi_insertpt' sil [ossa] @simple_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -200,16 +192,10 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] -// CHECK: [[ARG2_COPY_COPY:%.*]] = copy_value [[ARG2_COPY]] -// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] -// CHECK: [[ARG2_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY_COPY]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject) -// CHECK: [[RESULT_COPY:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -320,17 +306,12 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] // CHECK: [[SECOND_ADDR:%.*]] = struct_element_addr [[STACK]] // CHECK: [[SECOND_VAL_COPY:%.*]] = load [copy] [[SECOND_ADDR]] -// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] -// CHECK: [[SECOND_VAL_COPY_BORROW:%.*]] = begin_borrow [[SECOND_VAL_COPY]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY_BORROW]] : $Builtin.NativeObject) -// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] -// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY]] : $Builtin.NativeObject) // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT_COPY_2]] +// CHECK: return [[RESULT]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %arg2 : @owned $Builtin.NativeObject): @@ -432,11 +413,9 @@ bb3: // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] -// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] +// CHECK: return [[COPIED_ARG_FIELD]] // CHECK: } // end sil function 'simple_partialstructuse_load_promotion' sil [ossa] @simple_partialstructuse_load_promotion : $@convention(thin) (@owned NativeObjectPair) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectPair): @@ -458,11 +437,9 @@ bb0(%0 : @owned $NativeObjectPair): // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD_2]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] -// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] +// CHECK: return [[COPIED_ARG_FIELD]] // CHECK: } // end sil function 'simple_partialtupleuse_load_promotion' sil [ossa] @simple_partialtupleuse_load_promotion : $@convention(thin) (@owned NativeObjectAndTuple) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectAndTuple): @@ -482,10 +459,9 @@ bb0(%0 : @owned $NativeObjectAndTuple): // CHECK: store [[ARG0]] to [init] [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [assign] [[STACK]] -// CHECK: [[ARG1_COPY_1:%.*]] = copy_value [[ARG1_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG1_COPY_1]] +// CHECK: return [[ARG1_COPY]] // CHECK: } // end sil function 'simple_assignstore' sil [ossa] @simple_assignstore : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -512,15 +488,11 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] -// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] -// CHECK: br bb3([[LHS2_COPY_2]] : +// CHECK: br bb3([[LHS2_COPY]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] -// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] -// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] -// CHECK: br bb3([[LHS1_COPY_2]] : +// CHECK: br bb3([[LHS1_COPY]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_addr [[STACK]] @@ -1368,570 +1340,3 @@ bbEnd: return %9999 : $() } -//--- - -// CHECK-LABEL: sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load_borrow -// CHECK: } // end sil function 'load_copy_promote_with_loop_1' -sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { -bb0(%0 : @owned $NativeObjectPair): - %1 = alloc_stack $NativeObjectPair - store %0 to [init] %1 : $*NativeObjectPair - %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x - br bb2 - -bb2: - %3 = load [copy] %2 : $*Builtin.NativeObject - %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %3 : $Builtin.NativeObject - br bb2 -} - -// CHECK-LABEL: sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_loop_promote_with_loop_2' -sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { -bb0(%0 : @owned $NativeObjectPair): - %1 = alloc_stack $NativeObjectPair - store %0 to [init] %1 : $*NativeObjectPair - %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x - br bb2 - -bb2: - %3 = load [copy] %2 : $*Builtin.NativeObject - %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %3 : $Builtin.NativeObject - cond_br undef, bb3, bb4 - -bb3: - br bb2 - -bb4: - destroy_addr %1 : $*NativeObjectPair - dealloc_stack %1 : $*NativeObjectPair - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_promote_two_backedge_loop' -sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject): - %1 = alloc_stack $Builtin.NativeObject - store %0 to [init] %1 : $*Builtin.NativeObject - br bb1 - -bb1: - br bb2 - -bb2: - cond_br undef, bb3, bb4 - -bb3: - %2 = load [copy] %1 : $*Builtin.NativeObject - destroy_value %2 : $Builtin.NativeObject - cond_br undef, bb5, bb6 - -bb4: - %3 = load [copy] %1 : $*Builtin.NativeObject - destroy_value %3 : $Builtin.NativeObject - cond_br undef, bb7, bb8 - -bb5: - br bb2 - -bb6: - br bb9 - -bb7: - br bb2 - -bb8: - br bb9 - -bb9: - destroy_addr %1 : $*Builtin.NativeObject - dealloc_stack %1 : $*Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK: bb0( -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop' -sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): - %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () - %1 = alloc_stack $NativeObjectPair - %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x - %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y - cond_br undef, bb1, bb2 - -bb1: - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - br bb3 - -bb2: - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - br bb3 - -bb3: - br bb4 - -bb4: - br bb5 - -bb5: - %2 = load [copy] %1 : $*NativeObjectPair - cond_br undef, bb6, bb7 - -bb6: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - br bb5 - -bb7: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - destroy_addr %1 : $*NativeObjectPair - dealloc_stack %1 : $*NativeObjectPair - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair -// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_reload' -sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { -bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): - %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () - %1 = alloc_stack $NativeObjectPair - %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x - %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y - cond_br undef, bb1, bb2 - -bb1: - store %0a to [init] %1a : $*Builtin.NativeObject - store %0c to [init] %1b : $*Builtin.NativeObject - destroy_value %0b : $Builtin.NativeObject - br bb3 - -bb2: - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - destroy_value %0c : $Builtin.NativeObject - br bb3 - -bb3: - br bb4 - -bb4: - br bb5 - -bb5: - %2 = load [copy] %1 : $*NativeObjectPair - cond_br undef, bb6, bb7 - -bb6: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - br bb5 - -bb7: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - destroy_addr %1 : $*NativeObjectPair - dealloc_stack %1 : $*NativeObjectPair - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair -// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop' -sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): - %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () - %1 = alloc_stack $NativeObjectPair - %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x - %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y - %0bhat = copy_value %0b : $Builtin.NativeObject - cond_br undef, bb1, bb2 - -bb1: - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - br bb3 - -bb2: - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - br bb3 - -bb3: - br bb4 - -bb4: - br bb5 - -bb5: - %2 = load [copy] %1 : $*NativeObjectPair - cond_br undef, bb6, bb7 - -bb6: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - destroy_addr %1b : $*Builtin.NativeObject - %0bhat2 = copy_value %0bhat : $Builtin.NativeObject - store %0bhat2 to [init] %1b : $*Builtin.NativeObject - br bb5 - -bb7: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - destroy_value %0bhat : $Builtin.NativeObject - destroy_addr %1 : $*NativeObjectPair - dealloc_stack %1 : $*NativeObjectPair - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy' -sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject): - %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - %1 = alloc_stack $Builtin.NativeObject - store %0 to [init] %1 : $*Builtin.NativeObject - cond_br undef, bb1, bb7 - -bb1: - br bb2 - -bb2: - br bb3 - -bb3: - %2 = load [copy] %1 : $*Builtin.NativeObject - cond_br undef, bb4, bb5 - -bb4: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bb2 - -bb5: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bb6 - -bb6: - br bb8 - -bb7: - br bb8 - -bb8: - destroy_addr %1 : $*Builtin.NativeObject - dealloc_stack %1 : $*Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy_2' -sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject): - %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - %1 = alloc_stack $Builtin.NativeObject - store %0 to [init] %1 : $*Builtin.NativeObject - cond_br undef, bb1, bb7 - -bb1: - br bb2 - -bb2: - br bb3 - -bb3: - %2 = load [copy] %1 : $*Builtin.NativeObject - cond_br undef, bb4, bb5 - -bb4: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bb2 - -bb5: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bb6 - -bb6: - br bb8 - -bb7: - br bb8 - -bb8: - destroy_addr %1 : $*Builtin.NativeObject - dealloc_stack %1 : $*Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy_3' -sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): - %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () - %1 = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) - %1a = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 0 - %1b = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 1 - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - cond_br undef, bb1, bb7 - -bb1: - br bb2 - -bb2: - br bb3 - -bb3: - %0ccopy = copy_value %0c : $Builtin.NativeObject - destroy_addr %1a : $*Builtin.NativeObject - store %0ccopy to [init] %1a : $*Builtin.NativeObject - %2 = load [copy] %1 : $*(Builtin.NativeObject, Builtin.NativeObject) - cond_br undef, bb4, bb5 - -bb4: - apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () - destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) - br bb2 - -bb5: - apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () - destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) - br bb6 - -bb6: - br bb8 - -bb7: - br bb8 - -bb8: - destroy_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject) - dealloc_stack %1 : $*(Builtin.NativeObject, Builtin.NativeObject) - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'loop_carry_loadcopy_4' -sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { -bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): - %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () - %1 = alloc_stack $NativeObjectPair - %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x - %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y - store %0a to [init] %1a : $*Builtin.NativeObject - store %0b to [init] %1b : $*Builtin.NativeObject - cond_br undef, bb1, bb7 - -bb1: - br bb2 - -bb2: - br bb3 - -bb3: - %0ccopy = copy_value %0c : $Builtin.NativeObject - destroy_addr %1a : $*Builtin.NativeObject - store %0ccopy to [init] %1a : $*Builtin.NativeObject - %2 = load [copy] %1 : $*NativeObjectPair - cond_br undef, bb4, bb5 - -bb4: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - br bb2 - -bb5: - apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () - destroy_value %2 : $NativeObjectPair - br bb6 - -bb6: - br bb8 - -bb7: - br bb8 - -bb8: - destroy_addr %1 : $*NativeObjectPair - dealloc_stack %1 : $*NativeObjectPair - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent' -sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { -bb0(%arg : @owned $Builtin.NativeObject): - %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - %0 = alloc_stack $Builtin.NativeObject - cond_br undef, bb1, bb2 - -bb1: - cond_br undef, bb3, bb4 - -bb2: - store %arg to [init] %0 : $*Builtin.NativeObject - br bb5 - -bb3: - store %arg to [init] %0 : $*Builtin.NativeObject - br bb6 - -bb4: - store %arg to [init] %0 : $*Builtin.NativeObject - br bb7 - -bb5: - br bb8 - -bb6: - br bb8 - -bb7: - br bbPreLoopHeader - -bb8: - br bbPreLoopHeader - -bbPreLoopHeader: - br bbLoop - -bbLoop: - br bbLoop1 - -bbLoop1: - br bbLoop2 - -bbLoop2: - %2 = load [copy] %0 : $*Builtin.NativeObject - cond_br undef, bbLoop3, bbLoop4 - -bbLoop3: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bbLoop2 - -bbLoop4: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bbEnd - -bbEnd: - destroy_addr %0 : $*Builtin.NativeObject - dealloc_stack %0 : $*Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} - -// In this case, we will have that we need to separately lifetime extend our phi -// node's copy to prevent leaks along the edge skipping the loop. -// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -// CHECK-NOT: load [copy] -// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent_2' -sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { -bb0(%arg : @owned $Builtin.NativeObject): - %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - %0 = alloc_stack $Builtin.NativeObject - cond_br undef, bb1, bb2 - -bb1: - cond_br undef, bb3, bb4 - -bb2: - store %arg to [init] %0 : $*Builtin.NativeObject - br bb5 - -bb3: - store %arg to [init] %0 : $*Builtin.NativeObject - br bb6 - -bb4: - store %arg to [init] %0 : $*Builtin.NativeObject - br bb7 - -bb5: - br bb8a - -bb6: - br bb8a - -bb7: - br bbPreLoopHeader - -bb8a: - br bb8 - -bb8: - cond_br undef, bbPreLoopHeader1, bbSkipLoop - -bbPreLoopHeader: - br bbLoop - -bbPreLoopHeader1: - br bbLoop - -bbLoop: - br bbLoop1 - -bbLoop1: - br bbLoop2 - -bbLoop2: - %2 = load [copy] %0 : $*Builtin.NativeObject - br bbLoop6 - -bbLoop6: - cond_br undef, bbLoop3, bbLoop4 - -bbLoop3: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bbLoop5 - -bbLoop5: - br bbLoop2 - -bbLoop4: - apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () - destroy_value %2 : $Builtin.NativeObject - br bbEnd - -bbSkipLoop: - br bbEnd - -bbEnd: - destroy_addr %0 : $*Builtin.NativeObject - dealloc_stack %0 : $*Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} - diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index e18ace13123ac..ebb893ac4b803 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -211,12 +211,8 @@ bb0(%0 : @owned $ContainsNativeObject): // CHECK: [[f3:%.*]] = struct_extract [[BORROWED_ARG]] : $ComplexStruct, #ComplexStruct.f1 // CHECK: [[f3_copy:%.*]] = copy_value [[f3]] // CHECK: end_borrow [[BORROWED_ARG]] -// CHECK: [[f3_copy_1:%.*]] = copy_value [[f3_copy]] -// CHECK: [[f3_copy_2:%.*]] = copy_value [[f3_copy_1]] -// CHECK: [[f2_x_copy_1:%.*]] = copy_value [[f2_x_copy]] -// CHECK: [[f2_x_copy_2:%.*]] = copy_value [[f2_x_copy_1]] // CHECK: destroy_value [[ARG]] -// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy_2]] : $Builtin.NativeObject, [[f2_x_copy_2]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) +// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy]] : $Builtin.NativeObject, [[f2_x_copy]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) // CHECK: return [[RESULT]] // CHECK: } // end sil function 'multiple_level_extract_2' sil [ossa] @multiple_level_extract_2 : $@convention(thin) (@owned ComplexStruct) -> (@owned Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32) { @@ -563,15 +559,11 @@ bb3: // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] -// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] -// CHECK: br bb3([[LHS2_COPY_2]] : +// CHECK: br bb3([[LHS2_COPY]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] : $Builtin.NativeObject -// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] -// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] -// CHECK: br bb3([[LHS1_COPY_2]] : +// CHECK: br bb3([[LHS1_COPY]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_value [[ARG]] @@ -657,11 +649,9 @@ struct NativeObjectTriple { // CHECK-NEXT: br bb3([[PAIR_LHS_COPY]] : // // CHECK: bb3([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[PHI_COPY_1:%.*]] = copy_value [[PHI]] -// CHECK: [[PHI_COPY_2:%.*]] = copy_value [[PHI_COPY_1]] -// CHECK: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) -// CHECK: destroy_value [[REFORMED]] -// CHECK: return [[PHI_COPY_2]] +// CHECK-NEXT: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) +// CHECK-NEXT: destroy_value [[REFORMED]] +// CHECK-NEXT: return [[PHI]] // CHECK: } // end sil function 'diamond_test_4' sil [ossa] @diamond_test_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair): @@ -720,14 +710,9 @@ bb3: // CHECK: bb4: // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] -// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_RHS_VAL_COPY]] -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL_COPY_BORROW]] : {{.*}}) -// CHECK: [[STRUCT_COPY:%.*]] = copy_value [[STRUCT]] -// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL]] : {{.*}}) // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT_COPY_2]] +// CHECK: return [[STRUCT]] // CHECK: } // end sil function 'diamond_test_5' sil [ossa] @diamond_test_5 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): @@ -773,14 +758,10 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : +// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : +// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : // // CHECK: [[FALSE_BB]]: // CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 @@ -793,14 +774,10 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL_COPY]] : +// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] -// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL_COPY]] : +// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL]] : // // CHECK: [[SUCC_2]]([[PHI1:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 @@ -809,21 +786,15 @@ bb4: // CHECK: br [[EXIT_BB:bb[0-9]+]]([[PHI1:%.*]] : $Builtin.NativeObject) // // CHECK: [[SUCC_1]]([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] -// CHECK: br [[EXIT_BB]]([[PHI_COPY]] : {{.*}}) +// CHECK: br [[EXIT_BB]]([[PHI]] : {{.*}}) // // CHECK: [[EXIT_BB]]([[PHI:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] -// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] -// CHECK: [[PHI_COPY_BORROW:%.*]] = begin_borrow [[PHI_COPY]] -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[PHI_COPY_BORROW]] : {{.*}}) -// CHECK: [[STRUCT_COPY_1:%.*]] = copy_value [[STRUCT]] -// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY_1]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[PHI]] : {{.*}}) // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT_COPY_2]] +// CHECK: return [[STRUCT]] // CHECK: } // end sil function 'diamond_test_6' sil [ossa] @diamond_test_6 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): From fa6c07d31d63308fdb6bbd02a4415d2f8ab3a48e Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Sat, 16 Nov 2019 16:27:10 -0800 Subject: [PATCH 241/283] [Docs] [AutoDiff] Rename '@nondiff' to '@noDerivative'. We agreed to move to rename `@nondiff` to `@noDerivative` (in line with the `@noDerivative` declaration attribute) in #28278. --- docs/DifferentiableProgramming.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index fa536af3de8c9..6ebb34b1c7ccd 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -1827,13 +1827,13 @@ The `@differentiable` attribute requires the function type it is attached to have differentiable parameters and results. Each parameter and result must conform to the `Differentiable` protocol (or `Differentiable & AdditiveArithmetic` when the attribute is `@differentiable(linear)`) unless it -is marked with `@nondiff`. +is marked with `@noDerivative`.

-_Note: `@nondiff` stands for "not being differentiated", and will likely be +_Note: `@noDerivative` stands for "not being differentiated", and will likely be unified with `@noDerivative`._ #### Type conversion @@ -1899,13 +1899,13 @@ let f2: (Float) -> Float = f1 ``` A `@differentiable` function can also be converted to a function which is -identical except that more of its parameters are marked with `@nondiff`. +identical except that more of its parameters are marked with `@noDerivative`. ```swift func addOne(_ x: Float) -> Float { x + 1 } let f0: @differentiable (Float, Float, Float) -> Float = addOne -let f1: @differentiable (@nondiff Float, Float, Float) -> Float = f0 -let f2: @differentiable (@nondiff Float, Float, @nondiff Float) -> Float = f1 +let f1: @differentiable (@noDerivative Float, Float, Float) -> Float = f0 +let f2: @differentiable (@noDerivative Float, Float, @noDerivative Float) -> Float = f1 ``` #### Implied generic constraints @@ -1975,19 +1975,19 @@ Neural network trainer objects that store loss functions, e.g. Like function declarations with a `@differentiable` attribute, differentiable function values can also be differentiable with respect to a subset of parameters. This is expressed as part of type information, in `@differentiable` -and `@differentiable(linear)` function types, using a `@nondiff` attribute at +and `@differentiable(linear)` function types, using a `@noDerivative` attribute at each parameter that is not being differentiated with respect to. By default, all parameters are being differentiated with respect to. When a -`@nondiff` attribute is specified for a parameter in a `@differentiable` +`@noDerivative` attribute is specified for a parameter in a `@differentiable` function type, values of this function type are not differentiable (or linear) with respect to the parameter. ```swift let f0: @differentiable (Float, Float) -> Float = { $0 * $1 } let f1: @differentiable(linear) (Float, Float) -> Float = { $0 + $1 } -let f2: @differentiable(linear) (Float, @nondiff Float) -> Float = { $0 * $1 } -let f3: @differentiable (@nondiff Int, Float, @nondiff Int) -> Float = { +let f2: @differentiable(linear) (Float, @noDerivative Float) -> Float = { $0 * $1 } +let f3: @differentiable (@noDerivative Int, Float, @noDerivative Int) -> Float = { $0 ? Float($1) + $2 : 0 } ``` @@ -1995,13 +1995,13 @@ let f3: @differentiable (@nondiff Int, Float, @nondiff Int) -> Float = { Differentiability of parameters in a function type is important for type conversions and is part of the subtyping rule: Any `@differentiable` or `@differentiable(linear)` function type is a subtype of the same function type -with more `@nondiff` parameters than there originally are. +with more `@noDerivative` parameters than there originally are. ```swift let f0: @differentiable (Float, Float) -> Float = { $0 * $1 } -_ = f0 as @differentiable (Float, @nondiff Float) -> Float -_ = f0 as @differentiable (@nondiff Float, Float) -> Float -_ = f0 as @differentiable (@nondiff Float, @nondiff Float) -> Float +_ = f0 as @differentiable (Float, @noDerivative Float) -> Float +_ = f0 as @differentiable (@noDerivative Float, Float) -> Float +_ = f0 as @differentiable (@noDerivative Float, @noDerivative Float) -> Float ``` ### Differentiable operators From 01b53db1b2c2f40d4721ff349517b4945c9a0d61 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Sat, 16 Nov 2019 16:28:52 -0800 Subject: [PATCH 242/283] Remove a redundant paragraph. --- docs/DifferentiableProgramming.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/DifferentiableProgramming.md b/docs/DifferentiableProgramming.md index 6ebb34b1c7ccd..77b52180cce24 100644 --- a/docs/DifferentiableProgramming.md +++ b/docs/DifferentiableProgramming.md @@ -1833,9 +1833,6 @@ is marked with `@noDerivative`.

-_Note: `@noDerivative` stands for "not being differentiated", and will likely be -unified with `@noDerivative`._ - #### Type conversion The subtyping relation among `@differentiable(linear)`, `@differentiable`, and From 91e0db40b62069d7e4c98ac06a9f172951b4eb5e Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sun, 17 Nov 2019 00:12:26 -0800 Subject: [PATCH 243/283] [Build System] Update the llvm projects path to be inside llvm-project dir --- .../swift_build_support/products/product.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/utils/swift_build_support/swift_build_support/products/product.py b/utils/swift_build_support/swift_build_support/products/product.py index 6bdcb4f4f682f..6837e563c91ae 100644 --- a/utils/swift_build_support/swift_build_support/products/product.py +++ b/utils/swift_build_support/swift_build_support/products/product.py @@ -37,6 +37,16 @@ def product_source_name(cls): It provides a customization point for Product subclasses. It is set to the value of product_name() by default for this reason. """ + + llvm_projects = ['clang', + 'clang-tools-extra', + 'compiler-rt', + 'libcxx', + 'lldb', + 'llvm'] + + if cls.product_name() in llvm_projects: + return "llvm-project/{}".format(cls.product_name()) return cls.product_name() @classmethod From 6759d82dadb01b0f2e5506ef652798b041ffda3c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sat, 16 Nov 2019 22:13:34 -0800 Subject: [PATCH 244/283] Revert "Revert "[pmo] Fix load [copy] like I fixed load_borrow."" This reverts commit 2b8e266694dbdb03cd7d5d5c06c3e368a1ff6b09. That reapplies 7623367208e1fd84f069b2ac9216a68a8bed08b9. --- include/swift/Basic/STLExtras.h | 4 +- lib/SIL/OwnershipUtils.cpp | 14 +- .../Mandatory/PredictableMemOpt.cpp | 361 +++++----- .../predictable_memaccess_opts.sil | 621 +++++++++++++++++- .../predictable_memopt_ownership.sil | 59 +- 5 files changed, 869 insertions(+), 190 deletions(-) diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index a0025dfb4b917..96a7c9e6a6334 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -639,12 +639,12 @@ inline T accumulate(const Container &C, T init, BinaryOperation op) { } template -inline bool binary_search(const Container &C, T value) { +inline bool binary_search(const Container &C, const T &value) { return std::binary_search(C.begin(), C.end(), value); } template -inline bool binary_search(const Container &C, T value, BinaryOperation op) { +inline bool binary_search(const Container &C, const T &value, BinaryOperation op) { return std::binary_search(C.begin(), C.end(), value, op); } diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index bbb30c28a500d..feeab6da18e6f 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -163,6 +163,13 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } + // If v produces .none ownership, then we can ignore it. It is important + // that we put this before checking for guaranteed forwarding instructions, + // since we want to ignore guaranteed forwarding instructions that in this + // specific case produce a .none value. + if (v.getOwnershipKind() == ValueOwnershipKind::None) + continue; + // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isGuaranteedForwardingValue(v)) { @@ -173,10 +180,9 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } - // If v produces any ownership, then we can ignore it. Otherwise, we need to - // return false since this is an introducer we do not understand. - if (v.getOwnershipKind() != ValueOwnershipKind::None) - return false; + // Otherwise, this is an introducer we do not understand. Bail and return + // false. + return false; } return true; diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 9304d9bd786df..0e30b247206ec 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "predictable-memopt" #include "PMOMemoryUseCollector.h" +#include "swift/Basic/STLExtras.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/BranchPropagatedUser.h" #include "swift/SIL/OwnershipUtils.h" @@ -445,9 +446,6 @@ class AvailableValueAggregator { bool isTopLevel = true); bool canTake(SILType loadTy, unsigned firstElt) const; - SingleValueInstruction *addMissingDestroysForCopiedValues(LoadInst *li, - SILValue newVal); - void print(llvm::raw_ostream &os) const; void dump() const LLVM_ATTRIBUTE_USED; @@ -467,13 +465,19 @@ class AvailableValueAggregator { /// reference counts of the intermediate copies and phis to ensure that all /// forwarding operations in the CFG are strongly control equivalent (i.e. run /// the same number of times). - void fixupOwnership(LoadBorrowInst *lbi, SILValue newVal) { + void fixupOwnership(SILInstruction *load, SILValue newVal) { + assert(isa(load) || isa(load)); + + // Sort phi nodes so we can use it for bisection operations. + sort(insertedPhiNodes); + // Sort inserted insts so we can bisect upon it and mark copy_value as needing // to be skipped. sort(insertedInsts); + SmallBitVector instsToSkip(insertedInsts.size()); - addHandOffCopyDestroysForPhis(lbi, newVal, instsToSkip); - addMissingDestroysForCopiedValues(lbi, newVal, instsToSkip); + addHandOffCopyDestroysForPhis(load, newVal, instsToSkip); + addMissingDestroysForCopiedValues(load, newVal, instsToSkip); } private: @@ -490,8 +494,8 @@ class AvailableValueAggregator { /// If as a result of us copying values, we may have unconsumed destroys, find /// the appropriate location and place the values there. Only used when /// ownership is enabled. - void addMissingDestroysForCopiedValues(LoadBorrowInst *li, SILValue newVal, - const SmallBitVector &instsToSkip); + void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal, + const SmallBitVector &instsToSkip); /// As a result of us using the SSA updater, insert hand off copy/destroys at /// each phi and make sure that intermediate phis do not leak by inserting @@ -726,9 +730,15 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // Finally, grab the value from the SSA updater. SILValue result = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); - assert(result.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); - return result; + if (isTake() || !B.hasOwnership()) { + return result; + } + + // Be careful with this value and insert a copy in our load block to prevent + // any weird control equivalence issues. + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.emitCopyValueOperation(Loc, result); } SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, @@ -759,7 +769,7 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, // If we are going to use this to promote a borrowed value, insert borrow // operations. Eventually I am going to do this for everything, but this // should make it easier to bring up. - if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { + if (!isTake()) { for (unsigned i : indices(ResultElts)) { ResultElts[i] = B.emitBeginBorrowOperation(Loc, ResultElts[i]); } @@ -793,7 +803,7 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, firstElt += numSubElt; } - if (expectedOwnership == AvailableValueExpectedOwnership::Borrow) { + if (!isTake()) { for (unsigned i : indices(resultElts)) { resultElts[i] = B.emitBeginBorrowOperation(Loc, resultElts[i]); } @@ -840,7 +850,13 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, !builder.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - return eltVal; + + if (!builder.hasOwnership()) { + return eltVal; + } + + SILBuilderWithScope builder2(&*B.getInsertionPoint(), &insertedInsts); + return builder2.emitCopyValueOperation(Loc, eltVal); } // If we have an available value, then we want to extract the subelement from @@ -892,75 +908,50 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, assert(!B.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - return eltVal; + if (!B.hasOwnership()) + return eltVal; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.emitCopyValueOperation(Loc, eltVal); } -SingleValueInstruction * -AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li, - SILValue newVal) { - assert(B.hasOwnership() && - "We assume this is only called if we have ownership"); +namespace { - SmallPtrSet visitedBlocks; - SmallVector leakingBlocks; - bool foundLoop = false; - auto loc = RegularLocation::getAutoGeneratedLocation(); - while (!insertedInsts.empty()) { - auto *cvi = dyn_cast(insertedInsts.pop_back_val()); - if (!cvi) - continue; +struct PhiNodeCleanupState { + /// The incoming value that we need to cleanup. + SILValue incomingValue; - // Clear our state. - visitedBlocks.clear(); - leakingBlocks.clear(); - // The linear lifetime checker doesn't care if the passed in load is - // actually a user of our copy_value. What we care about is that the load is - // guaranteed to be in the block where we have reformed the tuple in a - // consuming manner. This means if we add it as the consuming use of the - // copy, we can find the leaking places if any exist. - // - // Then perform the linear lifetime check. If we succeed, continue. We have - // no further work to do. - auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; - LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); - auto error = checker.checkValue( - cvi, {BranchPropagatedUser(&li->getAllOperands()[0])}, {}, errorKind, - &leakingBlocks); - if (!error.getFoundError()) - continue; + /// The copy that we inserted right before the phi that will be fed into the + /// phi. + CopyValueInst *phiCopy; - // Ok, we found some leaking blocks. Since we are using the linear lifetime - // checker with memory, we do not have any guarantees that the store is out - // side of a loop and a load is in a loop. In such a case, we want to - // replace the load with a copy_value. - foundLoop |= error.getFoundOverConsume(); + PhiNodeCleanupState(SILValue incomingValue, CopyValueInst *phiCopy) + : incomingValue(incomingValue), phiCopy(phiCopy) {} - // Ok, we found some leaking blocks. Insert destroys at the - // beginning of these blocks for our copy_value. - for (auto *bb : leakingBlocks) { - SILBuilderWithScope b(bb->begin()); - b.emitDestroyValueOperation(loc, cvi); - } - } + /// If our incoming value is not defined in the block in our phi node's block, + /// return the insertion point to use to insert destroy_value for the incoming + /// value. Otherwise, return nullptr. + SILInstruction *getNonPhiBlockIncomingValueDef() const; +}; + +} // end anonymous namespace - // If we didn't find a loop, we are done, just return svi to get RAUWed. - if (!foundLoop) { - return li; +SILInstruction *PhiNodeCleanupState::getNonPhiBlockIncomingValueDef() const { + auto *phiBlock = phiCopy->getParent(); + if (phiBlock == incomingValue->getParentBlock()) { + return nullptr; } - // If we found a loop, then we know that our leaking blocks are the exiting - // blocks of the loop and the value has been lifetime extended over the loop. + if (auto *cvi = dyn_cast(incomingValue)) { + return cvi; + } - // If we have a load, we need to put in a copy so that the destroys within - // the loop are properly balanced. - newVal = SILBuilderWithScope(li).emitCopyValueOperation(loc, newVal); + assert(isa(incomingValue)); - li->replaceAllUsesWith(newVal); - SILValue addr = li->getOperand(); - li->eraseFromParent(); - if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); - return nullptr; + // Otherwise, our copy_value may not be post-dominated by our phi. To + // work around that, we need to insert destroys along the other + // paths. So set base to the first instruction in our argument's block, + // so we can insert destroys for our base. + return &*incomingValue->getParentBlock()->begin(); } void AvailableValueAggregator::addHandOffCopyDestroysForPhis( @@ -973,45 +964,72 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( SmallVector, 8> incomingValues; auto loc = RegularLocation::getAutoGeneratedLocation(); - LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); #ifndef NDEBUG + LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); for (auto *phi : insertedPhiNodes) { LLVM_DEBUG(llvm::dbgs() << "Phi: " << *phi); } #endif // Before we begin, identify the offset for all phis that are intermediate - // phis inserted by the SSA updater. + // phis inserted by the SSA updater. We are taking advantage of the fact that + // the SSA updater just constructs the web without knowledge of ownership. So + // if a phi node is only used by another phi node that we inserted, then we + // have an intermediate phi node. SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); for (unsigned i : indices(insertedPhiNodes)) { - if (insertedPhiNodes[i]->getSingleUserOfType()) { - intermediatePhiOffsets.set(i); + if (auto *termInst = insertedPhiNodes[i]->getSingleUserOfType()) { + // Only set the value if we find termInst has a successor with a phi node + // in our insertedPhiNodes. + for (auto succBBArgs : termInst->getSuccessorBlockArguments()) { + if (any_of(succBBArgs, [&](SILPhiArgument *arg) { + return binary_search(insertedPhiNodes, arg); + })) { + intermediatePhiOffsets.set(i); + break; + } + } } } // First go through all of our phi nodes doing the following: // // 1. If any of the phi node have a copy_value as an operand, we know that the - // copy_value does not dominate our final definition. In such a case since - // we may not have that the copy_value is post-dominated by the phi, we - // need to insert a copy_value at the phi to allow for post-domination and - // then use the ValueLifetimeChecker to determine the rest of the frontier - // for the value. + // copy_value does not dominate our final definition since otherwise the + // SSA updater would not have inserted a phi node here. In such a case + // since we may not have that the copy_value is post-dominated by the phi, + // we need to insert a copy_value at the phi to allow for post-domination + // and then use the ValueLifetimeChecker to determine the rest of the + // frontier for the base value. // // 2. If our phi node is used by another phi node, we run into a similar // problem where we could have that our original phi node does not dominate - // our final definition and may not be strongly control dependent on our - // phi. To work around this problem, we insert at the phi a copy_value to - // allow for the phi to post_dominate its copy and then extend the lifetime - // of the phied value over that copy. + // our final definition (since the SSA updater would not have inserted the + // phi) and may not be strongly control dependent on our phi. To work + // around this problem, we insert at the phi a copy_value to allow for the + // phi to post_dominate its copy and then extend the lifetime of the phied + // value over that copy. + // + // As an extra complication to this, when we insert compensating releases for + // any copy_values from (1), we need to insert the destroy_value on "base + // values" (either a copy_value or the first instruction of a phi argument's + // block) /after/ we have found all of the base_values to ensure that if the + // same base value is used by multiple phis, we do not insert too many destroy + // value. + // + // NOTE: At first glance one may think that such a problem could not occur + // with phi nodes as well. Sadly if we allow for double backedge loops, it is + // possible (there may be more cases). + llvm::SmallVector phiNodeCleanupState; + for (unsigned i : indices(insertedPhiNodes)) { - auto *phiArg = insertedPhiNodes[i]; + auto *phi = insertedPhiNodes[i]; - // If our phiArg is not owned, continue. No fixes are needed. - if (phiArg->getOwnershipKind() != ValueOwnershipKind::Owned) + // If our phi is not owned, continue. No fixes are needed. + if (phi->getOwnershipKind() != ValueOwnershipKind::Owned) continue; - LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phiArg); + LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phi); // Otherwise, we have a copy_value that may not be strongly control // equivalent with our phi node. In such a case, we need to use // ValueLifetimeAnalysis to lifetime extend the copy such that we can @@ -1021,8 +1039,8 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( leakingBlocks.clear(); incomingValues.clear(); - phiArg->getIncomingPhiValues(incomingValues); - unsigned phiIndex = phiArg->getIndex(); + phi->getIncomingPhiValues(incomingValues); + unsigned phiIndex = phi->getIndex(); for (auto pair : incomingValues) { SILValue value = pair.second; @@ -1055,54 +1073,13 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // that for our actual phi. auto *termInst = pair.first->getTerminator(); SILBuilderWithScope builder(termInst); - auto *phiCopy = builder.createCopyValue(loc, value); + CopyValueInst *phiCopy = builder.createCopyValue(loc, value); termInst->setOperand(phiIndex, phiCopy); - // Normalize on our base now that we have inserted the copy_value into the - // terminator block. If we have a copy_value, just use it directly as our - // base. We know it isn't in the block of our phiCopy due to a check - // above. - SILInstruction *base = nullptr; - if (auto *cvi = dyn_cast(value)) { - assert(cvi->getParent() != phiCopy->getParent() && - "Just to check invariant from above"); - base = cvi; - } else { - assert(isa(value)); - // If we have a phi argument and our incoming value block is the same as - // our phi block, we know that the copy_value we inserted will only be - // used by the phi. So insert a destroy_value in the incoming value - // block after the copy_value that we inserted and then continue. - if (pair.first == value->getParentBlock()) { - builder.createDestroyValue(loc, value); - continue; - } - - // Otherwise, our copy_value may not be post-dominated by our phi. To - // work around that, we need to insert destroys along the other - // paths. So set base to the first instruction in our argument's block, - // so we can insert destroys for our base. - base = &*value->getParentBlock()->begin(); - } - assert(base && "Should have been assigned"); - - // Then lifetime extend our base over the copy_value. - assert(lifetimeFrontier.empty()); - ValueLifetimeAnalysis analysis(base, phiCopy); - bool foundCriticalEdges = !analysis.computeFrontier( - lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, - &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - - while (!lifetimeFrontier.empty()) { - auto *insertPoint = lifetimeFrontier.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createDestroyValue(loc, value); - } - - visitedBlocks.clear(); - leakingBlocks.clear(); + // Now that we know our base, phi, phiCopy for this specific incoming + // value, append it to the phiNodeClenaupState so we can insert + // destroy_values late after we visit all insertedPhiNodes. + phiNodeCleanupState.emplace_back(value, phiCopy); } // Then see if our phi is an intermediate phi. If it is an intermediate phi, @@ -1131,8 +1108,8 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - phiArg, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, - errorKind, &leakingBlocks); + phi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, + &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly @@ -1140,7 +1117,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // for the copy_value. auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation(next->getLoc(), phiArg); + builder.emitDestroyValueOperation(next->getLoc(), phi); continue; } @@ -1151,23 +1128,96 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( if (!error.getFoundOverConsume()) { auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); - builder.emitDestroyValueOperation(next->getLoc(), phiArg); + builder.emitDestroyValueOperation(next->getLoc(), phi); } // Ok, we found some leaking blocks. Insert destroys at the beginning of // these blocks for our copy_value. for (auto *bb : leakingBlocks) { SILBuilderWithScope b(bb->begin()); - b.emitDestroyValueOperation(loc, phiArg); + b.emitDestroyValueOperation(loc, phi); + } + } + + // At this point, we have visited all of our phis, lifetime extended them to + // the the load block, and inserted phi copies at all of our intermediate phi + // nodes. Now we need to cleanup and insert all of the compensating + // destroy_value that we need. We do this by sorting our phiNodeCleanupState + // just by baseValue. This will ensure that all values with the same base + // value are able to have all of their phiCopies passed at the same time to + // the ValueLifetimeAnalysis. + stable_sort(phiNodeCleanupState, [](const PhiNodeCleanupState &lhs, + const PhiNodeCleanupState &rhs) { + return lhs.incomingValue < rhs.incomingValue; + }); + + for (auto ii = phiNodeCleanupState.begin(), ie = phiNodeCleanupState.end(); + ii != ie;) { + SILValue incomingValue = ii->incomingValue; + + // First find the end of the values for which ii does not equal baseValue. + auto rangeEnd = std::find_if_not( + std::next(ii), ie, [&](const PhiNodeCleanupState &next) { + return incomingValue == next.incomingValue; + }); + + SWIFT_DEFER { + // Once we have finished processing, set ii to rangeEnd. This ensures that + // the code below does not need to worry about updating the iterator. + ii = rangeEnd; + }; + + // Before we do anything, see if we have a single cleanup state. In such a + // case, we could have that we have a phi node as an incoming value and a + // copy_value in that same block. In such a case, we want to just insert the + // copy and continue. This means that + // cleanupState.getNonPhiBlockIncomingValueDef() should always return a + // non-null value in the code below. + if (std::next(ii) == rangeEnd && isa(ii->incomingValue)) { + auto *insertPt = ii->getNonPhiBlockIncomingValueDef(); + if (!insertPt) { + CopyValueInst *phiCopy = ii->phiCopy; + SILBasicBlock *phiBlock = phiCopy->getParent(); + SILBuilderWithScope builder(phiBlock->getTerminator()); + builder.createDestroyValue(loc, incomingValue); + continue; + } + } + + // Otherwise, we know that we have for this incomingValue, multiple + // potential insert pts that we need to handle at the same time with our + // lifetime query. Gather up those uses. + SmallVector users; + transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users), + [](const PhiNodeCleanupState &value) { return value.phiCopy; }); + + // Then lifetime extend our base over the copy_value. + assert(lifetimeFrontier.empty()); + auto *def = ii->getNonPhiBlockIncomingValueDef(); + assert(def && "Should never have a nullptr here since we handled all of " + "the single block cases earlier"); + ValueLifetimeAnalysis analysis(def, users); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createDestroyValue(loc, incomingValue); } + + visitedBlocks.clear(); + leakingBlocks.clear(); } + // Clear the phi node array now that we are done. insertedPhiNodes.clear(); } void AvailableValueAggregator::addMissingDestroysForCopiedValues( - LoadBorrowInst *lbi, SILValue newVal, - const SmallBitVector &instsToSkip) { + SILInstruction *load, SILValue newVal, const SmallBitVector &instsToSkip) { assert(B.hasOwnership() && "We assume this is only called if we have ownership"); @@ -1188,8 +1238,8 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // begin_borrow. if (auto *li = dyn_cast(insertedInsts[i])) { if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { - assert(li->getParent() == lbi->getParent()); - auto next = std::next(lbi->getIterator()); + assert(li->getParent() == load->getParent()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), li); continue; @@ -1217,14 +1267,14 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - cvi, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, errorKind, + cvi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, &leakingBlocks); if (!error.getFoundError()) { // If we did not find an error, then our copy_value must be strongly // control equivalent as our load_borrow. So just insert a destroy_value // for the copy_value. - auto next = std::next(lbi->getIterator()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), cvi); continue; @@ -1235,7 +1285,7 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // this if we found a loop since our leaking blocks will lifetime extend the // value over the loop. if (!error.getFoundOverConsume()) { - auto next = std::next(lbi->getIterator()); + auto next = std::next(load->getIterator()); SILBuilderWithScope builder(next); builder.emitDestroyValueOperation(next->getLoc(), cvi); } @@ -2031,16 +2081,15 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { // blocks that we may have can be found by performing a linear lifetime check // over all copies that we found using the load as the "consuming uses" (just // for the purposes of identifying the consuming block). - auto *oldLoad = agg.addMissingDestroysForCopiedValues(li, newVal); + agg.fixupOwnership(li, newVal); - // If we are returned the load, eliminate it. Otherwise, it was already - // handled for us... so return true. - if (!oldLoad) - return true; + // Now that we have fixed up all of our missing destroys, insert the copy + // value for our actual load and RAUW. + newVal = SILBuilderWithScope(li).emitCopyValueOperation(li->getLoc(), newVal); - oldLoad->replaceAllUsesWith(newVal); - SILValue addr = oldLoad->getOperand(0); - oldLoad->eraseFromParent(); + li->replaceAllUsesWith(newVal); + SILValue addr = li->getOperand(); + li->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) recursivelyDeleteTriviallyDeadInstructions(addrI); return true; diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index 192723cfbd731..8fa9df6db1b1e 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -60,9 +60,11 @@ bb0(%0 : $Builtin.Int32): // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] +// CHECK: destroy_value [[ARG_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG_COPY]] +// CHECK: return [[ARG_COPY_2]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion' sil [ossa] @simple_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -83,10 +85,14 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY]] +// CHECK: [[ARG2_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_BORROW:%.*]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion' sil [ossa] @struct_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -144,9 +150,11 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // CHECK: br bb3([[ARG_COPY]] : // // CHECK: bb3([[RESULT:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion_multi_insertpt' sil [ossa] @simple_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -192,10 +200,16 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] +// CHECK: [[ARG2_COPY_COPY:%.*]] = copy_value [[ARG2_COPY]] +// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] +// CHECK: [[ARG2_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -306,12 +320,17 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] // CHECK: [[SECOND_ADDR:%.*]] = struct_element_addr [[STACK]] // CHECK: [[SECOND_VAL_COPY:%.*]] = load [copy] [[SECOND_ADDR]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] +// CHECK: [[SECOND_VAL_COPY_BORROW:%.*]] = begin_borrow [[SECOND_VAL_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY_BORROW]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %arg2 : @owned $Builtin.NativeObject): @@ -413,9 +432,11 @@ bb3: // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] +// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD]] +// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] // CHECK: } // end sil function 'simple_partialstructuse_load_promotion' sil [ossa] @simple_partialstructuse_load_promotion : $@convention(thin) (@owned NativeObjectPair) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectPair): @@ -437,9 +458,11 @@ bb0(%0 : @owned $NativeObjectPair): // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD_2]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] +// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD]] +// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] // CHECK: } // end sil function 'simple_partialtupleuse_load_promotion' sil [ossa] @simple_partialtupleuse_load_promotion : $@convention(thin) (@owned NativeObjectAndTuple) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectAndTuple): @@ -459,9 +482,10 @@ bb0(%0 : @owned $NativeObjectAndTuple): // CHECK: store [[ARG0]] to [init] [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [assign] [[STACK]] +// CHECK: [[ARG1_COPY_1:%.*]] = copy_value [[ARG1_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG1_COPY]] +// CHECK: return [[ARG1_COPY_1]] // CHECK: } // end sil function 'simple_assignstore' sil [ossa] @simple_assignstore : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -488,11 +512,15 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: br bb3([[LHS2_COPY]] : +// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] +// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] +// CHECK: br bb3([[LHS2_COPY_2]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] -// CHECK: br bb3([[LHS1_COPY]] : +// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] +// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] +// CHECK: br bb3([[LHS1_COPY_2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_addr [[STACK]] @@ -1340,3 +1368,570 @@ bbEnd: return %9999 : $() } +//--- + +// CHECK-LABEL: sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'load_copy_promote_with_loop_1' +sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { +bb0(%0 : @owned $NativeObjectPair): + %1 = alloc_stack $NativeObjectPair + store %0 to [init] %1 : $*NativeObjectPair + %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + br bb2 + +bb2: + %3 = load [copy] %2 : $*Builtin.NativeObject + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + br bb2 +} + +// CHECK-LABEL: sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_promote_with_loop_2' +sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { +bb0(%0 : @owned $NativeObjectPair): + %1 = alloc_stack $NativeObjectPair + store %0 to [init] %1 : $*NativeObjectPair + %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + br bb2 + +bb2: + %3 = load [copy] %2 : $*Builtin.NativeObject + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + cond_br undef, bb3, bb4 + +bb3: + br bb2 + +bb4: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_promote_two_backedge_loop' +sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + br bb1 + +bb1: + br bb2 + +bb2: + cond_br undef, bb3, bb4 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + destroy_value %2 : $Builtin.NativeObject + cond_br undef, bb5, bb6 + +bb4: + %3 = load [copy] %1 : $*Builtin.NativeObject + destroy_value %3 : $Builtin.NativeObject + cond_br undef, bb7, bb8 + +bb5: + br bb2 + +bb6: + br bb9 + +bb7: + br bb2 + +bb8: + br bb9 + +bb9: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK: bb0( +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_reload' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0c to [init] %1b : $*Builtin.NativeObject + destroy_value %0b : $Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + destroy_value %0c : $Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + %0bhat = copy_value %0b : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1b : $*Builtin.NativeObject + %0bhat2 = copy_value %0bhat : $Builtin.NativeObject + store %0bhat2 to [init] %1b : $*Builtin.NativeObject + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_value %0bhat : $Builtin.NativeObject + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy' +sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_2' +sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_3' +sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + %1 = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) + %1a = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 0 + %1b = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 1 + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load [copy] %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + dealloc_stack %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_4' +sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent' +sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8 + +bb6: + br bb8 + +bb7: + br bbPreLoopHeader + +bb8: + br bbPreLoopHeader + +bbPreLoopHeader: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load [copy] %0 : $*Builtin.NativeObject + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// In this case, we will have that we need to separately lifetime extend our phi +// node's copy to prevent leaks along the edge skipping the loop. +// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent_2' +sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8a + +bb6: + br bb8a + +bb7: + br bbPreLoopHeader + +bb8a: + br bb8 + +bb8: + cond_br undef, bbPreLoopHeader1, bbSkipLoop + +bbPreLoopHeader: + br bbLoop + +bbPreLoopHeader1: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load [copy] %0 : $*Builtin.NativeObject + br bbLoop6 + +bbLoop6: + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbLoop5 + +bbLoop5: + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbEnd + +bbSkipLoop: + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index ebb893ac4b803..e18ace13123ac 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -211,8 +211,12 @@ bb0(%0 : @owned $ContainsNativeObject): // CHECK: [[f3:%.*]] = struct_extract [[BORROWED_ARG]] : $ComplexStruct, #ComplexStruct.f1 // CHECK: [[f3_copy:%.*]] = copy_value [[f3]] // CHECK: end_borrow [[BORROWED_ARG]] +// CHECK: [[f3_copy_1:%.*]] = copy_value [[f3_copy]] +// CHECK: [[f3_copy_2:%.*]] = copy_value [[f3_copy_1]] +// CHECK: [[f2_x_copy_1:%.*]] = copy_value [[f2_x_copy]] +// CHECK: [[f2_x_copy_2:%.*]] = copy_value [[f2_x_copy_1]] // CHECK: destroy_value [[ARG]] -// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy]] : $Builtin.NativeObject, [[f2_x_copy]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) +// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy_2]] : $Builtin.NativeObject, [[f2_x_copy_2]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) // CHECK: return [[RESULT]] // CHECK: } // end sil function 'multiple_level_extract_2' sil [ossa] @multiple_level_extract_2 : $@convention(thin) (@owned ComplexStruct) -> (@owned Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32) { @@ -559,11 +563,15 @@ bb3: // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: br bb3([[LHS2_COPY]] : +// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] +// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] +// CHECK: br bb3([[LHS2_COPY_2]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] : $Builtin.NativeObject -// CHECK: br bb3([[LHS1_COPY]] : +// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] +// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] +// CHECK: br bb3([[LHS1_COPY_2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_value [[ARG]] @@ -649,9 +657,11 @@ struct NativeObjectTriple { // CHECK-NEXT: br bb3([[PAIR_LHS_COPY]] : // // CHECK: bb3([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK-NEXT: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) -// CHECK-NEXT: destroy_value [[REFORMED]] -// CHECK-NEXT: return [[PHI]] +// CHECK: [[PHI_COPY_1:%.*]] = copy_value [[PHI]] +// CHECK: [[PHI_COPY_2:%.*]] = copy_value [[PHI_COPY_1]] +// CHECK: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) +// CHECK: destroy_value [[REFORMED]] +// CHECK: return [[PHI_COPY_2]] // CHECK: } // end sil function 'diamond_test_4' sil [ossa] @diamond_test_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair): @@ -710,9 +720,14 @@ bb3: // CHECK: bb4: // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL]] : {{.*}}) +// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] +// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_RHS_VAL_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL_COPY_BORROW]] : {{.*}}) +// CHECK: [[STRUCT_COPY:%.*]] = copy_value [[STRUCT]] +// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY]] // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT]] +// CHECK: return [[STRUCT_COPY_2]] // CHECK: } // end sil function 'diamond_test_5' sil [ossa] @diamond_test_5 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): @@ -758,10 +773,14 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[FALSE_BB]]: // CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 @@ -774,10 +793,14 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[SUCC_2]]([[PHI1:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 @@ -786,15 +809,21 @@ bb4: // CHECK: br [[EXIT_BB:bb[0-9]+]]([[PHI1:%.*]] : $Builtin.NativeObject) // // CHECK: [[SUCC_1]]([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: br [[EXIT_BB]]([[PHI]] : {{.*}}) +// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] +// CHECK: br [[EXIT_BB]]([[PHI_COPY]] : {{.*}}) // // CHECK: [[EXIT_BB]]([[PHI:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[PHI]] : {{.*}}) +// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] +// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] +// CHECK: [[PHI_COPY_BORROW:%.*]] = begin_borrow [[PHI_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[PHI_COPY_BORROW]] : {{.*}}) +// CHECK: [[STRUCT_COPY_1:%.*]] = copy_value [[STRUCT]] +// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY_1]] // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT]] +// CHECK: return [[STRUCT_COPY_2]] // CHECK: } // end sil function 'diamond_test_6' sil [ossa] @diamond_test_6 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): From 07c625b4b0a94853da8b79d4e3dc066cdd6ee281 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sat, 16 Nov 2019 23:25:02 -0800 Subject: [PATCH 245/283] [pmo] Eliminate non-determinism by unmixing some iteration order/sorting bisection code. Specifically, I was abusing some sorting behavior on some arrays that I really needed to iterate over == non-determinism. To work around these issues, I made two changes: 1. Rather than using a bit vector to mark copy_values that were handled as part of phi handling and thus needing a way to map copy_value -> bit vector index, I instead just added a separate small ptr set called copyValueProcessedWithPhiNodes. 2. I refactored/changed how copy cleanups were inserted for phi nodes by constructing a flat 2d-array that is stable sorted by the index of the incoming value associated with the cleanups. An incoming value's index is the count of the copy cleanup when we see it for the first time. Thus when we do the stable sort we will be visiting in cleanup insertion order and also will be doing insertion order along the incomingValue axis. --- .../Mandatory/PredictableMemOpt.cpp | 318 ++++++++++-------- 1 file changed, 183 insertions(+), 135 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 0e30b247206ec..4ad37b981c35a 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "predictable-memopt" #include "PMOMemoryUseCollector.h" +#include "swift/Basic/BlotSetVector.h" #include "swift/Basic/STLExtras.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/BranchPropagatedUser.h" @@ -423,8 +424,14 @@ class AvailableValueAggregator { /// take. SmallVector insertedInsts; + /// The list of phi nodes inserted by the SSA updater. SmallVector insertedPhiNodes; + /// A set of copy_values whose lifetime we balanced while inserting phi + /// nodes. This means that these copy_value must be skipped in + /// addMissingDestroysForCopiedValues. + SmallPtrSet copyValueProcessedWithPhiNodes; + public: AvailableValueAggregator(SILInstruction *Inst, MutableArrayRef AvailableValueList, @@ -468,16 +475,8 @@ class AvailableValueAggregator { void fixupOwnership(SILInstruction *load, SILValue newVal) { assert(isa(load) || isa(load)); - // Sort phi nodes so we can use it for bisection operations. - sort(insertedPhiNodes); - - // Sort inserted insts so we can bisect upon it and mark copy_value as needing - // to be skipped. - sort(insertedInsts); - - SmallBitVector instsToSkip(insertedInsts.size()); - addHandOffCopyDestroysForPhis(load, newVal, instsToSkip); - addMissingDestroysForCopiedValues(load, newVal, instsToSkip); + addHandOffCopyDestroysForPhis(load, newVal); + addMissingDestroysForCopiedValues(load, newVal); } private: @@ -494,15 +493,13 @@ class AvailableValueAggregator { /// If as a result of us copying values, we may have unconsumed destroys, find /// the appropriate location and place the values there. Only used when /// ownership is enabled. - void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal, - const SmallBitVector &instsToSkip); + void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal); /// As a result of us using the SSA updater, insert hand off copy/destroys at /// each phi and make sure that intermediate phis do not leak by inserting /// destroys along paths that go through the intermediate phi that do not also /// go through the - void addHandOffCopyDestroysForPhis(SILInstruction *load, SILValue newVal, - SmallBitVector &instsToSkipOut); + void addHandOffCopyDestroysForPhis(SILInstruction *load, SILValue newVal); }; } // end anonymous namespace @@ -914,28 +911,8 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, return builder.emitCopyValueOperation(Loc, eltVal); } -namespace { - -struct PhiNodeCleanupState { - /// The incoming value that we need to cleanup. - SILValue incomingValue; - - /// The copy that we inserted right before the phi that will be fed into the - /// phi. - CopyValueInst *phiCopy; - - PhiNodeCleanupState(SILValue incomingValue, CopyValueInst *phiCopy) - : incomingValue(incomingValue), phiCopy(phiCopy) {} - - /// If our incoming value is not defined in the block in our phi node's block, - /// return the insertion point to use to insert destroy_value for the incoming - /// value. Otherwise, return nullptr. - SILInstruction *getNonPhiBlockIncomingValueDef() const; -}; - -} // end anonymous namespace - -SILInstruction *PhiNodeCleanupState::getNonPhiBlockIncomingValueDef() const { +static SILInstruction *getNonPhiBlockIncomingValueDef(SILValue incomingValue, + CopyValueInst *phiCopy) { auto *phiBlock = phiCopy->getParent(); if (phiBlock == incomingValue->getParentBlock()) { return nullptr; @@ -954,11 +931,150 @@ SILInstruction *PhiNodeCleanupState::getNonPhiBlockIncomingValueDef() const { return &*incomingValue->getParentBlock()->begin(); } +static bool +terminatorHasAnyKnownPhis(TermInst *ti, + ArrayRef insertedPhiNodesSorted) { + for (auto succArgList : ti->getSuccessorBlockArguments()) { + if (llvm::any_of(succArgList, [&](SILPhiArgument *arg) { + return binary_search(insertedPhiNodesSorted, arg); + })) { + return true; + } + } + + return false; +} + +namespace { + +class PhiNodeCopyCleanupInserter { + llvm::SmallMapVector incomingValues; + + /// Map from index -> (incomingValueIndex, copy). + /// + /// We are going to stable_sort this array using the indices of + /// incomingValueIndex. This will ensure that we always visit in + /// insertion order our incoming values (since the indices we are + /// sorting by are the count of incoming values we have seen so far + /// when we see the incoming value) and maintain the internal + /// insertion sort within our range as well. This ensures that we + /// visit our incoming values in visitation order and that within + /// their own values, also visit them in visitation order with + /// respect to each other. + SmallVector, 16> copiesToCleanup; + + /// The lifetime frontier that we use to compute lifetime endpoints + /// when emitting cleanups. + ValueLifetimeAnalysis::Frontier lifetimeFrontier; + +public: + PhiNodeCopyCleanupInserter() = default; + + void trackNewCleanup(SILValue incomingValue, CopyValueInst *copy) { + auto entry = std::make_pair(incomingValue, incomingValues.size()); + auto iter = incomingValues.insert(entry); + // If we did not succeed, then iter.first.second is the index of + // incoming value. Otherwise, it will be nextIndex. + copiesToCleanup.emplace_back(iter.first->second, copy); + } + + void emit(DeadEndBlocks &deadEndBlocks) &&; +}; + +} // end anonymous namespace + +void PhiNodeCopyCleanupInserter::emit(DeadEndBlocks &deadEndBlocks) && { + // READ THIS: We are being very careful here to avoid allowing for + // non-determinism to enter here. + // + // 1. First we create a list of indices of our phi node data. Then we use a + // stable sort those indices into the order in which our phi node cleanups + // would be in if we compared just using incomingValues. We use a stable + // sort here to ensure that within the same "cohort" of values, our order + // is insertion order. + // + // 2. We go through the list of phiNodeCleanupStates in insertion order. We + // also maintain a set of already visited base values. When we visit the + // first phiNodeCleanupState for a specific phi, we process the phi + // then. This ensures that we always process the phis in insertion order as + // well. + SmallVector copiesToCleanupIndicesSorted; + llvm::copy(indices(copiesToCleanup), + std::back_inserter(copiesToCleanupIndicesSorted)); + + stable_sort(copiesToCleanupIndicesSorted, + [&](unsigned lhsIndex, unsigned rhsIndex) { + unsigned lhs = copiesToCleanup[lhsIndex].first; + unsigned rhs = copiesToCleanup[rhsIndex].first; + return lhs < rhs; + }); + + for (auto ii = copiesToCleanupIndicesSorted.begin(), + ie = copiesToCleanupIndicesSorted.end(); + ii != ie;) { + unsigned incomingValueIndex = copiesToCleanup[*ii].first; + + // First find the end of the values for which ii does not equal baseValue. + auto rangeEnd = std::find_if_not(std::next(ii), ie, [&](unsigned index) { + return incomingValueIndex == copiesToCleanup[index].first; + }); + + SWIFT_DEFER { + // Once we have finished processing, set ii to rangeEnd. This ensures that + // the code below does not need to worry about updating the iterator. + ii = rangeEnd; + }; + + SILValue incomingValue = + std::next(incomingValues.begin(), incomingValueIndex)->first; + CopyValueInst *phiCopy = copiesToCleanup[*ii].second; + auto *insertPt = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy); + auto loc = RegularLocation::getAutoGeneratedLocation(); + + // Before we do anything, see if we have a single cleanup state. In such a + // case, we could have that we have a phi node as an incoming value and a + // copy_value in that same block. In such a case, we want to just insert the + // copy and continue. This means that + // cleanupState.getNonPhiBlockIncomingValueDef() should always return a + // non-null value in the code below. + if (std::next(ii) == rangeEnd && isa(incomingValue) && + !insertPt) { + SILBasicBlock *phiBlock = phiCopy->getParent(); + SILBuilderWithScope builder(phiBlock->getTerminator()); + builder.createDestroyValue(loc, incomingValue); + continue; + } + + // Otherwise, we know that we have for this incomingValue, multiple + // potential insert pts that we need to handle at the same time with our + // lifetime query. Gather up those uses. + SmallVector users; + transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users), + [&](unsigned index) { return copiesToCleanup[index].second; }); + + // Then lifetime extend our base over the copy_value. + assert(lifetimeFrontier.empty()); + auto *def = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy); + assert(def && "Should never have a nullptr here since we handled all of " + "the single block cases earlier"); + ValueLifetimeAnalysis analysis(def, users); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createDestroyValue(loc, incomingValue); + } + } +} + void AvailableValueAggregator::addHandOffCopyDestroysForPhis( - SILInstruction *load, SILValue newVal, SmallBitVector &instsToSkip) { + SILInstruction *load, SILValue newVal) { assert(isa(load) || isa(load)); - ValueLifetimeAnalysis::Frontier lifetimeFrontier; SmallPtrSet visitedBlocks; SmallVector leakingBlocks; SmallVector, 8> incomingValues; @@ -976,18 +1092,20 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // the SSA updater just constructs the web without knowledge of ownership. So // if a phi node is only used by another phi node that we inserted, then we // have an intermediate phi node. + // + // TODO: There should be a better way of doing this than doing a copy + sort. + SmallVector insertedPhiNodesSorted; + llvm::copy(insertedPhiNodes, std::back_inserter(insertedPhiNodesSorted)); + llvm::sort(insertedPhiNodesSorted); + SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); for (unsigned i : indices(insertedPhiNodes)) { - if (auto *termInst = insertedPhiNodes[i]->getSingleUserOfType()) { + if (TermInst *termInst = + insertedPhiNodes[i]->getSingleUserOfType()) { // Only set the value if we find termInst has a successor with a phi node // in our insertedPhiNodes. - for (auto succBBArgs : termInst->getSuccessorBlockArguments()) { - if (any_of(succBBArgs, [&](SILPhiArgument *arg) { - return binary_search(insertedPhiNodes, arg); - })) { - intermediatePhiOffsets.set(i); - break; - } + if (terminatorHasAnyKnownPhis(termInst, insertedPhiNodesSorted)) { + intermediatePhiOffsets.set(i); } } } @@ -1020,7 +1138,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // NOTE: At first glance one may think that such a problem could not occur // with phi nodes as well. Sadly if we allow for double backedge loops, it is // possible (there may be more cases). - llvm::SmallVector phiNodeCleanupState; + PhiNodeCopyCleanupInserter cleanupInserter; for (unsigned i : indices(insertedPhiNodes)) { auto *phi = insertedPhiNodes[i]; @@ -1052,13 +1170,10 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // Otherwise, value should be from a copy_value or a phi node. assert(isa(value) || isa(value)); - // If we have a copy_value Set a bit for it in instsToSkip so that when we - // start processing insertedInstrs we know that we handled it here - // already. + // If we have a copy_value, remove it from the inserted insts set so we + // skip it when we start processing insertedInstrs. if (auto *cvi = dyn_cast(value)) { - auto iter = lower_bound(insertedInsts, cvi); - assert(iter != insertedInsts.end() && *iter == cvi); - instsToSkip[std::distance(insertedInsts.begin(), iter)] = true; + copyValueProcessedWithPhiNodes.insert(cvi); // Then check if our termInst is in the same block as our copy_value. In // such a case, we can just use the copy_value as our phi's value @@ -1079,7 +1194,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( // Now that we know our base, phi, phiCopy for this specific incoming // value, append it to the phiNodeClenaupState so we can insert // destroy_values late after we visit all insertedPhiNodes. - phiNodeCleanupState.emplace_back(value, phiCopy); + cleanupInserter.trackNewCleanup(value, phiCopy); } // Then see if our phi is an intermediate phi. If it is an intermediate phi, @@ -1139,85 +1254,18 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis( } } - // At this point, we have visited all of our phis, lifetime extended them to - // the the load block, and inserted phi copies at all of our intermediate phi - // nodes. Now we need to cleanup and insert all of the compensating - // destroy_value that we need. We do this by sorting our phiNodeCleanupState - // just by baseValue. This will ensure that all values with the same base - // value are able to have all of their phiCopies passed at the same time to - // the ValueLifetimeAnalysis. - stable_sort(phiNodeCleanupState, [](const PhiNodeCleanupState &lhs, - const PhiNodeCleanupState &rhs) { - return lhs.incomingValue < rhs.incomingValue; - }); - - for (auto ii = phiNodeCleanupState.begin(), ie = phiNodeCleanupState.end(); - ii != ie;) { - SILValue incomingValue = ii->incomingValue; - - // First find the end of the values for which ii does not equal baseValue. - auto rangeEnd = std::find_if_not( - std::next(ii), ie, [&](const PhiNodeCleanupState &next) { - return incomingValue == next.incomingValue; - }); - - SWIFT_DEFER { - // Once we have finished processing, set ii to rangeEnd. This ensures that - // the code below does not need to worry about updating the iterator. - ii = rangeEnd; - }; - - // Before we do anything, see if we have a single cleanup state. In such a - // case, we could have that we have a phi node as an incoming value and a - // copy_value in that same block. In such a case, we want to just insert the - // copy and continue. This means that - // cleanupState.getNonPhiBlockIncomingValueDef() should always return a - // non-null value in the code below. - if (std::next(ii) == rangeEnd && isa(ii->incomingValue)) { - auto *insertPt = ii->getNonPhiBlockIncomingValueDef(); - if (!insertPt) { - CopyValueInst *phiCopy = ii->phiCopy; - SILBasicBlock *phiBlock = phiCopy->getParent(); - SILBuilderWithScope builder(phiBlock->getTerminator()); - builder.createDestroyValue(loc, incomingValue); - continue; - } - } - - // Otherwise, we know that we have for this incomingValue, multiple - // potential insert pts that we need to handle at the same time with our - // lifetime query. Gather up those uses. - SmallVector users; - transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users), - [](const PhiNodeCleanupState &value) { return value.phiCopy; }); - - // Then lifetime extend our base over the copy_value. - assert(lifetimeFrontier.empty()); - auto *def = ii->getNonPhiBlockIncomingValueDef(); - assert(def && "Should never have a nullptr here since we handled all of " - "the single block cases earlier"); - ValueLifetimeAnalysis analysis(def, users); - bool foundCriticalEdges = !analysis.computeFrontier( - lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - - while (!lifetimeFrontier.empty()) { - auto *insertPoint = lifetimeFrontier.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createDestroyValue(loc, incomingValue); - } - - visitedBlocks.clear(); - leakingBlocks.clear(); - } + // Alright! In summary, we just lifetime extended all of our phis, + // lifetime extended them to the load block, and inserted phi copies + // at all of our intermediate phi nodes. Now we need to cleanup and + // insert all of the compensating destroy_value that we need. + std::move(cleanupInserter).emit(deadEndBlocks); // Clear the phi node array now that we are done. insertedPhiNodes.clear(); } void AvailableValueAggregator::addMissingDestroysForCopiedValues( - SILInstruction *load, SILValue newVal, const SmallBitVector &instsToSkip) { + SILInstruction *load, SILValue newVal) { assert(B.hasOwnership() && "We assume this is only called if we have ownership"); @@ -1225,18 +1273,13 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( SmallVector leakingBlocks; auto loc = RegularLocation::getAutoGeneratedLocation(); - for (unsigned i : indices(insertedInsts)) { - // If we already handled this instruction above when handling phi nodes, - // just continue. - if (instsToSkip[i]) - continue; - + for (auto *inst : insertedInsts) { // Otherwise, see if this is a load [copy]. It if it a load [copy], then we // know that the load [copy] must be in the load block meaing we can just // put a destroy_value /after/ the load_borrow to ensure that the value // lives long enough for us to copy_value it or a derived value for the // begin_borrow. - if (auto *li = dyn_cast(insertedInsts[i])) { + if (auto *li = dyn_cast(inst)) { if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { assert(li->getParent() == load->getParent()); auto next = std::next(load->getIterator()); @@ -1248,10 +1291,15 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues( // Our copy_value may have been unset above if it was used by a phi // (implying it does not dominate our final user). - auto *cvi = dyn_cast(insertedInsts[i]); + auto *cvi = dyn_cast(inst); if (!cvi) continue; + // If we already handled this copy_value above when handling phi nodes, just + // continue. + if (copyValueProcessedWithPhiNodes.count(cvi)) + continue; + // Clear our state. visitedBlocks.clear(); leakingBlocks.clear(); From 332d4e2fad28195945eba8a7603cbb500a8b41f0 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sun, 17 Nov 2019 10:33:53 -0800 Subject: [PATCH 246/283] [Update Checkout] Remove import errno --- utils/update_checkout/update_checkout/update_checkout.py | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/update_checkout/update_checkout/update_checkout.py b/utils/update_checkout/update_checkout/update_checkout.py index 4357c2d11721b..c5e20888bc4d1 100755 --- a/utils/update_checkout/update_checkout/update_checkout.py +++ b/utils/update_checkout/update_checkout/update_checkout.py @@ -11,7 +11,6 @@ from __future__ import print_function import argparse -import errno import json import os import platform From 0567c2af797a99e4d339f3967c06a77b8147f63c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 17 Nov 2019 04:16:15 -0800 Subject: [PATCH 247/283] Disable test until ownership lowering after diagnostics transition is complete. I talked with Ravi (the maintainer) about this and he is cool. I am going to provide a patch that he can review to the oslog specific pass that also undisables the test. This test was passing last week when I originally tested it so that suggests the special optimization pass is pattern matching too specific of a pattern. --- test/stdlib/OSLogPrototypeExecTest.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/stdlib/OSLogPrototypeExecTest.swift b/test/stdlib/OSLogPrototypeExecTest.swift index 9e5de5ec665ca..19996a4795bfd 100644 --- a/test/stdlib/OSLogPrototypeExecTest.swift +++ b/test/stdlib/OSLogPrototypeExecTest.swift @@ -8,6 +8,8 @@ // interpolations. The new APIs are still prototypes and must be used only in // tests. +// REQUIRES: disabled_temporarily_for_ownership_transition + import OSLogPrototype import StdlibUnittest From 269762eee33ee3e2ce469f0bf9f3e6262c87160c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sat, 2 Nov 2019 15:27:21 -0700 Subject: [PATCH 248/283] [ownership] Enable ownership lowering /after/ the diagnostic passes. I also updated the last group of straggling tests. --- include/swift/AST/SILOptions.h | 2 +- test/ClangImporter/serialization-sil.swift | 23 +-- .../mandatoryinlining-wrongdebugscope.swift | 6 +- test/Frontend/sil-merge-partial-modules.swift | 10 +- test/IRGen/multi_file_resilience.swift | 7 +- test/IRGen/multi_module_resilience.swift | 14 +- .../ModuleCache/SerializedSIL.swiftinterface | 2 +- .../SIL/Serialization/deserialize_generic.sil | 2 +- .../constantprop-wrongscope.swift | 2 +- .../definite_init_failable_initializers.swift | 132 +++++++++++------- ...nite_init_failable_initializers_objc.swift | 35 +++-- .../definite_init_protocol_init.swift | 4 +- .../definite_init_value_types.swift | 2 +- test/SILOptimizer/optionset.swift | 2 - test/Serialization/always_inline.swift | 4 +- test/Serialization/noinline.swift | 4 +- test/Serialization/serialize_attr.swift | 4 +- 17 files changed, 144 insertions(+), 111 deletions(-) diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index e19c2f3ca12b9..3b0c75aaed16f 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -148,7 +148,7 @@ class SILOptions { /// Should the default pass pipelines strip ownership during the diagnostic /// pipeline or after serialization. - bool StripOwnershipAfterSerialization = false; + bool StripOwnershipAfterSerialization = true; /// The name of the file to which the backend should save YAML optimization /// records. diff --git a/test/ClangImporter/serialization-sil.swift b/test/ClangImporter/serialization-sil.swift index 172a43662c548..afb46c8cd8f86 100644 --- a/test/ClangImporter/serialization-sil.swift +++ b/test/ClangImporter/serialization-sil.swift @@ -12,32 +12,39 @@ public func testPartialApply(_ obj: Test) { // CHECK: dynamic_method_br [[CURRIED1_OBJ:%.+]] : $@opened([[CURRIED1_EXISTENTIAL:.+]]) Test, #Test.normalObject!1.foreign, [[CURRIED1_TRUE:[^,]+]], [[CURRIED1_FALSE:[^,]+]] // CHECK: [[CURRIED1_FALSE]]: // CHECK: [[CURRIED1_TRUE]]([[CURRIED1_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject): - // CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED1_METHOD]]([[CURRIED1_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject - // CHECK: [[CURRIED1_THUNK:%.+]] = function_ref @$syXlIego_ypIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @owned AnyObject) -> @out Any - // CHECK: = partial_apply [callee_guaranteed] [[CURRIED1_THUNK]]([[CURRIED1_PARTIAL]]) + // CHECK: [[CURRIED1_OBJECT_COPY:%.*]] = copy_value [[CURRIED1_OBJ]] + // CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED1_METHOD]]([[CURRIED1_OBJECT_COPY]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject + // CHECK: [[CURRIED1_THUNK:%.+]] = function_ref @$syXlIego_ypIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @owned AnyObject) -> @out Any + // CHECK: = partial_apply [callee_guaranteed] [[CURRIED1_THUNK]]([[CURRIED1_PARTIAL]]) curried1() } if let curried2 = obj.innerPointer { // CHECK: dynamic_method_br [[CURRIED2_OBJ:%.+]] : $@opened([[CURRIED2_EXISTENTIAL:.+]]) Test, #Test.innerPointer!1.foreign, [[CURRIED2_TRUE:[^,]+]], [[CURRIED2_FALSE:[^,]+]] // CHECK: [[CURRIED2_FALSE]]: // CHECK: [[CURRIED2_TRUE]]([[CURRIED2_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer): - // CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED2_METHOD]]([[CURRIED2_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer + // CHECK: [[CURRIED2_OBJ_COPY:%.*]] = copy_value [[CURRIED2_OBJ]] + // CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED2_METHOD]]([[CURRIED2_OBJ_COPY]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer curried2() } if let prop1 = obj.normalObjectProp { // CHECK: dynamic_method_br [[PROP1_OBJ:%.+]] : $@opened([[PROP1_EXISTENTIAL:.+]]) Test, #Test.normalObjectProp!getter.1.foreign, [[PROP1_TRUE:[^,]+]], [[PROP1_FALSE:[^,]+]] // CHECK: [[PROP1_FALSE]]: // CHECK: [[PROP1_TRUE]]([[PROP1_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject): - // CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject - // CHECK: = apply [[PROP1_PARTIAL]]() : $@callee_guaranteed () -> @owned AnyObject + // CHECK: [[PROP1_OBJ_COPY:%.*]] = copy_value [[PROP1_OBJ]] + // CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject + // CHECK: [[PROP1_PARTIAL_COPY:%.*]] = copy_value [[PROP1_PARTIAL]] + // CHECK: [[PROP1_PARTIAL_COPY_BORROW:%.*]] = begin_borrow [[PROP1_PARTIAL_COPY]] + // CHECK: = apply [[PROP1_PARTIAL_COPY_BORROW]]() : $@callee_guaranteed () -> @owned AnyObject _ = prop1 } if let prop2 = obj.innerPointerProp { // CHECK: dynamic_method_br [[PROP2_OBJ:%.+]] : $@opened([[PROP2_EXISTENTIAL:.+]]) Test, #Test.innerPointerProp!getter.1.foreign, [[PROP2_TRUE:[^,]+]], [[PROP2_FALSE:[^,]+]] // CHECK: [[PROP2_FALSE]]: // CHECK: [[PROP2_TRUE]]([[PROP2_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer): - // CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP2_METHOD]]([[PROP2_OBJ]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer - // CHECK: = apply [[PROP2_PARTIAL]]() : $@callee_guaranteed () -> UnsafeMutableRawPointer + // CHECK: [[PROP2_OBJ_COPY:%.*]] = copy_value [[PROP2_OBJ]] + // CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP2_METHOD]]([[PROP2_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer + // CHECK: [[PROP2_PARTIAL_BORROW:%.*]] = begin_borrow [[PROP2_PARTIAL]] + // CHECK: = apply [[PROP2_PARTIAL_BORROW]]() : $@callee_guaranteed () -> UnsafeMutableRawPointer _ = prop2 } } // CHECK: // end sil function '$s4Test16testPartialApplyyySoAA_pF' diff --git a/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift b/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift index feb8bc8113227..7a8ab9aa9e7a6 100644 --- a/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift +++ b/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift @@ -2,9 +2,9 @@ // RUN: -sil-print-after=mandatory-inlining \ // RUN: -Xllvm -sil-print-debuginfo -o /dev/null 2>&1 | %FileCheck %s -// CHECK: strong_release {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 -// CHECK: strong_release {{.*}} : $@callee_guaranteed () -> @out (), loc {{.*}}:18:17, scope 9 -// CHECK: strong_release {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 +// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 +// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> @out (), loc {{.*}}:18:17, scope 9 +// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 func patatino(_ function: @escaping (d) -> i, g: d...) -> () -> i { return { typealias h = ([d]) -> i diff --git a/test/Frontend/sil-merge-partial-modules.swift b/test/Frontend/sil-merge-partial-modules.swift index 55a84cd6b0940..249941e100b1e 100644 --- a/test/Frontend/sil-merge-partial-modules.swift +++ b/test/Frontend/sil-merge-partial-modules.swift @@ -42,11 +42,11 @@ public class CircleManager : ShapeManager { // CHECK-LABEL: sil [canonical] @$s4test14publicFunctionyyF : $@convention(thin) () -> () -// CHECK-LABEL: sil [serialized] [canonical] @$s4test17inlinableFunctionyyF : $@convention(thin) () -> () { +// CHECK-LABEL: sil [serialized] [canonical] [ossa] @$s4test17inlinableFunctionyyF : $@convention(thin) () -> () { // CHECK: function_ref @$s4test17inlinableFunctionyyFyycfU_ // CHECK: } -// CHECK-LABEL: sil shared [serialized] [canonical] @$s4test17inlinableFunctionyyFyycfU_ : $@convention(thin) () -> () { +// CHECK-LABEL: sil shared [serialized] [canonical] [ossa] @$s4test17inlinableFunctionyyFyycfU_ : $@convention(thin) () -> () { // CHECK: function_ref @$s4test17versionedFunctionyyF // CHECK: } @@ -54,15 +54,15 @@ public class CircleManager : ShapeManager { // CHECK-LABEL: sil [canonical] @$s4test9RectangleV4areaSfvg : $@convention(method) (Rectangle) -> Float -// CHECK-LABEL: sil [serialized] [canonical] @$s4test9RectangleV4drawyyF : $@convention(method) (Rectangle) -> () { +// CHECK-LABEL: sil [serialized] [canonical] [ossa] @$s4test9RectangleV4drawyyF : $@convention(method) (Rectangle) -> () { // CHECK: function_ref @$s4test14publicFunctionyyF // CHECK: } -// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] @$s4test9RectangleVAA5ShapeA2aDP4areaSfvgTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> Float { +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] [ossa] @$s4test9RectangleVAA5ShapeA2aDP4areaSfvgTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> Float { // CHECK: function_ref @$s4test9RectangleV4areaSfvg // CHECK: } -// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] @$s4test9RectangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> () { +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] [ossa] @$s4test9RectangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> () { // CHECK: function_ref @$s4test9RectangleV4drawyyF // CHECK: } diff --git a/test/IRGen/multi_file_resilience.swift b/test/IRGen/multi_file_resilience.swift index 5487120c83bda..aaadb088feec4 100644 --- a/test/IRGen/multi_file_resilience.swift +++ b/test/IRGen/multi_file_resilience.swift @@ -28,13 +28,10 @@ // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* %1 to %swift.opaque* // CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) -// Perform 'initializeWithTake' via the VWT. -// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4 -// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]], -// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* +// Perform 'initializeWithCopy' via the VWT. // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* %0 to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* -// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) +// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) public func copyFoo(foo: Foo) -> Foo { let copy = foo return copy diff --git a/test/IRGen/multi_module_resilience.swift b/test/IRGen/multi_module_resilience.swift index 06a3896c448f7..b9fdfcf79a1af 100644 --- a/test/IRGen/multi_module_resilience.swift +++ b/test/IRGen/multi_module_resilience.swift @@ -32,13 +32,10 @@ import OtherModule // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* %1 to %swift.opaque* // CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) -// Perform 'initializeWithTake' via the VWT. -// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4 -// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]], -// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* +// Perform 'initializeWithCopy' via the VWT. // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* %0 to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* -// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) +// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) public func copyFoo(foo: Foo) -> Foo { let copy = foo return copy @@ -61,13 +58,10 @@ public func copyFoo(foo: Foo) -> Foo { // CHECK: [[DEST:%.*]] = bitcast [[BAR]]* [[COPY]] to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[BAR]]* %1 to %swift.opaque* // CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) -// Perform 'initializeWithTake' via the VWT. -// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4 -// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]], -// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* +// Perform 'initializeWithCopy' via the VWT. // CHECK: [[DEST:%.*]] = bitcast [[BAR]]* %0 to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[BAR]]* [[COPY]] to %swift.opaque* -// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) +// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) public func copyBar(bar: Bar) -> Bar { let copy = bar return copy diff --git a/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface b/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface index fd934c5879cb2..d7dd3611b3282 100644 --- a/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface +++ b/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface @@ -46,7 +46,7 @@ public var readOnlyVar: Int { get } public var readWriteVar: Int { get set } public func verySimpleFunction() -// CHECK: sil [serialized] [exact_self_class] [canonical] @$s13SerializedSIL9TestClassCACycfC : $@convention(method) (@thick TestClass.Type) -> @owned TestClass { +// CHECK: sil [serialized] [exact_self_class] [canonical] [ossa] @$s13SerializedSIL9TestClassCACycfC : $@convention(method) (@thick TestClass.Type) -> @owned TestClass { // NEGATIVE-NOT: {{sil .*@.+storedProp}} diff --git a/test/SIL/Serialization/deserialize_generic.sil b/test/SIL/Serialization/deserialize_generic.sil index 68d402784fcac..1fd291035a78f 100644 --- a/test/SIL/Serialization/deserialize_generic.sil +++ b/test/SIL/Serialization/deserialize_generic.sil @@ -22,5 +22,5 @@ bb0: } // Make sure the function body is deserialized. -// CHECK-LABEL: sil public_external [serialized] [canonical] @$s11def_generic1AC23convertFromArrayLiteralyACyxGxd_tF : $@convention(method) (@guaranteed Array, @guaranteed A) -> @owned A { +// CHECK-LABEL: sil public_external [serialized] [canonical] [ossa] @$s11def_generic1AC23convertFromArrayLiteralyACyxGxd_tF : $@convention(method) (@guaranteed Array, @guaranteed A) -> @owned A { sil @$s11def_generic1AC23convertFromArrayLiteralyACyxGxd_tF : $@convention(method) (@guaranteed Array, @guaranteed A) -> @owned A diff --git a/test/SILOptimizer/constantprop-wrongscope.swift b/test/SILOptimizer/constantprop-wrongscope.swift index 12f9d62ba7709..92961fc96625e 100644 --- a/test/SILOptimizer/constantprop-wrongscope.swift +++ b/test/SILOptimizer/constantprop-wrongscope.swift @@ -13,7 +13,7 @@ // instructions surrounding it. // CHECK: destroy_addr %7 : $*Any, loc {{.*}}:22:19, scope 2 -// CHECK: dealloc_stack %12 : $*Optional, loc {{.*}}:22:23, scope 2 +// CHECK: dealloc_stack %13 : $*Optional, loc {{.*}}:22:23, scope 2 // CHECK: dealloc_stack %7 : $*Any, loc {{.*}}:22:23, scope 2 // CHECK: dealloc_stack %6 : $*A, loc {{.*}}:22:7, scope 2 diff --git a/test/SILOptimizer/definite_init_failable_initializers.swift b/test/SILOptimizer/definite_init_failable_initializers.swift index 96bf16b5ba44f..c444af21b5469 100644 --- a/test/SILOptimizer/definite_init_failable_initializers.swift +++ b/test/SILOptimizer/definite_init_failable_initializers.swift @@ -133,8 +133,8 @@ struct FailableStruct { // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -311,8 +311,8 @@ struct ThrowStruct { // CHECK: bb1([[RESULT:%.*]] : $Int): // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -333,8 +333,8 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV4failACyt_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowStruct): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -359,8 +359,10 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV20failBeforeDelegationACSi_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]]([[RESULT]], %1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowStruct): +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: dealloc_stack [[SELF_BOX]] +// CHECK-NEXT: destroy_addr [[SELF_BOX]] +// CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) @@ -379,8 +381,8 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV4failACyt_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowStruct): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -396,11 +398,12 @@ struct ThrowStruct { // CHECK: [[SELF_BOX:%.*]] = alloc_stack $ThrowStruct // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -424,11 +427,12 @@ struct ThrowStruct { // CHECK: bb1([[NEW_SELF:.*]] : $ThrowStruct): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -436,6 +440,7 @@ struct ThrowStruct { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] @@ -467,11 +472,12 @@ struct ThrowStruct { // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -479,6 +485,7 @@ struct ThrowStruct { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] @@ -520,8 +527,8 @@ struct ThrowStruct { // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -530,8 +537,8 @@ struct ThrowStruct { // CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional): // CHECK-NEXT: return [[NEW_SELF]] : $Optional // -// CHECK: [[TRY_APPLY_FAIL_BB]]([[ERROR]] : $Error): -// CHECK-NEXT: strong_release [[ERROR:%.*]] : $Error +// CHECK: [[TRY_APPLY_FAIL_BB]]([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[ERROR]] : $Error // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: br [[TRY_APPLY_CONT]]([[NEW_SELF]] : $Optional) init?(throwsToOptional: Int) { @@ -567,9 +574,9 @@ struct ThrowStruct { // CHECK-NEXT: try_apply [[INIT_FN]]([[SELF_TYPE]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowStruct): // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [static] [[SELF_BOX]] : $*ThrowStruct +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*ThrowStruct -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -587,16 +594,17 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[SELF_TYPE]]) // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [static] [[SELF_BOX]] : $*ThrowStruct +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*ThrowStruct // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -773,8 +781,8 @@ class FailableBaseClass { // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -822,25 +830,30 @@ class FailableDerivedClass : FailableBaseClass { // CHECK: bb0(%0 : $FailableDerivedClass): // CHECK: [[SELF_BOX:%.*]] = alloc_stack $FailableDerivedClass // CHECK: store %0 to [[SELF_BOX]] +// CHECK-NEXT: [[RELOAD_FROM_SELF_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick FailableDerivedClass.Type -// CHECK-NEXT: dealloc_partial_ref %0 : $FailableDerivedClass, [[METATYPE]] : $@thick FailableDerivedClass.Type +// CHECK-NEXT: dealloc_partial_ref [[RELOAD_FROM_SELF_BOX]] : $FailableDerivedClass, [[METATYPE]] : $@thick FailableDerivedClass.Type // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: return [[RESULT]] +// CHECK: } // end sil function '$s35definite_init_failable_initializers20FailableDerivedClassC27derivedFailBeforeDelegationACSgyt_tcfc' init?(derivedFailBeforeDelegation: ()) { return nil } // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers20FailableDerivedClassC27derivedFailDuringDelegationACSgyt_tcfc -// CHECK: bb0(%0 : $FailableDerivedClass): +// CHECK: bb0([[SELF:%.*]] : $FailableDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $FailableDerivedClass -// CHECK: store %0 to [[SELF_BOX]] -// CHECK: [[CANARY:%.*]] = apply -// CHECK-NEXT: [[MEMBER_ADDR:%.*]] = ref_element_addr %0 +// CHECK: store [[SELF]] to [[SELF_BOX]] +// CHECK: [[CANARY_FUN:%.*]] = function_ref @$s35definite_init_failable_initializers6CanaryCACycfC : +// CHECK: [[CANARY:%.*]] = apply [[CANARY_FUN]]( +// CHECK-NEXT: [[MEMBER_ADDR:%.*]] = ref_element_addr [[SELF]] // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[MEMBER_ADDR]] : $*Canary // CHECK-NEXT: store [[CANARY]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*Canary -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %0 +// CHECK-NEXT: strong_release [[SELF]] +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17FailableBaseClassC28failBeforeFullInitializationACSgyt_tcfc // CHECK-NEXT: [[SELF_OPTIONAL:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: [[COND:%.*]] = select_enum [[SELF_OPTIONAL]] @@ -855,8 +868,8 @@ class FailableDerivedClass : FailableBaseClass { // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[BASE_SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_ref_cast [[BASE_SELF_VALUE]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -864,6 +877,7 @@ class FailableDerivedClass : FailableBaseClass { // // CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional): // CHECK-NEXT: return [[NEW_SELF]] : $Optional +// CHECK: } // end sil function '$s35definite_init_failable_initializers20FailableDerivedClassC27derivedFailDuringDelegationACSgyt_tcfc' init?(derivedFailDuringDelegation: ()) { self.otherMember = Canary() super.init(failBeforeFullInitialization: ()) @@ -905,16 +919,17 @@ class ThrowBaseClass { class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfc -// CHECK: bb0(%0 : $ThrowDerivedClass): +// CHECK: bb0([[SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass -// CHECK: store %0 to [[SELF_BOX]] -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %0 +// CHECK: store [[SELF]] to [[SELF_BOX]] +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: try_apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[DERIVED_SELF]] +// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] @@ -930,24 +945,26 @@ class ThrowDerivedClass : ThrowBaseClass { } // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC28failBeforeFullInitializationACSi_tKcfc -// CHECK: bb0(%0 : $Int, %1 : $ThrowDerivedClass): +// CHECK: bb0(%0 : $Int, [[SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass -// CHECK: store %1 to [[SELF_BOX]] +// CHECK: store [[SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %1 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[DERIVED_SELF]] +// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] : $ThrowDerivedClass // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick ThrowDerivedClass.Type -// CHECK-NEXT: dealloc_partial_ref %1 : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type +// CHECK-NEXT: dealloc_partial_ref [[RELOAD_SELF]] : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failBeforeFullInitialization: Int) throws { @@ -965,15 +982,16 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int) -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %2 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] // CHECK: try_apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[DERIVED_SELF]] +// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -988,8 +1006,9 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb6: // CHECK-NEXT: br bb8 // CHECK: bb7: +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick ThrowDerivedClass.Type -// CHECK-NEXT: dealloc_partial_ref %2 : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type +// CHECK-NEXT: dealloc_partial_ref [[RELOAD_SELF]] : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -1004,19 +1023,21 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb0(%0 : $Int, %1 : $ThrowDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass // CHECK: store %1 to [[SELF_BOX]] -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %1 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -1032,18 +1053,19 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0 // CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: store %2 to [[SELF_BOX]] -// CHECK-NEXT: [[DERIVED_SELF:%.*]] = upcast %2 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[DERIVED_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK: try_apply [[INIT_FN]]([[DERIVED_SELF]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1051,6 +1073,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1084,17 +1107,18 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb1([[RESULT:%.*]] : $Int): // CHECK-NEXT: [[TWO:%.*]] = integer_literal $Builtin.Int2, -2 // CHECK-NEXT: store [[TWO]] to [[BITMAP_BOX]] -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %2 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK-NEXT: store [[ONE]] to [[BITMAP_BOX]] // CHECK: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%1) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1102,6 +1126,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1145,7 +1170,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %3 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 // CHECK-NEXT: store [[ONE]] to [[BITMAP_BOX]] @@ -1154,11 +1180,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[NEG_ONE:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK-NEXT: store [[NEG_ONE]] to [[BITMAP_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%2) // CHECK: bb3([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1168,6 +1194,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb7([[ERROR]] : $Error) // CHECK: bb6([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: br bb7([[ERROR]] : $Error) // CHECK: bb7([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1213,8 +1240,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb1([[ARG:%.*]] : $Int): // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1232,8 +1259,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowDerivedClass): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1253,8 +1280,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowDerivedClass): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1279,8 +1306,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC20failBeforeDelegationACSi_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]]([[ARG]], %1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowDerivedClass): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1300,15 +1327,16 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) +// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -1328,11 +1356,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1340,6 +1368,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR1:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR1]] : $Error) // CHECK: bb4([[ERROR2:%.*]] : $Error): +// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR2]] : $Error) // CHECK: bb5([[ERROR3:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1371,11 +1400,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1383,6 +1412,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] diff --git a/test/SILOptimizer/definite_init_failable_initializers_objc.swift b/test/SILOptimizer/definite_init_failable_initializers_objc.swift index a1e387452e236..f6b40025e5cb9 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_objc.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_objc.swift @@ -38,32 +38,37 @@ class Cat : FakeNSObject { // CHECK: end sil function '$s40definite_init_failable_initializers_objc3CatC1n5afterACSgSi_SbtcfC' // CHECK-LABEL: sil hidden @$s40definite_init_failable_initializers_objc3CatC1n5afterACSgSi_Sbtcfc : $@convention(method) (Int, Bool, @owned Cat) -> @owned Optional - // CHECK: bb0(%0 : $Int, %1 : $Bool, %2 : $Cat): + // CHECK: bb0([[ARG0:%.*]] : $Int, [[ARG1:%.*]] : $Bool, [[ARG2:%.*]] : $Cat): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $Cat - // CHECK: store %2 to [[SELF_BOX]] : $*Cat - // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr %2 : $Cat, #Cat.x + // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat + // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[COND:%.*]] = struct_extract %1 : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 // CHECK: bb1: - // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr %2 : $Cat, #Cat.x + // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x // CHECK-NEXT: destroy_addr [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: strong_release [[ARG2]] + // CHECK-NEXT: [[RELOAD_FROM_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick Cat.Type - // CHECK-NEXT: dealloc_partial_ref %2 : $Cat, [[METATYPE]] : $@thick Cat.Type + // CHECK-NEXT: dealloc_partial_ref [[RELOAD_FROM_BOX]] : $Cat, [[METATYPE]] : $@thick Cat.Type // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: br bb3([[RESULT]] : $Optional) // CHECK: bb2: - // CHECK-NEXT: [[SUPER:%.*]] = upcast %2 : $Cat to $FakeNSObject + // CHECK-NEXT: strong_release [[ARG2]] + // CHECK-NEXT: [[RELOAD_ARG2:%.*]] = load [[SELF_BOX]] + // CHECK-NEXT: [[SUPER:%.*]] = upcast [[RELOAD_ARG2]] : $Cat to $FakeNSObject // CHECK-NEXT: [[SUB:%.*]] = unchecked_ref_cast [[SUPER]] : $FakeNSObject to $Cat // CHECK-NEXT: [[SUPER_FN:%.*]] = objc_super_method [[SUB]] : $Cat, #FakeNSObject.init!initializer.1.foreign : (FakeNSObject.Type) -> () -> FakeNSObject, $@convention(objc_method) (@owned FakeNSObject) -> @owned FakeNSObject // CHECK-NEXT: [[NEW_SUPER_SELF:%.*]] = apply [[SUPER_FN]]([[SUPER]]) : $@convention(objc_method) (@owned FakeNSObject) -> @owned FakeNSObject // CHECK-NEXT: [[NEW_SELF:%.*]] = unchecked_ref_cast [[NEW_SUPER_SELF]] : $FakeNSObject to $Cat - // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // TODO: Once we re-enable arbitrary take promotion, this retain and the associated destroy_addr will go away. // CHECK-NEXT: strong_retain [[NEW_SELF]] + // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[NEW_SELF]] : $Cat // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat @@ -89,18 +94,19 @@ class Cat : FakeNSObject { // CHECK: end sil function '$s40definite_init_failable_initializers_objc3CatC4fail5afterACSgSb_SbtcfC' // CHECK-LABEL: sil hidden @$s40definite_init_failable_initializers_objc3CatC4fail5afterACSgSb_Sbtcfc : $@convention(method) (Bool, Bool, @owned Cat) -> @owned Optional - // CHECK: bb0(%0 : $Bool, %1 : $Bool, %2 : $Cat): + // CHECK: bb0([[ARG0:%.*]] : $Bool, [[ARG1:%.*]] : $Bool, [[ARG2:%.*]] : $Cat): // CHECK-NEXT: [[HAS_RUN_INIT_BOX:%.+]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.+]] = alloc_stack [dynamic_lifetime] $Cat - // CHECK: store %2 to [[SELF_BOX]] : $*Cat - // CHECK-NEXT: [[COND:%.+]] = struct_extract %0 : $Bool, #Bool._value + // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat + // CHECK-NEXT: [[COND:%.+]] = struct_extract [[ARG0]] : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 // CHECK: bb1: // CHECK-NEXT: br [[ERROR_BRANCH:bb[0-9]+]] // CHECK: bb{{[0-9]+}}: - // CHECK: [[SELF_INIT:%.+]] = objc_method %2 : $Cat, #Cat.init!initializer.1.foreign : (Cat.Type) -> (Int, Bool) -> Cat? + // CHECK: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] + // CHECK: [[SELF_INIT:%.+]] = objc_method [[RELOAD_SELF]] : $Cat, #Cat.init!initializer.1.foreign : (Cat.Type) -> (Int, Bool) -> Cat? // CHECK: [[NEW_OPT_SELF:%.+]] = apply [[SELF_INIT]]({{%.+}}, {{%.+}}, {{%.+}}) : $@convention(objc_method) (Int, ObjCBool, @owned Cat) -> @owned Optional // CHECK: [[COND:%.+]] = select_enum [[NEW_OPT_SELF]] : $Optional // CHECK-NEXT: cond_br [[COND]], [[SUCCESS_BRANCH:bb[0-9]+]], [[RELEASE_THEN_ERROR_BRANCH:bb[0-9]+]] @@ -111,9 +117,9 @@ class Cat : FakeNSObject { // CHECK: [[SUCCESS_BRANCH]]: // CHECK-NEXT: [[NEW_SELF:%.+]] = unchecked_enum_data [[NEW_OPT_SELF]] : $Optional, #Optional.some!enumelt.1 - // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // TODO: Once we re-enable arbitrary take promotion, this retain and the associated destroy_addr will go away. // CHECK-NEXT: strong_retain [[NEW_SELF]] + // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[RESULT:%.+]] = enum $Optional, #Optional.some!enumelt.1, [[NEW_SELF]] : $Cat // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat @@ -127,8 +133,9 @@ class Cat : FakeNSObject { // CHECK-NEXT: br [[ERROR_CLEANUP_BRANCH:bb[0-9]+]] // CHECK: [[ERROR_WITH_DESTROY_BRANCH]]: - // CHECK-NEXT: [[MOST_DERIVED_TYPE:%.+]] = value_metatype $@thick Cat.Type, %2 : $Cat - // CHECK-NEXT: dealloc_partial_ref %2 : $Cat, [[MOST_DERIVED_TYPE]] : $@thick Cat.Type + // CHECK: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] + // CHECK-NEXT: [[MOST_DERIVED_TYPE:%.+]] = value_metatype $@thick Cat.Type, [[RELOAD_SELF]] : $Cat + // CHECK-NEXT: dealloc_partial_ref [[RELOAD_SELF]] : $Cat, [[MOST_DERIVED_TYPE]] : $@thick Cat.Type // CHECK-NEXT: br [[ERROR_CLEANUP_BRANCH]] // CHECK: [[ERROR_CLEANUP_BRANCH]]: diff --git a/test/SILOptimizer/definite_init_protocol_init.swift b/test/SILOptimizer/definite_init_protocol_init.swift index 182affb558f6c..fca217255fabd 100644 --- a/test/SILOptimizer/definite_init_protocol_init.swift +++ b/test/SILOptimizer/definite_init_protocol_init.swift @@ -35,10 +35,10 @@ class TrivialClass : TriviallyConstructible { // CHECK-NEXT: [[FN:%.*]] = function_ref @$s023definite_init_protocol_B022TriviallyConstructiblePAAE6middlexSi_tcfC // CHECK-NEXT: apply [[FN]]<@dynamic_self TrivialClass>([[RESULT]], %0, [[METATYPE]]) // CHECK-NEXT: [[NEW_SELF:%.*]] = load [[RESULT]] - // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] - // CHECK-NEXT: dealloc_stack [[RESULT]] // TODO: Once we restore arbitrary takes, the strong_retain/destroy_addr pair below will go away. // CHECK-NEXT: strong_retain [[NEW_SELF]] + // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] + // CHECK-NEXT: dealloc_stack [[RESULT]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] diff --git a/test/SILOptimizer/definite_init_value_types.swift b/test/SILOptimizer/definite_init_value_types.swift index aa23047d53533..dc5538d43930f 100644 --- a/test/SILOptimizer/definite_init_value_types.swift +++ b/test/SILOptimizer/definite_init_value_types.swift @@ -59,9 +59,9 @@ enum ValueEnum { // CHECK: bb6: // CHECK-NEXT: [[NEW_STATE:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[NEW_STATE]] to [[STATE]] + // CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_ACCESS]] // CHECK-NEXT: end_access [[SELF_ACCESS]] - // CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[STATE]] diff --git a/test/SILOptimizer/optionset.swift b/test/SILOptimizer/optionset.swift index 3be6ff165e7df..ba74ab1f561e9 100644 --- a/test/SILOptimizer/optionset.swift +++ b/test/SILOptimizer/optionset.swift @@ -16,7 +16,6 @@ public struct TestOptions: OptionSet { // CHECK-NEXT: bb0: // CHECK-NEXT: integer_literal {{.*}}, 15 // CHECK-NEXT: struct $Int -// CHECK-NEXT: debug_value // CHECK-NEXT: struct $TestOptions // CHECK-NEXT: return public func returnTestOptions() -> TestOptions { @@ -27,7 +26,6 @@ public func returnTestOptions() -> TestOptions { // CHECK-NEXT: global_addr // CHECK-NEXT: integer_literal {{.*}}, 15 // CHECK-NEXT: struct $Int -// CHECK-NEXT: debug_value // CHECK-NEXT: struct $TestOptions // CHECK-NEXT: store // CHECK-NEXT: tuple diff --git a/test/Serialization/always_inline.swift b/test/Serialization/always_inline.swift index 178b529aeca55..8c6dfc584d790 100644 --- a/test/Serialization/always_inline.swift +++ b/test/Serialization/always_inline.swift @@ -8,9 +8,9 @@ import def_always_inline -// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] @$s17def_always_inline16testAlwaysInline1xS2b_tF : $@convention(thin) (Bool) -> Bool { +// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] [ossa] @$s17def_always_inline16testAlwaysInline1xS2b_tF : $@convention(thin) (Bool) -> Bool { -// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] @$s17def_always_inline22AlwaysInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin AlwaysInlineInitStruct.Type) -> AlwaysInlineInitStruct { +// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] [ossa] @$s17def_always_inline22AlwaysInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin AlwaysInlineInitStruct.Type) -> AlwaysInlineInitStruct { // SIL-LABEL: sil @main // SIL: [[RAW:%.+]] = global_addr @$s13always_inline3rawSbvp : $*Bool diff --git a/test/Serialization/noinline.swift b/test/Serialization/noinline.swift index 4a775d2e0cc45..a57ac201dcef6 100644 --- a/test/Serialization/noinline.swift +++ b/test/Serialization/noinline.swift @@ -8,9 +8,9 @@ import def_noinline -// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] @$s12def_noinline12testNoinline1xS2b_tF : $@convention(thin) (Bool) -> Bool { +// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] [ossa] @$s12def_noinline12testNoinline1xS2b_tF : $@convention(thin) (Bool) -> Bool { -// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] @$s12def_noinline18NoInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin NoInlineInitStruct.Type) -> NoInlineInitStruct { +// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] [ossa] @$s12def_noinline18NoInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin NoInlineInitStruct.Type) -> NoInlineInitStruct { // SIL-LABEL: sil @main // SIL: [[RAW:%.+]] = global_addr @$s8noinline3rawSbvp : $*Bool diff --git a/test/Serialization/serialize_attr.swift b/test/Serialization/serialize_attr.swift index ceeddbbabe606..c5b48d90b497a 100644 --- a/test/Serialization/serialize_attr.swift +++ b/test/Serialization/serialize_attr.swift @@ -79,6 +79,6 @@ public class CC { } } -// CHECK-DAG: sil [serialized] [_specialize exported: false, kind: full, where T == Int, U == Float] [canonical] @$s14serialize_attr14specializeThis_1uyx_q_tr0_lF : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () { +// CHECK-DAG: sil [serialized] [_specialize exported: false, kind: full, where T == Int, U == Float] [canonical] [ossa] @$s14serialize_attr14specializeThis_1uyx_q_tr0_lF : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () { -// CHECK-DAG: sil [serialized] [noinline] [_specialize exported: false, kind: full, where T == RR, U == SS] [canonical] @$s14serialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lF : $@convention(method) (@in_guaranteed U, GG, @guaranteed CC) -> (@out U, GG) { +// CHECK-DAG: sil [serialized] [noinline] [_specialize exported: false, kind: full, where T == RR, U == SS] [canonical] [ossa] @$s14serialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lF : $@convention(method) (@in_guaranteed U, GG, @guaranteed CC) -> (@out U, GG) { From 6fe6ca0d4e07c45c3aab98ba7c03d585b8cc3956 Mon Sep 17 00:00:00 2001 From: Dain Miller Date: Sun, 17 Nov 2019 15:52:10 -0500 Subject: [PATCH 249/283] Fix typo in docs --- docs/AndroidBuild.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AndroidBuild.md b/docs/AndroidBuild.md index 275d98a843d90..03d5f9eddad27 100644 --- a/docs/AndroidBuild.md +++ b/docs/AndroidBuild.md @@ -19,7 +19,7 @@ Windows. may not be supported. This guide assumes that your sources live at the root of `S:`. If your sources -live elsewhere, you can create a subsitution for this: +live elsewhere, you can create a substitution for this: ```cmd subst S: From 976c8c93100cc32c19a9b700f401d4ad30ae7c60 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 17 Nov 2019 13:44:15 -0800 Subject: [PATCH 250/283] Add tests --- test/SILGen/keypaths.swift | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index f84bb016981f8..bd932dcec5b29 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -404,7 +404,6 @@ func subscripts(x: T, y: U, s: String) { _ = \SubscriptDefaults1.[0] _ = \SubscriptDefaults1.[0, 0] _ = \SubscriptDefaults1.[0, 0, 0] - _ = \SubscriptDefaults1.[false] _ = \SubscriptDefaults1.[false, bool: false] _ = \SubscriptDefaults1.[bool: false, 0] @@ -414,26 +413,31 @@ func subscripts(x: T, y: U, s: String) { _ = \SubscriptDefaults2.[0] _ = \SubscriptDefaults3.[] _ = \SubscriptDefaults3.[0] + _ = \SubscriptDefaults4.[x: 0] + _ = \SubscriptDefaults4.[x: 0, y: 0] _ = \SubscriptDefaults5.[x: ""] _ = \SubscriptDefaults5.[x: "", y: ""] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}check_default_subscripts func check_default_subscripts() { - // CHECK: [[INTINIT:%[0-9]+]] = integer_literal $Builtin.Int64, 0 - // CHECK: [[I:%[0-9]+]] = struct $Int ([[INTINIT]] : $Builtin.Int64) - // CHECK: [[DFN:%[0-9]+]] = function_ref @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipfA0_ : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 + // CHECK: [[INTX:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0 + // CHECK: [[IX:%[0-9]+]] = apply %{{[0-9]+}}([[INTX]], {{.*}} + // CHECK: [[INTY:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0 + // CHECK: [[IY:%[0-9]+]] = apply %{{[0-9]+}}([[INTY]], {{.*}} + // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[IX]], [[IY]]) + _ = \SubscriptDefaults4.[x: 0, y: 0] + + // CHECK: [[INTINIT:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0 + // CHECK: [[I:%[0-9]+]] = apply %{{[0-9]+}}([[INTINIT]], {{.*}} + // CHECK: [[DFN:%[0-9]+]] = function_ref @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipfA0_ : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 // CHECK: [[ALLOC:%[0-9]+]] = alloc_stack $Int // CHECK: apply [[DFN]]([[ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 - // CHECK: [[LOAD:%[0-9]+]] = load [[ALLOC]] : $*Int - // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[I]], [[LOAD]]) + // CHECK: [[LOAD:%[0-9]+]] = load [trivial] [[ALLOC]] : $*Int + // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[I]], [[LOAD]]) _ = \SubscriptDefaults4.[x: 0] - // CHECK: [[INTX:%[0-9]+]] = integer_literal $Builtin.Int64, 0 - // CHECK: [[IX:%[0-9]+]] = struct $Int ([[INTX]] : $Builtin.Int64) - // CHECK: [[INTY:%[0-9]+]] = integer_literal $Builtin.Int64, 0 - // CHECK: [[IY:%[0-9]+]] = struct $Int ([[INTY]] : $Builtin.Int64) - // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s3run18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[IX]], [[XY]]) - _ = \SubscriptDefaults4.[x: 0, y: 0] + + } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From fd02663b790713994449630fa86e1e10b5432e8c Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 17 Nov 2019 14:29:05 -0800 Subject: [PATCH 251/283] More tests --- test/SILGen/Inputs/default_arg_other.swift | 12 +++++ .../SILGen/default_arg_multiple_modules.swift | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/SILGen/Inputs/default_arg_other.swift create mode 100644 test/SILGen/default_arg_multiple_modules.swift diff --git a/test/SILGen/Inputs/default_arg_other.swift b/test/SILGen/Inputs/default_arg_other.swift new file mode 100644 index 0000000000000..d45733916862c --- /dev/null +++ b/test/SILGen/Inputs/default_arg_other.swift @@ -0,0 +1,12 @@ + +public func foo(x: Int = 0) -> Int { x } + +public struct Subscript1 { + public init() { } + public subscript(x: Int = 0) -> Int { x } +} + +public struct Subscript2 { + public init() { } + public subscript(x: String = #function) -> String { x } +} diff --git a/test/SILGen/default_arg_multiple_modules.swift b/test/SILGen/default_arg_multiple_modules.swift new file mode 100644 index 0000000000000..2deff092a9457 --- /dev/null +++ b/test/SILGen/default_arg_multiple_modules.swift @@ -0,0 +1,53 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path=%t/default_arg_other.swiftmodule -module-name=default_arg_other %S/Inputs/default_arg_other.swift +// RUN: %target-swift-emit-silgen -module-name default_arg_multiple_modules -I %t %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime + +import default_arg_other + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test1{{.*}} +func test1() -> Int { + // CHECK: [[DEF_ARG_FN:%[0-9]+]] = function_ref @$s17default_arg_other3foo1xS2i_tFfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_ARG_FN]]() {{.*}} + // CHECK: [[FN:%[0-9]+]] = function_ref @$s17default_arg_other3foo1xS2i_tF : $@convention(thin) (Int) -> Int + // CHECK: [[CALL:%[0-9]+]] = apply [[FN]]([[DEF_ARG]]) {{.*}} + // CHECK: return [[CALL]] : $Int + return foo() +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test2{{.*}} +func test2() -> Int { + // CHECK: [[DEF_ARG_FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript1VyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_ARG_FN]]() {{.*}} + // CHECK: [[FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript1VyS2icig : $@convention(method) (Int, Subscript1) -> Int + // CHECK: [[CALL:%[0-9]+]] = apply [[FN]]([[DEF_ARG]], {{.*}} + // CHECK: return [[CALL]] : $Int + return Subscript1()[] +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test3{{.*}} +func test3() -> String { + // This should not call default arg constructor + // CHECK: [[STR_LIT:%[0-9]+]] = string_literal utf8 "test3()" + // CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STR_LIT]], {{.*}} + // CHECK: [[FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript2VyS2Scig : $@convention(method) (@guaranteed String, Subscript2) -> @owned String + // CHECK: [[CALL:%[0-9]+]] = apply [[FN]]([[DEF_ARG]], {{.*}} + // CHECK: return [[CALL]] : $String + return Subscript2()[] +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test4{{.*}} +func test4() { + // CHECK: [[DEF_ARG_FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript1VyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_ARG_FN]]() {{.*}} + // CHECK: keypath $KeyPath, (root $Subscript1; gettable_property $Int, id @$s17default_arg_other10Subscript1VyS2icig : $@convention(method) (Int, Subscript1) -> Int, getter @$s17default_arg_other10Subscript1VyS2icipACTK : $@convention(thin) (@in_guaranteed Subscript1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int, external #Subscript1.subscript) ([[DEF_ARG]]) + _ = \Subscript1.[] +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test5{{.*}} +func test5() { + // This should not call default arg constructor + // CHECK: [[STR_LIT:%[0-9]+]] = string_literal utf8 "test5()" + // CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STR_LIT]], {{.*}} + // CHECK: keypath $KeyPath, (root $Subscript2; gettable_property $String, id @$s17default_arg_other10Subscript2VyS2Scig : $@convention(method) (@guaranteed String, Subscript2) -> @owned String, getter @$s17default_arg_other10Subscript2VyS2ScipACTK : $@convention(thin) (@in_guaranteed Subscript2, UnsafeRawPointer) -> @out String, indices [%$0 : $String : $String], indices_equals @$sSSTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSSTh : $@convention(thin) (UnsafeRawPointer) -> Int, external #Subscript2.subscript) ([[DEF_ARG]]) + _ = \Subscript2.[] +} From a8953b1f6d66ab120b27601d6c542e77ff8c7569 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 17 Nov 2019 14:29:26 -0800 Subject: [PATCH 252/283] Format with clang-format --- lib/SILGen/SILGenApply.cpp | 15 +++++++-------- lib/SILGen/SILGenFunction.h | 10 +++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 87961ce4f6afe..af8a0f59e8d3b 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6078,16 +6078,15 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( AbstractionPattern origFnType(substFnType); auto fnType = getLoweredType(origFnType, substFnType).castTo(); - + SmallVector argValues; SmallVector delayedArgs; - ArgEmitter emitter( - *this, fnType->getRepresentation(), - /*yield*/ false, - /*isForCoroutine*/ false, - ClaimedParamsRef(fnType, fnType->getParameters()), argValues, - delayedArgs, - /*foreign error*/ None, ImportAsMemberStatus()); + ArgEmitter emitter(*this, fnType->getRepresentation(), + /*yield*/ false, + /*isForCoroutine*/ false, + ClaimedParamsRef(fnType, fnType->getParameters()), + argValues, delayedArgs, + /*foreign error*/ None, ImportAsMemberStatus()); auto prepared = prepareSubscriptIndices(subscript, subs, diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 91d616286dbbb..9f647cc51d5fb 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -665,14 +665,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction IsFreeFunctionWitness_t isFree, bool isSelfConformance); - /// Generates subscript arguments for keypath. This function handles lowering of all index expressions - /// including default arguments. + /// Generates subscript arguments for keypath. This function handles lowering + /// of all index expressions including default arguments. /// /// \returns Lowered index arguments. /// \param subscript - The subscript decl who's arguments are being lowered. - /// \param subs - Used to get subscript function type and to substitute generic args. - /// \param indexExpr - An expression holding the indices of the subscript (either a TupleExpr - /// or a ParanExpr). + /// \param subs - Used to get subscript function type and to substitute + /// generic args. \param indexExpr - An expression holding the indices of the + /// subscript (either a TupleExpr or a ParanExpr). SmallVector emitKeyPathSubscriptOperands(SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr); From 62a75d762dd3f6f99b8235c55a4e7eeadd4e26b6 Mon Sep 17 00:00:00 2001 From: Sergo Beruashvili Date: Fri, 3 Nov 2017 10:28:46 +0100 Subject: [PATCH 253/283] Use precondition instead of fatalError in DateInterval initializer --- stdlib/public/Darwin/Foundation/DateInterval.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/stdlib/public/Darwin/Foundation/DateInterval.swift b/stdlib/public/Darwin/Foundation/DateInterval.swift index 6ee7f4afe01fc..13b45c1271e28 100644 --- a/stdlib/public/Darwin/Foundation/DateInterval.swift +++ b/stdlib/public/Darwin/Foundation/DateInterval.swift @@ -54,10 +54,7 @@ public struct DateInterval : ReferenceConvertible, Comparable, Hashable, Codable /// /// - precondition: `end >= start` public init(start: Date, end: Date) { - if end < start { - fatalError("Reverse intervals are not allowed") - } - + precondition(end >= start, "Reverse intervals are not allowed") self.start = start duration = end.timeIntervalSince(start) } From 86f6570662a0fabedd3bcc36e609b9b263fa84aa Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 15 Nov 2019 14:45:50 -0500 Subject: [PATCH 254/283] Stop using SharedTimer except to implement FrontendStatsTracer Name binding can trigger swiftinterface compilation, which creates a new ASTContext and runs a compilation job. If the compiler was run with -stats-output-dir, this could trigger an assertion because SharedTimer is not re-entrant. Fix this by replacing all direct uses of SharedTimer in the frontend with FrontendStatsTracer. SharedTimer is still used to _implement_ FrontendStatsTracer, however we can collapse some of the layers in the implementation later. Many of the usages should also become redundant over time once more code is converted over to requests. --- lib/Frontend/Frontend.cpp | 9 ++++++--- lib/IRGen/IRGen.cpp | 6 +++--- lib/Parse/Parser.cpp | 9 +++++---- lib/ParseSIL/ParseSIL.cpp | 2 +- lib/SILGen/SILGen.cpp | 2 +- lib/Sema/NameBinding.cpp | 3 ++- lib/Sema/TypeChecker.cpp | 4 ++-- lib/Serialization/Serialization.cpp | 18 ++++++++++++------ test/Misc/Inputs/empty.swiftinterface | 2 ++ test/Misc/stats_dir_module_interface.swift | 6 ++++++ 10 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 test/Misc/Inputs/empty.swiftinterface create mode 100644 test/Misc/stats_dir_module_interface.swift diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index a3b97bda8d66f..d97bdd8572ea3 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -1139,7 +1139,8 @@ static bool performMandatorySILPasses(CompilerInvocation &Invocation, /// These may change across compiler versions. static void performSILOptimizations(CompilerInvocation &Invocation, SILModule *SM) { - SharedTimer timer("SIL optimization"); + FrontendStatsTracer tracer(SM->getASTContext().Stats, + "SIL optimization"); if (Invocation.getFrontendOptions().RequestedAction == FrontendOptions::ActionType::MergeModules || !Invocation.getSILOptions().shouldOptimize()) { @@ -1182,7 +1183,8 @@ bool CompilerInstance::performSILProcessing(SILModule *silModule, return true; { - SharedTimer timer("SIL verification, pre-optimization"); + FrontendStatsTracer tracer(silModule->getASTContext().Stats, + "SIL verification, pre-optimization"); silModule->verify(); } @@ -1192,7 +1194,8 @@ bool CompilerInstance::performSILProcessing(SILModule *silModule, countStatsPostSILOpt(*stats, *silModule); { - SharedTimer timer("SIL verification, post-optimization"); + FrontendStatsTracer tracer(silModule->getASTContext().Stats, + "SIL verification, post-optimization"); silModule->verify(); } diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 3986fb19dd5cd..35cb5ea6a46a8 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -877,7 +877,7 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, runIRGenPreparePasses(*SILMod, IGM); { - SharedTimer timer("IRGen"); + FrontendStatsTracer tracer(Ctx.Stats, "IRGen"); // Emit the module contents. irgen.emitGlobalTopLevel(); @@ -948,7 +948,7 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, if (outModuleHash) { *outModuleHash = IGM.ModuleHash; } else { - SharedTimer timer("LLVM pipeline"); + FrontendStatsTracer tracer(Ctx.Stats, "LLVM pipeline"); // Since no out module hash was set, we need to performLLVM. if (performLLVM(Opts, &IGM.Context.Diags, nullptr, IGM.ModuleHash, @@ -1236,7 +1236,7 @@ static void performParallelIRGeneration( // Bail out if there are any errors. if (Ctx.hadError()) return; - SharedTimer timer("LLVM pipeline"); + FrontendStatsTracer tracer(Ctx.Stats, "LLVM pipeline"); llvm::sys::Mutex DiagMutex; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index d91e464650d19..8b1601b5b5af9 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -119,15 +119,16 @@ void swift::performCodeCompletionSecondPass( void Parser::performCodeCompletionSecondPass( PersistentParserState &ParserState, CodeCompletionCallbacksFactory &Factory) { - SharedTimer timer("CodeCompletionSecondPass"); if (!ParserState.hasCodeCompletionDelayedDeclState()) return; auto state = ParserState.takeCodeCompletionDelayedDeclState(); - auto &SF = *state->ParentContext->getParentSourceFile(); - auto &SM = SF.getASTContext().SourceMgr; - auto BufferID = SM.findBufferContainingLoc(state->BodyPos.Loc); + auto &Ctx = SF.getASTContext(); + + FrontendStatsTracer tracer(Ctx.Stats, "CodeCompletionSecondPass"); + + auto BufferID = Ctx.SourceMgr.findBufferContainingLoc(state->BodyPos.Loc); Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr); std::unique_ptr CodeCompletion( diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 178da31ae7a18..8900fcdf8f34e 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -140,7 +140,7 @@ static bool parseIntoSourceFileImpl(SourceFile &SF, if (SIL) DelayBodyParsing = false; - SharedTimer timer("Parsing"); + FrontendStatsTracer tracer(SF.getASTContext().Stats, "Parsing"); Parser P(BufferID, SF, SIL ? SIL->Impl.get() : nullptr, PersistentState, STreeCreator, DelayBodyParsing); PrettyStackTraceParser StackTrace(P); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index cc8d42e11bcc5..a3dced1451974 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1689,7 +1689,7 @@ void SILGenModule::emitSourceFile(SourceFile *sf) { std::unique_ptr SILModule::constructSIL(ModuleDecl *mod, TypeConverter &tc, SILOptions &options, FileUnit *SF) { - SharedTimer timer("SILGen"); + FrontendStatsTracer tracer(mod->getASTContext().Stats, "SILGen"); const DeclContext *DC; if (SF) { DC = SF; diff --git a/lib/Sema/NameBinding.cpp b/lib/Sema/NameBinding.cpp index 4d17b4eefc760..4b86626e941da 100644 --- a/lib/Sema/NameBinding.cpp +++ b/lib/Sema/NameBinding.cpp @@ -398,7 +398,8 @@ static void insertPrecedenceGroupDecl(NameBinder &binder, SourceFile &SF, /// unresolved type names as well. This handles import directives and forward /// references. void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { - SharedTimer timer("Name binding"); + FrontendStatsTracer tracer(SF.getASTContext().Stats, "Name binding"); + // Make sure we skip adding the standard library imports if the // source file is empty. if (SF.ASTStage == SourceFile::NameBound || SF.Decls.empty()) { diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index e80b551022874..94046f57f1883 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -340,7 +340,7 @@ void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) { // Could build scope maps here because the AST is stable now. { - SharedTimer timer("Type checking and Semantic analysis"); + FrontendStatsTracer tracer(Ctx.Stats, "Type checking and Semantic analysis"); if (Ctx.TypeCheckerOpts.SkipNonInlinableFunctionBodies) // Disable this optimization if we're compiling SwiftOnoneSupport, because @@ -388,7 +388,7 @@ void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) { SF.ASTStage = SourceFile::TypeChecked; { - SharedTimer timer("AST verification"); + FrontendStatsTracer tracer(Ctx.Stats, "AST verification"); // Verify the SourceFile. verify(SF); diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 77075484269c4..c490924d422e5 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -4815,7 +4815,8 @@ void swift::serializeToBuffers( assert(!StringRef::withNullAsEmpty(options.OutputPath).empty()); { - SharedTimer timer("Serialization, swiftmodule, to buffer"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftmodule, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); Serializer::writeToStream(stream, DC, M, options); @@ -4833,7 +4834,8 @@ void swift::serializeToBuffers( } if (!StringRef::withNullAsEmpty(options.DocOutputPath).empty()) { - SharedTimer timer("Serialization, swiftdoc, to buffer"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftdoc, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); writeDocToStream(stream, DC, options.GroupInfoPath); @@ -4849,7 +4851,8 @@ void swift::serializeToBuffers( } if (!StringRef::withNullAsEmpty(options.SourceInfoOutputPath).empty()) { - SharedTimer timer("Serialization, swiftsourceinfo, to buffer"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftsourceinfo, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); writeSourceInfoToStream(stream, DC); @@ -4880,7 +4883,8 @@ void swift::serialize(ModuleOrSourceFile DC, bool hadError = withOutputFile(getContext(DC).Diags, options.OutputPath, [&](raw_ostream &out) { - SharedTimer timer("Serialization, swiftmodule"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftmodule"); Serializer::writeToStream(out, DC, M, options); return false; }); @@ -4891,7 +4895,8 @@ void swift::serialize(ModuleOrSourceFile DC, (void)withOutputFile(getContext(DC).Diags, options.DocOutputPath, [&](raw_ostream &out) { - SharedTimer timer("Serialization, swiftdoc"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftdoc"); writeDocToStream(out, DC, options.GroupInfoPath); return false; }); @@ -4901,7 +4906,8 @@ void swift::serialize(ModuleOrSourceFile DC, (void)withOutputFile(getContext(DC).Diags, options.SourceInfoOutputPath, [&](raw_ostream &out) { - SharedTimer timer("Serialization, swiftsourceinfo"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftsourceinfo"); writeSourceInfoToStream(out, DC); return false; }); diff --git a/test/Misc/Inputs/empty.swiftinterface b/test/Misc/Inputs/empty.swiftinterface new file mode 100644 index 0000000000000..0b76e7dd9f37a --- /dev/null +++ b/test/Misc/Inputs/empty.swiftinterface @@ -0,0 +1,2 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -swift-version 5 diff --git a/test/Misc/stats_dir_module_interface.swift b/test/Misc/stats_dir_module_interface.swift new file mode 100644 index 0000000000000..254d1aaf8fd78 --- /dev/null +++ b/test/Misc/stats_dir_module_interface.swift @@ -0,0 +1,6 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir %t/stats +// RUN: mkdir %t/cache +// RUN: %target-swift-frontend -typecheck %s -I %S/Inputs -stats-output-dir %t/stats -module-cache-path %t/cache + +import empty From b8947c4e16196d9d3081f31e5cd340db37e2e0bf Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 18 Nov 2019 09:19:18 -0800 Subject: [PATCH 255/283] Add more FileCheck tests --- test/SILGen/keypaths.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index bd932dcec5b29..38ea8c64e6c39 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -437,7 +437,19 @@ func check_default_subscripts() { // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[I]], [[LOAD]]) _ = \SubscriptDefaults4.[x: 0] + // CHECK: [[STRX_LIT:%[0-9]+]] = string_literal utf8 "" + // CHECK: [[STRX:%[0-9]+]] = apply %{{[0-9]+}}([[STRX_LIT]], {{.*}} + // CHECK: [[STRY_LIT:%[0-9]+]] = string_literal utf8 "check_default_subscripts()" + // CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STRY_LIT]], {{.*}} + // CHECK: keypath $WritableKeyPath, (root $SubscriptDefaults5; settable_property $String, id @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults5) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(thin) (@in_guaranteed SubscriptDefaults5, UnsafeRawPointer) -> @out String, setter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTk : $@convention(thin) (@in_guaranteed String, @inout SubscriptDefaults5, UnsafeRawPointer) -> (), indices [%$0 : $String : $String, %$1 : $String : $String], indices_equals @$sS2STH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2STh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[STRX]], [[DEF_ARG]]) + _ = \SubscriptDefaults5.[x: ""] + // CHECK: [[STRX_LIT:%[0-9]+]] = string_literal utf8 "" + // CHECK: [[STRX:%[0-9]+]] = apply %{{[0-9]+}}([[STRX_LIT]], {{.*}} + // CHECK: [[STRY_LIT:%[0-9]+]] = string_literal utf8 "" + // CHECK: [[STRY:%[0-9]+]] = apply %{{[0-9]+}}([[STRY_LIT]], {{.*}} + // CHECK: keypath $WritableKeyPath, (root $SubscriptDefaults5; settable_property $String, id @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults5) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(thin) (@in_guaranteed SubscriptDefaults5, UnsafeRawPointer) -> @out String, setter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTk : $@convention(thin) (@in_guaranteed String, @inout SubscriptDefaults5, UnsafeRawPointer) -> (), indices [%$0 : $String : $String, %$1 : $String : $String], indices_equals @$sS2STH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2STh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[STRX]], [[STRY]]) + _ = \SubscriptDefaults5.[x: "", y: ""] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics From 3f7ee854b8f0ddbbdd688b4f07d2fed62bffdb9f Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 18 Nov 2019 09:20:36 -0800 Subject: [PATCH 256/283] Fix nits --- lib/SILGen/SILGenFunction.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 9f647cc51d5fb..46c6676400de1 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -670,9 +670,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// /// \returns Lowered index arguments. /// \param subscript - The subscript decl who's arguments are being lowered. - /// \param subs - Used to get subscript function type and to substitute - /// generic args. \param indexExpr - An expression holding the indices of the - /// subscript (either a TupleExpr or a ParanExpr). + /// \param subs - Used to get subscript function type and to substitute generic args. + /// \param indexExpr - An expression holding the indices of the + /// subscript (either a TupleExpr or a ParenExpr). SmallVector emitKeyPathSubscriptOperands(SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr); From cda56765250688f20386f242b585339bf4167c83 Mon Sep 17 00:00:00 2001 From: Robert Pieta Date: Sun, 1 Jul 2018 18:51:26 -0500 Subject: [PATCH 257/283] IndexSet.union performance improvement --- .../public/Darwin/Foundation/IndexSet.swift | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/stdlib/public/Darwin/Foundation/IndexSet.swift b/stdlib/public/Darwin/Foundation/IndexSet.swift index 157e9bfaf8218..4c82ebc5f5168 100644 --- a/stdlib/public/Darwin/Foundation/IndexSet.swift +++ b/stdlib/public/Darwin/Foundation/IndexSet.swift @@ -442,16 +442,23 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio /// Union the `IndexSet` with `other`. public func union(_ other: IndexSet) -> IndexSet { - // This algorithm is naïve but it works. We could avoid calling insert in some cases. - - var result = IndexSet() - for r in self.rangeView { - result.insert(integersIn: r) + var result: IndexSet + var dense: IndexSet + + // Prepare to make a copy of the more sparse IndexSet to prefer copy over repeated inserts + if self.rangeView.count > other.rangeView.count { + result = self + dense = other + } else { + result = other + dense = self } - - for r in other.rangeView { - result.insert(integersIn: r) + + // Insert each range from the less sparse IndexSet + dense.rangeView.forEach { + result.insert(integersIn: $0) } + return result } From 2488f4801c0bc26749d311e3b03647bd4d1f02dd Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 18 Nov 2019 10:39:14 -0800 Subject: [PATCH 258/283] [docs] Add a DevelopmentTips.md file. (#28248) --- docs/DevelopmentTips.md | 43 ++++++++++++++++++++++++ docs/StandardLibraryProgrammersManual.md | 21 ------------ 2 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 docs/DevelopmentTips.md diff --git a/docs/DevelopmentTips.md b/docs/DevelopmentTips.md new file mode 100644 index 0000000000000..4e408baaf0ae8 --- /dev/null +++ b/docs/DevelopmentTips.md @@ -0,0 +1,43 @@ +# Development Tips + +## Compile times + +### Build using Ninja + +To *be* a productivity ninja, one must *use* `ninja`. `ninja` can be invoked inside the swift build directory, e.g. `/build/Ninja-ReleaseAssert/swift-macosx-x86_64/`. Running `ninja` (which is equivalent to `ninja all`) will build the local swift, stdlib and overlays. It doesn’t necessarily build all the testing infrastructure, benchmarks, etc. + +`ninja -t targets` gives a list of all possible targets to pass to ninja. This is useful for grepping. + +For this example, we will figure out how to quickly iterate on a change to the standard library to fix 32-bit build errors while building on a 64-bit host, suppressing warnings along the way. + +`ninja -t targets | grep stdlib | grep i386` will output many targets, but at the bottom we see `swift-stdlib-iphonesimulator-i386`, which looks like a good first step. This target will just build i386 parts and not waste our time also building the 64-bit stdlib, overlays, etc. + +Going further, ninja can spawn a web browser for you to navigate dependencies and rules. `ninja -t browse swift-stdlib-iphonesimulator-i386` will open a webpage with hyperlinks for all related targets. “target is built using” lists all this target’s dependencies, while “dependent edges build” list all the targets that depend directly on this. + +Clicking around a little bit, we can find `lib/swift/iphonesimulator/i386/libswiftCore.dylib` as a commonly-depended-upon target. This will perform just what is needed to compile the standard library for i386 and nothing else. + +Going further, for various reasons the standard library has lots of warnings. This is actively being addressed, but fixing all of them may require language features, etc. In the mean time, let’s suppress warnings in our build so that we just see the errors. `ninja -nv lib/swift/iphonesimulator/i386/libswiftCore.dylib` will show us the actual commands ninja will issue to build the i386 stdlib. (You’ll notice that an incremental build here is merely 3 commands as opposed to ~150 for `swift-stdlib-iphonesimulator-i386`). + +Copy the invocation that has ` -o /swift-macosx-x86_64/stdlib/public/core/iphonesimulator/i386/Swift.o`, so that we can perform the actual call to swiftc ourselves. Tack on `-suppress-warnings` at the end, and now we have the command to just build `Swift.o` for i386 while only displaying the actual errors. + +### Use sccache to cache build artifacts + +Compilation times for the compiler and the standard library can be agonizing, especially for cold builds. This is particularly painful if + +* You're bisecting an issue over a large period of time. +* You're switching between Xcode versions to debug issues. +* You have multiple distinct work directories, say for working on multiple things at once. + +[`sccache`](https://github.com/mozilla/sccache) provides a mostly automatic caching experience for C and C++ build artifacts. Here is how you can set it up and use it on macOS: + +``` +$ brew install sccache +$ sccache --start-server +$ ./swift/utils/build-script MY_ARGS --cmake-c-launcher $(which sccache) --cmake-cxx-launcher $(which sccache) +``` + +Given the size of artifacts generated, you might also want to bump the cache size from the default 10GB to something larger, say by putting `export SCCACHE_CACHE_SIZE="50G"` in your dotfile(s). + +You can run some compiles to see if it is actually doing something by running `sccache --show-stats`. Depending on the exact compilation task you're running, you might see very different cache hit rates. For example, `sccache` is particularly effective if you're rebuilding LLVM, which doesn't change so frequently from the Swift compiler's perspective. On the other hand, if you're changing the compiler's AST, the cache hit rate is likely to be much lower. + +One known issue with `sccache` is that you might occassionally get an "error: Connection to server timed out", even though you didn't stop the server. Simply re-running the build command usually works. diff --git a/docs/StandardLibraryProgrammersManual.md b/docs/StandardLibraryProgrammersManual.md index 8731c2db79392..4ef49ccd1c2ec 100644 --- a/docs/StandardLibraryProgrammersManual.md +++ b/docs/StandardLibraryProgrammersManual.md @@ -239,24 +239,3 @@ internal mutating func _roundSlowPath(_ rule: FloatingPointRoundingRule) { Because `_roundSlowPath(_:)` isn't inlinable, the version of `round(_:)` that gets called at run time will always be the version implemented in the standard library dylib. And since FloatingPointRoundingRule is *also* defined in the standard library, we know it'll never be out of sync with this version of `round(_:)`. Maybe some day we'll have special syntax in the language to say "call this method without allowing inlining" to get the same effect, but for now, this Curiously Recursive Inlinable Switch Pattern allows for safe inlining of switches over non-frozen enums with less boilerplate than you might otherwise have. Not none, but less. - - -## Productivity Hacks - -### Be a Ninja - -To *be* a productivity ninja, one must *use* `ninja`. `ninja` can be invoked inside the swift build directory, e.g. `/build/Ninja-ReleaseAssert/swift-macosx-x86_64/`. Running `ninja` (which is equivalent to `ninja all`) will build the local swift, stdlib and overlays. It doesn’t necessarily build all the testing infrastructure, benchmarks, etc. - -`ninja -t targets` gives a list of all possible targets to pass to ninja. This is useful for grepping. - -For this example, we will figure out how to quickly iterate on a change to the standard library to fix 32-bit build errors while building on a 64-bit host, suppressing warnings along the way. - -`ninja -t targets | grep stdlib | grep i386` will output many targets, but at the bottom we see `swift-stdlib-iphonesimulator-i386`, which looks like a good first step. This target will just build i386 parts and not waste our time also building the 64-bit stdlib, overlays, etc. - -Going further, ninja can spawn a web browser for you to navigate dependencies and rules. `ninja -t browse swift-stdlib-iphonesimulator-i386` will open a webpage with hyperlinks for all related targets. “target is built using” lists all this target’s dependencies, while “dependent edges build” list all the targets that depend directly on this. - -Clicking around a little bit, we can find `lib/swift/iphonesimulator/i386/libswiftCore.dylib` as a commonly-depended-upon target. This will perform just what is needed to compile the standard library for i386 and nothing else. - -Going further, for various reasons the standard library has lots of warnings. This is actively being addressed, but fixing all of them may require language features, etc. In the mean time, let’s suppress warnings in our build so that we just see the errors. `ninja -nv lib/swift/iphonesimulator/i386/libswiftCore.dylib` will show us the actual commands ninja will issue to build the i386 stdlib. (You’ll notice that an incremental build here is merely 3 commands as opposed to ~150 for `swift-stdlib-iphonesimulator-i386`). - -Copy the invocation that has ` -o /swift-macosx-x86_64/stdlib/public/core/iphonesimulator/i386/Swift.o`, so that we can perform the actual call to swiftc ourselves. Tack on `-suppress-warnings` at the end, and now we have the command to just build `Swift.o` for i386 while only displaying the actual errors. From 498a240a98fd41e8cfea93984fc7afbcfa3689ea Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 5 Mar 2018 14:04:49 -0800 Subject: [PATCH 259/283] [Foundation] Reorder _DataStorage to save 14 bytes of overhead --- stdlib/public/Darwin/Foundation/Data.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Darwin/Foundation/Data.swift b/stdlib/public/Darwin/Foundation/Data.swift index bf8ed4b6a3580..031c714a93f11 100644 --- a/stdlib/public/Darwin/Foundation/Data.swift +++ b/stdlib/public/Darwin/Foundation/Data.swift @@ -106,10 +106,10 @@ internal final class __DataStorage { @usableFromInline var _bytes: UnsafeMutableRawPointer? @usableFromInline var _length: Int @usableFromInline var _capacity: Int - @usableFromInline var _needToZero: Bool - @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? @usableFromInline var _offset: Int - + @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? + @usableFromInline var _needToZero: Bool + @inlinable // This is @inlinable as trivially computable. var bytes: UnsafeRawPointer? { return UnsafeRawPointer(_bytes)?.advanced(by: -_offset) From 43523df08a604f2df9b1d66c1ee1c593b76ab0ba Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Mon, 18 Nov 2019 10:34:58 -0800 Subject: [PATCH 260/283] [IDE] Remove call to getFormalAccess() in inferAccessSyntactically() It should have been recursing into inferAccessSyntactically() to avoid any chance of triggering name lookup. --- .../DocumentStructure/Inputs/main.swift | 20 +++- .../structure.swift.empty.response | 96 +++++++++++++++++-- .../structure.swift.foobar.response | 96 +++++++++++++++++-- .../structure.swift.response | 96 +++++++++++++++++-- tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp | 7 +- 5 files changed, 282 insertions(+), 33 deletions(-) diff --git a/test/SourceKit/DocumentStructure/Inputs/main.swift b/test/SourceKit/DocumentStructure/Inputs/main.swift index bba0489d7a99b..2901836ce4691 100644 --- a/test/SourceKit/DocumentStructure/Inputs/main.swift +++ b/test/SourceKit/DocumentStructure/Inputs/main.swift @@ -149,7 +149,19 @@ deinit { } #if false - extension Result { - func foo() {} - } - #endif +extension Result { + func foo() {} +} + +extension Outer { + class Inner { + deinit {} + } +} + +public extension Outer2 { + class Inner2 { + deinit {} + } +} +#endif diff --git a/test/SourceKit/DocumentStructure/structure.swift.empty.response b/test/SourceKit/DocumentStructure/structure.swift.empty.response index 6cade62798a42..ce39fc3f36c5f 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.empty.response +++ b/test/SourceKit/DocumentStructure/structure.swift.empty.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2472, + key.length: 2587, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -1519,24 +1519,102 @@ { key.kind: source.lang.swift.decl.extension, key.name: "Result", - key.offset: 2425, - key.length: 38, - key.nameoffset: 2435, + key.offset: 2424, + key.length: 36, + key.nameoffset: 2434, key.namelength: 6, - key.bodyoffset: 2443, - key.bodylength: 19, + key.bodyoffset: 2442, + key.bodylength: 17, key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, key.name: "foo()", - key.offset: 2447, + key.offset: 2445, key.length: 13, - key.nameoffset: 2452, + key.nameoffset: 2450, key.namelength: 5, - key.bodyoffset: 2459, + key.bodyoffset: 2457, key.bodylength: 0 } ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer", + key.offset: 2462, + key.length: 53, + key.nameoffset: 2472, + key.namelength: 5, + key.bodyoffset: 2479, + key.bodylength: 35, + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.name: "Inner", + key.offset: 2482, + key.length: 31, + key.nameoffset: 2488, + key.namelength: 5, + key.bodyoffset: 2495, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "deinit", + key.offset: 2500, + key.length: 9, + key.nameoffset: 2500, + key.namelength: 6, + key.bodyoffset: 2508, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Outer2", + key.offset: 2524, + key.length: 55, + key.nameoffset: 2534, + key.namelength: 6, + key.bodyoffset: 2542, + key.bodylength: 36, + key.attributes: [ + { + key.offset: 2517, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Inner2", + key.offset: 2545, + key.length: 32, + key.nameoffset: 2551, + key.namelength: 6, + key.bodyoffset: 2559, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "deinit", + key.offset: 2564, + key.length: 9, + key.nameoffset: 2564, + key.namelength: 6, + key.bodyoffset: 2572, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ diff --git a/test/SourceKit/DocumentStructure/structure.swift.foobar.response b/test/SourceKit/DocumentStructure/structure.swift.foobar.response index f8b89e0089b25..ebf0786fa318e 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.foobar.response +++ b/test/SourceKit/DocumentStructure/structure.swift.foobar.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2472, + key.length: 2587, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -1519,24 +1519,102 @@ { key.kind: source.lang.swift.decl.extension, key.name: "Result", - key.offset: 2425, - key.length: 38, - key.nameoffset: 2435, + key.offset: 2424, + key.length: 36, + key.nameoffset: 2434, key.namelength: 6, - key.bodyoffset: 2443, - key.bodylength: 19, + key.bodyoffset: 2442, + key.bodylength: 17, key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, key.name: "foo()", - key.offset: 2447, + key.offset: 2445, key.length: 13, - key.nameoffset: 2452, + key.nameoffset: 2450, key.namelength: 5, - key.bodyoffset: 2459, + key.bodyoffset: 2457, key.bodylength: 0 } ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer", + key.offset: 2462, + key.length: 53, + key.nameoffset: 2472, + key.namelength: 5, + key.bodyoffset: 2479, + key.bodylength: 35, + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.name: "Inner", + key.offset: 2482, + key.length: 31, + key.nameoffset: 2488, + key.namelength: 5, + key.bodyoffset: 2495, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "deinit", + key.offset: 2500, + key.length: 9, + key.nameoffset: 2500, + key.namelength: 6, + key.bodyoffset: 2508, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Outer2", + key.offset: 2524, + key.length: 55, + key.nameoffset: 2534, + key.namelength: 6, + key.bodyoffset: 2542, + key.bodylength: 36, + key.attributes: [ + { + key.offset: 2517, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Inner2", + key.offset: 2545, + key.length: 32, + key.nameoffset: 2551, + key.namelength: 6, + key.bodyoffset: 2559, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "deinit", + key.offset: 2564, + key.length: 9, + key.nameoffset: 2564, + key.namelength: 6, + key.bodyoffset: 2572, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ diff --git a/test/SourceKit/DocumentStructure/structure.swift.response b/test/SourceKit/DocumentStructure/structure.swift.response index 8e6f1a01d121b..45fb37a2ba1a7 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.response +++ b/test/SourceKit/DocumentStructure/structure.swift.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2472, + key.length: 2587, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -1519,24 +1519,102 @@ { key.kind: source.lang.swift.decl.extension, key.name: "Result", - key.offset: 2425, - key.length: 38, - key.nameoffset: 2435, + key.offset: 2424, + key.length: 36, + key.nameoffset: 2434, key.namelength: 6, - key.bodyoffset: 2443, - key.bodylength: 19, + key.bodyoffset: 2442, + key.bodylength: 17, key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, key.name: "foo()", - key.offset: 2447, + key.offset: 2445, key.length: 13, - key.nameoffset: 2452, + key.nameoffset: 2450, key.namelength: 5, - key.bodyoffset: 2459, + key.bodyoffset: 2457, key.bodylength: 0 } ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer", + key.offset: 2462, + key.length: 53, + key.nameoffset: 2472, + key.namelength: 5, + key.bodyoffset: 2479, + key.bodylength: 35, + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.name: "Inner", + key.offset: 2482, + key.length: 31, + key.nameoffset: 2488, + key.namelength: 5, + key.bodyoffset: 2495, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "deinit", + key.offset: 2500, + key.length: 9, + key.nameoffset: 2500, + key.namelength: 6, + key.bodyoffset: 2508, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Outer2", + key.offset: 2524, + key.length: 55, + key.nameoffset: 2534, + key.namelength: 6, + key.bodyoffset: 2542, + key.bodylength: 36, + key.attributes: [ + { + key.offset: 2517, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Inner2", + key.offset: 2545, + key.length: 32, + key.nameoffset: 2551, + key.namelength: 6, + key.bodyoffset: 2559, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "deinit", + key.offset: 2564, + key.length: 9, + key.nameoffset: 2564, + key.namelength: 6, + key.bodyoffset: 2572, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index ce2f229b7eae6..8914b992bcc22 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -1143,8 +1143,11 @@ static Optional inferAccessSyntactically(const ValueDecl *D) { if (D->getKind() == DeclKind::Destructor || D->getKind() == DeclKind::EnumElement) { - if (auto container = dyn_cast(D->getDeclContext())) - return std::max(container->getFormalAccess(), AccessLevel::Internal); + if (auto container = dyn_cast(D->getDeclContext())) { + if (auto containerAccess = inferAccessSyntactically(container)) + return std::max(containerAccess.getValue(), AccessLevel::Internal); + return None; + } return AccessLevel::Private; } From 633986e4db2a6f1f58675dc717b35275aa1ecac0 Mon Sep 17 00:00:00 2001 From: tokorom Date: Tue, 2 Oct 2018 11:15:58 +0900 Subject: [PATCH 261/283] [vim] tidy up coding styles for syntax/swift.vim --- utils/vim/syntax/swift.vim | 65 ++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/utils/vim/syntax/swift.vim b/utils/vim/syntax/swift.vim index 9a70c9bf3225b..3eb4a47700f71 100644 --- a/utils/vim/syntax/swift.vim +++ b/utils/vim/syntax/swift.vim @@ -154,40 +154,59 @@ syn region swiftCaseLabelRegion syn region swiftDefaultLabelRegion \ matchgroup=swiftKeyword start=/\/ matchgroup=Delimiter end=/:/ oneline -syn region swiftParenthesisRegion matchgroup=NONE start=/(/ end=/)/ contains=TOP - -syn region swiftString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=swiftInterpolationRegion -syn region swiftInterpolationRegion matchgroup=swiftInterpolation start=/\\(/ end=/)/ contained contains=TOP -syn region swiftComment start="/\*" end="\*/" contains=swiftComment,swiftTodo -syn region swiftLineComment start="//" end="$" contains=swiftTodo - -syn match swiftDecimal /[+\-]\?\<\([0-9][0-9_]*\)\([.][0-9_]*\)\?\([eE][+\-]\?[0-9][0-9_]*\)\?\>/ -syn match swiftHex /[+\-]\?\<0x[0-9A-Fa-f][0-9A-Fa-f_]*\(\([.][0-9A-Fa-f_]*\)\?[pP][+\-]\?[0-9][0-9_]*\)\?\>/ -syn match swiftOct /[+\-]\?\<0o[0-7][0-7_]*\>/ -syn match swiftBin /[+\-]\?\<0b[01][01_]*\>/ - -syn match swiftOperator +\.\@!&|^~]\@!&|^~]*\|*/\@![/=\-+*%<>!&|^~]*\|->\@![/=\-+*%<>!&|^~]*\|[=+%<>!&|^~][/=\-+*%<>!&|^~]*\)+ skipwhite skipempty nextgroup=swiftTypeParameters -syn match swiftOperator "\.\.[<.]" skipwhite skipempty nextgroup=swiftTypeParameters - -syn match swiftChar /'\([^'\\]\|\\\(["'tnr0\\]\|x[0-9a-fA-F]\{2}\|u[0-9a-fA-F]\{4}\|U[0-9a-fA-F]\{8}\)\)'/ +syn region swiftParenthesisRegion contains=TOP + \ matchgroup=NONE start=/(/ end=/)/ + +syn region swiftString contains=swiftInterpolationRegion + \ start=/"/ skip=/\\\\\|\\"/ end=/"/ +syn region swiftInterpolationRegion contained contains=TOP + \ matchgroup=swiftInterpolation start=/\\(/ end=/)/ +syn region swiftComment contains=swiftComment,swiftLineComment,swiftTodo + \ start="/\*" end="\*/" +syn region swiftLineComment contains=swiftComment,swiftTodo + \ start="//" end="$" + +syn match swiftDecimal + \ /[+\-]\?\<\([0-9][0-9_]*\)\([.][0-9_]*\)\?\([eE][+\-]\?[0-9][0-9_]*\)\?\>/ +syn match swiftHex + \ /[+\-]\?\<0x[0-9A-Fa-f][0-9A-Fa-f_]*\(\([.][0-9A-Fa-f_]*\)\?[pP][+\-]\?[0-9][0-9_]*\)\?\>/ +syn match swiftOct + \ /[+\-]\?\<0o[0-7][0-7_]*\>/ +syn match swiftBin + \ /[+\-]\?\<0b[01][01_]*\>/ + +syn match swiftOperator skipwhite skipempty nextgroup=swiftTypeParameters + \ +\.\@!&|^~]\@!&|^~]*\|*/\@![/=\-+*%<>!&|^~]*\|->\@![/=\-+*%<>!&|^~]*\|[=+%<>!&|^~][/=\-+*%<>!&|^~]*\)+ +syn match swiftOperator skipwhite skipempty nextgroup=swiftTypeParameters + \ "\.\.[<.]" + +syn match swiftChar + \ /'\([^'\\]\|\\\(["'tnr0\\]\|x[0-9a-fA-F]\{2}\|u[0-9a-fA-F]\{4}\|U[0-9a-fA-F]\{8}\)\)'/ syn match swiftTupleIndexNumber contains=swiftDecimal \ /\.[0-9]\+/ syn match swiftDecimal contained \ /[0-9]\+/ -syn match swiftPreproc /#\(\\|\\|\\)/ -syn match swiftPreproc /^\s*#\(\\|\\|\\|\\|\\|\\)/ -syn region swiftPreprocFalse start="^\s*#\\s\+\" end="^\s*#\(\\|\\|\\)" +syn match swiftPreproc + \ /#\(\\|\\|\\)/ +syn match swiftPreproc + \ /^\s*#\(\\|\\|\\|\\|\\|\\)/ +syn region swiftPreprocFalse + \ start="^\s*#\\s\+\" end="^\s*#\(\\|\\|\\)" -syn match swiftAttribute /@\<\w\+\>/ skipwhite skipempty nextgroup=swiftType,swiftTypeDefinition +syn match swiftAttribute + \ /@\<\w\+\>/ skipwhite skipempty nextgroup=swiftType,swiftTypeDefinition syn keyword swiftTodo MARK TODO FIXME contained -syn match swiftCastOp "\" skipwhite skipempty nextgroup=swiftType -syn match swiftCastOp "\[!?]\?" skipwhite skipempty nextgroup=swiftType +syn match swiftCastOp skipwhite skipempty nextgroup=swiftType + \ "\" +syn match swiftCastOp skipwhite skipempty nextgroup=swiftType + \ "\[!?]\?" -syn match swiftNilOps "??" +syn match swiftNilOps + \ "??" syn region swiftReservedIdentifier oneline \ start=/`/ end=/`/ From a128b64a359e03b2fcd96471bbf24986e003fbfe Mon Sep 17 00:00:00 2001 From: tokorom Date: Tue, 2 Oct 2018 12:00:13 +0900 Subject: [PATCH 262/283] [vim] replace + to " --- utils/vim/syntax/swift.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/vim/syntax/swift.vim b/utils/vim/syntax/swift.vim index 3eb4a47700f71..d20dd3fe22756 100644 --- a/utils/vim/syntax/swift.vim +++ b/utils/vim/syntax/swift.vim @@ -176,7 +176,7 @@ syn match swiftBin \ /[+\-]\?\<0b[01][01_]*\>/ syn match swiftOperator skipwhite skipempty nextgroup=swiftTypeParameters - \ +\.\@!&|^~]\@!&|^~]*\|*/\@![/=\-+*%<>!&|^~]*\|->\@![/=\-+*%<>!&|^~]*\|[=+%<>!&|^~][/=\-+*%<>!&|^~]*\)+ + \ "\.\@!&|^~]\@!&|^~]*\|*/\@![/=\-+*%<>!&|^~]*\|->\@![/=\-+*%<>!&|^~]*\|[=+%<>!&|^~][/=\-+*%<>!&|^~]*\)" syn match swiftOperator skipwhite skipempty nextgroup=swiftTypeParameters \ "\.\.[<.]" From 4d85c8bcd171d720bc66c0f494ff1adb4467aae5 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Thu, 1 Nov 2018 09:56:00 -0300 Subject: [PATCH 263/283] Index optional is pattern --- lib/Sema/CSApply.cpp | 2 +- test/Index/patterns.swift | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 3215bb72e56ba..59392aecabdf0 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3179,7 +3179,7 @@ namespace { castKind == CheckedCastKind::SetDowncast) { auto toOptType = OptionalType::get(toType); ConditionalCheckedCastExpr *cast = new (ctx) ConditionalCheckedCastExpr( - sub, expr->getLoc(), SourceLoc(), TypeLoc::withoutLoc(toType)); + sub, expr->getLoc(), SourceLoc(), expr->getCastTypeLoc()); cs.setType(cast, toOptType); cs.setType(cast->getCastTypeLoc(), toType); if (expr->isImplicit()) diff --git a/test/Index/patterns.swift b/test/Index/patterns.swift index 20ccaf9bc6d52..1b210caddbe30 100644 --- a/test/Index/patterns.swift +++ b/test/Index/patterns.swift @@ -4,6 +4,8 @@ struct Foo { enum Inner { case bar } + + struct Bar {} } let a: Foo.Inner = .bar @@ -26,3 +28,19 @@ case Foo.Inner.bar: default: break } + +let prop1 = 1 + +if prop1 is Foo {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +if prop1 is Foo.Bar {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +// CHECK: [[@LINE-2]]:17 | struct/Swift | Bar | + +let prop2: Int? = 1 + +if prop2 is Foo {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +if prop2 is Foo.Bar {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +// CHECK: [[@LINE-2]]:17 | struct/Swift | Bar | From 1a4b36250351eaba1e0c7a433c46aa59ce704857 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 18 Nov 2019 13:23:22 -0800 Subject: [PATCH 264/283] [oslog] Fix bug where OSLog was not invalidating any state that it was changing. This is important both for correctness reasons and for triaging reasons. The correctness is obvious, but for those unaware there are many triaging utilities that only verify or print pass output if the pass makes a change as indicated by invalidating state. --- lib/SILOptimizer/Mandatory/OSLogOptimization.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index e6d183876b1aa..434f9b1665886 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -1063,7 +1063,7 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, /// Constant evaluate instructions starting from 'start' and fold the uses /// of the value 'oslogMessage'. Stop when oslogMessageValue is released. -static void constantFold(SILInstruction *start, +static bool constantFold(SILInstruction *start, SingleValueInstruction *oslogMessage, unsigned assertConfig) { SILFunction *fun = start->getFunction(); @@ -1077,7 +1077,7 @@ static void constantFold(SILInstruction *start, auto errorInfo = collectConstants(state); if (errorInfo) // Evaluation failed with diagnostics. - return; + return false; // At this point, the `OSLogMessage` instance should be mapped to a constant // value in the interpreter state. If this is not the case, it means the @@ -1085,9 +1085,10 @@ static void constantFold(SILInstruction *start, // incorrect. Detect and diagnose this scenario. bool errorDetected = checkOSLogMessageIsConstant(oslogMessage, state); if (errorDetected) - return; + return false; substituteConstants(state); + return true; } /// Given a call to the initializer of OSLogMessage, which conforms to @@ -1283,13 +1284,20 @@ class OSLogOptimization : public SILFunctionTransform { } } + bool madeChange = false; + // Constant fold the uses of properties of OSLogMessage instance. Note that // the function body will change due to constant folding, after each // iteration. for (auto *oslogInit : oslogMessageInits) { SILInstruction *interpolationStart = beginOfInterpolation(oslogInit); assert(interpolationStart); - constantFold(interpolationStart, oslogInit, assertConfig); + madeChange |= constantFold(interpolationStart, oslogInit, assertConfig); + } + + // TODO: Can we be more conservative here with our invalidation? + if (madeChange) { + invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } } }; From 4990bc4c1ee9c1e42fcc3eff6bd53ef18c4363da Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Mon, 18 Nov 2019 13:34:41 -0800 Subject: [PATCH 265/283] [build] amend search path for Cmake modules Since we are not using symlinks anymore, we need to point to the correct directory with LLVM cmake modules. Fixes rdar://problem/57294763 --- cmake/modules/StandaloneOverlay.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/StandaloneOverlay.cmake b/cmake/modules/StandaloneOverlay.cmake index 14829c59a5085..5a886c931e94e 100644 --- a/cmake/modules/StandaloneOverlay.cmake +++ b/cmake/modules/StandaloneOverlay.cmake @@ -10,7 +10,7 @@ endif() list(APPEND CMAKE_MODULE_PATH - "${SWIFT_SOURCE_ROOT}/llvm/cmake/modules" + "${SWIFT_SOURCE_ROOT}/llvm-project/llvm/cmake/modules" "${PROJECT_SOURCE_DIR}/../../../../cmake/modules" "${PROJECT_SOURCE_DIR}/../../../cmake/modules") From b96ae61a5ccd81623c222b1caf763dac8a7994fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=C3=96zbek?= <45393818+MrNightcall@users.noreply.github.com> Date: Thu, 6 Dec 2018 10:55:36 +0100 Subject: [PATCH 266/283] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f03ac4ca3911a..56ed6746f48c0 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,8 @@ several hours. Naturally, incremental builds are much faster. ### System Requirements -macOS, Ubuntu Linux LTS, and the latest Ubuntu Linux release are the current -supported host development operating systems. +macOS, Ubuntu Linux LTS, and the latest Ubuntu Linux release are currently +supported as host development operating systems. Please make sure you use Python 2.x. Python 3.x is not supported currently. From 1945ee2639ba8406db974a4ecd1687b985e739d0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 18 Nov 2019 13:57:47 -0800 Subject: [PATCH 267/283] [ConstraintSystem] NFC: Fix type variable printing in connected compoents and constraint system state --- lib/Sema/ConstraintGraph.cpp | 5 ++++- lib/Sema/TypeCheckConstraints.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 130a50ad1d35f..3e210ca5f4fee 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -1403,8 +1403,11 @@ void ConstraintGraphNode::dump() const { void ConstraintGraph::print(ArrayRef typeVars, llvm::raw_ostream &out) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + for (auto typeVar : typeVars) { - (*this)[typeVar].print(out, 2); + (*this)[typeVar].print(out, 2, PO); out << "\n"; } } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5bebe709b4950..372121feabb6d 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3697,7 +3697,7 @@ void ConstraintSystem::print(raw_ostream &out) const { if (rep == tv) { if (auto fixed = getFixedType(tv)) { out << " as "; - fixed->print(out); + Type(fixed).print(out, PO); } else { getPotentialBindings(tv).dump(out, 1); } From fb581fdc8fe8799a9ad1c71d6fef710630376d95 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 18 Nov 2019 18:12:02 -0500 Subject: [PATCH 268/283] Add a regression test for https://bugs.swift.org/browse/SR-10612 --- .../compiler_crashers_2_fixed/sr-10612.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/sr-10612.swift diff --git a/validation-test/compiler_crashers_2_fixed/sr-10612.swift b/validation-test/compiler_crashers_2_fixed/sr-10612.swift new file mode 100644 index 0000000000000..ed2ccb14212fc --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr-10612.swift @@ -0,0 +1,16 @@ +// RUN: not %target-swift-frontend -typecheck %s + +protocol P1: class { + associatedtype P1P1: P1 + associatedtype P1AnyP2: AnyP2 + + var anyP2: P1AnyP2? { get set } +} + +protocol P2 { + associatedtype P2P1: P1 +} + +final class AnyP2: P2 { + typealias P2P1 = AP2P1 +} From 8de96f3959abfaf8b59edbce00f15857da286a54 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 18 Nov 2019 12:15:15 -0800 Subject: [PATCH 269/283] [silopt] Add a new SerializeSIL utility pipeline. This pipeline just runs the Serialize SIL pass. The reason I am adding this is that currently if one passes -disable-sil-perf-optzns or mess with -sil-opt-pass-count, one can cause serialization to not occur, making it difficult to bisect/turn off-on opts. --- .../SILOptimizer/PassManager/PassPipeline.def | 1 + lib/SILOptimizer/PassManager/PassPipeline.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/swift/SILOptimizer/PassManager/PassPipeline.def b/include/swift/SILOptimizer/PassManager/PassPipeline.def index 005943e86582b..12132e4d180bb 100644 --- a/include/swift/SILOptimizer/PassManager/PassPipeline.def +++ b/include/swift/SILOptimizer/PassManager/PassPipeline.def @@ -30,6 +30,7 @@ PASSPIPELINE(Onone, "Passes run at -Onone") PASSPIPELINE(InstCount, "Utility pipeline to just run the inst count pass") PASSPIPELINE(Lowering, "SIL Address Lowering") PASSPIPELINE(IRGenPrepare, "Pipeline to run during IRGen") +PASSPIPELINE(SerializeSIL, "Utility pipeline that just runs SerializeSILPass ") #undef PASSPIPELINE_WITH_OPTIONS #undef PASSPIPELINE diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index d666cee5a14da..d974675fa64a5 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -684,6 +684,20 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { return P; } +//===----------------------------------------------------------------------===// +// Serialize SIL Pass Pipeline +//===----------------------------------------------------------------------===// + +// Add to P a new pipeline that just serializes SIL. Meant to be used in +// situations where perf optzns are disabled, but we may need to serialize. +SILPassPipelinePlan +SILPassPipelinePlan::getSerializeSILPassPipeline(const SILOptions &Options) { + SILPassPipelinePlan P(Options); + P.startPipeline("Serialize SIL"); + P.addSerializeSILPass(); + return P; +} + //===----------------------------------------------------------------------===// // Inst Count Pass Pipeline //===----------------------------------------------------------------------===// From a9a0bba31b825ddcb18297eed8e55eca66774951 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 18 Nov 2019 16:19:33 -0800 Subject: [PATCH 270/283] [Diagnostics] Don't apply the single parameter tuple splat fix if the parameter type is a generic parameter. --- include/swift/AST/DiagnosticsSema.def | 2 - lib/Sema/CSDiagnostics.cpp | 20 ++------- lib/Sema/CSFix.cpp | 7 ++-- test/Constraints/enum_cases.swift | 8 ++-- test/Constraints/function_builder_diags.swift | 11 +++++ test/Constraints/tuple_arguments.swift | 42 +++++++++---------- 6 files changed, 43 insertions(+), 47 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index df60139eea9e4..dcb71bf35790c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3580,8 +3580,6 @@ ERROR(single_tuple_parameter_mismatch_special,none, ERROR(single_tuple_parameter_mismatch_normal,none, "%0 %1 expects a single parameter of type %2%3", (DescriptiveDeclKind, DeclBaseName, Type, StringRef)) -NOTE(note_maybe_forgot_to_form_tuple,none, - "did you mean to pass a tuple?", ()) ERROR(unknown_single_tuple_parameter_mismatch,none, "single parameter of type %0 is expected in call", (Type)) ERROR(cannot_convert_single_tuple_into_multiple_arguments,none, diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 40d7642971064..cf13f7d9064f3 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5171,23 +5171,9 @@ bool InvalidTupleSplatWithSingleParameterFailure::diagnoseAsError() { } } - // If the parameter is a generic parameter, it's hard to say - // whether use of a tuple is really intended here, so let's - // attach a fix-it to a note instead of the diagnostic message - // to indicate that it's not the only right solution possible. - if (auto *typeVar = ParamType->getAs()) { - if (typeVar->getImpl().getGenericParameter()) { - diagnostic.flush(); - - emitDiagnostic(argExpr->getLoc(), diag::note_maybe_forgot_to_form_tuple) - .fixItInsertAfter(newLeftParenLoc, "(") - .fixItInsert(argExpr->getEndLoc(), ")"); - } - } else { - diagnostic.highlight(argExpr->getSourceRange()) - .fixItInsertAfter(newLeftParenLoc, "(") - .fixItInsert(argExpr->getEndLoc(), ")"); - } + diagnostic.highlight(argExpr->getSourceRange()) + .fixItInsertAfter(newLeftParenLoc, "(") + .fixItInsert(argExpr->getEndLoc(), ")"); return true; } diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index ee46d2b50066c..40535f5fa1e90 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -779,8 +779,7 @@ bool AllowTupleSplatForSingleParameter::attempt( // Parameter type has to be either a tuple (with the same arity as // argument list), or a type variable. if (!(paramTy->is() && - paramTy->castTo()->getNumElements() == args.size()) && - !paramTy->is()) + paramTy->castTo()->getNumElements() == args.size())) return true; SmallVector argElts; @@ -792,9 +791,9 @@ bool AllowTupleSplatForSingleParameter::attempt( auto flags = arg.getParameterFlags(); // In situations where there is a single labeled parameter - // we need to form a tuple with omits first label e.g. + // we need to form a tuple which omits the label e.g. // - // func foo(x: T) {} + // func foo(x: (T, T)) {} // foo(x: 0, 1) // // We'd want to suggest argument list to be `x: (0, 1)` instead diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index ea97b7ac0086b..2b162eb03d9db 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -96,15 +96,17 @@ foo(Foo.a, Foo.b) // Ok in Swift 4 because we strip labels from the arguments // rdar://problem/32551313 - Useless SE-0110 diagnostic -enum E_32551313 { +enum E_32551313 { // expected-note {{'R' declared as parameter to type 'E_32551313'}} case Left(L) case Right(R) } struct Foo_32551313 { + // FIXME(diagnostics): We should be able to figure out L and R from contextual type static func bar() -> E_32551313<(String, Foo_32551313?), (String, String)>? { - return E_32551313.Left("", Foo_32551313()) // expected-error {{enum case 'Left' expects a single parameter of type 'L' [with L = (String, Foo_32551313?)]}} - // expected-note@-1 {{did you mean to pass a tuple?}} {{28-28=(}} {{46-46=)}} + return E_32551313.Left("", Foo_32551313()) // expected-error {{extra argument in call}} + // expected-error@-1 {{generic parameter 'R' could not be inferred}} expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} + // expected-error@-2 {{cannot convert return expression of type 'E_32551313' to return type 'E_32551313<(String, Foo_32551313?), (String, String)>?'}} } } diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index 4d8924fb5e33a..45485a2983d99 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -214,3 +214,14 @@ func erroneousSR11350(x: Int) { }).domap(0) // expected-error{{value of type 'Optional<()>' has no member 'domap'}} } } + +func extraArg() { + tuplify(true) { _ in + 1 + 2 + 3 + 4 + 5 + 6 // expected-error {{extra argument in call}} + } +} diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index a1e694ff37250..7ac39a8445752 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -69,12 +69,12 @@ func genericTuple(_ x: (T, U)) {} do { generic(3) - generic(3, 4) // expected-error {{global function 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{11-11=(}} {{15-15=)}} + generic(3, 4) // expected-error {{extra argument in call}} generic((3)) generic((3, 4)) genericLabeled(x: 3) - genericLabeled(x: 3, 4) // expected-error {{global function 'genericLabeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{20-20=(}} {{25-25=)}} + genericLabeled(x: 3, 4) // expected-error {{extra argument in call}} genericLabeled(x: (3)) genericLabeled(x: (3, 4)) @@ -92,7 +92,7 @@ do { let d = (a, b) generic(a) - generic(a, b) // expected-error {{global function 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{11-11=(}} {{15-15=)}} + generic(a, b) // expected-error {{extra argument in call}} generic((a)) generic(c) generic((a, b)) @@ -114,7 +114,7 @@ do { var d = (a, b) generic(a) - generic(a, b) // expected-error {{global function 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{11-11=(}} {{15-15=)}} + generic(a, b) // expected-error {{extra argument in call}} generic((a)) generic(c) generic((a, b)) @@ -256,12 +256,12 @@ do { let s = Concrete() s.generic(3) - s.generic(3, 4) // expected-error {{instance method 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{13-13=(}} {{17-17=)}} + s.generic(3, 4) // expected-error {{extra argument in call}} s.generic((3)) s.generic((3, 4)) s.genericLabeled(x: 3) - s.genericLabeled(x: 3, 4) // expected-error {{instance method 'genericLabeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{22-22=(}} {{27-27=)}} + s.genericLabeled(x: 3, 4) // expected-error {{extra argument in call}} s.genericLabeled(x: (3)) s.genericLabeled(x: (3, 4)) @@ -281,7 +281,7 @@ do { let d = (a, b) s.generic(a) - s.generic(a, b) // expected-error {{instance method 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{13-13=(}} {{17-17=)}} + s.generic(a, b) // expected-error {{extra argument in call}} s.generic((a)) s.generic((a, b)) s.generic(d) @@ -304,7 +304,7 @@ do { var d = (a, b) s.generic(a) - s.generic(a, b) // expected-error {{instance method 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{13-13=(}} {{17-17=)}} + s.generic(a, b) // expected-error {{extra argument in call}} s.generic((a)) s.generic((a, b)) s.generic(d) @@ -390,12 +390,12 @@ do { var s = Concrete() s.mutatingGeneric(3) - s.mutatingGeneric(3, 4) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{21-21=(}} {{25-25=)}} + s.mutatingGeneric(3, 4) // expected-error {{extra argument in call}} s.mutatingGeneric((3)) s.mutatingGeneric((3, 4)) s.mutatingGenericLabeled(x: 3) - s.mutatingGenericLabeled(x: 3, 4) // expected-error {{instance method 'mutatingGenericLabeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{30-30=(}} {{35-35=)}} + s.mutatingGenericLabeled(x: 3, 4) // expected-error {{extra argument in call}} s.mutatingGenericLabeled(x: (3)) s.mutatingGenericLabeled(x: (3, 4)) @@ -415,7 +415,7 @@ do { let d = (a, b) s.mutatingGeneric(a) - s.mutatingGeneric(a, b) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{21-21=(}} {{25-25=)}} + s.mutatingGeneric(a, b) // expected-error {{extra argument in call}} s.mutatingGeneric((a)) s.mutatingGeneric((a, b)) s.mutatingGeneric(d) @@ -438,7 +438,7 @@ do { var d = (a, b) s.mutatingGeneric(a) - s.mutatingGeneric(a, b) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{21-21=(}} {{25-25=)}} + s.mutatingGeneric(a, b) // expected-error {{extra argument in call}} s.mutatingGeneric((a)) s.mutatingGeneric((a, b)) s.mutatingGeneric(d) @@ -929,10 +929,10 @@ struct GenericInitLabeledTuple { } do { - _ = GenericInit(3, 4) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{19-19=(}} {{23-23=)}} + _ = GenericInit(3, 4) // expected-error {{extra argument in call}} _ = GenericInit((3, 4)) - _ = GenericInitLabeled(x: 3, 4) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{28-28=(}} {{33-33=)}} + _ = GenericInitLabeled(x: 3, 4) // expected-error {{extra argument in call}} _ = GenericInitLabeled(x: (3, 4)) _ = GenericInitTwo(3, 4) @@ -967,7 +967,7 @@ do { let b = 4 let c = (a, b) - _ = GenericInit(a, b) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{19-19=(}} {{23-23=)}} + _ = GenericInit(a, b) // expected-error {{extra argument in call}} _ = GenericInit((a, b)) _ = GenericInit(c) @@ -1003,7 +1003,7 @@ do { var b = 4 var c = (a, b) - _ = GenericInit(a, b) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{19-19=(}} {{23-23=)}} + _ = GenericInit(a, b) // expected-error {{extra argument in call}} _ = GenericInit((a, b)) _ = GenericInit(c) @@ -1127,12 +1127,12 @@ enum GenericEnum { } do { - _ = GenericEnum.one(3, 4) // expected-error {{enum case 'one' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{23-23=(}} {{27-27=)}} + _ = GenericEnum.one(3, 4) // expected-error {{extra argument in call}} _ = GenericEnum.one((3, 4)) - _ = GenericEnum.labeled(x: 3, 4) // expected-error {{enum case 'labeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{29-29=(}} {{34-34=)}} + _ = GenericEnum.labeled(x: 3, 4) // expected-error {{extra argument in call}} _ = GenericEnum.labeled(x: (3, 4)) - _ = GenericEnum.labeled(3, 4) // expected-error {{enum case 'labeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{27-27=(}} {{31-31=)}} + _ = GenericEnum.labeled(3, 4) // expected-error {{extra argument in call}} _ = GenericEnum.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) @@ -1163,7 +1163,7 @@ do { let b = 4 let c = (a, b) - _ = GenericEnum.one(a, b) // expected-error {{enum case 'one' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{23-23=(}} {{27-27=)}} + _ = GenericEnum.one(a, b) // expected-error {{extra argument in call}} _ = GenericEnum.one((a, b)) _ = GenericEnum.one(c) @@ -1199,7 +1199,7 @@ do { var b = 4 var c = (a, b) - _ = GenericEnum.one(a, b) // expected-error {{enum case 'one' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{23-23=(}} {{27-27=)}} + _ = GenericEnum.one(a, b) // expected-error {{extra argument in call}} _ = GenericEnum.one((a, b)) _ = GenericEnum.one(c) From e06fb4bed39bf90b39b586d77569c06539a65129 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 18 Nov 2019 16:34:01 -0800 Subject: [PATCH 271/283] [Constraint system] Capture "salvage" result in a self-contained data structure Rework the interface to ConstraintSystem::salvage() to (a) not require an existing set of solutions, which it overwrites anyway, (b) not depend on having a single expression as input, and (c) be clear with its client about whether the operation has already emitted a diagnostic vs. the client being expected to produce a diagnostic. --- lib/Sema/CSApply.cpp | 48 ++++++++++ lib/Sema/CSSolver.cpp | 26 +++++- lib/Sema/ConstraintSystem.cpp | 23 +++-- lib/Sema/ConstraintSystem.h | 13 +-- lib/Sema/SolutionResult.h | 145 ++++++++++++++++++++++++++++++ lib/Sema/TypeCheckConstraints.cpp | 28 +++++- lib/Sema/TypeChecker.h | 1 + 7 files changed, 253 insertions(+), 31 deletions(-) create mode 100644 lib/Sema/SolutionResult.h diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index a6621983de558..be91dbf38f9f6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -20,6 +20,7 @@ #include "CodeSynthesis.h" #include "CSDiagnostics.h" #include "MiscDiagnostics.h" +#include "SolutionResult.h" #include "TypeCheckProtocol.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" @@ -7531,3 +7532,50 @@ void Solution::setExprTypes(Expr *expr) const { SetExprTypes SET(*this); expr->walk(SET); } + +/// MARK: SolutionResult implementation. + +SolutionResult SolutionResult::forSolved(Solution &&solution) { + SolutionResult result(Kind::Success); + result.solutions = new Solution(std::move(solution)); + result.numSolutions = 1; + return result; +} + +SolutionResult SolutionResult::forAmbiguous( + MutableArrayRef solutions) { + assert(solutions.size() > 1 && "Not actually ambiguous"); + SolutionResult result(Kind::Ambiguous); + result.solutions = + (Solution *)malloc(sizeof(Solution) * solutions.size()); + result.numSolutions = solutions.size(); + std::uninitialized_copy(std::make_move_iterator(solutions.begin()), + std::make_move_iterator(solutions.end()), + result.solutions); + return result; +} + +SolutionResult::~SolutionResult() { + assert((!requiresDiagnostic() || emittedDiagnostic) && + "SolutionResult was destroyed without emitting a diagnostic"); + + for (unsigned i : range(numSolutions)) { + solutions[i].~Solution(); + } + free(solutions); +} + +const Solution &SolutionResult::getSolution() const { + assert(numSolutions == 1 && "Wrong number of solutions"); + return solutions[0]; +} + +Solution &&SolutionResult::takeSolution() && { + assert(numSolutions == 1 && "Wrong number of solutions"); + return std::move(solutions[0]); +} + +ArrayRef SolutionResult::getAmbiguousSolutions() const { + assert(getKind() == Ambiguous); + return makeArrayRef(solutions, numSolutions); +} diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 31c5d7e9689e0..a6fdc435cbc36 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -16,6 +16,7 @@ #include "CSStep.h" #include "ConstraintGraph.h" #include "ConstraintSystem.h" +#include "SolutionResult.h" #include "TypeCheckType.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" @@ -1121,10 +1122,27 @@ bool ConstraintSystem::solve(Expr *&expr, if (shouldSuppressDiagnostics()) return true; - // Try to provide a decent diagnostic. - if (salvage(solutions, expr)) { - // If salvage produced an error message, then it failed to salvage the - // expression, just bail out having reported the error. + // Try to fix the system or provide a decent diagnostic. + auto salvagedResult = salvage(); + switch (salvagedResult.getKind()) { + case SolutionResult::Kind::Success: + solutions.clear(); + solutions.push_back(std::move(salvagedResult).takeSolution()); + break; + + case SolutionResult::Kind::Error: + case SolutionResult::Kind::Ambiguous: + return true; + + case SolutionResult::Kind::UndiagnosedError: + diagnoseFailureForExpr(expr); + salvagedResult.markAsDiagnosed(); + return true; + + case SolutionResult::Kind::TooComplex: + getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex) + .highlight(expr->getSourceRange()); + salvagedResult.markAsDiagnosed(); return true; } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 656d2c43c7b38..422f68d1bb91c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -19,6 +19,7 @@ #include "ConstraintGraph.h" #include "CSDiagnostics.h" #include "CSFix.h" +#include "SolutionResult.h" #include "TypeCheckType.h" #include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" @@ -2444,7 +2445,7 @@ bool OverloadChoice::isImplicitlyUnwrappedValueOrReturnValue() const { llvm_unreachable("unhandled kind"); } -bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { +SolutionResult ConstraintSystem::salvage() { auto &ctx = getASTContext(); if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); @@ -2456,6 +2457,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { // // FIXME: can this be removed? We need to arrange for recordFixes to be // eliminated. + SmallVector viable; viable.clear(); { @@ -2472,13 +2474,13 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { if (*best != 0) viable[0] = std::move(viable[*best]); viable.erase(viable.begin() + 1, viable.end()); - return false; + return SolutionResult::forSolved(std::move(viable[0])); } // Before removing any "fixed" solutions, let's check // if ambiguity is caused by fixes and diagnose if possible. if (diagnoseAmbiguityWithFixes(viable)) - return true; + return SolutionResult::forAmbiguous(viable); // FIXME: If we were able to actually fix things along the way, // we may have to hunt for the best solution. For now, we don't care. @@ -2504,23 +2506,18 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { } if (diagnoseAmbiguity(viable)) { - return true; + return SolutionResult::forAmbiguous(viable); } } // Fall through to produce diagnostics. } - if (getExpressionTooComplex(viable)) { - ctx.Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); - return true; - } + if (getExpressionTooComplex(viable)) + return SolutionResult::forTooComplex(); - // If all else fails, diagnose the failure by looking through the system's - // constraints. - diagnoseFailureForExpr(expr); - return true; + // Could not produce a specific diagnostic; punt to the client. + return SolutionResult::forUndiagnosedError(); } static void diagnoseOperatorAmbiguity(ConstraintSystem &cs, diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 80aaba2865e64..3446b103b9e46 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2081,17 +2081,8 @@ class ConstraintSystem { ValueDecl *findResolvedMemberRef(ConstraintLocator *locator); /// Try to salvage the constraint system by applying (speculative) - /// fixes to the underlying expression. - /// - /// \param viable the set of viable solutions produced by the initial - /// solution attempt. - /// - /// \param expr the expression we're trying to salvage. - /// - /// \returns false if we were able to salvage the system, in which case - /// \c viable[0] contains the resulting solution. Otherwise, emits a - /// diagnostic and returns true. - bool salvage(SmallVectorImpl &viable, Expr *expr); + /// fixes. + SolutionResult salvage(); /// Mine the active and inactive constraints in the constraint /// system to generate a plausible diagnosis of why the system could not be diff --git a/lib/Sema/SolutionResult.h b/lib/Sema/SolutionResult.h new file mode 100644 index 0000000000000..8e3085a3a69c1 --- /dev/null +++ b/lib/Sema/SolutionResult.h @@ -0,0 +1,145 @@ +//===--- SolutionResult.h - Constraint System Solution ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the SolutionResult class. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_TYPECHECK_SOLUTION_RESULT_H +#define SWIFT_TYPECHECK_SOLUTION_RESULT_H + +#include "llvm/ADT/ArrayRef.h" + +namespace swift { + +using llvm::ArrayRef; +using llvm::makeArrayRef; + +namespace constraints { + +class Solution; + +/// Describes the result of solving a constraint system, after +/// potentially taking various corrective actions. +class SolutionResult { +public: + enum Kind : unsigned char { + /// The constraint system was successfully solved, and one can + /// retrieve the resulting solution. + Success, + /// The constraint system had multiple solutions, none of which + /// was better than the others. + Ambiguous, + /// The constraint system had no solution, and a diagnostic has + /// already been emitted. + Error, + /// The constraint system had no solution, but no diagnostic has + /// been emitted yet. + UndiagnosedError, + /// The constraint system was too complex to solve, but no + /// diagnostic has been emitted yet. + TooComplex, + }; + +private: + /// The kind of solution result. + Kind kind; + + /// Whether the client has emitted a diagnostic. + unsigned emittedDiagnostic : 1; + + /// The number of solutions owned by this result. + unsigned numSolutions = 0; + + /// A pointer to the set of solutions, of which there are + /// \c numSolutions entries. + Solution *solutions = nullptr; + + /// General constructor for the named constructors. + SolutionResult(Kind kind) : kind(kind) { + emittedDiagnostic = false; + } + +public: + SolutionResult(const SolutionResult &other) = delete; + + SolutionResult(SolutionResult &&other) + : kind(other.kind), numSolutions(other.numSolutions), + solutions(other.solutions) { + emittedDiagnostic = false; + other.kind = Error; + other.numSolutions = 0; + other.solutions = nullptr; + } + + SolutionResult &operator=(const SolutionResult &other) = delete; + SolutionResult &operator=(SolutionResult &&other) = delete; + + ~SolutionResult(); + + /// Produce a "solved" result, embedding the given solution. + static SolutionResult forSolved(Solution &&solution); + + /// Produce an "ambiguous" result, providing the set of + /// potential solutions. + static SolutionResult forAmbiguous(MutableArrayRef solutions); + + /// Produce a "too complex" failure, which was not yet been + /// diagnosed. + static SolutionResult forTooComplex() { + return SolutionResult(TooComplex); + } + + /// Produce a failure that has already been diagnosed. + static SolutionResult forError() { + return SolutionResult(Error); + } + + /// Produce a failure that has not yet been diagnosed. + static SolutionResult forUndiagnosedError() { + return SolutionResult(UndiagnosedError); + } + + Kind getKind() const{ return kind; } + + /// Retrieve the solution, where there is one. + const Solution &getSolution() const; + + /// Retrieve the solution, where there is one. + Solution &&takeSolution() &&; + + /// Retrieve the set of solutions when there is an ambiguity. + ArrayRef getAmbiguousSolutions() const; + + /// Whether this solution requires the client to produce a diagnostic. + bool requiresDiagnostic() const { + switch (kind) { + case Success: + case Ambiguous: + case Error: + return false; + + case UndiagnosedError: + case TooComplex: + return true; + } + } + + /// Note that the failure has been diagnosed. + void markAsDiagnosed() { + emittedDiagnostic = true; + } +}; + +} } + +#endif /* SWIFT_TYPECHECK_SOLUTION_RESULT_H */ diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 4bbf6ea8954e0..234c32c90e03c 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -18,6 +18,7 @@ #include "ConstraintSystem.h" #include "MiscDiagnostics.h" +#include "SolutionResult.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "TypoCorrection.h" @@ -3465,9 +3466,30 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, // Attempt to solve the constraint system. SmallVector viable; - if ((cs.solve(viable) || viable.size() != 1) && - cs.salvage(viable, expr)) { - return true; + if ((cs.solve(viable) || viable.size() != 1)) { + // Try to fix the system or provide a decent diagnostic. + auto salvagedResult = cs.salvage(); + switch (salvagedResult.getKind()) { + case SolutionResult::Kind::Success: + viable.clear(); + viable.push_back(std::move(salvagedResult).takeSolution()); + break; + + case SolutionResult::Kind::Error: + case SolutionResult::Kind::Ambiguous: + return true; + + case SolutionResult::Kind::UndiagnosedError: + cs.diagnoseFailureForExpr(expr); + salvagedResult.markAsDiagnosed(); + return true; + + case SolutionResult::Kind::TooComplex: + Context.Diags.diagnose(expr->getLoc(), diag::expression_too_complex) + .highlight(expr->getSourceRange()); + salvagedResult.markAsDiagnosed(); + return true; + } } auto &solution = viable[0]; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index efc04df053a81..1073022742d15 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -50,6 +50,7 @@ namespace constraints { enum class SolutionKind : char; class ConstraintSystem; class Solution; + class SolutionResult; } /// A mapping from substitutable types to the protocol-conformance From 0f3ed3fc98c8e682501a124a609d83bf205e0e84 Mon Sep 17 00:00:00 2001 From: Tapan Thaker Date: Mon, 18 Nov 2019 18:09:16 -0800 Subject: [PATCH 272/283] Fix the crash-in-user-code test for macos and Linux --- test/Frontend/crash-in-user-code.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift index 5a2055e2c60f9..e552c6cc7ca71 100644 --- a/test/Frontend/crash-in-user-code.swift +++ b/test/Frontend/crash-in-user-code.swift @@ -9,7 +9,7 @@ // CHECK-NEXT: --- // CHECK-NEXT: crash-in-user-code.swift // CHECK-NEXT: --- -// CHECK-NEXT: While running user code "SOURCE_DIR/test/FrontEnd/crash-in-user-code.swift" +// CHECK-NEXT: While running user code "SOURCE_DIR/test/Front{{e|E}}nd/crash-in-user-code.swift" let x: Int? = nil x! From 15eea66b9f5690c15f27eee9fb074ed027181cf4 Mon Sep 17 00:00:00 2001 From: Tapan Thaker Date: Mon, 18 Nov 2019 20:00:59 -0800 Subject: [PATCH 273/283] Make the crash-in-user-code check agnostic for macOS, Linux and Windows Co-Authored-By: Harlan Haskins --- test/Frontend/crash-in-user-code.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift index e552c6cc7ca71..0ece04ae5b504 100644 --- a/test/Frontend/crash-in-user-code.swift +++ b/test/Frontend/crash-in-user-code.swift @@ -9,7 +9,7 @@ // CHECK-NEXT: --- // CHECK-NEXT: crash-in-user-code.swift // CHECK-NEXT: --- -// CHECK-NEXT: While running user code "SOURCE_DIR/test/Front{{e|E}}nd/crash-in-user-code.swift" +// CHECK-NEXT: While running user code "{{.*}}crash-in-user-code.swift" let x: Int? = nil x! From ab062fcd14789c4a86277d68de8d2a05305d4a97 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 18 Nov 2019 22:58:36 -0800 Subject: [PATCH 274/283] [Runtime] Handle dynamic casting to NSObject via error bridging. The dynamic casting machinery failed to account for NSError bridging when casting to NSObject; check for it explicitly. --- stdlib/public/runtime/Casting.cpp | 8 +++++--- stdlib/public/runtime/SwiftObject.h | 9 +++++++++ stdlib/public/runtime/SwiftObject.mm | 5 +++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index c25154c8d061a..373f4038292e4 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -37,6 +37,7 @@ #include "llvm/Support/Compiler.h" #if SWIFT_OBJC_INTEROP #include "swift/Runtime/ObjCBridge.h" +#include "SwiftObject.h" #include "SwiftValue.h" #endif @@ -2330,9 +2331,10 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: #if SWIFT_OBJC_INTEROP - // If the destination type is an NSError, and the source type is an - // Error, then the cast can succeed by NSError bridging. - if (targetType == getNSErrorMetadata()) { + // If the destination type is an NSError or NSObject, and the source type + // is an Error, then the cast can succeed by NSError bridging. + if (targetType == getNSErrorMetadata() || + targetType == getNSObjectMetadata()) { // Don't rebridge if the source is already some kind of NSError. if (srcType->isAnyClass() && swift_dynamicCastObjCClass(*reinterpret_cast(src), diff --git a/stdlib/public/runtime/SwiftObject.h b/stdlib/public/runtime/SwiftObject.h index 5b1bbcca2ab29..1f266c4efadeb 100644 --- a/stdlib/public/runtime/SwiftObject.h +++ b/stdlib/public/runtime/SwiftObject.h @@ -29,6 +29,7 @@ #if SWIFT_OBJC_INTEROP +#if __OBJC__ // Source code: "SwiftObject" // Real class name: mangled "Swift._SwiftObject" @@ -83,5 +84,13 @@ id getDescription(OpaqueValue *value, const Metadata *type); } #endif +#endif + +namespace swift { + +/// Get the NSObject metadata. +const Metadata *getNSObjectMetadata(); + +} #endif diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 182e479a7e683..95ba09346ba44 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -1552,6 +1552,11 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector, free(nullTerminatedFilename); } +const Metadata *swift::getNSObjectMetadata() { + return SWIFT_LAZY_CONSTANT( + swift_getObjCClassMetadata((const ClassMetadata *)[NSObject class])); +} + #endif const ClassMetadata *swift::getRootSuperclass() { From fe60ace8688dd7fc81ed3f09fa6a3c06af2da41d Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Tue, 19 Nov 2019 15:59:58 +0200 Subject: [PATCH 275/283] [CMake] Add missing dep when building with BUILD_SHARED_LIBS --- lib/Serialization/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index f569669e05960..44a2a90660bdf 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -6,7 +6,11 @@ add_swift_host_library(swiftSerialization STATIC SerializedModuleLoader.cpp SerializedSILLoader.cpp SerializeDoc.cpp - SerializeSIL.cpp) + SerializeSIL.cpp + + LLVM_LINK_COMPONENTS + BitstreamReader + ) target_link_libraries(swiftSerialization PRIVATE swiftClangImporter) From 18570c723f7369da72566a81ec906d5e534f7ef6 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 18 Nov 2019 15:02:05 -0800 Subject: [PATCH 276/283] build: remove `SWIFT_NEED_EXPLICIT_LIBDISPATCH` (NFC) This flag was being used as an alias for whether or not the host is non-Darwin. Rather than adding custom checks for this, use the explicit check that CMake supports. This simplifies the logic and avoids the proliferation of custom variables which can become confusing for others who are not familiar with the custom build infrastructure. --- CMakeLists.txt | 7 ++----- tools/SourceKit/lib/Support/CMakeLists.txt | 6 ++++-- tools/SourceKit/tools/complete-test/CMakeLists.txt | 6 ++++-- tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt | 6 ++++-- tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt | 6 ++++-- tools/libSwiftSyntaxParser/CMakeLists.txt | 5 +++-- tools/swift-syntax-parser-test/CMakeLists.txt | 5 +++-- unittests/SyntaxParser/CMakeLists.txt | 5 +++-- 8 files changed, 27 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a38a7e591da7..5e39201e1e17b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -465,10 +465,7 @@ option(SWIFT_BUILD_SOURCEKIT "Build SourceKit" TRUE) option(SWIFT_ENABLE_SOURCEKIT_TESTS "Enable running SourceKit tests" ${SWIFT_BUILD_SOURCEKIT}) if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) - if(CMAKE_SYSTEM_NAME STREQUAL Darwin) - set(SWIFT_NEED_EXPLICIT_LIBDISPATCH FALSE) - else() - set(SWIFT_NEED_EXPLICIT_LIBDISPATCH TRUE) + if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) if(NOT EXISTS "${SWIFT_PATH_TO_LIBDISPATCH_SOURCE}") message(SEND_ERROR "SyntaxParserLib and SourceKit require libdispatch on non-Darwin hosts. Please specify SWIFT_PATH_TO_LIBDISPATCH_SOURCE") endif() @@ -954,7 +951,7 @@ if (LLVM_ENABLE_DOXYGEN) message(STATUS "Doxygen: enabled") endif() -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) if(CMAKE_C_COMPILER_ID STREQUAL Clang AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 3.8 OR LLVM_USE_SANITIZER) diff --git a/tools/SourceKit/lib/Support/CMakeLists.txt b/tools/SourceKit/lib/Support/CMakeLists.txt index 568a9611a7957..901720a521069 100644 --- a/tools/SourceKit/lib/Support/CMakeLists.txt +++ b/tools/SourceKit/lib/Support/CMakeLists.txt @@ -11,7 +11,9 @@ target_link_libraries(SourceKitSupport PRIVATE swiftSyntax clangBasic clangRewrite) -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(SourceKitSupport INTERFACE dispatch BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(SourceKitSupport INTERFACE + dispatch + BlocksRuntime) endif() diff --git a/tools/SourceKit/tools/complete-test/CMakeLists.txt b/tools/SourceKit/tools/complete-test/CMakeLists.txt index 94c9bbf231e56..71a9ffde2c46d 100644 --- a/tools/SourceKit/tools/complete-test/CMakeLists.txt +++ b/tools/SourceKit/tools/complete-test/CMakeLists.txt @@ -7,8 +7,10 @@ if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) else() target_link_libraries(complete-test PRIVATE sourcekitd) endif() -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(complete-test PRIVATE dispatch BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(complete-test PRIVATE + dispatch + BlocksRuntime) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt index 60aaa65c16c3e..49c40efe5b9ff 100644 --- a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt @@ -12,8 +12,10 @@ if(HAVE_UNICODE_LIBEDIT) else() target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) endif() - if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(sourcekitd-repl PRIVATE dispatch BlocksRuntime) + if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(sourcekitd-repl PRIVATE + dispatch + BlocksRuntime) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt index 2febbf274b8ca..b2cd4056e599a 100644 --- a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt @@ -17,8 +17,10 @@ if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) else() target_link_libraries(sourcekitd-test PRIVATE sourcekitd) endif() -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(sourcekitd-test PRIVATE dispatch BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(sourcekitd-test PRIVATE + dispatch + BlocksRuntime) endif() add_dependencies(sourcekitd-test sourcekitdTestOptionsTableGen) diff --git a/tools/libSwiftSyntaxParser/CMakeLists.txt b/tools/libSwiftSyntaxParser/CMakeLists.txt index 86818279b9f77..42dda212164de 100644 --- a/tools/libSwiftSyntaxParser/CMakeLists.txt +++ b/tools/libSwiftSyntaxParser/CMakeLists.txt @@ -45,8 +45,9 @@ endif() set_property(TARGET libSwiftSyntaxParser APPEND_STRING PROPERTY COMPILE_FLAGS " -fblocks") -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(libSwiftSyntaxParser PRIVATE BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(libSwiftSyntaxParser PRIVATE + BlocksRuntime) endif() add_dependencies(parser-lib libSwiftSyntaxParser) diff --git a/tools/swift-syntax-parser-test/CMakeLists.txt b/tools/swift-syntax-parser-test/CMakeLists.txt index 259f60211fd5c..e5cdd6d5b83dc 100644 --- a/tools/swift-syntax-parser-test/CMakeLists.txt +++ b/tools/swift-syntax-parser-test/CMakeLists.txt @@ -27,6 +27,7 @@ endif() set_property(TARGET swift-syntax-parser-test APPEND_STRING PROPERTY COMPILE_FLAGS " -fblocks") -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(swift-syntax-parser-test PRIVATE BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(swift-syntax-parser-test PRIVATE + BlocksRuntime) endif() diff --git a/unittests/SyntaxParser/CMakeLists.txt b/unittests/SyntaxParser/CMakeLists.txt index bfb9e46ba9fd2..48e82ac3b6d8d 100644 --- a/unittests/SyntaxParser/CMakeLists.txt +++ b/unittests/SyntaxParser/CMakeLists.txt @@ -24,6 +24,7 @@ endif() set_property(TARGET SwiftSyntaxParserTests APPEND_STRING PROPERTY COMPILE_FLAGS " -fblocks") -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(SwiftSyntaxParserTests PRIVATE BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(SwiftSyntaxParserTests PRIVATE + BlocksRuntime) endif() From 76591217fed524f476b9febde5b213a8cf74a0a3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 19 Nov 2019 08:45:23 -0800 Subject: [PATCH 277/283] [Runtime] Add test for casting an Error type to NSObject --- test/stdlib/ErrorBridged.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/stdlib/ErrorBridged.swift b/test/stdlib/ErrorBridged.swift index 958ed7c896cbc..b11d71bda1210 100644 --- a/test/stdlib/ErrorBridged.swift +++ b/test/stdlib/ErrorBridged.swift @@ -767,4 +767,29 @@ ErrorBridgingTests.test("@objc error domains for nested types") { String(reflecting: NonPrintAsObjCError.self)) } +@inline(never) +@_optimize(none) +func anyToAny(_ a: T, _ : U.Type) -> U { + return a as! U +} + +ErrorBridgingTests.test("error-to-NSObject casts") { + let error = MyCustomizedError(code: 12345) + + // Unconditional cast + let nsErrorAsObject1 = unconditionalCast(error, to: NSObject.self) + let nsError1 = unconditionalCast(nsErrorAsObject1, to: NSError.self) + expectEqual("custom", nsError1.domain) + expectEqual(12345, nsError1.code) + + // Conditional cast + let nsErrorAsObject2 = conditionalCast(error, to: NSObject.self)! + let nsError2 = unconditionalCast(nsErrorAsObject2, to: NSError.self) + expectEqual("custom", nsError2.domain) + expectEqual(12345, nsError2.code) + + // "is" check + expectTrue(error is NSObject) +} + runAllTests() From edd281ca48310d5a0e98c714d06718b12b14bcd4 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Tue, 19 Nov 2019 09:27:15 -0800 Subject: [PATCH 278/283] Remove test of dynamic replacement and dlclose dlclose is not really well supported with frameworks. --- .../dynamic_replacement_dlclose.swift | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 test/Interpreter/dynamic_replacement_dlclose.swift diff --git a/test/Interpreter/dynamic_replacement_dlclose.swift b/test/Interpreter/dynamic_replacement_dlclose.swift deleted file mode 100644 index 4c9339b9cb414..0000000000000 --- a/test/Interpreter/dynamic_replacement_dlclose.swift +++ /dev/null @@ -1,72 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(Module1)) -DMODULE -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_dlclose.swift -Xfrontend -enable-private-imports -// RUN: %target-build-swift-dylib(%t/%target-library-name(Module2)) -I%t -L%t -lModule1 %target-rpath(%t) -DMODULE2 -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_dlclose2.swift -// RUN: %target-build-swift -I%t -L%t -lModule1 -DMAIN -o %t/main %target-rpath(%t) %s -swift-version 5 -// RUN: %target-codesign %t/main %t/%target-library-name(Module1) %t/%target-library-name(Module2) -// RUN: %target-run %t/main %t/%target-library-name(Module1) %t/%target-library-name(Module2) - -// REQUIRES: executable_test - -import Module1 - -import StdlibUnittest - -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) - import Glibc -#elseif os(Windows) - import MSVCRT - import WinSDK -#else -#error("Unsupported platform") -#endif - -var DynamicallyReplaceable = TestSuite("DynamicallyReplaceable") - - - -private func target_library_name(_ name: String) -> String { -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) - return "lib\(name).dylib" -#elseif os(Windows) - return "\(name).dll" -#else - return "lib\(name).so" -#endif -} - - -DynamicallyReplaceable.test("DynamicallyReplaceable") { - var executablePath = CommandLine.arguments[0] - executablePath.removeLast(4) - expectEqual(1, test()) - // Now, test with the module containing the replacements. - -#if os(Linux) - let h = dlopen(target_library_name("Module2"), RTLD_NOW) -#elseif os(Windows) - let h = LoadLibraryA(target_library_name("Module2")) -#else - let h = dlopen(executablePath+target_library_name("Module2"), RTLD_NOW) -#endif - - expectEqual(2, test()) - -#if os(Linux) -#elseif os(Windows) -#else - dlclose(h) -#endif - -#if os(Linux) - _ = dlopen(target_library_name("Module2"), RTLD_NOW) -#elseif os(Windows) -#else - _ = dlopen(executablePath+target_library_name("Module2"), RTLD_NOW) -#endif - expectEqual(2, test()) - -} - -runAllTests() From b8866006c3d93fd2a2a10da6529a8b26ff30eaf7 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 19 Nov 2019 15:37:58 -0800 Subject: [PATCH 279/283] Revert "[FrontEnd] Pretty stack trace indicating running user code" --- lib/FrontendTool/FrontendTool.cpp | 6 ------ test/Frontend/crash-in-user-code.swift | 15 --------------- 2 files changed, 21 deletions(-) delete mode 100644 test/Frontend/crash-in-user-code.swift diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 85dba1f7a1fdd..44629ad48987c 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1337,12 +1337,6 @@ static bool processCommandLineAndRunImmediately(CompilerInvocation &Invocation, ProcessCmdLine(opts.ImmediateArgv.begin(), opts.ImmediateArgv.end()); Instance.setSILModule(std::move(SM)); - - PrettyStackTraceStringAction trace( - "running user code", - MSF.is() ? MSF.get()->getFilename() - : MSF.get()->getModuleFilename()); - ReturnValue = RunImmediately(Instance, CmdLine, IRGenOpts, Invocation.getSILOptions()); return Instance.getASTContext().hadError(); diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift deleted file mode 100644 index 0ece04ae5b504..0000000000000 --- a/test/Frontend/crash-in-user-code.swift +++ /dev/null @@ -1,15 +0,0 @@ - -// RUN: echo %s > %t.filelist.txt -// RUN: not --crash %target-swift-frontend -interpret -filelist %t.filelist.txt 2>&1 | %FileCheck %s - -// CHECK: Stack dump: -// CHECK-NEXT: Program arguments: -// CHECK-NEXT: Swift version -// CHECK-NEXT: Contents of {{.*}}.filelist.txt: -// CHECK-NEXT: --- -// CHECK-NEXT: crash-in-user-code.swift -// CHECK-NEXT: --- -// CHECK-NEXT: While running user code "{{.*}}crash-in-user-code.swift" - -let x: Int? = nil -x! From 95c369baf4a17917fb85d30401595b68b5cbdfe0 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 21 Nov 2019 16:15:22 -0800 Subject: [PATCH 280/283] Fix merge conflicts. Notes: - Upstream AutoDiff changes led to code duplication from `master`/`tensorflow` branches. I deduped code, moved code to match locations on `master` branch, and added `SWIFT_ENABLE_TENSORFLOW` markers in various files, notably `include/swift/AST/AutoDiff.h`. - There are some breakages since `-enable-strip-ownership-after-serialization` now defaults to true. Ownership model eliminator no longer runs immediately after differentiation. Details below. Todos: - TF-989: make differentiation transform generate ossa reabstraction thunks. This is necessary to re-enable marking the thunks as transparent. - TF-990: fix control flow differentiation to work with `mergeBasicBlocks` called during mandatory inlining. --- README.md | 19 - include/swift/AST/ASTContext.h | 12 +- include/swift/AST/Attr.def | 7 - include/swift/AST/Attr.h | 498 +++++++----------- include/swift/AST/AutoDiff.h | 196 +++---- include/swift/AST/DiagnosticsParse.def | 34 +- include/swift/AST/Types.h | 103 +--- include/swift/Basic/LangOptions.h | 2 +- include/swift/Parse/Parser.h | 26 - lib/AST/ASTDemangler.cpp | 8 - lib/AST/ASTPrinter.cpp | 10 - lib/AST/Attr.cpp | 194 ++----- lib/AST/Type.cpp | 78 +-- lib/Parse/ParseDecl.cpp | 80 +-- lib/Parse/Parser.cpp | 4 - lib/ParseSIL/ParseSIL.cpp | 3 +- lib/SIL/SILFunctionType.cpp | 13 +- lib/SIL/TypeLowering.cpp | 8 +- lib/SILGen/SILGen.cpp | 9 - lib/SILGen/SILGenExpr.cpp | 16 - lib/SILGen/SILGenType.cpp | 15 +- .../Mandatory/Differentiation.cpp | 51 +- lib/SILOptimizer/Transforms/Outliner.cpp | 13 - .../UtilityPasses/BugReducerTester.cpp | 9 - .../UtilityPasses/SerializeSILPass.cpp | 7 + lib/Sema/CSGen.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 46 +- lib/Sema/DerivedConformanceDifferentiable.cpp | 9 - .../DerivedConformanceElementaryFunctions.cpp | 4 - .../DerivedConformanceRingMathProtocols.cpp | 4 - .../DerivedConformanceTensorArrayProtocol.cpp | 4 - lib/Sema/DerivedConformanceTensorGroup.cpp | 2 - lib/Sema/DerivedConformanceVectorProtocol.cpp | 2 - lib/Sema/DerivedConformances.cpp | 38 +- lib/Sema/DerivedConformances.h | 13 +- lib/Sema/QuoteTransform.cpp | 54 +- lib/Sema/TypeCheckAttr.cpp | 7 - lib/Sema/TypeCheckCompilerEvaluable.cpp | 2 +- lib/Sema/TypeCheckDeclOverride.cpp | 9 +- lib/Sema/TypeCheckType.cpp | 36 +- lib/Sema/TypeChecker.h | 2 +- lib/Serialization/Deserialization.cpp | 75 +-- lib/Serialization/DeserializeSIL.cpp | 38 +- lib/Serialization/DeserializeSIL.h | 8 +- lib/Serialization/ModuleFormat.h | 40 +- lib/Serialization/Serialization.cpp | 54 +- .../Serialization/differentiable_attr.swift | 6 +- test/IDE/complete_decl_attribute.swift | 36 +- .../cmake/modules/AddSwiftSourceKit.cmake | 7 +- .../tools/sourcekitd-test/CMakeLists.txt | 13 +- tools/sil-opt/SILOpt.cpp | 5 +- utils/gyb_syntax_support/AttributeNodes.py | 170 +----- .../NodeSerializationCodes.py | 9 +- 53 files changed, 564 insertions(+), 1546 deletions(-) diff --git a/README.md b/README.md index c329ff8f8a963..81d7fec3b582e 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,8 @@ several hours. Naturally, incremental builds are much faster. ### System Requirements -<<<<<<< HEAD macOS and Ubuntu Linux LTS 18.04 are the current supported host development operating systems. -======= -macOS, Ubuntu Linux LTS, and the latest Ubuntu Linux release are currently -supported as host development operating systems. ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a Please make sure you use Python 2.x. Python 3.x is not supported currently. @@ -117,35 +112,21 @@ First, make sure that you're in the swift directory: To build using Ninja, run: -<<<<<<< HEAD swift/utils/build-script --enable-tensorflow --release-debuginfo -======= - utils/build-script --release-debuginfo ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a When developing Swift, it helps to build what you're working on in a debug configuration while building the rest of the project with optimizations. Below are some examples of using debug variants: -<<<<<<< HEAD swift/utils/build-script --enable-tensorflow --release-debuginfo --debug-swift # Swift frontend built in debug swift/utils/build-script --enable-tensorflow --release-debuginfo --debug-swift-stdlib # Standard library built in debug swift/utils/build-script --enable-tensorflow --release-debuginfo --debug-swift --force-optimized-typechecker # Swift frontend sans type checker built in debug -======= - utils/build-script --release-debuginfo --debug-swift # Swift frontend built in debug - utils/build-script --release-debuginfo --debug-swift-stdlib # Standard library built in debug - utils/build-script --release-debuginfo --debug-swift --force-optimized-typechecker # Swift frontend sans type checker built in debug ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a Limiting the amount of debug code in the compiler has a very large impact on Swift compile times, and in turn the test execution time. If you want to build the entire project in debug, you can run: -<<<<<<< HEAD swift/utils/build-script --enable-tensorflow --debug -======= - utils/build-script --debug ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a For documentation of all available arguments, as well as additional usage information, see the inline help: diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index f66f62335dfed..2347b879e4ee6 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -279,7 +279,11 @@ class ASTContext final { /// The # of times we have performed typo correction. unsigned NumTypoCorrections = 0; -<<<<<<< HEAD + /// The next auto-closure discriminator. This needs to be preserved + /// across invocations of both the parser and the type-checker. + unsigned NextAutoClosureDiscriminator = 0; + + // SWIFT_ENABLE_TENSORFLOW /// Cache of autodiff-associated vector spaces. llvm::DenseMap> AutoDiffVectorSpaces; @@ -288,11 +292,7 @@ class ASTContext final { /// same set of parameters. llvm::DenseMap, DifferentiableAttr *> DifferentiableAttrs; -======= - /// The next auto-closure discriminator. This needs to be preserved - /// across invocations of both the parser and the type-checker. - unsigned NextAutoClosureDiscriminator = 0; ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + // SWIFT_ENABLE_TENSORFLOW END private: /// The current generation number, which reflects the number of diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 4973b030be6eb..de88db96a64a2 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -506,16 +506,11 @@ SIMPLE_DECL_ATTR(_nonEphemeral, NonEphemeral, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, 90) -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a DECL_ATTR(differentiable, Differentiable, OnAccessor | OnConstructor | OnFunc | OnVar | OnSubscript | LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, 91) -<<<<<<< HEAD DECL_ATTR(differentiating, Differentiating, OnFunc | LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | @@ -532,8 +527,6 @@ DECL_ATTR(transposing, Transposing, OnFunc | LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | NotSerialized, 96) -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SIMPLE_DECL_ATTR(IBSegueAction, IBSegueAction, OnFunc | diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index b58b228a2ea04..8f4e400815599 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -36,8 +36,6 @@ #include "swift/AST/Requirement.h" #include "swift/AST/TrailingCallArguments.h" #include "swift/AST/TypeLoc.h" -// SWIFT_ENABLE_TENSORFLOW -#include "swift/AST/AutoDiff.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" @@ -1524,300 +1522,6 @@ class CustomAttr final : public DeclAttribute, } }; -// SWIFT_ENABLE_TENSORFLOW -struct DeclNameWithLoc { - DeclName Name; - DeclNameLoc Loc; -}; - -// SWIFT_ENABLE_TENSORFLOW -/// Attribute that marks a function as differentiable and optionally specifies -/// custom associated derivative functions: 'jvp' and 'vjp'. -/// -/// Examples: -/// @differentiable(jvp: jvpFoo where T : FloatingPoint) -/// @differentiable(wrt: (self, x, y), jvp: jvpFoo) -class DifferentiableAttr final - : public DeclAttribute, - private llvm::TrailingObjects { - friend TrailingObjects; - friend class DifferentiableAttributeParameterIndicesRequest; - - /// The declaration on which the `@differentiable` attribute is declared. - Decl *OriginalDeclaration = nullptr; - /// Whether this function is linear. - bool Linear; - /// The number of parsed parameters specified in 'wrt:'. - unsigned NumParsedParameters = 0; - /// The JVP function. - Optional JVP; - /// The VJP function. - Optional VJP; - /// The JVP function (optional), resolved by the type checker if JVP name is - /// specified. - FuncDecl *JVPFunction = nullptr; - /// The VJP function (optional), resolved by the type checker if VJP name is - /// specified. - FuncDecl *VJPFunction = nullptr; - /// The differentiation parameters' indices, resolved by the type checker. - /// The bit stores whether the parameter indices have been computed. - llvm::PointerIntPair ParameterIndicesAndBit; - /// The trailing where clause (optional). - TrailingWhereClause *WhereClause = nullptr; - /// The generic signature for autodiff derivative functions. Resolved by the - /// type checker based on the original function's generic signature and the - /// attribute's where clause requirements. This is set only if the attribute - /// has a where clause. - GenericSignature DerivativeGenericSignature = GenericSignature(); - - explicit DifferentiableAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, - ArrayRef parameters, - Optional jvp, - Optional vjp, - TrailingWhereClause *clause); - - explicit DifferentiableAttr(Decl *original, bool implicit, SourceLoc atLoc, - SourceRange baseRange, bool linear, - IndexSubset *indices, - Optional jvp, - Optional vjp, - GenericSignature derivativeGenericSignature); - -public: - static DifferentiableAttr *create(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, - ArrayRef params, - Optional jvp, - Optional vjp, - TrailingWhereClause *clause); - - static DifferentiableAttr *create(Decl *original, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, IndexSubset *indices, - Optional jvp, - Optional vjp, - GenericSignature derivativeGenSig); - - Decl *getOriginalDeclaration() const { return OriginalDeclaration; } - void setOriginalDeclaration(Decl *decl); - - /// Get the optional 'jvp:' function name and location. - /// Use this instead of `getJVPFunction` to check whether the attribute has a - /// registered JVP. - Optional getJVP() const { return JVP; } - - /// Get the optional 'vjp:' function name and location. - /// Use this instead of `getVJPFunction` to check whether the attribute has a - /// registered VJP. - Optional getVJP() const { return VJP; } - - bool hasComputedParameterIndices() const; - IndexSubset *getParameterIndices() const; - void setParameterIndices(IndexSubset *paramIndices); - - /// The parsed differentiation parameters, i.e. the list of parameters - /// specified in 'wrt:'. - ArrayRef getParsedParameters() const { - return {getTrailingObjects(), NumParsedParameters}; - } - MutableArrayRef getParsedParameters() { - return {getTrailingObjects(), NumParsedParameters}; - } - size_t numTrailingObjects(OverloadToken) const { - return NumParsedParameters; - } - - bool isLinear() const { return Linear; } - - TrailingWhereClause *getWhereClause() const { return WhereClause; } - - GenericSignature getDerivativeGenericSignature() const { - return DerivativeGenericSignature; - } - void setDerivativeGenericSignature(GenericSignature derivativeGenSig) { - DerivativeGenericSignature = derivativeGenSig; - } - - FuncDecl *getJVPFunction() const { return JVPFunction; } - void setJVPFunction(FuncDecl *decl); - FuncDecl *getVJPFunction() const { return VJPFunction; } - void setVJPFunction(FuncDecl *decl); - - bool parametersMatch(const DifferentiableAttr &other) const { - return getParameterIndices() == other.getParameterIndices(); - } - - /// Get the derivative generic environment for the given `@differentiable` - /// attribute and original function. - GenericEnvironment * - getDerivativeGenericEnvironment(AbstractFunctionDecl *original) const; - - // Print the attribute to the given stream. - // If `omitWrtClause` is true, omit printing the `wrt:` clause. - // If `omitDerivativeFunctions` is true, omit printing derivative functions. - void print(llvm::raw_ostream &OS, const Decl *D, - bool omitWrtClause = false, - bool omitDerivativeFunctions = false) const; - - static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DAK_Differentiable; - } -}; - -// SWIFT_ENABLE_TENSORFLOW -/// Attribute that registers a function as a derivative of another function. -/// -/// Examples: -/// @differentiating(sin(_:)) -/// @differentiating(+, wrt: (lhs, rhs)) -class DifferentiatingAttr final - : public DeclAttribute, - private llvm::TrailingObjects { - friend TrailingObjects; - - /// The original function name. - DeclNameWithLoc Original; - /// The original function, resolved by the type checker. - FuncDecl *OriginalFunction = nullptr; - /// Whether this function is linear. - bool Linear; - /// The number of parsed parameters specified in 'wrt:'. - unsigned NumParsedParameters = 0; - /// The differentiation parameters' indices, resolved by the type checker. - IndexSubset *ParameterIndices = nullptr; - - explicit DifferentiatingAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - DeclNameWithLoc original, bool linear, - ArrayRef params); - - explicit DifferentiatingAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - DeclNameWithLoc original, bool linear, - IndexSubset *indices); - -public: - static DifferentiatingAttr *create(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - DeclNameWithLoc original, bool linear, - ArrayRef params); - - static DifferentiatingAttr *create(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - DeclNameWithLoc original, bool linear, - IndexSubset *indices); - - DeclNameWithLoc getOriginal() const { return Original; } - - bool isLinear() const { return Linear; } - - FuncDecl *getOriginalFunction() const { return OriginalFunction; } - void setOriginalFunction(FuncDecl *decl) { OriginalFunction = decl; } - - /// The parsed differentiation parameters, i.e. the list of parameters - /// specified in 'wrt:'. - ArrayRef getParsedParameters() const { - return {getTrailingObjects(), NumParsedParameters}; - } - MutableArrayRef getParsedParameters() { - return {getTrailingObjects(), NumParsedParameters}; - } - size_t numTrailingObjects(OverloadToken) const { - return NumParsedParameters; - } - - IndexSubset *getParameterIndices() const { - return ParameterIndices; - } - void setParameterIndices(IndexSubset *pi) { - ParameterIndices = pi; - } - - static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DAK_Differentiating; - } -}; - -/// Attribute that registers a function as a transpose of another function. -/// -/// Examples: -/// @transposing(foo) -/// @transposing(+, wrt: (lhs, rhs)) -class TransposingAttr final - : public DeclAttribute, - private llvm::TrailingObjects { - friend TrailingObjects; - - /// The base type of the original function. - /// This is non-null only when the original function is not top-level (i.e. it - /// is an instance/static method). - TypeRepr *BaseType; - /// The original function name. - DeclNameWithLoc Original; - /// The original function, resolved by the type checker. - FuncDecl *OriginalFunction = nullptr; - /// The number of parsed parameters specified in 'wrt:'. - unsigned NumParsedParameters = 0; - /// The differentiation parameters' indices, resolved by the type checker. - IndexSubset *ParameterIndices = nullptr; - - explicit TransposingAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - TypeRepr *baseType, DeclNameWithLoc original, - ArrayRef params); - - explicit TransposingAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - TypeRepr *baseType, DeclNameWithLoc original, - IndexSubset *indices); - -public: - static TransposingAttr *create(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - TypeRepr *baseType, DeclNameWithLoc original, - ArrayRef params); - - static TransposingAttr *create(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - TypeRepr *baseType, DeclNameWithLoc original, - IndexSubset *indices); - - TypeRepr *getBaseType() const { return BaseType; } - DeclNameWithLoc getOriginal() const { return Original; } - - FuncDecl *getOriginalFunction() const { return OriginalFunction; } - void setOriginalFunction(FuncDecl *decl) { OriginalFunction = decl; } - - /// The parsed transposing parameters, i.e. the list of parameters - /// specified in 'wrt:'. - ArrayRef getParsedParameters() const { - return {getTrailingObjects(), NumParsedParameters}; - } - MutableArrayRef getParsedParameters() { - return {getTrailingObjects(), NumParsedParameters}; - } - size_t numTrailingObjects(OverloadToken) const { - return NumParsedParameters; - } - - IndexSubset *getParameterIndices() const { - return ParameterIndices; - } - void setParameterIndices(IndexSubset *pi) { - ParameterIndices = pi; - } - - static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DAK_Transposing; - } -}; - /// Relates a property to its projection value property, as described by a property wrapper. For /// example, given /// \code @@ -2083,7 +1787,6 @@ class DeclAttributes { SourceLoc getStartLoc(bool forModifiers = false) const; }; -/// A declaration name with location. struct DeclNameWithLoc { DeclName Name; DeclNameLoc Loc; @@ -2100,9 +1803,12 @@ class DifferentiableAttr final private llvm::TrailingObjects { friend TrailingObjects; + friend class DifferentiableAttributeParameterIndicesRequest; - /// Whether this function is linear (optional). - bool linear; + /// The declaration on which the `@differentiable` attribute is declared. + Decl *OriginalDeclaration = nullptr; + /// Whether this function is linear. + bool Linear; /// The number of parsed parameters specified in 'wrt:'. unsigned NumParsedParameters = 0; /// The JVP function. @@ -2116,26 +1822,26 @@ class DifferentiableAttr final /// specified. FuncDecl *VJPFunction = nullptr; /// The differentiation parameters' indices, resolved by the type checker. - IndexSubset *ParameterIndices = nullptr; + /// The bit stores whether the parameter indices have been computed. + llvm::PointerIntPair ParameterIndicesAndBit; /// The trailing where clause (optional). TrailingWhereClause *WhereClause = nullptr; - /// The generic signature for autodiff associated functions. Resolved by the + /// The generic signature for autodiff derivative functions. Resolved by the /// type checker based on the original function's generic signature and the /// attribute's where clause requirements. This is set only if the attribute /// has a where clause. - GenericSignature DerivativeGenericSignature; + GenericSignature DerivativeGenericSignature = GenericSignature(); - explicit DifferentiableAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, + explicit DifferentiableAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, ArrayRef parameters, Optional jvp, Optional vjp, TrailingWhereClause *clause); - explicit DifferentiableAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, IndexSubset *indices, + explicit DifferentiableAttr(Decl *original, bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, + IndexSubset *indices, Optional jvp, Optional vjp, GenericSignature derivativeGenericSignature); @@ -2149,13 +1855,16 @@ class DifferentiableAttr final Optional vjp, TrailingWhereClause *clause); - static DifferentiableAttr *create(ASTContext &context, bool implicit, + static DifferentiableAttr *create(Decl *original, bool implicit, SourceLoc atLoc, SourceRange baseRange, bool linear, IndexSubset *indices, Optional jvp, Optional vjp, GenericSignature derivativeGenSig); + Decl *getOriginalDeclaration() const { return OriginalDeclaration; } + void setOriginalDeclaration(Decl *decl); + /// Get the optional 'jvp:' function name and location. /// Use this instead of `getJVPFunction` to check whether the attribute has a /// registered JVP. @@ -2166,12 +1875,9 @@ class DifferentiableAttr final /// registered VJP. Optional getVJP() const { return VJP; } - IndexSubset *getParameterIndices() const { - return ParameterIndices; - } - void setParameterIndices(IndexSubset *pi) { - ParameterIndices = pi; - } + bool hasComputedParameterIndices() const; + IndexSubset *getParameterIndices() const; + void setParameterIndices(IndexSubset *paramIndices); /// The parsed differentiation parameters, i.e. the list of parameters /// specified in 'wrt:'. @@ -2185,15 +1891,14 @@ class DifferentiableAttr final return NumParsedParameters; } - bool isLinear() const { return linear; } + bool isLinear() const { return Linear; } TrailingWhereClause *getWhereClause() const { return WhereClause; } GenericSignature getDerivativeGenericSignature() const { return DerivativeGenericSignature; } - void setDerivativeGenericSignature(ASTContext &context, - GenericSignature derivativeGenSig) { + void setDerivativeGenericSignature(GenericSignature derivativeGenSig) { DerivativeGenericSignature = derivativeGenSig; } @@ -2203,8 +1908,7 @@ class DifferentiableAttr final void setVJPFunction(FuncDecl *decl); bool parametersMatch(const DifferentiableAttr &other) const { - assert(ParameterIndices && other.ParameterIndices); - return ParameterIndices == other.ParameterIndices; + return getParameterIndices() == other.getParameterIndices(); } /// Get the derivative generic environment for the given `@differentiable` @@ -2214,16 +1918,166 @@ class DifferentiableAttr final // Print the attribute to the given stream. // If `omitWrtClause` is true, omit printing the `wrt:` clause. - // If `omitAssociatedFunctions` is true, omit printing associated functions. + // If `omitDerivativeFunctions` is true, omit printing derivative functions. void print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause = false, - bool omitAssociatedFunctions = false) const; + bool omitDerivativeFunctions = false) const; static bool classof(const DeclAttribute *DA) { return DA->getKind() == DAK_Differentiable; } }; +// SWIFT_ENABLE_TENSORFLOW +/// Attribute that registers a function as a derivative of another function. +/// +/// Examples: +/// @differentiating(sin(_:)) +/// @differentiating(+, wrt: (lhs, rhs)) +class DifferentiatingAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The original function name. + DeclNameWithLoc Original; + /// The original function, resolved by the type checker. + FuncDecl *OriginalFunction = nullptr; + /// Whether this function is linear. + bool Linear; + /// The number of parsed parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The differentiation parameters' indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + + explicit DifferentiatingAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + DeclNameWithLoc original, bool linear, + ArrayRef params); + + explicit DifferentiatingAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + DeclNameWithLoc original, bool linear, + IndexSubset *indices); + +public: + static DifferentiatingAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + DeclNameWithLoc original, bool linear, + ArrayRef params); + + static DifferentiatingAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + DeclNameWithLoc original, bool linear, + IndexSubset *indices); + + DeclNameWithLoc getOriginal() const { return Original; } + + bool isLinear() const { return Linear; } + + FuncDecl *getOriginalFunction() const { return OriginalFunction; } + void setOriginalFunction(FuncDecl *decl) { OriginalFunction = decl; } + + /// The parsed differentiation parameters, i.e. the list of parameters + /// specified in 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *pi) { + ParameterIndices = pi; + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Differentiating; + } +}; + +/// Attribute that registers a function as a transpose of another function. +/// +/// Examples: +/// @transposing(foo) +/// @transposing(+, wrt: (lhs, rhs)) +class TransposingAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The base type of the original function. + /// This is non-null only when the original function is not top-level (i.e. it + /// is an instance/static method). + TypeRepr *BaseType; + /// The original function name. + DeclNameWithLoc Original; + /// The original function, resolved by the type checker. + FuncDecl *OriginalFunction = nullptr; + /// The number of parsed parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The differentiation parameters' indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + + explicit TransposingAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameWithLoc original, + ArrayRef params); + + explicit TransposingAttr(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameWithLoc original, + IndexSubset *indices); + +public: + static TransposingAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameWithLoc original, + ArrayRef params); + + static TransposingAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameWithLoc original, + IndexSubset *indices); + + TypeRepr *getBaseType() const { return BaseType; } + DeclNameWithLoc getOriginal() const { return Original; } + + FuncDecl *getOriginalFunction() const { return OriginalFunction; } + void setOriginalFunction(FuncDecl *decl) { OriginalFunction = decl; } + + /// The parsed transposing parameters, i.e. the list of parameters + /// specified in 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *pi) { + ParameterIndices = pi; + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Transposing; + } +}; + void simple_display(llvm::raw_ostream &out, const DeclAttribute *attr); diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index 2c7d6bd1c9609..a2ca3cf8925ec 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -1,16 +1,8 @@ -<<<<<<< HEAD -//===--- AutoDiff.h - Swift Differentiable Programming --------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -======= //===--- AutoDiff.h - Swift Automatic Differentiation ---------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2019 Apple Inc. and the Swift project authors ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -18,20 +10,95 @@ // //===----------------------------------------------------------------------===// // -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW -// This file defines AST support for the experimental differentiable -// programming feature. -======= // This file defines AST support for automatic differentiation. ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_AUTODIFF_H #define SWIFT_AST_AUTODIFF_H -<<<<<<< HEAD +#include + +#include "swift/AST/Identifier.h" +#include "swift/AST/IndexSubset.h" +#include "swift/Basic/SourceLoc.h" +#include "swift/Basic/Range.h" + +namespace swift { + +class ParsedAutoDiffParameter { +public: + enum class Kind { Named, Ordered, Self }; + +private: + SourceLoc loc; + Kind kind; + union Value { + struct { Identifier name; } Named; + struct { unsigned index; } Ordered; + struct {} self; + Value(Identifier name) : Named({name}) {} + Value(unsigned index) : Ordered({index}) {} + Value() {} + } value; + +public: + ParsedAutoDiffParameter(SourceLoc loc, Kind kind, Value value) + : loc(loc), kind(kind), value(value) {} + + ParsedAutoDiffParameter(SourceLoc loc, Kind kind, unsigned index) + : loc(loc), kind(kind), value(index) {} + + static ParsedAutoDiffParameter getNamedParameter(SourceLoc loc, + Identifier name) { + return { loc, Kind::Named, name }; + } + + static ParsedAutoDiffParameter getOrderedParameter(SourceLoc loc, + unsigned index) { + return { loc, Kind::Ordered, index }; + } + + static ParsedAutoDiffParameter getSelfParameter(SourceLoc loc) { + return { loc, Kind::Self, {} }; + } + + Identifier getName() const { + assert(kind == Kind::Named); + return value.Named.name; + } + + unsigned getIndex() const { + return value.Ordered.index; + } + + Kind getKind() const { + return kind; + } + + SourceLoc getLoc() const { + return loc; + } + + bool isEqual(const ParsedAutoDiffParameter &other) const { + if (getKind() != other.getKind()) + return false; + if (getKind() == Kind::Named) + return getName() == other.getName(); + return getKind() == Kind::Self; + } +}; + +enum class DifferentiabilityKind : uint8_t { + NonDifferentiable = 0, + Normal = 1, + Linear = 2 +}; + +} // end namespace swift + +// SWIFT_ENABLE_TENSORFLOW +// Not-yet-upstreamed additions on `tensorflow` branch is below. #include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/IndexSubset.h" @@ -53,12 +120,6 @@ class SILFunctionType; typedef CanTypeWrapper CanSILFunctionType; enum class SILLinkage : uint8_t; -enum class DifferentiabilityKind : uint8_t { - NonDifferentiable = 0, - Normal = 1, - Linear = 2 -}; - /// The kind of an linear map. struct AutoDiffLinearMapKind { enum innerty : uint8_t { @@ -149,89 +210,6 @@ struct LinearDifferentiableFunctionTypeComponent { operator innerty() const { return rawValue; } }; -======= -#include - -#include "swift/AST/Identifier.h" -#include "swift/AST/IndexSubset.h" -#include "swift/Basic/SourceLoc.h" -#include "swift/Basic/Range.h" - -namespace swift { - ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a -class ParsedAutoDiffParameter { -public: - enum class Kind { Named, Ordered, Self }; - -private: - SourceLoc loc; - Kind kind; - union Value { - struct { Identifier name; } Named; - struct { unsigned index; } Ordered; - struct {} self; - Value(Identifier name) : Named({name}) {} - Value(unsigned index) : Ordered({index}) {} - Value() {} - } value; - -public: - ParsedAutoDiffParameter(SourceLoc loc, Kind kind, Value value) - : loc(loc), kind(kind), value(value) {} - - ParsedAutoDiffParameter(SourceLoc loc, Kind kind, unsigned index) - : loc(loc), kind(kind), value(index) {} - - static ParsedAutoDiffParameter getNamedParameter(SourceLoc loc, - Identifier name) { - return { loc, Kind::Named, name }; - } -<<<<<<< HEAD - -======= - ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a - static ParsedAutoDiffParameter getOrderedParameter(SourceLoc loc, - unsigned index) { - return { loc, Kind::Ordered, index }; - } - - static ParsedAutoDiffParameter getSelfParameter(SourceLoc loc) { - return { loc, Kind::Self, {} }; - } - - Identifier getName() const { - assert(kind == Kind::Named); - return value.Named.name; - } -<<<<<<< HEAD - -======= - ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a - unsigned getIndex() const { - return value.Ordered.index; - } - - Kind getKind() const { - return kind; - } - - SourceLoc getLoc() const { - return loc; - } - - bool isEqual(const ParsedAutoDiffParameter &other) const { - if (getKind() != other.getKind()) - return false; - if (getKind() == Kind::Named) - return getName() == other.getName(); - return getKind() == Kind::Self; - } -}; - -<<<<<<< HEAD /// SIL-level automatic differentiation indices. Consists of a source index, /// i.e. index of the dependent result to differentiate from, and parameter /// indices, i.e. index of independent parameters to differentiate with @@ -468,17 +446,10 @@ class VectorSpace { Type getType() const; CanType getCanonicalType() const; NominalTypeDecl *getNominal() const; -======= -enum class DifferentiabilityKind : uint8_t { - NonDifferentiable = 0, - Normal = 1, - Linear = 2 ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a }; } // end namespace swift -<<<<<<< HEAD namespace llvm { using swift::AutoDiffConfig; @@ -543,7 +514,6 @@ template<> struct DenseMapInfo { }; } // end namespace llvm +// SWIFT_ENABLE_TENSORFLOW END -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a #endif // SWIFT_AST_AUTODIFF_H diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 3adb93e72307b..a3976f2e9e824 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1561,18 +1561,13 @@ ERROR(differentiable_attribute_expected_rparen,none, "expected ')' in '@differentiable' attribute", ()) ERROR(unexpected_argument_differentiable,none, "unexpected argument '%0' in '@differentiable' attribute", (StringRef)) -// SWIFT_ENABLE_TENSORFLOW -ERROR(attr_differentiable_expected_function_name,PointsToFirstBadToken, - "expected a %0 function name", (StringRef)) -ERROR(attr_differentiable_expected_parameter_list,PointsToFirstBadToken, - "expected a list of parameters to differentiate with respect to", ()) -ERROR(attr_differentiable_use_wrt_not_withrespectto,none, - "use 'wrt:' to specify parameters to differentiate with respect to", ()) -ERROR(attr_differentiable_missing_label,PointsToFirstBadToken, - "missing label '%0:' in '@differentiable' attribute", (StringRef)) -ERROR(attr_differentiable_expected_label,none, - "expected either 'wrt:' or a function specifier label, e.g. 'jvp:', " - "or 'vjp:'", ()) + +// differentiation `wrt` parameters clause +ERROR(expected_colon_after_label,PointsToFirstBadToken, + "expected a colon ':' after '%0'", (StringRef)) +ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, + "expected a parameter, which can be a function parameter name, " + "parameter index, or 'self'", ()) // differentiating ERROR(attr_differentiating_expected_original_name,PointsToFirstBadToken, @@ -1590,14 +1585,6 @@ ERROR(attr_transposing_expected_label_linear_or_wrt,none, ERROR(transposing_params_clause_expected_parameter,PointsToFirstBadToken, "expected a parameter, which can be a 'unsigned int' parameter number " "or 'self'", ()) - -// differentiation `wrt` parameters clause -ERROR(expected_colon_after_label,PointsToFirstBadToken, - "expected a colon ':' after '%0'", (StringRef)) -ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, - "expected a parameter, which can be a function parameter name, " - "parameter index, or 'self'", ()) - // [differentiable ...] (sil-decl attr) ERROR(sil_attr_differentiable_expected_keyword,PointsToFirstBadToken, "expected '%0' in 'differentiable' attribute", (StringRef)) @@ -1648,13 +1635,6 @@ ERROR(attr_quoted_enable_experimental_quasiquotes,PointsToFirstBadToken, "'@quoted' is unsupported; pass -enable-experimental-quasiquotes to " "enable support for quasiquotes", ()) -// differentiation `wrt` parameters clause -ERROR(expected_colon_after_label,PointsToFirstBadToken, - "expected a colon ':' after '%0'", (StringRef)) -ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, - "expected a parameter, which can be a function parameter name, " - "parameter index, or 'self'", ()) - //------------------------------------------------------------------------------ // MARK: Generics parsing diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 8197982b7cb42..9e7431b2e0cc1 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -17,13 +17,7 @@ #ifndef SWIFT_TYPES_H #define SWIFT_TYPES_H -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW -#include "swift/AST/AutoDiff.h" -#include "swift/AST/Attr.h" -======= #include "swift/AST/AutoDiff.h" ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a #include "swift/AST/DeclContext.h" #include "swift/AST/GenericParamKey.h" #include "swift/AST/Identifier.h" @@ -315,10 +309,6 @@ class alignas(1 << TypeAlignInBits) TypeBase { } protected: -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a enum { NumAFTExtInfoBits = 8 }; enum { NumSILExtInfoBits = 8 }; union { uint64_t OpaqueBits; @@ -2935,31 +2925,19 @@ class AnyFunctionType : public TypeBase { // If bits are added or removed, then TypeBase::AnyFunctionTypeBits // and NumMaskBits must be updated, and they must match. // -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // |representation|noEscape|throws|differentiability| // | 0 .. 3 | 4 | 5 | 6 .. 7 | // enum : unsigned { -<<<<<<< HEAD RepresentationMask = 0xF << 0, NoEscapeMask = 1 << 4, ThrowsMask = 1 << 5, - // SWIFT_ENABLE_TENSORFLOW DifferentiabilityMaskOffset = 6, DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + // SWIFT_ENABLE_TENSORFLOW NumDifferentiabilityMaskBits = 2, + // SWIFT_ENABLE_TENSORFLOW END NumMaskBits = 8 -======= - RepresentationMask = 0xF << 0, - NoEscapeMask = 1 << 4, - ThrowsMask = 1 << 5, - DifferentiabilityMaskOffset = 6, - DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, - NumMaskBits = 8 ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a }; unsigned Bits; // Naturally sized for speed. @@ -2982,35 +2960,20 @@ class AnyFunctionType : public TypeBase { // Constructor with no defaults. ExtInfo(Representation Rep, bool IsNoEscape, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - bool Throws, DifferentiabilityKind diffKind) - : ExtInfo(Rep, Throws) { - Bits |= (IsNoEscape ? NoEscapeMask : 0); - // SWIFT_ENABLE_TENSORFLOW - Bits |= ((unsigned)diffKind << DifferentiabilityMaskOffset) - & DifferentiabilityMask; -======= bool Throws, DifferentiabilityKind DiffKind) : ExtInfo(Rep, Throws) { Bits |= (IsNoEscape ? NoEscapeMask : 0); Bits |= ((unsigned)DiffKind << DifferentiabilityMaskOffset) & DifferentiabilityMask; ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } bool isNoEscape() const { return Bits & NoEscapeMask; } bool throws() const { return Bits & ThrowsMask; } -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - bool isDifferentiable() const { - return getDifferentiabilityKind() >= DifferentiabilityKind::Normal; -======= bool isDifferentiable() const { + // return getDifferentiabilityKind() >= DifferentiabilityKind::Normal; return getDifferentiabilityKind() > DifferentiabilityKind::NonDifferentiable; ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } DifferentiabilityKind getDifferentiabilityKind() const { return DifferentiabilityKind((Bits & DifferentiabilityMask) >> @@ -3216,14 +3179,6 @@ class AnyFunctionType : public TypeBase { bool throws() const { return getExtInfo().throws(); } - - // SWIFT_ENABLE_TENSORFLOW - bool isDifferentiable() const { - return getExtInfo().isDifferentiable(); - } - DifferentiabilityKind getDifferentiabilityKind() const { - return getExtInfo().getDifferentiabilityKind(); - } bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } DifferentiabilityKind getDifferentiabilityKind() const { @@ -3923,21 +3878,6 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // If bits are added or removed, then TypeBase::SILFunctionTypeBits // and NumMaskBits must be updated, and they must match. -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - // |representation|pseudogeneric|noescape|differentiability| - // | 0 .. 3 | 4 | 5 | 6 .. 7 | - // - enum : unsigned { - RepresentationMask = 0xF << 0, - PseudogenericMask = 1 << 4, - NoEscapeMask = 1 << 5, - // SWIFT_ENABLE_TENSORFLOW - DifferentiabilityMaskOffset = 6, - DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, - NumDifferentiabilityMaskBits = 2, - NumMaskBits = 8 -======= // |representation|pseudogeneric| noescape |differentiability| // | 0 .. 3 | 4 | 5 | 6 .. 7 | // @@ -3947,8 +3887,10 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, NoEscapeMask = 1 << 5, DifferentiabilityMaskOffset = 6, DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + // SWIFT_ENABLE_TENSORFLOW + NumDifferentiabilityMaskBits = 2, + // SWIFT_ENABLE_TENSORFLOW END NumMaskBits = 8 ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a }; unsigned Bits; // Naturally sized for speed. @@ -3961,24 +3903,13 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, ExtInfo() : Bits(0) { } // Constructor for polymorphic type. -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape, DifferentiabilityKind diffKind) { Bits = ((unsigned) rep) | (isPseudogeneric ? PseudogenericMask : 0) | -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - (isNoEscape ? NoEscapeMask : 0) | - (((unsigned)diffKind << DifferentiabilityMaskOffset) - & DifferentiabilityMask); -======= (isNoEscape ? NoEscapeMask : 0) | (((unsigned)diffKind << DifferentiabilityMaskOffset) & DifferentiabilityMask); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } /// Is this function pseudo-generic? A pseudo-generic function @@ -3987,16 +3918,6 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // Is this function guaranteed to be no-escape by the type system? bool isNoEscape() const { return Bits & NoEscapeMask; } - - // SWIFT_ENABLE_TENSORFLOW - bool isDifferentiable() const { - return getDifferentiabilityKind() >= DifferentiabilityKind::Normal; - } - - DifferentiabilityKind getDifferentiabilityKind() const { - return DifferentiabilityKind((Bits & DifferentiabilityMask) >> - DifferentiabilityMaskOffset); - } bool isDifferentiable() const { return getDifferentiabilityKind() != @@ -4444,22 +4365,12 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, getRepresentation() == SILFunctionTypeRepresentation::Thick; } -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + DifferentiabilityKind getDifferentiabilityKind() const { return getExtInfo().getDifferentiabilityKind(); } -<<<<<<< HEAD - bool isDifferentiable() const { - return getExtInfo().isDifferentiable(); - } - -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a bool isNoReturnFunction(SILModule &M) const; // Defined in SILType.cpp /// Create a SILFunctionType with the same parameters, results, and attributes as this one, but with diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 0785e89f91cc2..1107a3e39374a 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -285,7 +285,7 @@ namespace swift { /// Whether to enable experimental differentiable programming features: /// `@differentiable` declaration attribute, etc. // SWIFT_ENABLE_TENSORFLOW - // Use default value to true on `tensorflow` branch. + // Use default value true on `tensorflow` branch. bool EnableExperimentalDifferentiableProgramming = true; // SWIFT_ENABLE_TENSORFLOW END diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 1ce9dc23399ba..97ce067d2a1b1 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -717,18 +717,6 @@ class Parser { return Tok.is(tok::identifier) && Tok.getText() == value; } -<<<<<<< HEAD -======= - /// Returns true if token is the identifier "wrt". - bool isWRTIdentifier(Token tok) { return isIdentifier(Tok, "wrt"); } - - /// Returns true if token is the identifier "jvp". - bool isJVPIdentifier(Token Tok) { return isIdentifier(Tok, "jvp"); } - - /// Returns true if token is the identifier "vjp". - bool isVJPIdentifier(Token Tok) { return isIdentifier(Tok, "vjp"); } - ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /// Consume the starting '<' of the current token, which may either /// be a complete '<' token or some kind of operator token starting with '<', /// e.g., '<>'. @@ -842,13 +830,6 @@ class Parser { bool parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag, SourceLoc OtherLoc); - /// SWIFT_ENABLE_TENSORFLOW - /// \brief Parse an unsigned integer and returns it in \p Result. On failure - /// emit the specified error diagnostic, and a note at the specified note - /// location. - bool parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, - const Diagnostic &D); - /// Returns the proper location for a missing right brace, parenthesis, etc. SourceLoc getLocForMissingMatchingToken() const; @@ -998,10 +979,6 @@ class Parser { ParserResult parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc); -<<<<<<< HEAD - /// SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /// Parse the @differentiable attribute. ParserResult parseDifferentiableAttribute(SourceLoc AtLoc, SourceLoc Loc); @@ -1015,7 +992,6 @@ class Parser { /// Parse a differentiation parameters clause. bool parseDifferentiationParametersClause( SmallVectorImpl ¶ms, StringRef attrName); -<<<<<<< HEAD /// Parse a transposing parameters clause. bool parseTransposingParametersClause( @@ -1030,8 +1006,6 @@ class Parser { /// Parse the @quoted attribute. ParserResult parseQuotedAttribute(SourceLoc AtLoc, SourceLoc Loc); -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc); diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index e48a480429037..993eb1edbe521 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -495,17 +495,9 @@ Type ASTBuilder::createImplFunctionType( break; } -<<<<<<< HEAD - auto einfo = SILFunctionType::ExtInfo(representation, - flags.isPseudogeneric(), - !flags.isEscaping(), - DifferentiabilityKind:: - NonDifferentiable); -======= auto einfo = SILFunctionType::ExtInfo( representation, flags.isPseudogeneric(), !flags.isEscaping(), DifferentiabilityKind::NonDifferentiable); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a llvm::SmallVector funcParams; llvm::SmallVector funcYields; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index d479caf403713..305d65c7a338c 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3801,13 +3801,8 @@ class TypePrinter : public TypeVisitor { if (Options.SkipAttributes) return; -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - if (!Options.excludeAttrKind(TAK_differentiable) && info.isDifferentiable()) { -======= if (!Options.excludeAttrKind(TAK_differentiable) && info.isDifferentiable()) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (info.getDifferentiabilityKind() == DifferentiabilityKind::Linear) { Printer << "@differentiable(linear) "; } else { @@ -3858,13 +3853,8 @@ class TypePrinter : public TypeVisitor { if (Options.SkipAttributes) return; -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - if (!Options.excludeAttrKind(TAK_differentiable) && info.isDifferentiable()) { -======= if (!Options.excludeAttrKind(TAK_differentiable) && info.isDifferentiable()) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (info.getDifferentiabilityKind() == DifferentiabilityKind::Linear) { Printer << "@differentiable(linear) "; } else { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 0d3f682813f7c..1ed28a71810b9 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -20,21 +20,16 @@ #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" -<<<<<<< HEAD // SWIFT_ENABLE_TENSORFLOW #include "swift/AST/GenericSignatureBuilder.h" -======= +// SWIFT_ENABLE_TENSORFLOW END #include "swift/AST/IndexSubset.h" ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a #include "swift/AST/Module.h" #include "swift/AST/TypeRepr.h" // SWIFT_ENABLE_TENSORFLOW #include "swift/AST/TypeCheckRequests.h" +// SWIFT_ENABLE_TENSORFLOW END #include "swift/AST/Types.h" -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a #include "swift/AST/ParameterList.h" #include "swift/Basic/Defer.h" #include "llvm/ADT/SmallString.h" @@ -364,7 +359,6 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer.printNewline(); } -<<<<<<< HEAD // Returns the differentiation parameters clause string for the given function, // parameter indices, and parsed parameters. Use the parameter indices if // specified; otherwise, use the parsed parameters. @@ -379,18 +373,6 @@ static std::string getDifferentiationParametersClauseString( // Use the parameter indices, if specified. if (paramIndices) { auto parameters = paramIndices->getBitVector(); -======= -static std::string getDifferentiationParametersClauseString( - const AbstractFunctionDecl *function, IndexSubset *indices, - ArrayRef parsedParams) { - bool isInstanceMethod = function && function->isInstanceMember(); - std::string result; - llvm::raw_string_ostream printer(result); - - // Use parameter indices from `IndexSubset`, if specified. - if (indices) { - auto parameters = indices->getBitVector(); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a auto parameterCount = parameters.count(); printer << "wrt: "; if (parameterCount > 1) @@ -425,12 +407,7 @@ static std::string getDifferentiationParametersClauseString( case ParsedAutoDiffParameter::Kind::Ordered: auto *paramList = function->getParameters(); assert(param.getIndex() <= paramList->size() && -<<<<<<< HEAD - "'wrt:' parameter index should be less than the number " - "of parameters"); -======= "wrt parameter is out of range"); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a auto *funcParam = paramList->get(param.getIndex()); printer << funcParam->getNameStr(); break; @@ -442,7 +419,6 @@ static std::string getDifferentiationParametersClauseString( return printer.str(); } -<<<<<<< HEAD // SWIFT_ENABLE_TENSORFLOW // Returns the transposed parameters clause string for the given function, // parameter indices, and parsed parameters. Use the parameter indices if @@ -516,30 +492,16 @@ static void printDifferentiableAttrArguments( const Decl *D, bool omitWrtClause = false, bool omitDerivativeFunctions = false) { assert(D); -======= -// Print the arguments of the given `@differentiable` attribute. -static void printDifferentiableAttrArguments( - const DifferentiableAttr *attr, ASTPrinter &printer, PrintOptions Options, - const Decl *D, bool omitWrtClause = false, - bool omitAssociatedFunctions = false) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // Create a temporary string for the attribute argument text. std::string attrArgText; llvm::raw_string_ostream stream(attrArgText); // Get original function. -<<<<<<< HEAD auto *original = dyn_cast(D); // Handle stored/computed properties and subscript methods. if (auto *asd = dyn_cast(D)) original = asd->getAccessor(AccessorKind::Get); assert(original && "Must resolve original declaration"); -======= - auto *original = dyn_cast_or_null(D); - // Handle stored/computed properties and subscript methods. - if (auto *asd = dyn_cast_or_null(D)) - original = asd->getAccessor(AccessorKind::Get); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // Print comma if not leading clause. bool isLeadingClause = true; @@ -550,22 +512,14 @@ static void printDifferentiableAttrArguments( } stream << ", "; }; -<<<<<<< HEAD - -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // Print if the function is marked as linear. if (attr->isLinear()) { isLeadingClause = false; stream << "linear"; } -<<<<<<< HEAD // Print differentiation parameters clause, unless it is to be omitted. -======= - // Print differentiation parameters, unless they are to be omitted. ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (!omitWrtClause) { auto diffParamsString = getDifferentiationParametersClauseString( original, attr->getParameterIndices(), attr->getParsedParameters()); @@ -578,13 +532,8 @@ static void printDifferentiableAttrArguments( stream << diffParamsString; } } -<<<<<<< HEAD // Print derivative function names, unless they are to be omitted. if (!omitDerivativeFunctions) { -======= - // Print associated function names, unless they are to be omitted. - if (!omitAssociatedFunctions) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // Print jvp function name, if specified. if (auto jvp = attr->getJVP()) { printCommaIfNecessary(); @@ -602,18 +551,6 @@ static void printDifferentiableAttrArguments( ArrayRef derivativeRequirements; if (auto derivativeGenSig = attr->getDerivativeGenericSignature()) derivativeRequirements = derivativeGenSig->getRequirements(); -<<<<<<< HEAD - llvm::SmallVector requirementsToPrint; - std::copy_if(derivativeRequirements.begin(), derivativeRequirements.end(), - std::back_inserter(requirementsToPrint), - [&](Requirement req) { - if (auto originalGenSig = original->getGenericSignature()) - if (originalGenSig->isRequirementSatisfied(req)) - return false; - return true; - }); - if (!requirementsToPrint.empty()) { -======= auto requirementsToPrint = llvm::make_filter_range(derivativeRequirements, [&](Requirement req) { if (const auto &originalGenSig = original->getGenericSignature()) @@ -622,7 +559,6 @@ static void printDifferentiableAttrArguments( return true; }); if (!llvm::empty(requirementsToPrint)) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (!isLeadingClause) stream << ' '; stream << "where "; @@ -639,11 +575,7 @@ static void printDifferentiableAttrArguments( }; } interleave(requirementsToPrint, [&](Requirement req) { -<<<<<<< HEAD - if (auto originalGenSig = original->getGenericSignature()) -======= if (const auto &originalGenSig = original->getGenericSignature()) ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (originalGenSig->isRequirementSatisfied(req)) return; auto FirstTy = getInterfaceType(req.getFirstType()); @@ -979,7 +911,31 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } - // SWIFT_ENABLE_TENSORFLOW + case DAK_DynamicReplacement: { + Printer.printAttrName("@_dynamicReplacement"); + Printer << "(for: \""; + auto *attr = cast(this); + Printer << attr->getReplacedFunctionName() << "\")"; + break; + } + + case DAK_Custom: { + Printer.printAttrName("@"); + const TypeLoc &typeLoc = cast(this)->getTypeLoc(); + if (auto type = typeLoc.getType()) + type->print(Printer, Options); + else + typeLoc.getTypeRepr()->print(Printer, Options); + break; + } + + case DAK_ProjectedValueProperty: + Printer.printAttrName("@_projectedValueProperty"); + Printer << "("; + Printer << cast(this)->ProjectionPropertyName; + Printer << ")"; + break; + case DAK_Differentiable: { Printer.printAttrName("@differentiable"); auto *attr = cast(this); @@ -1017,38 +973,6 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } - case DAK_DynamicReplacement: { - Printer.printAttrName("@_dynamicReplacement"); - Printer << "(for: \""; - auto *attr = cast(this); - Printer << attr->getReplacedFunctionName() << "\")"; - break; - } - - case DAK_Custom: { - Printer.printAttrName("@"); - const TypeLoc &typeLoc = cast(this)->getTypeLoc(); - if (auto type = typeLoc.getType()) - type->print(Printer, Options); - else - typeLoc.getTypeRepr()->print(Printer, Options); - break; - } - - case DAK_ProjectedValueProperty: - Printer.printAttrName("@_projectedValueProperty"); - Printer << "("; - Printer << cast(this)->ProjectionPropertyName; - Printer << ")"; - break; - - case DAK_Differentiable: { - Printer.printAttrName("@differentiable"); - auto *attr = cast(this); - printDifferentiableAttrArguments(attr, Printer, Options, D); - break; - } - case DAK_Count: llvm_unreachable("exceed declaration attribute kinds"); @@ -1179,20 +1103,16 @@ StringRef DeclAttribute::getAttrName() const { return "<>"; case DAK_ProjectedValueProperty: return "_projectedValueProperty"; -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW case DAK_Differentiable: return "differentiable"; + // SWIFT_ENABLE_TENSORFLOW case DAK_Differentiating: return "differentiating"; case DAK_Transposing: return "transposing"; case DAK_Quoted: return "quoted"; -======= - case DAK_Differentiable: - return "differentiable"; ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + // SWIFT_ENABLE_TENSORFLOW END } llvm_unreachable("bad DeclAttrKind"); } @@ -1528,53 +1448,30 @@ SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, specializedSignature); } -<<<<<<< HEAD - -// SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a -DifferentiableAttr::DifferentiableAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, +DifferentiableAttr::DifferentiableAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, ArrayRef params, Optional jvp, Optional vjp, TrailingWhereClause *clause) : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), -<<<<<<< HEAD Linear(linear), NumParsedParameters(params.size()), JVP(std::move(jvp)), -======= - linear(linear), NumParsedParameters(params.size()), JVP(std::move(jvp)), ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a VJP(std::move(vjp)), WhereClause(clause) { std::copy(params.begin(), params.end(), getTrailingObjects()); } -<<<<<<< HEAD DifferentiableAttr::DifferentiableAttr(Decl *original, bool implicit, SourceLoc atLoc, SourceRange baseRange, bool linear, IndexSubset *indices, -======= -DifferentiableAttr::DifferentiableAttr(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, - IndexSubset *indices, ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a Optional jvp, Optional vjp, GenericSignature derivativeGenSig) : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), -<<<<<<< HEAD Linear(linear), JVP(std::move(jvp)), VJP(std::move(vjp)) { setOriginalDeclaration(original); setParameterIndices(indices); setDerivativeGenericSignature(derivativeGenSig); -======= - linear(linear), JVP(std::move(jvp)), VJP(std::move(vjp)), - ParameterIndices(indices) { - setDerivativeGenericSignature(context, derivativeGenSig); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } DifferentiableAttr * @@ -1587,13 +1484,12 @@ DifferentiableAttr::create(ASTContext &context, bool implicit, TrailingWhereClause *clause) { unsigned size = totalSizeToAlloc(parameters.size()); void *mem = context.Allocate(size, alignof(DifferentiableAttr)); - return new (mem) DifferentiableAttr(context, implicit, atLoc, baseRange, - linear, parameters, std::move(jvp), + return new (mem) DifferentiableAttr(implicit, atLoc, baseRange, linear, + parameters, std::move(jvp), std::move(vjp), clause); } DifferentiableAttr * -<<<<<<< HEAD DifferentiableAttr::create(Decl *original, bool implicit, SourceLoc atLoc, SourceRange baseRange, bool linear, IndexSubset *indices, Optional jvp, @@ -1603,22 +1499,11 @@ DifferentiableAttr::create(Decl *original, bool implicit, SourceLoc atLoc, void *mem = ctx.Allocate(sizeof(DifferentiableAttr), alignof(DifferentiableAttr)); return new (mem) DifferentiableAttr(original, implicit, atLoc, baseRange, -======= -DifferentiableAttr::create(ASTContext &context, bool implicit, - SourceLoc atLoc, SourceRange baseRange, - bool linear, IndexSubset *indices, - Optional jvp, - Optional vjp, - GenericSignature derivativeGenSig) { - void *mem = context.Allocate(sizeof(DifferentiableAttr), - alignof(DifferentiableAttr)); - return new (mem) DifferentiableAttr(context, implicit, atLoc, baseRange, ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a linear, indices, std::move(jvp), std::move(vjp), derivativeGenSig); } -<<<<<<< HEAD +// SWIFT_ENABLE_TENSORFLOW void DifferentiableAttr::setOriginalDeclaration(Decl *decl) { assert(decl && "Original declaration must be non-null"); assert(!OriginalDeclaration && @@ -1650,9 +1535,8 @@ void DifferentiableAttr::setParameterIndices(IndexSubset *paramIndices) { const_cast(this), getOriginalDeclaration()}, std::move(paramIndices)); } +// SWIFT_ENABLE_TENSORFLOW END -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a void DifferentiableAttr::setJVPFunction(FuncDecl *decl) { JVPFunction = decl; if (decl && !JVP) @@ -1675,7 +1559,6 @@ GenericEnvironment *DifferentiableAttr::getDerivativeGenericEnvironment( void DifferentiableAttr::print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause, -<<<<<<< HEAD bool omitDerivativeFunctions) const { StreamPrinter P(OS); P << "@" << getAttrName(); @@ -1763,13 +1646,6 @@ TransposingAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, context.Allocate(sizeof(TransposingAttr), alignof(TransposingAttr)); return new (mem) TransposingAttr(context, implicit, atLoc, baseRange, baseType, std::move(original), indices); -======= - bool omitAssociatedFunctions) const { - StreamPrinter P(OS); - P << "@" << getAttrName(); - printDifferentiableAttrArguments(this, P, PrintOptions(), D, omitWrtClause, - omitAssociatedFunctions); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a304848621975..7984f1d269cf7 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4643,7 +4643,6 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { return opened; } -<<<<<<< HEAD // SWIFT_ENABLE_TENSORFLOW // Makes a function with the same generic signature and extinfo as `copy`, but // with `params` parameters and `retTy` return type. @@ -4888,6 +4887,44 @@ AnyFunctionType::getAutoDiffOriginalFunctionType() { return originalType; } +bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { + if (auto *structDecl = getStructOrBoundGenericStruct()) { + for (auto *field : structDecl->getStoredProperties()) { + auto fieldTy = field->getInterfaceType()->getCanonicalType(); + if (fieldTy->hasOpaqueArchetype() || + fieldTy->hasOpaqueArchetypePropertiesOrCases()) + return true; + } + } + + if (auto *enumDecl = getEnumOrBoundGenericEnum()) { + for (auto *elt : enumDecl->getAllElements()) { + auto eltType = elt->getInterfaceType(); + if (eltType->hasOpaqueArchetype() || + eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) + return true; + } + } + return false; +} + +CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, + TypeExpansionContext context, + bool allowLoweredTypes) { + if (!context.shouldLookThroughOpaqueTypeArchetypes() || + !ty->hasOpaqueArchetype()) + return ty; + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + SubstOptions flags = SubstFlags::SubstituteOpaqueArchetypes; + if (allowLoweredTypes) + flags = + SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes; + return ty.subst(replacer, replacer, flags)->getCanonicalType(); +} + // SWIFT_ENABLE_TENSORFLOW static AnyFunctionType * makeFunctionType(ArrayRef params, Type retTy, @@ -5004,42 +5041,5 @@ AnyFunctionType *AnyFunctionType::getWithoutDifferentiability() const { assert(isa(this)); return GenericFunctionType::get(getOptGenericSignature(), newParams, getResult(), nonDiffExtInfo); -======= -bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { - if (auto *structDecl = getStructOrBoundGenericStruct()) { - for (auto *field : structDecl->getStoredProperties()) { - auto fieldTy = field->getInterfaceType()->getCanonicalType(); - if (fieldTy->hasOpaqueArchetype() || - fieldTy->hasOpaqueArchetypePropertiesOrCases()) - return true; - } - } - - if (auto *enumDecl = getEnumOrBoundGenericEnum()) { - for (auto *elt : enumDecl->getAllElements()) { - auto eltType = elt->getInterfaceType(); - if (eltType->hasOpaqueArchetype() || - eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) - return true; - } - } - return false; -} - -CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, - TypeExpansionContext context, - bool allowLoweredTypes) { - if (!context.shouldLookThroughOpaqueTypeArchetypes() || - !ty->hasOpaqueArchetype()) - return ty; - - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - context.getContext(), context.getResilienceExpansion(), - context.isWholeModuleContext()); - SubstOptions flags = SubstFlags::SubstituteOpaqueArchetypes; - if (allowLoweredTypes) - flags = - SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes; - return ty.subst(replacer, replacer, flags)->getCanonicalType(); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } +// SWIFT_ENABLE_TENSORFLOW END diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 48c0d6e3c123e..c874c404c21e7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -804,10 +804,6 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { ProtocolType.get(), MemberName, MemberNameLoc)); } -<<<<<<< HEAD -/// SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a ParserResult Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "differentiable"; @@ -925,7 +921,7 @@ bool Parser::parseDifferentiationParametersClause( return false; } -<<<<<<< HEAD +// SWIFT_ENABLE_TENSORFLOW bool Parser::parseTransposingParametersClause( SmallVectorImpl ¶ms, StringRef attrName) { // Set parse error, skip until ')' and parse it. @@ -1003,9 +999,8 @@ bool Parser::parseTransposingParametersClause( } return false; } +// SWIFT_ENABLE_TENSORFLOW END -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a bool Parser::parseDifferentiableAttributeArguments( bool &linear, SmallVectorImpl ¶ms, Optional &jvpSpec, Optional &vjpSpec, @@ -1031,7 +1026,6 @@ bool Parser::parseDifferentiableAttributeArguments( diagnose(Tok, diag::unexpected_separator, ","); return true; } -<<<<<<< HEAD // Check that token after comma is 'wrt' or a function specifier label. if (isIdentifier(Tok, "wrt") || isIdentifier(Tok, "jvp") || isIdentifier(Tok, "vjp")) { @@ -1039,15 +1033,6 @@ bool Parser::parseDifferentiableAttributeArguments( } diagnose(Tok, diag::attr_differentiable_expected_label); return true; -======= - // Check that token after comma is 'wrt:' or a function specifier label. - if (!(isWRTIdentifier(Tok) || isJVPIdentifier(Tok) || - isVJPIdentifier(Tok))) { - diagnose(Tok, diag::attr_differentiable_expected_label); - return true; - } - return false; ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a }; // Store starting parser position. @@ -1058,11 +1043,7 @@ bool Parser::parseDifferentiableAttributeArguments( // Parse optional differentiation parameters. // Parse 'linear' label (optional). linear = false; -<<<<<<< HEAD if (isIdentifier(Tok, "linear")) { -======= - if (Tok.is(tok::identifier) && Tok.getText() == "linear") { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a linear = true; consumeToken(tok::identifier); // If no trailing comma or 'where' clause, terminate parsing arguments. @@ -1073,23 +1054,15 @@ bool Parser::parseDifferentiableAttributeArguments( } // If 'withRespectTo' is used, make the user change it to 'wrt'. -<<<<<<< HEAD if (isIdentifier(Tok, "withRespectTo")) { -======= - if (Tok.is(tok::identifier) && Tok.getText() == "withRespectTo") { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SourceRange withRespectToRange(Tok.getLoc(), peekToken().getLoc()); diagnose(Tok, diag::attr_differentiable_use_wrt_not_withrespectto) .highlight(withRespectToRange) .fixItReplace(withRespectToRange, "wrt:"); return errorAndSkipToEnd(); } -<<<<<<< HEAD // Parse differentiation parameters' clause. if (isIdentifier(Tok, "wrt")) { -======= - if (isWRTIdentifier(Tok)) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (parseDifferentiationParametersClause(params, AttrName)) return true; // If no trailing comma or 'where' clause, terminate parsing arguments. @@ -1127,11 +1100,7 @@ bool Parser::parseDifferentiableAttributeArguments( bool terminateParsingArgs = false; // Parse 'jvp: ' (optional). -<<<<<<< HEAD if (isIdentifier(Tok, "jvp")) { -======= - if (isJVPIdentifier(Tok)) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SyntaxParsingContext JvpContext( SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); jvpSpec = DeclNameWithLoc(); @@ -1144,11 +1113,7 @@ bool Parser::parseDifferentiableAttributeArguments( } // Parse 'vjp: ' (optional). -<<<<<<< HEAD if (isIdentifier(Tok, "vjp")) { -======= - if (isVJPIdentifier(Tok)) { ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SyntaxParsingContext VjpContext( SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); vjpSpec = DeclNameWithLoc(); @@ -1180,8 +1145,7 @@ bool Parser::parseDifferentiableAttributeArguments( return false; } -<<<<<<< HEAD -/// SWIFT_ENABLE_TENSORFLOW +// SWIFT_ENABLE_TENSORFLOW ParserResult Parser::parseDifferentiatingAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "differentiating"; @@ -1254,8 +1218,9 @@ Parser::parseDifferentiatingAttribute(SourceLoc atLoc, SourceLoc loc) { SourceRange(loc, rParenLoc), original, linear, params)); } +// SWIFT_ENABLE_TENSORFLOW END -/// SWIFT_ENABLE_TENSORFLOW +// SWIFT_ENABLE_TENSORFLOW /// Helper function that parses 'type-identifier' for `parseQualifiedDeclName`. /// Returns true on error. Sets `baseType` to the parsed type, if present, or to /// `nullptr` if not. A missing base type is not considered an error. @@ -1278,8 +1243,9 @@ static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { baseType = result.getPtrOrNull(); return false; } +// SWIFT_ENABLE_TENSORFLOW END -/// SWIFT_ENABLE_TENSORFLOW +// SWIFT_ENABLE_TENSORFLOW /// parseQualifiedDeclName /// /// qualified-decl-name: @@ -1312,7 +1278,9 @@ bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, // If name could not be parsed, return true for error. return !original.Name; } +// SWIFT_ENABLE_TENSORFLOW END +// SWIFT_ENABLE_TENSORFLOW ParserResult Parser::parseTransposingAttribute(SourceLoc atLoc, SourceLoc loc) { StringRef AttrName = "transposing"; @@ -1375,7 +1343,9 @@ ParserResult Parser::parseTransposingAttribute(SourceLoc atLoc, Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, original, params)); } +// SWIFT_ENABLE_TENSORFLOW END +// SWIFT_ENABLE_TENSORFLOW ParserResult Parser::parseQuotedAttribute(SourceLoc atLoc, SourceLoc loc) { if (Context.LangOpts.EnableExperimentalQuasiquotes) { @@ -1386,8 +1356,7 @@ ParserResult Parser::parseQuotedAttribute(SourceLoc atLoc, return makeParserError(); } } -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a +// SWIFT_ENABLE_TENSORFLOW END void Parser::parseObjCSelector(SmallVector &Names, SmallVector &NameLocs, @@ -2188,10 +2157,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a case DAK_Differentiable: { auto Attr = parseDifferentiableAttribute(AtLoc, Loc); if (Attr.isNonNull()) @@ -2199,7 +2164,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } -<<<<<<< HEAD // SWIFT_ENABLE_TENSORFLOW case DAK_Differentiating: { auto Attr = parseDifferentiatingAttribute(AtLoc, Loc); @@ -2216,8 +2180,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a case DAK_ProjectedValueProperty: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -3406,22 +3368,6 @@ void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { } } -<<<<<<< HEAD -void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition, - ParseDeclOptions Flags) { - auto CurLoc = Tok.getLoc(); - backtrackToPosition(BeginParserPosition); - SourceLoc BeginLoc = Tok.getLoc(); - SourceLoc EndLoc = CurLoc; - State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, - Flags.toRaw(), - CurDeclContext, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); - - while (Tok.isNot(tok::eof)) - consumeToken(); -} - // SWIFT_ENABLE_TENSORFLOW static void setOriginalFunctionInDifferentiableAttributes( DeclAttributes Attributes, Decl *D) { @@ -3430,8 +3376,6 @@ static void setOriginalFunctionInDifferentiableAttributes( } // SWIFT_ENABLE_TENSORFLOW END -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /// Parse a single syntactic declaration and return a list of decl /// ASTs. This can return multiple results for var decls that bind to multiple /// values, structs that define a struct decl and a constructor, etc. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 9f38bd2bf104b..8b1601b5b5af9 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -983,10 +983,6 @@ bool Parser::parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag, return false; } -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a bool Parser::parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, const Diagnostic &D) { auto IntTok = Tok; diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index f76a49b748ab5..4650c0d9c7db6 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -2207,8 +2207,7 @@ parseSILDifferentiabilityWitnessConfigAndFunction(Parser &P, SILParser &SP, Scope genericsScope(&P, ScopeKind::Generics); auto *genericParams = P.maybeParseGenericParams().getPtrOrNull(); if (genericParams) { - auto *witnessGenEnv = - handleSILGenericParams(P.Context, genericParams, &P.SF); + auto *witnessGenEnv = handleSILGenericParams(genericParams, &P.SF); witnessGenSig = witnessGenEnv->getGenericSignature(); } } diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index eaf17302ece2d..02bd60fad2a9b 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -246,7 +246,7 @@ CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( // Given a type, returns its formal SIL parameter info. auto getTangentParameterInfoForOriginalResult = [&]( CanType tanType, ResultConvention origResConv) -> SILParameterInfo { - auto &tl = TC.getTypeLowering(tanType, ResilienceExpansion::Minimal); + auto &tl = TC.getTypeLowering(tanType, TypeExpansionContext::minimal()); ParameterConvention conv; switch (origResConv) { case ResultConvention::Owned: @@ -269,7 +269,8 @@ CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( // Given a type, returns its formal SIL result info. auto getTangentResultInfoForOriginalParameter = [&]( CanType tanType, ParameterConvention origParamConv) -> SILResultInfo { - auto &tl = TC.getTypeLowering(tanType, ResilienceExpansion::Minimal); + auto &tl = + TC.getTypeLowering(tanType, TypeExpansionContext::minimal()); ResultConvention conv; switch (origParamConv) { case ParameterConvention::Direct_Owned: @@ -370,7 +371,7 @@ CanSILFunctionType SILFunctionType::getAutoDiffTransposeFunctionType( auto getParameterInfoForOriginalResult = [&]( const SILResultInfo &result) -> SILParameterInfo { auto &tl = TC.getTypeLowering( - result.getInterfaceType(), ResilienceExpansion::Minimal); + result.getInterfaceType(), TypeExpansionContext::minimal()); ParameterConvention newConv; switch (result.getConvention()) { case ResultConvention::Owned: @@ -394,7 +395,7 @@ CanSILFunctionType SILFunctionType::getAutoDiffTransposeFunctionType( auto getResultInfoForOriginalParameter = [&]( const SILParameterInfo ¶m) -> SILResultInfo { auto &tl = TC.getTypeLowering( - param.getInterfaceType(), ResilienceExpansion::Minimal); + param.getInterfaceType(), TypeExpansionContext::minimal()); ResultConvention newConv; switch (param.getConvention()) { case ParameterConvention::Direct_Owned: @@ -2636,8 +2637,8 @@ TypeConverter::getConstantInfo(TypeExpansionContext expansion, // // We hackily fix this problem by redoing the computation in the right order. if (auto *autoDiffFuncId = constant.autoDiffDerivativeFunctionIdentifier) { - auto origFnConstantInfo = - getConstantInfo(constant.asAutoDiffOriginalFunction()); + auto origFnConstantInfo = getConstantInfo( + TypeExpansionContext::minimal(), constant.asAutoDiffOriginalFunction()); auto loweredIndices = autodiff::getLoweredParameterIndices( autoDiffFuncId->getParameterIndices(), formalInterfaceType); silFnType = origFnConstantInfo.SILFnType->getAutoDiffDerivativeFunctionType( diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index e54bb19ca709a..f134c9d4d6762 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -935,7 +935,7 @@ namespace { auto paramIndices = fnTy->getDifferentiationParameterIndices(); children.push_back(Child{ NormalDifferentiableFunctionTypeComponent::Original, - TC.getTypeLowering(origFnTy, getResilienceExpansion()) + TC.getTypeLowering(origFnTy, getExpansionContext()) }); for (AutoDiffDerivativeFunctionKind kind : {AutoDiffDerivativeFunctionKind::JVP, @@ -951,7 +951,7 @@ namespace { // wrong extractee. assert(extractee.getAsDerivativeFunctionKind() == kind); children.push_back(Child{ - extractee, TC.getTypeLowering(silTy, getResilienceExpansion())}); + extractee, TC.getTypeLowering(silTy, getExpansionContext())}); } assert(children.size() == 3); } @@ -987,14 +987,14 @@ namespace { auto paramIndices = fnTy->getDifferentiationParameterIndices(); children.push_back(Child{ LinearDifferentiableFunctionTypeComponent::Original, - TC.getTypeLowering(origFnTy, getResilienceExpansion()) + TC.getTypeLowering(origFnTy, getExpansionContext()) }); auto transposeFnTy = origFnTy->getAutoDiffTransposeFunctionType( paramIndices, TC, LookUpConformanceInModule(&TC.M)); auto transposeSILFnTy = SILType::getPrimitiveObjectType(transposeFnTy); children.push_back(Child{ LinearDifferentiableFunctionTypeComponent::Transpose, - TC.getTypeLowering(transposeSILFnTy, getResilienceExpansion()) + TC.getTypeLowering(transposeSILFnTy, getExpansionContext()) }); assert(children.size() == 2); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 6757a72e8f807..38b749759bfcb 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -416,19 +416,10 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, : ParameterConvention::Indirect_In_Guaranteed }, }; -<<<<<<< HEAD - auto extInfo = - SILFunctionType::ExtInfo(SILFunctionTypeRepresentation::Thin, - /*pseudogeneric*/false, - // SWIFT_ENABLE_TENSORFLOW - /*non-escaping*/false, - DifferentiabilityKind::NonDifferentiable); -======= auto extInfo = SILFunctionType::ExtInfo( SILFunctionTypeRepresentation::Thin, /*pseudogeneric*/ false, /*non-escaping*/ false, DifferentiabilityKind::NonDifferentiable); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a auto functionTy = SILFunctionType::get(sig, extInfo, SILCoroutineKind::YieldOnce, diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index c91a82b54de10..35870f3e68871 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2711,10 +2711,6 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*noescape*/ false, DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, @@ -2860,10 +2856,6 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*noescape*/ false, DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, @@ -3041,10 +3033,6 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*noescape*/ false, DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, @@ -3222,10 +3210,6 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*noescape*/ false, DifferentiabilityKind::NonDifferentiable), SILCoroutineKind::None, diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 47f077a1c8a23..1fe367ccc2705 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -87,21 +87,18 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, derived.kind != SILDeclRef::Kind::Allocator); if (usesObjCDynamicDispatch) { -<<<<<<< HEAD - implFn = getDynamicThunk(derived, Types.getConstantInfo(derived).SILFnType); + implFn = getDynamicThunk( + derived, Types.getConstantInfo(TypeExpansionContext::minimal(), derived) + .SILFnType); // SWIFT_ENABLE_TENSORFLOW } else if (auto *adafi = derived.autoDiffDerivativeFunctionIdentifier) { // For JVP/VJP methods, create a vtable entry thunk. The thunk contains an // `differentiable_function` instruction, which is later filled during the // differentiation transform. - implFn = getOrCreateAutoDiffClassMethodThunk( - derived, Types.getConstantInfo(derived).SILFnType); + auto derivedFnType = Types.getConstantInfo( + TypeExpansionContext::minimal(), derived).SILFnType; + implFn = getOrCreateAutoDiffClassMethodThunk(derived, derivedFnType); // SWIFT_ENABLE_TENSORFLOW END -======= - implFn = getDynamicThunk( - derived, Types.getConstantInfo(TypeExpansionContext::minimal(), derived) - .SILFnType); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } else { implFn = getFunction(derived, NotForDefinition); } diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 359a72b047d13..c6dc00194dafa 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -817,7 +817,7 @@ class LinearMapInfo { auto linMapStructType = linMapStruct->getDeclaredInterfaceType()->getCanonicalType(); return typeConverter.getLoweredType(linMapStructType, - ResilienceExpansion::Minimal); + TypeExpansionContext::minimal()); } /// Returns the branching trace enum associated with the given original block. @@ -832,7 +832,7 @@ class LinearMapInfo { auto traceDeclType = traceDecl->getDeclaredInterfaceType()->getCanonicalType(); return typeConverter.getLoweredType(traceDeclType, - ResilienceExpansion::Minimal); + TypeExpansionContext::minimal()); } /// Returns the enum element in the given successor block's branching trace @@ -2924,7 +2924,8 @@ static void emitZeroIntoBuffer( assert(zeroDecl->isProtocolRequirement()); auto *accessorDecl = zeroDecl->getAccessor(AccessorKind::Get); SILDeclRef accessorDeclRef(accessorDecl, SILDeclRef::Kind::Func); - auto silFnType = typeConverter.getConstantType(accessorDeclRef); + auto silFnType = typeConverter.getConstantType( + TypeExpansionContext::minimal(), accessorDeclRef); // %wm = witness_method ... auto *getter = builder.createWitnessMethod( loc, type, confRef, accessorDeclRef, silFnType); @@ -3210,8 +3211,12 @@ static SILFunction *getOrCreateReabstractionThunk(SILOptFunctionBuilder &fb, thunkType, fromInterfaceType, toInterfaceType, Type(), module.getSwiftModule()); + // FIXME(TF-989): Mark reabstraction thunks as transparent. This requires + // generating ossa reabstraction thunks so that they can be inlined during + // mandatory inlining when `-enable-strip-ownership-after-serialization` is + // true and ownership model eliminator is not run after differentiation. auto *thunk = fb.getOrCreateSharedFunction( - loc, name, thunkDeclType, IsBare, IsTransparent, IsSerialized, + loc, name, thunkDeclType, IsBare, IsNotTransparent, IsSerialized, ProfileCounter(), IsReabstractionThunk, IsNotDynamic); if (!thunk->empty()) return thunk; @@ -3473,7 +3478,7 @@ class VJPEmitter final auto getTangentParameterInfoForOriginalResult = [&]( CanType tanType, ResultConvention origResConv) -> SILParameterInfo { auto &tl = context.getTypeConverter().getTypeLowering( - tanType, ResilienceExpansion::Minimal); + tanType, TypeExpansionContext::minimal()); ParameterConvention conv; switch (origResConv) { case ResultConvention::Owned: @@ -3497,7 +3502,7 @@ class VJPEmitter final auto getTangentResultInfoForOriginalParameter = [&]( CanType tanType, ParameterConvention origParamConv) -> SILResultInfo { auto &tl = context.getTypeConverter().getTypeLowering( - tanType, ResilienceExpansion::Minimal); + tanType, TypeExpansionContext::minimal()); ResultConvention conv; switch (origParamConv) { case ParameterConvention::Direct_Owned: @@ -3601,7 +3606,7 @@ class VJPEmitter final auto enumTy = getOpASTType(predEnum->getDeclaredInterfaceType() ->getCanonicalType()); auto enumLoweredTy = context.getTypeConverter().getLoweredType( - enumTy, ResilienceExpansion::Minimal); + enumTy, TypeExpansionContext::minimal()); vjpBB->createPhiArgument(enumLoweredTy, ValueOwnershipKind::Owned); remappedBasicBlocks.insert(bb); return vjpBB; @@ -3627,7 +3632,7 @@ class VJPEmitter final auto nomType = getOpASTType( nominal->getDeclaredInterfaceType()->getCanonicalType()); auto nomSILType = context.getTypeConverter().getLoweredType( - nomType, ResilienceExpansion::Minimal); + nomType, TypeExpansionContext::minimal()); return nomSILType; } @@ -3659,8 +3664,8 @@ class VJPEmitter final auto enumLoweredTy = getNominalDeclLoweredType(succEnum); auto *enumEltDecl = pullbackInfo.lookUpBranchingTraceEnumElement(predBB, succBB); - auto enumEltType = getOpType( - enumLoweredTy.getEnumElementType(enumEltDecl, getModule())); + auto enumEltType = getOpType(enumLoweredTy.getEnumElementType( + enumEltDecl, getModule(), TypeExpansionContext::minimal())); // If the enum element type does not have a box type (i.e. the enum case is // not indirect), then directly create an enum. auto boxType = dyn_cast(enumEltType.getASTType()); @@ -4043,7 +4048,7 @@ class VJPEmitter final auto loweredPullbackType = getOpType(context.getTypeConverter().getLoweredType( pullbackDecl->getInterfaceType()->getCanonicalType(), - ResilienceExpansion::Minimal)) + TypeExpansionContext::minimal())) .castTo(); if (!loweredPullbackType->isEqual(actualPullbackType)) { // Set non-reabstracted original pullback type in nested apply info. @@ -4415,7 +4420,7 @@ class JVPEmitter final auto nomType = getOpASTType(nominal->getDeclaredInterfaceType()->getCanonicalType()); auto nomSILType = context.getTypeConverter().getLoweredType( - nomType, ResilienceExpansion::Minimal); + nomType, TypeExpansionContext::minimal()); return nomSILType; } @@ -4485,7 +4490,7 @@ class JVPEmitter final SILValue emitZeroDirect(CanType type, SILLocation loc) { auto diffBuilder = getDifferentialBuilder(); auto silType = getModule().Types.getLoweredLoadableType( - type, ResilienceExpansion::Minimal, getModule()); + type, TypeExpansionContext::minimal(), getModule()); auto *buffer = diffBuilder.createAllocStack(loc, silType); emitZeroIndirect(type, buffer, loc); auto loaded = diffBuilder.emitLoadValueOperation( @@ -5720,7 +5725,7 @@ class JVPEmitter final auto loweredDifferentialType = getOpType(context.getTypeConverter().getLoweredType( differentialDecl->getInterfaceType()->getCanonicalType(), - ResilienceExpansion::Minimal)) + TypeExpansionContext::minimal())) .castTo(); // If actual differential type does not match lowered differential type, // reabstract the differential using a thunk. @@ -6444,8 +6449,8 @@ class PullbackEmitter final : public SILInstructionVisitor { getPullbackInfo().getBranchingTraceEnumLoweredType(succBB); auto *enumEltDecl = getPullbackInfo().lookUpBranchingTraceEnumElement(origBB, succBB); - auto enumEltType = remapType( - enumLoweredTy.getEnumElementType(enumEltDecl, getModule())); + auto enumEltType = remapType(enumLoweredTy.getEnumElementType( + enumEltDecl, getModule(), TypeExpansionContext::minimal())); pullbackTrampolineBB->createPhiArgument(enumEltType, ValueOwnershipKind::Owned); } @@ -7123,7 +7128,7 @@ class PullbackEmitter final : public SILInstructionVisitor { auto tangentVectorTy = getTangentSpace(structTy)->getType()->getCanonicalType(); assert(!getModule().Types.getTypeLowering( - tangentVectorTy, ResilienceExpansion::Minimal) + tangentVectorTy, TypeExpansionContext::minimal()) .isAddressOnly()); auto *tangentVectorDecl = tangentVectorTy->getStructOrBoundGenericStruct(); @@ -7187,7 +7192,7 @@ class PullbackEmitter final : public SILInstructionVisitor { auto tangentVectorTy = getTangentSpace(structTy)->getType()->getCanonicalType(); assert(!getModule().Types.getTypeLowering( - tangentVectorTy, ResilienceExpansion::Minimal) + tangentVectorTy, TypeExpansionContext::minimal()) .isAddressOnly()); auto tangentVectorSILTy = SILType::getPrimitiveObjectType(tangentVectorTy); @@ -7233,7 +7238,7 @@ class PullbackEmitter final : public SILInstructionVisitor { auto fieldTy = field->getType().subst(substMap); auto fieldSILTy = getContext().getTypeConverter().getLoweredType( - fieldTy, ResilienceExpansion::Minimal); + fieldTy, TypeExpansionContext::minimal()); assert(fieldSILTy.isObject()); eltVals.push_back(makeZeroAdjointValue(fieldSILTy)); } @@ -7681,7 +7686,7 @@ void PullbackEmitter::emitZeroIndirect(CanType type, SILValue bufferAccess, SILValue PullbackEmitter::emitZeroDirect(CanType type, SILLocation loc) { auto silType = getModule().Types.getLoweredLoadableType( - type, ResilienceExpansion::Minimal, getModule()); + type, TypeExpansionContext::minimal(), getModule()); auto *buffer = builder.createAllocStack(loc, silType); emitZeroIndirect(type, buffer, loc); auto loaded = builder.emitLoadValueOperation( @@ -7849,7 +7854,8 @@ void PullbackEmitter::accumulateIndirect( proto); assert(!confRef.isInvalid() && "Missing conformance to `AdditiveArithmetic`"); SILDeclRef declRef(combinerFuncDecl, SILDeclRef::Kind::Func); - auto silFnTy = getContext().getTypeConverter().getConstantType(declRef); + auto silFnTy = getContext().getTypeConverter().getConstantType( + TypeExpansionContext::minimal(), declRef); // %0 = witness_method @+ auto witnessMethod = builder.createWitnessMethod(loc, adjointASTTy, confRef, declRef, @@ -7906,7 +7912,8 @@ void PullbackEmitter::accumulateIndirect(SILValue lhsDestAccess, auto confRef = swiftMod->lookupConformance(astType, proto); assert(!confRef.isInvalid() && "Missing conformance to `AdditiveArithmetic`"); SILDeclRef declRef(accumulatorFuncDecl, SILDeclRef::Kind::Func); - auto silFnTy = getContext().getTypeConverter().getConstantType(declRef); + auto silFnTy = getContext().getTypeConverter().getConstantType( + TypeExpansionContext::minimal(), declRef); // %0 = witness_method @+= auto witnessMethod = builder.createWitnessMethod(loc, astType, confRef, declRef, silFnTy); diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index bc5e2d8e1d06d..d350a63081a55 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -288,10 +288,6 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { ResultConvention::Owned)); auto ExtInfo = SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*pseudogeneric*/ false, /*noescape*/ false, DifferentiabilityKind::NonDifferentiable); auto FunctionType = SILFunctionType::get( @@ -1113,19 +1109,10 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { OrigSigIdx++; } -<<<<<<< HEAD - auto ExtInfo = - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - // SWIFT_ENABLE_TENSORFLOW - /*noescape*/ false, - DifferentiabilityKind::NonDifferentiable); -======= auto ExtInfo = SILFunctionType::ExtInfo( SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, /*noescape*/ false, DifferentiabilityKind::NonDifferentiable); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SmallVector Results; // If we don't have a bridged return we changed from @autoreleased to @owned diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index 1cfedd5fb409f..5ac534a26e9fa 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -83,19 +83,10 @@ class BugReducerTester : public SILFunctionTransform { ResultInfoArray.push_back( SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned)); auto FuncType = SILFunctionType::get( -<<<<<<< HEAD - nullptr, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - false /*isPseudoGeneric*/, - // SWIFT_ENABLE_TENSORFLOW - false /*noescape*/, - DifferentiabilityKind - ::NonDifferentiable), -======= nullptr, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, false /*isPseudoGeneric*/, false /*noescape*/, DifferentiabilityKind::NonDifferentiable), ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SILCoroutineKind::None, ParameterConvention::Direct_Unowned, ArrayRef(), ArrayRef(), ResultInfoArray, None, SubstitutionMap(), false, diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 75645fa97a748..cd192bcc9f9ef 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -327,6 +327,13 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::CondFailInst: case SILInstructionKind::DestructureStructInst: case SILInstructionKind::DestructureTupleInst: + // SWIFT_ENABLE_TENSORFLOW + case SILInstructionKind::DifferentiableFunctionInst: + case SILInstructionKind::DifferentiableFunctionExtractInst: + case SILInstructionKind::LinearFunctionInst: + case SILInstructionKind::LinearFunctionExtractInst: + case SILInstructionKind::DifferentiabilityWitnessFunctionInst: + // SWIFT_ENABLE_TENSORFLOW END // Handle by operand and result check. break; diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 0046e6bb87798..973dcf627c137 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1331,7 +1331,7 @@ namespace { Type visitDeclQuoteExpr(DeclQuoteExpr *expr) { auto &tc = CS.getTypeChecker(); - return tc.getTypeOfQuoteDecl(expr->getLoc()); + return tc.getTypeOfQuoteDecl(CS.getASTContext(), expr->getLoc()); } Type visitDeclRefExpr(DeclRefExpr *E) { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 5596f9684db7e..b786ba4ea6d9f 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1699,10 +1699,6 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, auto bodyClosure = FunctionType::get(arg, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ true, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*throws*/ true, DifferentiabilityKind::NonDifferentiable)); FunctionType::Param args[] = { @@ -1713,10 +1709,6 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, refType = FunctionType::get(args, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ false, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*throws*/ true, DifferentiabilityKind::NonDifferentiable)); openedFullType = refType; @@ -1741,10 +1733,6 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, auto bodyClosure = FunctionType::get(bodyArgs, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ true, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*throws*/ true, DifferentiabilityKind::NonDifferentiable)); FunctionType::Param args[] = { @@ -1754,10 +1742,6 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, refType = FunctionType::get(args, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ false, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a /*throws*/ true, DifferentiabilityKind::NonDifferentiable)); openedFullType = refType; @@ -2471,21 +2455,6 @@ SolutionResult ConstraintSystem::salvage() { log << "---Attempting to salvage and emit diagnostics---\n"; } - // SWIFT_ENABLE_TENSORFLOW - if (DC->getParentModule()->getNameStr().startswith("__lldb_expr") && - viable.size() > 1) { - // TODO(https://bugs.swift.org/browse/SR-9814): - // If in LLDB repl mode, patch up the solution if we have ambiguity. - // - // This is a *temporary* short-term hack that simply returns the last - // solution. It seems to work for now and returns the lastly added - // definition during the repl session. However, this is extremely brittle and - // is not expected to work correctly all the time. - viable[0] = std::move(viable.back()); - viable.erase(viable.begin() + 1, viable.end()); - return false; - } - // Attempt to solve again, capturing all states that come from our attempts to // select overloads or bind type variables. // @@ -2527,6 +2496,21 @@ SolutionResult ConstraintSystem::salvage() { // If there are multiple solutions, try to diagnose an ambiguity. if (viable.size() > 1) { + // SWIFT_ENABLE_TENSORFLOW + if (DC->getParentModule()->getNameStr().startswith("__lldb_expr")) { + // TODO(https://bugs.swift.org/browse/SR-9814): + // If in LLDB repl mode, patch up the solution if we have ambiguity. + // + // This is a *temporary* short-term hack that simply returns the last + // solution. It seems to work for now and returns the lastly added + // definition during the repl session. However, this is extremely brittle and + // is not expected to work correctly all the time. + viable[0] = std::move(viable.back()); + viable.erase(viable.begin() + 1, viable.end()); + return SolutionResult::forSolved(std::move(viable[0])); + } + // SWIFT_ENABLE_TENSORFLOW + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Ambiguity error: " << viable.size() diff --git a/lib/Sema/DerivedConformanceDifferentiable.cpp b/lib/Sema/DerivedConformanceDifferentiable.cpp index ee1f8515c10e8..2a517bb88a35c 100644 --- a/lib/Sema/DerivedConformanceDifferentiable.cpp +++ b/lib/Sema/DerivedConformanceDifferentiable.cpp @@ -337,8 +337,6 @@ static ValueDecl *deriveDifferentiable_method( funcDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({funcDecl}); - C.addSynthesizedDecl(funcDecl); - return funcDecl; } @@ -602,8 +600,6 @@ getOrSynthesizeTangentVectorStruct(DerivedConformance &derived, Identifier id) { structDecl->addMember(memberBinding); newMember->copyFormalAccessFrom(member, /*sourceIsParentContext*/ true); newMember->setSetterAccess(member->getFormalAccess()); - C.addSynthesizedDecl(newMember); - C.addSynthesizedDecl(memberBinding); // Now that this member is in the `TangentVector` type, it should be marked // `@differentiable` so that the differentiation transform will synthesize @@ -644,15 +640,12 @@ getOrSynthesizeTangentVectorStruct(DerivedConformance &derived, Identifier id) { // the memberwise constructor is synthesized during SILGen, which is too late. auto *initDecl = createMemberwiseImplicitConstructor(C, structDecl); structDecl->addMember(initDecl); - C.addSynthesizedDecl(initDecl); // After memberwise initializer is synthesized, mark members as implicit. for (auto *member : structDecl->getStoredProperties()) member->setImplicit(); derived.addMembersToConformanceContext({structDecl}); - C.addSynthesizedDecl(structDecl); - return structDecl; } @@ -683,7 +676,6 @@ static void addAssociatedTypeAliasDecl(Identifier name, aliasDecl->setGenericSignature(sourceDC->getGenericSignatureOfContext()); cast(sourceDC->getAsDecl())->addMember(aliasDecl); aliasDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); - Context.addSynthesizedDecl(aliasDecl); }; /// Diagnose stored properties in the nominal that do not have an explicit @@ -823,7 +815,6 @@ deriveDifferentiable_TangentVectorStruct(DerivedConformance &derived) { aliasDecl->setImplicit(); aliasDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({aliasDecl}); - C.addSynthesizedDecl(aliasDecl); return selfType; } diff --git a/lib/Sema/DerivedConformanceElementaryFunctions.cpp b/lib/Sema/DerivedConformanceElementaryFunctions.cpp index 1ba996ad7e162..5eeb9cbc677fd 100644 --- a/lib/Sema/DerivedConformanceElementaryFunctions.cpp +++ b/lib/Sema/DerivedConformanceElementaryFunctions.cpp @@ -104,12 +104,10 @@ static ValueDecl *getElementaryFunctionRequirement( // it if it does not exist. static ConstructorDecl *getOrCreateEffectiveMemberwiseInitializer( ASTContext &ctx, NominalTypeDecl *nominal) { - auto &C = nominal->getASTContext(); if (auto *initDecl = nominal->getEffectiveMemberwiseInitializer()) return initDecl; auto *initDecl = createMemberwiseImplicitConstructor(ctx, nominal); nominal->addMember(initDecl); - C.addSynthesizedDecl(initDecl); return initDecl; } @@ -303,8 +301,6 @@ ElementaryFunction op) { operatorDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({operatorDecl}); - C.addSynthesizedDecl(operatorDecl); - return operatorDecl; } diff --git a/lib/Sema/DerivedConformanceRingMathProtocols.cpp b/lib/Sema/DerivedConformanceRingMathProtocols.cpp index f874b1c393621..5ab1d55345018 100644 --- a/lib/Sema/DerivedConformanceRingMathProtocols.cpp +++ b/lib/Sema/DerivedConformanceRingMathProtocols.cpp @@ -79,13 +79,11 @@ static ValueDecl *getProtocolRequirement(ProtocolDecl *proto, Identifier name) { // it if it does not exist. static ConstructorDecl *getOrCreateEffectiveMemberwiseInitializer( ASTContext &ctx, NominalTypeDecl *nominal) { - auto &C = nominal->getASTContext(); if (auto *initDecl = nominal->getEffectiveMemberwiseInitializer()) return initDecl; auto *initDecl = createMemberwiseImplicitConstructor( ctx, nominal); nominal->addMember(initDecl); - C.addSynthesizedDecl(initDecl); return initDecl; } @@ -256,8 +254,6 @@ static ValueDecl *deriveMathOperator(DerivedConformance &derived, operatorDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({operatorDecl}); - C.addSynthesizedDecl(operatorDecl); - return operatorDecl; } diff --git a/lib/Sema/DerivedConformanceTensorArrayProtocol.cpp b/lib/Sema/DerivedConformanceTensorArrayProtocol.cpp index 762b65b9b8ea0..4fc9f3eaec6d1 100644 --- a/lib/Sema/DerivedConformanceTensorArrayProtocol.cpp +++ b/lib/Sema/DerivedConformanceTensorArrayProtocol.cpp @@ -245,8 +245,6 @@ static ValueDecl *deriveTensorArrayProtocol_method( funcDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({funcDecl}); - C.addSynthesizedDecl(funcDecl); - return funcDecl; } @@ -643,8 +641,6 @@ static ValueDecl initDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({initDecl}); - C.addSynthesizedDecl(initDecl); - return initDecl; } diff --git a/lib/Sema/DerivedConformanceTensorGroup.cpp b/lib/Sema/DerivedConformanceTensorGroup.cpp index 45d5cfe6234e1..7ccafdd8d07da 100644 --- a/lib/Sema/DerivedConformanceTensorGroup.cpp +++ b/lib/Sema/DerivedConformanceTensorGroup.cpp @@ -339,8 +339,6 @@ static ValueDecl *deriveTensorGroup_constructor( initDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({initDecl}); - C.addSynthesizedDecl(initDecl); - return initDecl; } diff --git a/lib/Sema/DerivedConformanceVectorProtocol.cpp b/lib/Sema/DerivedConformanceVectorProtocol.cpp index f441196d21318..565f8346cc5a9 100644 --- a/lib/Sema/DerivedConformanceVectorProtocol.cpp +++ b/lib/Sema/DerivedConformanceVectorProtocol.cpp @@ -227,7 +227,6 @@ static ValueDecl *deriveVectorProtocol_method( funcDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); derived.addMembersToConformanceContext({funcDecl}); - C.addSynthesizedDecl(funcDecl); // Returned nominal type must define a memberwise initializer. // Add memberwise initializer if necessary. @@ -237,7 +236,6 @@ static ValueDecl *deriveVectorProtocol_method( // constructor is synthesized during SILGen, which is too late. auto *initDecl = createMemberwiseImplicitConstructor(C, nominal); nominal->addMember(initDecl); - C.addSynthesizedDecl(initDecl); } return funcDecl; diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index bb65ae00fed86..6185624671cbf 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -490,31 +490,6 @@ DerivedConformance::createSelfDeclRef(AbstractFunctionDecl *fn) { AccessorDecl *DerivedConformance:: addGetterToReadOnlyDerivedProperty(VarDecl *property, Type propertyContextType) { -<<<<<<< HEAD - auto getter = - declareDerivedPropertyGetter(property, propertyContextType); - - property->setImplInfo(StorageImplInfo::getImmutableComputed()); - property->setAccessors(SourceLoc(), {getter}, SourceLoc()); - - return getter; -} - -std::pair -DerivedConformance::addGetterAndSetterToMutableDerivedProperty( - VarDecl *property, Type propertyContextType) { - auto *getter = declareDerivedPropertyGetter(property, propertyContextType); - auto *setter = declareDerivedPropertySetter(property, propertyContextType); - property->setImplInfo(StorageImplInfo::getMutableComputed()); - property->setAccessors(SourceLoc(), {getter, setter}, SourceLoc()); - return std::make_pair(getter, setter); -} - -AccessorDecl * -DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, - Type propertyContextType) { -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a auto &C = property->getASTContext(); auto parentDC = property->getDeclContext(); ParameterList *params = ParameterList::createEmpty(C); @@ -539,6 +514,18 @@ DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, return getter; } +// SWIFT_ENABLE_TENSORFLOW +std::pair +DerivedConformance::addGetterAndSetterToMutableDerivedProperty( + VarDecl *property, Type propertyContextType) { + auto *getter = addGetterToReadOnlyDerivedProperty(property, propertyContextType); + auto *setter = declareDerivedPropertySetter(property, propertyContextType); + property->setImplInfo(StorageImplInfo::getMutableComputed()); + property->setAccessors(SourceLoc(), {getter, setter}, SourceLoc()); + return std::make_pair(getter, setter); +} +// SWIFT_ENABLE_TENSORFLOW END + // SWIFT_ENABLE_TENSORFLOW AccessorDecl * DerivedConformance::declareDerivedPropertySetter(VarDecl *property, @@ -578,7 +565,6 @@ DerivedConformance::declareDerivedPropertySetter(VarDecl *property, setterDecl->setGenericSignature(parentDC->getGenericSignatureOfContext()); setterDecl->copyFormalAccessFrom(property); - C.addSynthesizedDecl(setterDecl); return setterDecl; } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 2fba4c84df4d8..6513cbbd74219 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -319,8 +319,7 @@ class DerivedConformance { /// Add a getter to a derived property. The property becomes read-only. static AccessorDecl * addGetterToReadOnlyDerivedProperty(VarDecl *property, -<<<<<<< HEAD - Type propertyContextType); + Type propertyContextType); // SWIFT_ENABLE_TENSORFLOW /// Add a getter and setter to a derived property. The property becomes @@ -329,19 +328,11 @@ class DerivedConformance { addGetterAndSetterToMutableDerivedProperty(VarDecl *property, Type propertyContextType); - /// Declare a getter for a derived property. - /// The getter will not be added to the property yet. - static AccessorDecl *declareDerivedPropertyGetter(VarDecl *property, - Type propertyContextType); -======= - Type propertyContextType); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a - - /// SWIFT_ENABLE_TENSORFLOW /// Declare a setter for a derived property. /// The setter will not be added to the property yet. static AccessorDecl *declareDerivedPropertySetter(VarDecl *property, Type propertyContextType); + // SWIFT_ENABLE_TENSORFLOW END /// Build a reference to the 'self' decl of a derived function. static DeclRefExpr *createSelfDeclRef(AbstractFunctionDecl *fn); diff --git a/lib/Sema/QuoteTransform.cpp b/lib/Sema/QuoteTransform.cpp index c014fec409427..c75834ba64b88 100644 --- a/lib/Sema/QuoteTransform.cpp +++ b/lib/Sema/QuoteTransform.cpp @@ -1224,7 +1224,8 @@ Expr *TypeChecker::quoteExpr(Expr *expr, DeclContext *dc) { assert(expr->getType()); Breadcrumbs bcs; - ASTQuoter astQuoter(*this, Context, bcs); + auto &ctx = dc->getASTContext(); + ASTQuoter astQuoter(*this, ctx, bcs); Expr *quotedExpr = astQuoter.visit(expr); if (!quotedExpr) { return nullptr; @@ -1240,19 +1241,18 @@ Expr *TypeChecker::quoteExpr(Expr *expr, DeclContext *dc) { return nullptr; } - auto quoteRef = - new (Context) UnresolvedDeclRefExpr(quoteClassType->getDecl()->getName(), - DeclRefKind::Ordinary, DeclNameLoc()); + auto quoteRef = new (ctx) UnresolvedDeclRefExpr( + quoteClassType->getDecl()->getName(), DeclRefKind::Ordinary, + DeclNameLoc()); SmallVector quoteTargs; for (auto targ : quoteClassType->getGenericArgs()) { - auto targRepr = new (Context) FixedTypeRepr(targ, SourceLoc()); + auto targRepr = new (ctx) FixedTypeRepr(targ, SourceLoc()); quoteTargs.push_back(TypeLoc(targRepr)); } auto quoteInit = UnresolvedSpecializeExpr::create( - Context, quoteRef, SourceLoc(), quoteTargs, SourceLoc()); + ctx, quoteRef, SourceLoc(), quoteTargs, SourceLoc()); quoteInit->setImplicit(); - Expr *quoteCall = - CallExpr::createImplicit(Context, quoteInit, {quotedExpr}, {}); + Expr *quoteCall = CallExpr::createImplicit(ctx, quoteInit, {quotedExpr}, {}); // TODO(TF-727): Improve error reporting when quoting fails. if (!typeCheckExpression(quoteCall, dc)) { @@ -1272,16 +1272,16 @@ Expr *TypeChecker::quoteExpr(Expr *expr, DeclContext *dc) { Type TypeChecker::getTypeOfQuoteExpr(Type exprType, SourceLoc loc) { assert(exprType); - if (!Context.getQuoteModule()) { - Context.Diags.diagnose(loc, diag::quote_literal_no_quote_module); + auto &ctx = exprType->getASTContext(); + if (!ctx.getQuoteModule()) { + ctx.Diags.diagnose(loc, diag::quote_literal_no_quote_module); return Type(); } if (auto fnExprType = exprType->getAs()) { auto n = fnExprType->getParams().size(); - auto quoteClass = Context.getFunctionQuoteDecl(n); + auto quoteClass = ctx.getFunctionQuoteDecl(n); if (!quoteClass) { - Context.Diags.diagnose(loc, diag::quote_literal_no_function_quote_class, - n); + ctx.Diags.diagnose(loc, diag::quote_literal_no_function_quote_class, n); return Type(); } SmallVector typeArgs; @@ -1295,9 +1295,9 @@ Type TypeChecker::getTypeOfQuoteExpr(Type exprType, SourceLoc loc) { typeArgs.push_back(fnExprType->getResult()); return BoundGenericClassType::get(quoteClass, Type(), typeArgs); } else { - auto quoteClass = Context.getQuoteDecl(); + auto quoteClass = ctx.getQuoteDecl(); if (!quoteClass) { - Context.Diags.diagnose(loc, diag::quote_literal_no_quote_class); + ctx.Diags.diagnose(loc, diag::quote_literal_no_quote_class); return Type(); } if (auto lvalueExprType = exprType->getAs()) { @@ -1309,19 +1309,19 @@ Type TypeChecker::getTypeOfQuoteExpr(Type exprType, SourceLoc loc) { Type TypeChecker::getTypeOfUnquoteExpr(Type exprType, SourceLoc loc) { assert(exprType); + auto &ctx = exprType->getASTContext(); if (auto genericType = exprType->getAs()) { auto classDecl = genericType->getDecl(); auto typeArgs = genericType->getGenericArgs(); - if (classDecl == Context.getQuoteDecl()) { + if (classDecl == ctx.getQuoteDecl()) { return typeArgs[0]; - } else if (classDecl == Context.getFunctionQuoteDecl(typeArgs.size() - 1)) { + } else if (classDecl == ctx.getFunctionQuoteDecl(typeArgs.size() - 1)) { SmallVector paramTypes; for (unsigned i = 0; i < typeArgs.size() - 1; ++i) { auto typeArg = typeArgs[i]; auto flags = ParameterTypeFlags(); if (auto genericTypeArg = typeArg->getAs()) { - if (genericTypeArg->getDecl() == - Context.getUnsafeMutablePointerDecl()) { + if (genericTypeArg->getDecl() == ctx.getUnsafeMutablePointerDecl()) { flags = flags.withInOut(true); } } @@ -1330,18 +1330,18 @@ Type TypeChecker::getTypeOfUnquoteExpr(Type exprType, SourceLoc loc) { } return FunctionType::get(paramTypes, typeArgs[typeArgs.size() - 1]); } else { - Context.Diags.diagnose(loc, diag::unquote_wrong_type); + ctx.Diags.diagnose(loc, diag::unquote_wrong_type); return Type(); } } else { - Context.Diags.diagnose(loc, diag::unquote_wrong_type); + ctx.Diags.diagnose(loc, diag::unquote_wrong_type); return Type(); } } Expr *TypeChecker::quoteDecl(Decl *decl, DeclContext *dc) { Breadcrumbs bcs; - ASTQuoter astQuoter(*this, Context, bcs); + ASTQuoter astQuoter(*this, dc->getASTContext(), bcs); Expr *quotedDecl = astQuoter.visit(decl); if (!quotedDecl) { return nullptr; @@ -1355,14 +1355,14 @@ Expr *TypeChecker::quoteDecl(Decl *decl, DeclContext *dc) { return quotedDecl; } -Type TypeChecker::getTypeOfQuoteDecl(SourceLoc loc) { - if (!Context.getQuoteModule()) { - Context.Diags.diagnose(loc, diag::quote_literal_no_quote_module); +Type TypeChecker::getTypeOfQuoteDecl(ASTContext &ctx, SourceLoc loc) { + if (!ctx.getQuoteModule()) { + ctx.Diags.diagnose(loc, diag::quote_literal_no_quote_module); return Type(); } - auto treeProto = Context.getTreeDecl(); + auto treeProto = ctx.getTreeDecl(); if (!treeProto) { - Context.Diags.diagnose(loc, diag::quote_literal_no_tree_proto); + ctx.Diags.diagnose(loc, diag::quote_literal_no_tree_proto); return Type(); } return treeProto->getDeclaredType(); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 2526585d64d05..8c52796e62162 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -111,16 +111,9 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(DisfavoredOverload) IGNORED_ATTR(ProjectedValueProperty) IGNORED_ATTR(ReferenceOwnership) -<<<<<<< HEAD // SWIFT_ENABLE_TENSORFLOW // TODO(TF-715): Allow @quoted on more decls. IGNORED_ATTR(Quoted) -======= - - // TODO(TF-828): Upstream `@differentiable` attribute type-checking from - // tensorflow branch. - IGNORED_ATTR(Differentiable) ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { diff --git a/lib/Sema/TypeCheckCompilerEvaluable.cpp b/lib/Sema/TypeCheckCompilerEvaluable.cpp index f9ff32876a848..9fab534b23b25 100644 --- a/lib/Sema/TypeCheckCompilerEvaluable.cpp +++ b/lib/Sema/TypeCheckCompilerEvaluable.cpp @@ -241,7 +241,7 @@ void TypeChecker::checkFunctionBodyCompilerEvaluable(AbstractFunctionDecl *D) { assert(D->getBodyKind() == AbstractFunctionDecl::BodyKind::TypeChecked && "cannot check @compilerEvaluable body that is not type checked"); - CheckCompilerEvaluableBody Checker(this->Context, D); + CheckCompilerEvaluableBody Checker(D->getASTContext(), D); D->getBody()->walk(Checker); if (!Checker.getCompilerEvaluable()) { compilerEvaluableAttr->setInvalid(); diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 2874d573f7759..40174f06c42c2 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1431,17 +1431,14 @@ namespace { UNINTERESTING_ATTR(DynamicReplacement) UNINTERESTING_ATTR(PrivateImport) -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW + // Differentiation-related attributes. UNINTERESTING_ATTR(Differentiable) + // SWIFT_ENABLE_TENSORFLOW UNINTERESTING_ATTR(Differentiating) UNINTERESTING_ATTR(CompilerEvaluable) UNINTERESTING_ATTR(NoDerivative) UNINTERESTING_ATTR(Transposing) -======= - // Differentiation-related attributes. - UNINTERESTING_ATTR(Differentiable) ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + // SWIFT_ENABLE_TENSORFLOW END // These can't appear on overridable declarations. UNINTERESTING_ATTR(Prefix) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 28ff58e7fd29f..bddddb31131ae 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2204,14 +2204,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } } - // SWIFT_ENABLE_TENSORFLOW - DifferentiabilityKind diffkind = DifferentiabilityKind::NonDifferentiable; - if (attrs.has(TAK_differentiable)) { - diffkind = attrs.linear - ? DifferentiabilityKind::Linear - : DifferentiabilityKind::Normal; - } - if (attrs.has(TAK_differentiable) && !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { diagnose(attrs.getLoc(TAK_differentiable), @@ -2226,13 +2218,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Resolve the function type directly with these attributes. SILFunctionType::ExtInfo extInfo(rep, attrs.has(TAK_pseudogeneric), -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - attrs.has(TAK_noescape), - diffkind); -======= attrs.has(TAK_noescape), diffKind); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a ty = resolveSILFunctionType(fnRepr, options, coroutineKind, extInfo, calleeConvention, witnessMethodProtocol); @@ -2278,17 +2264,10 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, attrs.clearAttribute(TAK_autoclosure); } - // SWIFT_ENABLE_TENSORFLOW - DifferentiabilityKind diffkind = DifferentiabilityKind::NonDifferentiable; - if (attrs.has(TAK_differentiable)) { - if(!Context.LangOpts.EnableExperimentalDifferentiableProgramming) { - diagnose(attrs.getLoc(TAK_differentiable), - diag::experimental_differentiable_programming_disabled); - } else { - diffkind = attrs.linear - ? DifferentiabilityKind::Linear - : DifferentiabilityKind::Normal; - } + if (attrs.has(TAK_differentiable) && + !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnose(attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); } DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; @@ -2298,15 +2277,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } // Resolve the function type directly with these attributes. -<<<<<<< HEAD - FunctionType::ExtInfo extInfo(rep, /*noescape=*/false, - // SWIFT_ENABLE_TENSORFLOW - fnRepr->throws(), - diffkind); -======= FunctionType::ExtInfo extInfo(rep, /*noescape=*/false, fnRepr->throws(), diffKind); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a ty = resolveASTFunctionType(fnRepr, options, extInfo); if (!ty || ty->hasError()) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 218b9f2b4c67f..f46205b4d698e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -945,7 +945,7 @@ class TypeChecker final { /// /// TODO(TF-736): In the future, we may want to infer more precise type /// based on the shape of the quoted declaration. - Type getTypeOfQuoteDecl(SourceLoc loc); + Type getTypeOfQuoteDecl(ASTContext &ctx, SourceLoc loc); private: Type typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 52229fb080b06..77a3da3633446 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4495,23 +4495,6 @@ Optional getActualParameterConvention(uint8_t raw) { return None; } -// SWIFT_ENABLE_TENSORFLOW -/// Translate from the serialization DifferentiabilityKind enumerators, -/// which are guaranteed to be stable, to the AST ones. -static Optional -getActualDifferentiabilityKind(uint8_t raw) { - switch (serialization::DifferentiabilityKind(raw)) { -#define CASE(ID) \ - case serialization::DifferentiabilityKind::ID: \ - return swift::DifferentiabilityKind::ID; - CASE(NonDifferentiable) - CASE(Normal) - CASE(Linear) -#undef CASE - } - return None; -} - /// Translate from the serialization SILParameterDifferentiability enumerators, /// which are guaranteed to be stable, to the AST ones. static Optional @@ -4761,34 +4744,6 @@ class swift::TypeDeserializer { StringRef blobData, bool isGeneric) { TypeID resultID; -<<<<<<< HEAD - uint8_t rawRepresentation; - - // SWIFT_ENABLE_TENSORFLOW - bool noescape = false, throws = false; - uint8_t rawDiffKind = 0; - auto diffKind = DifferentiabilityKind::NonDifferentiable; - GenericSignature genericSig = GenericSignature(); - - if (!isGeneric) { - decls_block::FunctionTypeLayout::readRecord(scratch, resultID, - rawRepresentation, - noescape, - // SWIFT_ENABLE_TENSORFLOW - throws, - rawDiffKind); - diffKind = DifferentiabilityKind(rawDiffKind); - } else { - GenericSignatureID rawGenericSig; - decls_block::GenericFunctionTypeLayout::readRecord(scratch, - resultID, - rawRepresentation, - throws, - // SWIFT_ENABLE_TENSORFLOW - rawDiffKind, - rawGenericSig); - diffKind = DifferentiabilityKind(rawDiffKind); -======= uint8_t rawRepresentation, rawDiffKind; bool noescape = false, throws; GenericSignature genericSig = GenericSignature(); @@ -4801,7 +4756,6 @@ class swift::TypeDeserializer { decls_block::GenericFunctionTypeLayout::readRecord( scratch, resultID, rawRepresentation, throws, rawDiffKind, rawGenericSig); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a genericSig = MF.getGenericSignature(rawGenericSig); } @@ -4809,18 +4763,12 @@ class swift::TypeDeserializer { if (!representation.hasValue()) MF.fatal(); -<<<<<<< HEAD - auto info = FunctionType::ExtInfo(*representation, noescape, - // SWIFT_ENABLE_TENSORFLOW - throws, diffKind); -======= auto diffKind = getActualDifferentiabilityKind(rawDiffKind); if (!diffKind.hasValue()) MF.fatal(); auto info = FunctionType::ExtInfo(*representation, noescape, throws, *diffKind); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a auto resultTy = MF.getTypeChecked(resultID); if (!resultTy) @@ -5150,8 +5098,6 @@ class swift::TypeDeserializer { uint8_t rawDiffKind; bool pseudogeneric = false; bool noescape; - // SWIFT_ENABLE_TENSORFLOW - uint8_t rawDifferentiabilityKind; bool hasErrorResult; unsigned numParams; unsigned numYields; @@ -5165,12 +5111,7 @@ class swift::TypeDeserializer { rawRepresentation, pseudogeneric, noescape, -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - rawDifferentiabilityKind, -======= rawDiffKind, ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a hasErrorResult, numParams, numYields, @@ -5183,20 +5124,11 @@ class swift::TypeDeserializer { = getActualSILFunctionTypeRepresentation(rawRepresentation); if (!representation.hasValue()) MF.fatal(); -<<<<<<< HEAD - auto differentiabilityKind = - getActualDifferentiabilityKind(rawDifferentiabilityKind); - if (!differentiabilityKind.hasValue()) - MF.fatal(); - SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, - noescape, *differentiabilityKind); -======= auto diffKind = getActualDifferentiabilityKind(rawDiffKind); if (!diffKind.hasValue()) MF.fatal(); SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape, *diffKind); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // Process the coroutine kind. auto coroutineKind = getActualSILCoroutineKind(rawCoroutineKind); @@ -5221,7 +5153,7 @@ class swift::TypeDeserializer { // SWIFT_ENABLE_TENSORFLOW auto paramDiff = swift::SILParameterDifferentiability::DifferentiableOrNotApplicable; - if (differentiabilityKind != DifferentiabilityKind::NonDifferentiable) { + if (diffKind != DifferentiabilityKind::NonDifferentiable) { auto paramDiffOpt = getActualSILParameterDifferentiability(rawParamDiff); if (!paramDiffOpt) { @@ -5259,8 +5191,7 @@ class swift::TypeDeserializer { // Bounds check. FIXME: overflow // SWIFT_ENABLE_TENSORFLOW unsigned entriesPerParam = - differentiabilityKind != DifferentiabilityKind::NonDifferentiable - ? 3 : 2; + diffKind != DifferentiabilityKind::NonDifferentiable ? 3 : 2; if (entriesPerParam * numParams + 2 * numResults + 2 * unsigned(hasErrorResult) > variableData.size()) { @@ -5277,7 +5208,7 @@ class swift::TypeDeserializer { auto rawConvention = variableData[nextVariableDataIndex++]; // SWIFT_ENABLE_TENSORFLOW uint64_t paramDiff = 0; - if (differentiabilityKind != DifferentiabilityKind::NonDifferentiable) + if (diffKind != DifferentiabilityKind::NonDifferentiable) paramDiff = variableData[nextVariableDataIndex++]; auto param = processParameter(typeID, rawConvention, paramDiff); if (!param) diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 1139f684cca94..2c637b7567c4d 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -715,11 +715,18 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // SWIFT_ENABLE_TENSORFLOW // Read and instantiate the differentiable attributes. while (numDifferentiableAttrs--) { - auto next = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeNext = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeNext) + return maybeNext.takeError(); + llvm::BitstreamEntry next = maybeNext.get(); assert(next.Kind == llvm::BitstreamEntry::Record); scratch.clear(); - kind = SILCursor.readRecord(next.ID, scratch); + llvm::Expected maybeKind = SILCursor.readRecord(next.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + unsigned kind = maybeKind.get(); assert(kind == SIL_DIFFERENTIABLE_ATTR && "Missing differentiable attribute"); @@ -1622,7 +1629,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, for (auto i = numParamIndices; i < numParamIndices + numOperands * 3; i += 3) { auto astTy = MF->getType(ListOfValues[i]); - auto silTy = getSILType(astTy, (SILValueCategory)ListOfValues[i+1]); + auto silTy = getSILType(astTy, (SILValueCategory)ListOfValues[i+1], Fn); operands.push_back(getLocalValue(ListOfValues[i+2], silTy)); } Optional> derivativeFunctions = None; @@ -1647,7 +1654,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, for (auto i = numParamIndices; i < numParamIndices + numOperands * 3; i += 3) { auto astTy = MF->getType(ListOfValues[i]); - auto silTy = getSILType(astTy, (SILValueCategory)ListOfValues[i+1]); + auto silTy = getSILType(astTy, (SILValueCategory)ListOfValues[i+1], Fn); operands.push_back(getLocalValue(ListOfValues[i+2], silTy)); } Optional transposeFunction = None; @@ -1659,7 +1666,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::DifferentiableFunctionExtractInst: { auto astTy = MF->getType(TyID); - auto silTy = getSILType(astTy, SILValueCategory::Object); + auto silTy = getSILType(astTy, SILValueCategory::Object, Fn); auto val = getLocalValue(ValID, silTy); NormalDifferentiableFunctionTypeComponent extractee(Attr); Optional explicitExtracteeType = None; @@ -1672,7 +1679,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::LinearFunctionExtractInst: { auto astTy = MF->getType(TyID); - auto silTy = getSILType(astTy, SILValueCategory::Object); + auto silTy = getSILType(astTy, SILValueCategory::Object, Fn); auto val = getLocalValue(ValID, silTy); LinearDifferentiableFunctionTypeComponent extractee(Attr); ResultVal = Builder.createLinearFunctionExtract(Loc, extractee, val); @@ -3533,17 +3540,26 @@ SILDeserializer::readDifferentiabilityWitness(DeclID DId) { return diffWitnessOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(diffWitnessOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (auto err = SILCursor.JumpToBit(diffWitnessOrOffset.getOffset())) + MF->fatal(std::move(err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + auto entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { - LLVM_DEBUG(llvm::dbgs() - << "Cursor advance error in readDifferentiabilityWitness.\n"); + LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in " + "readDefaultWitnessTable.\n"); return nullptr; } SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_DIFFERENTIABILITY_WITNESS && "Expected sil_differentiability_witness"); (void)kind; diff --git a/lib/Serialization/DeserializeSIL.h b/lib/Serialization/DeserializeSIL.h index f3af33be37bef..0600c58a09e3d 100644 --- a/lib/Serialization/DeserializeSIL.h +++ b/lib/Serialization/DeserializeSIL.h @@ -117,15 +117,13 @@ namespace swift { SILValue getLocalValue(serialization::ValueID Id, SILType Type); -<<<<<<< HEAD + SILType getSILType(Type ty, SILValueCategory category, + SILFunction *inContext); + // SWIFT_ENABLE_TENSORFLOW SILDifferentiabilityWitness *getSILDifferentiabilityWitnessForReference( StringRef mangledKey); // SWIFT_ENABLE_TENSORFLOW_END -======= - SILType getSILType(Type ty, SILValueCategory category, - SILFunction *inContext); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a SILFunction *getFuncForReference(StringRef Name, SILType Ty); SILFunction *getFuncForReference(StringRef Name); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 28c9568c92564..60fd1c1bb9a80 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -52,11 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -<<<<<<< HEAD -const uint16_t SWIFTMODULE_VERSION_MINOR = 524; // differentiable_function_extract explicit extractee type -======= -const uint16_t SWIFTMODULE_VERSION_MINOR = 524; // function type differentiability ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a +const uint16_t SWIFTMODULE_VERSION_MINOR = 525; // tensorflow merge; function type differentiability /// A standard hash seed used for all string hashes in a serialized module. /// @@ -339,16 +335,6 @@ enum class ParameterConvention : uint8_t { }; using ParameterConventionField = BCFixed<4>; -// SWIFT_ENABLE_TENSORFLOW -// These IDs must \em not be renumbered or reordered without incrementing the -// module version. -enum class DifferentiabilityKind : uint8_t { - NonDifferentiable = 0, - Normal = 1, - Linear = 2 -}; -using DifferentiabilityKindField = BCFixed<2>; - // These IDs must \em not be renumbered or reordered without incrementing // the module version. enum class SILParameterDifferentiability : uint8_t { @@ -905,15 +891,8 @@ namespace decls_block { TypeIDField, // output FunctionTypeRepresentationField, // representation BCFixed<1>, // noescape? -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - BCFixed<1>, // throws? - BCFixed<2> // differentiability kind -======= BCFixed<1>, // throws? DifferentiabilityKindField // differentiability kind - ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // trailed by parameters >; @@ -987,12 +966,7 @@ namespace decls_block { TypeIDField, // output FunctionTypeRepresentationField, // representation BCFixed<1>, // throws? -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - BCFixed<2>, // differentiable & linear? -======= DifferentiabilityKindField, // differentiability kind ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a GenericSignatureIDField // generic signture // trailed by parameters @@ -1005,10 +979,6 @@ namespace decls_block { SILFunctionTypeRepresentationField, // representation BCFixed<1>, // pseudogeneric? BCFixed<1>, // noescape? -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a DifferentiabilityKindField, // differentiability kind BCFixed<1>, // error result? BCVBR<6>, // number of parameters @@ -1784,10 +1754,6 @@ namespace decls_block { GenericSignatureIDField // specialized signature >; -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a using DifferentiableDeclAttrLayout = BCRecordLayout< Differentiable_DECL_ATTR, BCFixed<1>, // Implicit flag. @@ -1800,7 +1766,6 @@ namespace decls_block { BCArray> // Differentiation parameter indices' bitvector. >; -<<<<<<< HEAD // SWIFT_ENABLE_TENSORFLOW using DifferentiatingDeclAttrLayout = BCRecordLayout< Differentiating_DECL_ATTR, @@ -1819,10 +1784,7 @@ namespace decls_block { BCArray> // Differentiation parameter indices' bitvector. >; -#define SIMPLE_DECL_ATTR(X, CLASS, ...) \ -======= #define SIMPLE_DECL_ATTR(X, CLASS, ...) \ ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a using CLASS##DeclAttrLayout = BCRecordLayout< \ CLASS##_DECL_ATTR, \ BCFixed<1> /* implicit flag */ \ diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index c19e8e1a60730..34e8fe7497637 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2306,19 +2306,12 @@ class Serializer::DeclSerializer : public DeclVisitor { break; } -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW case DAK_Differentiable: { auto abbrCode = S.DeclTypeAbbrCodes[DifferentiableDeclAttrLayout::Code]; auto *attr = cast(DA); assert(attr->getOriginalDeclaration() && "`@differentiable` attribute should have original declaration set " "during construction or parsing"); -======= - case DAK_Differentiable: { - auto abbrCode = S.DeclTypeAbbrCodes[DifferentiableDeclAttrLayout::Code]; - auto *attr = cast(DA); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a IdentifierID jvpName = 0; DeclID jvpRef = 0; @@ -2335,14 +2328,6 @@ class Serializer::DeclSerializer : public DeclVisitor { vjpRef = S.addDeclRef(vjpFunction); auto paramIndices = attr->getParameterIndices(); -<<<<<<< HEAD -======= - // TODO(TF-837): Implement `@differentiable` attribute serialization. - // Blocked by TF-828: `@differentiable` attribute type-checking, which - // resolves parameter indices (`IndexSubset *`). - if (!paramIndices) - return; ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a assert(paramIndices && "Checked parameter indices must be resolved"); SmallVector indices; for (unsigned i : range(paramIndices->getCapacity())) @@ -2355,7 +2340,6 @@ class Serializer::DeclSerializer : public DeclVisitor { indices); return; } -<<<<<<< HEAD case DAK_Quoted: { auto abbrCode = S.DeclTypeAbbrCodes[QuotedDeclAttrLayout::Code]; @@ -2366,8 +2350,6 @@ class Serializer::DeclSerializer : public DeclVisitor { S.addDeclRef(attr->getQuoteDecl())); return; } -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a } } @@ -3710,19 +3692,6 @@ static uint8_t getRawStableSILCoroutineKind( llvm_unreachable("bad kind"); } -// SWIFT_ENABLE_TENSORFLOW -/// Translate from the AST differentiability kind enum to the Serialization enum -/// values, which are guaranteed to be stable. -static uint8_t getRawStableDifferentiabilityKind( - swift::DifferentiabilityKind kind) { - switch (kind) { - SIMPLE_CASE(DifferentiabilityKind, NonDifferentiable) - SIMPLE_CASE(DifferentiabilityKind, Normal) - SIMPLE_CASE(DifferentiabilityKind, Linear) - } - llvm_unreachable("bad differentiability kind"); -} - /// Translate from the AST ownership enum to the Serialization enum /// values, which are guaranteed to be stable. static uint8_t @@ -4034,14 +4003,8 @@ class Serializer::TypeSerializer : public TypeVisitor { S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), fnTy->isNoEscape(), -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - fnTy->throws(), - (uint8_t)fnTy->getDifferentiabilityKind()); -======= fnTy->throws(), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind())); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a serializeFunctionTypeParams(fnTy); } @@ -4054,13 +4017,8 @@ class Serializer::TypeSerializer : public TypeVisitor { GenericFunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - fnTy->throws(), fnTy->isDifferentiable(), -======= fnTy->throws(), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()), ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a S.addGenericSignatureRef(genericSig)); serializeFunctionTypeParams(fnTy); @@ -4086,11 +4044,8 @@ class Serializer::TypeSerializer : public TypeVisitor { using namespace decls_block; auto representation = fnTy->getRepresentation(); - // SWIFT_ENABLE_TENSORFLOW auto stableRepresentation = - getRawStableSILFunctionTypeRepresentation(representation); - auto stableDifferentiabilityKind = - getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()); + getRawStableSILFunctionTypeRepresentation(representation); SmallVector variableData; for (auto param : fnTy->getParameters()) { @@ -4135,16 +4090,9 @@ class Serializer::TypeSerializer : public TypeVisitor { S.Out, S.ScratchRecord, abbrCode, stableCoroutineKind, stableCalleeConvention, stableRepresentation, fnTy->isPseudogeneric(), fnTy->isNoEscape(), -<<<<<<< HEAD - // SWIFT_ENABLE_TENSORFLOW - stableDifferentiabilityKind, fnTy->hasErrorResult(), - fnTy->getParameters().size(), fnTy->getNumYields(), - fnTy->getNumResults(), S.addGenericSignatureRef(sig), variableData); -======= stableDiffKind, fnTy->hasErrorResult(), fnTy->getParameters().size(), fnTy->getNumYields(), fnTy->getNumResults(), S.addGenericSignatureRef(sig), variableData); ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a if (auto conformance = fnTy->getWitnessMethodConformanceOrInvalid()) S.writeConformance(conformance, S.DeclTypeAbbrCodes); diff --git a/test/AutoDiff/Serialization/differentiable_attr.swift b/test/AutoDiff/Serialization/differentiable_attr.swift index 88b0f0bb844c7..ba540be5f19a1 100644 --- a/test/AutoDiff/Serialization/differentiable_attr.swift +++ b/test/AutoDiff/Serialization/differentiable_attr.swift @@ -8,7 +8,11 @@ // TODO(TF-836): Enable this test. // Blocked by TF-828: `@differentiating` attribute type-checking. -// XFAIL: * +// SWIFT_ENABLE_TENSORFLOW +// This test currently only fails on `master` branch. +// Disable the XFAIL on `tensorflow` branch. +// XFAI: * +// SWIFT_ENABLE_TENSORFLOW END // BCANALYZER-NOT: UnknownCode diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 412b32261ea1e..249a11f6df10c 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -62,16 +62,12 @@ struct MyStruct {} // KEYWORD2-NEXT: Keyword/None: warn_unqualified_access[#Func Attribute#]; name=warn_unqualified_access{{$}} // KEYWORD2-NEXT: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // KEYWORD2-NEXT: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW // KEYWORD2-NEXT: Keyword/None: differentiable[#Func Attribute#]; name=differentiable +// SWIFT_ENABLE_TENSORFLOW // KEYWORD2-NEXT: Keyword/None: differentiating[#Func Attribute#]; name=differentiating // KEYWORD2-NEXT: Keyword/None: compilerEvaluable[#Func Attribute#]; name=compilerEvaluable // SWIFT_ENABLE_TENSORFLOW // KEYWORD2-NEXT: Keyword/None: transposing[#Func Attribute#]; name=transposing -======= -// KEYWORD2-NEXT: Keyword/None: differentiable[#Func Attribute#]; name=differentiable ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // KEYWORD2-NEXT: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction{{$}} // KEYWORD2-NEXT: Keyword/None: quoted[#Func Attribute#]; name=quoted // KEYWORD2-NOT: Keyword @@ -135,13 +131,9 @@ struct MyStruct {} // ON_GLOBALVAR-DAG: Keyword/None: inlinable[#Var Attribute#]; name=inlinable // ON_GLOBALVAR-DAG: Keyword/None: usableFromInline[#Var Attribute#]; name=usableFromInline // ON_GLOBALVAR-DAG: Keyword/None: GKInspectable[#Var Attribute#]; name=GKInspectable -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW // ON_GLOBALVAR-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable +// SWIFT_ENABLE_TENSORFLOW // ON_GLOBALVAR-DAG: Keyword/None: noDerivative[#Var Attribute#]; name=noDerivative -======= -// ON_GLOBALVAR-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // ON_GLOBALVAR-NOT: Keyword // ON_GLOBALVAR: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_GLOBALVAR: End completions @@ -171,13 +163,9 @@ struct _S { // ON_PROPERTY-DAG: Keyword/None: inlinable[#Var Attribute#]; name=inlinable // ON_PROPERTY-DAG: Keyword/None: usableFromInline[#Var Attribute#]; name=usableFromInline // ON_PROPERTY-DAG: Keyword/None: GKInspectable[#Var Attribute#]; name=GKInspectable -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW // ON_PROPERTY-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable +// SWIFT_ENABLE_TENSORFLOW // ON_PROPERTY-DAG: Keyword/None: noDerivative[#Var Attribute#]; name=noDerivative -======= -// ON_PROPERTY-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // ON_PROPERTY-NOT: Keyword // ON_PROPERTY: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_PROPERTY-NOT: Decl[PrecedenceGroup] @@ -197,17 +185,13 @@ struct _S { // ON_METHOD-DAG: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // ON_METHOD-DAG: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult // ON_METHOD-DAG: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW // ON_METHOD-DAG: Keyword/None: differentiable[#Func Attribute#]; name=differentiable +// SWIFT_ENABLE_TENSORFLOW // ON_METHOD-DAG: Keyword/None: differentiating[#Func Attribute#]; name=differentiating // ON_METHOD-DAG: Keyword/None: compilerEvaluable[#Func Attribute#]; name=compilerEvaluable // SWIFT_ENABLE_TENSORFLOW // ON_METHOD-DAG: Keyword/None: transposing[#Func Attribute#]; name=transposing // ON_METHOD-DAG: Keyword/None: quoted[#Func Attribute#]; name=quoted -======= -// ON_METHOD-DAG: Keyword/None: differentiable[#Func Attribute#]; name=differentiable ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // ON_METHOD-NOT: Keyword // ON_METHOD: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_METHOD: End completions @@ -262,17 +246,13 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction // ON_MEMBER_LAST-DAG: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper // ON_MEMBER_LAST-DAG: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW // ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable +// SWIFT_ENABLE_TENSORFLOW // ON_MEMBER_LAST-DAG: Keyword/None: differentiating[#Declaration Attribute#]; name=differentiating // ON_MEMBER_LAST-DAG: Keyword/None: compilerEvaluable[#Declaration Attribute#]; name=compilerEvaluable // ON_MEMBER_LAST-DAG: Keyword/None: noDerivative[#Declaration Attribute#]; name=noDerivative // ON_MEMBER_LAST-DAG: Keyword/None: transposing[#Declaration Attribute#]; name=transposing // ON_MEMBER_LAST-DAG: Keyword/None: quoted[#Declaration Attribute#]; name=quoted -======= -// ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // ON_MEMBER_LAST-NOT: Keyword // ON_MEMBER_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_MEMBER_LAST-NOT: Decl[PrecedenceGroup] @@ -315,16 +295,12 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: frozen[#Declaration Attribute#]; name=frozen // KEYWORD_LAST-NEXT: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper // KEYWORD_LAST-NEXT: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder{{$}} -<<<<<<< HEAD -// SWIFT_ENABLE_TENSORFLOW // KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable +// SWIFT_ENABLE_TENSORFLOW // KEYWORD_LAST-NEXT: Keyword/None: differentiating[#Declaration Attribute#]; name=differentiating // KEYWORD_LAST-NEXT: Keyword/None: compilerEvaluable[#Declaration Attribute#]; name=compilerEvaluable // KEYWORD_LAST-NEXT: Keyword/None: noDerivative[#Declaration Attribute#]; name=noDerivative // KEYWORD_LAST-NEXT: Keyword/None: transposing[#Declaration Attribute#]; name=transposing -======= -// KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a // KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}} // KEYWORD_LAST-NEXT: Keyword/None: quoted[#Declaration Attribute#]; name=quoted // KEYWORD_LAST-NOT: Keyword diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index f3e270eb7b225..8c4f510fde58e 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -220,12 +220,9 @@ macro(add_sourcekit_executable name) cmake_parse_arguments(SOURCEKITEXE "EXCLUDE_FROM_ALL" "" -<<<<<<< HEAD # SWIFT_ENABLE_TENSORFLOW - "C_COMPILE_FLAGS;LINK_LIBS;LLVM_LINK_COMPONENTS" -======= - "LLVM_LINK_COMPONENTS" ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + "C_COMPILE_FLAGS;LLVM_LINK_COMPONENTS" + # SWIFT_ENABLE_TENSORFLOW END ${ARGN}) if (${SOURCEKITEXE_EXCLUDE_FROM_ALL}) diff --git a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt index 4751730d01c13..822f338a452d1 100644 --- a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt @@ -2,28 +2,19 @@ set(LLVM_TARGET_DEFINITIONS Options.td) swift_tablegen(Options.inc -gen-opt-parser-defs) swift_add_public_tablegen_target(sourcekitdTestOptionsTableGen) -<<<<<<< HEAD +# SWIFT_ENABLE_TENSORFLOW if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_TEST_LINK_LIBS sourcekitdInProc) set(SOURCEKITD_TEST_CFLAGS "-DSWIFT_SOURCEKIT_USE_INPROC_LIBRARY") else() - set(SOURCEKITD_TEST_LINK_LIBS sourcekitd) set(SOURCEKITD_TEST_CFLAGS) endif() +# SWIFT_ENABLE_TENSORFLOW END add_sourcekit_executable(sourcekitd-test sourcekitd-test.cpp TestOptions.cpp C_COMPILE_FLAGS ${SOURCEKITD_TEST_CFLAGS} - LINK_LIBS ${SOURCEKITD_TEST_LINK_LIBS} SourceKitSupport - clangRewrite clangLex clangBasic - LLVM_LINK_COMPONENTS core support option coverage lto -======= -add_sourcekit_executable(sourcekitd-test - sourcekitd-test.cpp - TestOptions.cpp LLVM_LINK_COMPONENTS core option coverage lto ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a ) target_link_libraries(sourcekitd-test PRIVATE SourceKitSupport diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index d4300a138e3aa..4c9cfb0701de0 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -224,7 +224,10 @@ EnableExperimentalStaticAssert( static llvm::cl::opt EnableExperimentalDifferentiableProgramming( "enable-experimental-differentiable-programming", llvm::cl::Hidden, - llvm::cl::init(false), + // SWIFT_ENABLE_TENSORFLOW + // Use default value true on `tensorflow` branch. + llvm::cl::init(true), + // SWIFT_ENABLE_TENSORFLOW END llvm::cl::desc("Enable experimental differentiable programming")); /// Regular expression corresponding to the value given in one of the diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index 6732e694906f2..71fface4b4003 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -61,20 +61,14 @@ Child('ObjCName', kind='ObjCSelector'), Child('ImplementsArguments', kind='ImplementsAttributeArguments'), -<<<<<<< HEAD - # SWIFT_ENABLE_TENSORFLOW Child('DifferentiableArguments', kind='DifferentiableAttributeArguments'), # SWIFT_ENABLE_TENSORFLOW Child('DifferentiatingArguments', kind='DifferentiatingAttributeArguments'), - # SWIFT_ENABLE_TENSORFLOW Child('TransposingArguments', kind='DifferentiatingAttributeArguments'), -======= - Child('DifferentiableArguments', - kind='DifferentiableAttributeArguments'), ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + # SWIFT_ENABLE_TENSORFLOW END Child('NamedAttributeString', kind='NamedAttributeStringArgument'), ], description=''' @@ -199,20 +193,27 @@ '''), ]), - # SWIFT_ENABLE_TENSORFLOW + # objc-selector-piece -> identifier? ':'? + Node('ObjCSelectorPiece', kind='Syntax', + description=''' + A piece of an Objective-C selector. Either consisiting of just an + identifier for a nullary selector, an identifier and a colon for a + labeled argument or just a colon for an unlabeled argument + ''', + children=[ + Child('Name', kind='IdentifierToken', is_optional=True), + Child('Colon', kind='ColonToken', is_optional=True), + ]), + + # objc-selector -> objc-selector-piece objc-selector? + Node('ObjCSelector', kind='SyntaxCollection', element='ObjCSelectorPiece'), + # The argument of '@differentiable(...)'. # differentiable-attr-arguments -> # differentiation-params-clause? ','? - # differentiable-attr-func-specifier? # primal - # differentiable-attr-func-specifier? # adjoint # differentiable-attr-func-specifier? # jvp # differentiable-attr-func-specifier? # vjp # generic-where-clause? - # FIXME: There is currently no guarantee that 'MaybePrimal' is in fact - # the primal specifier, it could be any specifier. The current syntax - # definitions only ensure that there are between 0 and 4 function - # specifiers. A more robust definition would enforce that specific function - # specifiers appear only once, in order. Node('DifferentiableAttributeArguments', kind='Syntax', description=''' The arguments for the `@differentiable` attribute: an optional @@ -347,138 +348,11 @@ function and an optional differentiation parameter list. ''', children=[ - Child('Original', kind='FunctionDeclName', - description='The referenced original function.'), - Child('Comma', kind='CommaToken', is_optional=True), - Child('DiffParams', kind='DifferentiationParamsClause', - is_optional=True), - ]), - - # objc-selector-piece -> identifier? ':'? - Node('ObjCSelectorPiece', kind='Syntax', - description=''' - A piece of an Objective-C selector. Either consisiting of just an - identifier for a nullary selector, an identifier and a colon for a - labeled argument or just a colon for an unlabeled argument - ''', - children=[ - Child('Name', kind='IdentifierToken', is_optional=True), - Child('Colon', kind='ColonToken', is_optional=True), - ]), - - # objc-selector -> objc-selector-piece objc-selector? - Node('ObjCSelector', kind='SyntaxCollection', element='ObjCSelectorPiece'), - - # The argument of '@differentiable(...)'. - # differentiable-attr-arguments -> - # differentiation-params-clause? ','? - # differentiable-attr-func-specifier? # jvp - # differentiable-attr-func-specifier? # vjp - # generic-where-clause? - Node('DifferentiableAttributeArguments', kind='Syntax', - description=''' - The arguments for the `@differentiable` attribute: an optional - differentiation parameter list and associated functions. - ''', - children=[ - Child('DiffParams', kind='DifferentiationParamsClause', - is_optional=True), - Child('DiffParamsComma', kind='CommaToken', description=''' - The comma following the differentiation parameters clause, - if it exists. - ''', is_optional=True), - Child('MaybeJVP', kind='DifferentiableAttributeFuncSpecifier', - is_optional=True), - Child('MaybeVJP', kind='DifferentiableAttributeFuncSpecifier', - is_optional=True), - Child('WhereClause', kind='GenericWhereClause', is_optional=True), - ]), + Child('Original', kind='FunctionDeclName', + description='The referenced original function.'), + Child('Comma', kind='CommaToken', is_optional=True), + Child('DiffParams', kind='DifferentiationParamsClause', + is_optional=True), + ]), - # differentiation-params-clause -> - # 'wrt' ':' (differentiation-param | differentiation-params) - Node('DifferentiationParamsClause', kind='Syntax', - description='A clause containing differentiation parameters.', - children=[ - Child('WrtLabel', kind='IdentifierToken', - text_choices=['wrt'], description='The "wrt" label.'), - Child('Colon', kind='ColonToken', description=''' - The colon separating "wrt" and the parameter list. - '''), - Child('Parameters', kind='Syntax', - node_choices=[ - Child('Parameter', kind='DifferentiationParam'), - Child('ParameterList', kind='DifferentiationParams'), - ]), - ]), - - # differentiation-params -> '(' differentiation-param-list ')' - Node('DifferentiationParams', kind='Syntax', - description='The differentiation parameters.', - children=[ - Child('LeftParen', kind='LeftParenToken'), - Child('DiffParams', kind='DifferentiationParamList', - collection_element_name='DifferentiationParam', - description='The parameters for differentiation.'), - Child('RightParen', kind='RightParenToken'), - ]), - - # differentiation-param-list -> - # differentiation-param differentiation-param-list? - Node('DifferentiationParamList', kind='SyntaxCollection', - element='DifferentiationParam'), - - # differentiation-param -> ('self' | identifer) ','? - Node('DifferentiationParam', kind='Syntax', - description=''' - A differentiation parameter: either the "self" identifier or a - function parameter name. - ''', - traits=['WithTrailingComma'], - children=[ - Child('Parameter', kind='Syntax', - node_choices=[ - Child('Self', kind='SelfToken'), - Child('Name', kind='IdentifierToken'), - ]), - Child('TrailingComma', kind='CommaToken', is_optional=True), - ]), - - # differentiable-attr-func-specifier -> - # ('jvp' | 'vjp') ':' func-decl-name ','? - Node('DifferentiableAttributeFuncSpecifier', kind='Syntax', - description=''' - A function specifier, consisting of an identifier, colon, and a - function declaration name (e.g. `vjp: foo(_:_:)`). - ''', - traits=['WithTrailingComma'], - children=[ - Child('Label', kind='IdentifierToken', - text_choices=['jvp', 'vjp']), - Child('Colon', kind='ColonToken'), - Child('FunctionDeclName', kind='FunctionDeclName', - description='The referenced function name.'), - Child('TrailingComma', kind='CommaToken', is_optional=True), - ]), - - # func-decl-name -> (identifier | operator) decl-name-arguments? - # NOTE: This is duplicated with `DeclName` above. Change `DeclName` - # description and use it if possible. - Node('FunctionDeclName', kind='Syntax', - description='A function declaration name (e.g. `foo(_:_:)`).', - children=[ - Child('Name', kind='Syntax', description=''' - The base name of the referenced function. - ''', - node_choices=[ - Child('Identifier', kind='IdentifierToken'), - Child('PrefixOperator', kind='PrefixOperatorToken'), - Child('SpacedBinaryOperator', - kind='SpacedBinaryOperatorToken'), - ]), - Child('Arguments', kind='DeclNameArguments', - is_optional=True, description=''' - The argument labels of the referenced function, optionally - specified. - '''), - ]), ] diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 652aea78eb72d..403be99cc60eb 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -234,10 +234,6 @@ 'SomeType': 230, 'CustomAttribute': 231, 'GenericRequirement': 232, -<<<<<<< HEAD - # SWIFT_ENABLE_TENSORFLOW -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a 'DifferentiableAttributeArguments': 233, 'DifferentiationParamsClause': 234, 'DifferentiationParams': 235, @@ -245,13 +241,12 @@ 'DifferentiationParam': 237, 'DifferentiableAttributeFuncSpecifier': 238, 'FunctionDeclName': 239, -<<<<<<< HEAD + # SWIFT_ENABLE_TENSORFLOW 'DifferentiatingAttributeArguments': 240, 'TransposingAttributeArguments': 241, 'QuoteLiteralExpr': 242, 'UnquoteExpr': 243, -======= ->>>>>>> swift-DEVELOPMENT-SNAPSHOT-2019-11-20-a + # SWIFT_ENABLE_TENSORFLOW END } From f9956c10a9abe3ea611ce36d05f465225e275546 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 23 Nov 2019 15:04:37 -0800 Subject: [PATCH 281/283] [AutoDiff] Fix control flow AD when OME is skipped. (#28459) `-enable-strip-ownership-after-serialization` is now true by default, skipping ownership model eliminator after differentiation. This is problematic for control flow differentiation. `VJPEmitter` creates trampoline bbs, which create predecessor enum values and immediately branch to a destination bb. Trampoline bbs share the argument of their destination bb, which may have `@guaranteed` ownership kind. `@guaranteed` values need a single lifetime-ending use, e.g. an `end_borrow`. Previously, `VJPEmitter::trampolinedGuaranteedPhiArguments` tracked all `@guaranteed` trampoline/destination bb argument pairs and emitted an `end_borrow` for the trampoline bb argument after the cloned `end_borrow` for the destination bb argument. However, this does not work when ownership model eliminator is skipped: mandatory inlining invokes `mergeBasicBlocks` on VJPs, which causes the `end_borrow` instructions to be redundant. Now, `VJPEmitter` preemptively calls `mergeBasicBlocks`, which merges trampoline bbs with their destinations. `@guaranteed` bb arguments are merged and no longer duplicated, so there is no need to specially emit `end_borrow` for them. Consequences: - VJP functions now have simplified CFGs. - All VJP trampoline bbs are merged with their destinations. Consider rewriting the transform to generate the merged destination bbs in the first place. Resolves TF-990. --- .../Mandatory/Differentiation.cpp | 48 +++-------- test/AutoDiff/control_flow_sil.swift | 80 ++++++++++++++++--- 2 files changed, 78 insertions(+), 50 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 23a2356271f10..7d4e0a368847b 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -47,6 +47,7 @@ #include "swift/SILOptimizer/Analysis/LoopAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/LoopUtils.h" #include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "llvm/ADT/APSInt.h" @@ -3380,19 +3381,6 @@ class VJPEmitter final /// predecessor enum argument). SmallPtrSet remappedBasicBlocks; - /// A pair of a trampoline block phi argument and its corresponding - /// destination block phi argument. - struct TrampolinedArgumentPair { - SILPhiArgument *trampolineArgument; - SILPhiArgument *destinationArgument; - }; - /// An array that keeps track of all `@guaranteed` phi arguments in any - /// trampoline blocks we've added. Each of these arguments needs to have a - /// lifetime-ending use past its destination argument's lifetime-ending use, - /// so we keep track of these pairs of arguments and emit `end_borrow`s when - /// function cloning is finished. - SmallVector trampolinedGuaranteedPhiArguments; - bool errorOccurred = false; /// Mapping from original blocks to pullback values. Used to build pullback @@ -3771,18 +3759,9 @@ class VJPEmitter final auto *vjpSuccBB = getOpBasicBlock(origSuccBB); // Create the trampoline block. auto *trampolineBB = vjp->createBasicBlockBefore(vjpSuccBB); - for (auto *destArg : vjpSuccBB->getArguments().drop_back()) { - auto *trampolineArg = trampolineBB->createPhiArgument( + for (auto *destArg : vjpSuccBB->getArguments().drop_back()) + trampolineBB->createPhiArgument( destArg->getType(), destArg->getOwnershipKind()); - // Each `@guaranteed` trampoline argument needs to have a - // lifetime-ending use past its destination argument's lifetime-ending - // uses, so we keep track of these pairs of arguments in - // `trampolinedGuaranteedPhiArguments` and emit `end_borrow`s when - // function cloning is finished. - if (trampolineArg->getOwnershipKind() == ValueOwnershipKind::Guaranteed) - trampolinedGuaranteedPhiArguments.push_back( - {trampolineArg, cast(destArg)}); - } // Build predecessor enum value for successor block and branch to it. SILBuilder trampolineBuilder(trampolineBB); auto *succEnumVal = buildPredecessorEnumValue( @@ -7956,21 +7935,12 @@ bool VJPEmitter::run() { if (errorOccurred) return true; - // Each `@guaranteed` trampoline argument needs to have a lifetime-ending use - // past its destination argument's lifetime-ending uses (aka. `end_borrow`). - // `trampolinedGuaranteedPhiArguments` tracks all `@guaranteed` trampoline - // arguments. We emit an `end_borrow` immediately past each destination - // argument's lifetime-ending uses. - for (auto &trampolinedArgPair : trampolinedGuaranteedPhiArguments) { - for (auto *destArgUse : trampolinedArgPair.destinationArgument->getUses()) { - if (auto *lifetimeEnd = dyn_cast(destArgUse->getUser())) { - getBuilder().setInsertionPoint(lifetimeEnd->getParentBlock(), - std::next(lifetimeEnd->getIterator())); - getBuilder().emitEndBorrowOperation( - lifetimeEnd->getLoc(), trampolinedArgPair.trampolineArgument); - } - } - } + // Merge VJP basic blocks. This is significant for control flow + // differentiation: trampoline destination bbs are merged into trampoline bbs. + // NOTE(TF-990): Merging basic blocks ensures that `@guaranteed` trampoline + // bb arguments have a lifetime-ending `end_borrow` use, and is robust when + // `-enable-strip-ownership-after-serialization` is true. + mergeBasicBlocks(vjp); // Generate pullback code. PullbackEmitter PullbackEmitter(*this); diff --git a/test/AutoDiff/control_flow_sil.swift b/test/AutoDiff/control_flow_sil.swift index 3f18db2ccb379..e3edc17211a39 100644 --- a/test/AutoDiff/control_flow_sil.swift +++ b/test/AutoDiff/control_flow_sil.swift @@ -47,27 +47,21 @@ func cond(_ x: Float) -> Float { // CHECK-SIL-LABEL: sil hidden [ossa] @AD__cond__vjp_src_0_wrt_0 : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) { // CHECK-SIL: bb0([[INPUT_ARG:%.*]] : $Float): // CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = struct $_AD__cond_bb0__PB__src_0_wrt_0 () -// CHECK-SIL: cond_br {{%.*}}, bb1, bb3 +// CHECK-SIL: cond_br {{%.*}}, bb1, bb2 // CHECK-SIL: bb1: // CHECK-SIL: [[BB1_PRED:%.*]] = enum $_AD__cond_bb1__Pred__src_0_wrt_0, #_AD__cond_bb1__Pred__src_0_wrt_0.bb0!enumelt.1, [[BB0_PB_STRUCT]] -// CHECK-SIL: br bb2([[BB1_PRED]] : $_AD__cond_bb1__Pred__src_0_wrt_0) - -// CHECK-SIL: bb2([[BB1_PRED_ARG:%.*]] : $_AD__cond_bb1__Pred__src_0_wrt_0) // CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = struct $_AD__cond_bb1__PB__src_0_wrt_0 // CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__cond_bb3__Pred__src_0_wrt_0, #_AD__cond_bb3__Pred__src_0_wrt_0.bb1!enumelt.1, [[BB1_PB_STRUCT]] -// CHECK-SIL: br bb5({{.*}} : $Float, [[BB3_PRED_PRED1]] : $_AD__cond_bb3__Pred__src_0_wrt_0) +// CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED1]] : $_AD__cond_bb3__Pred__src_0_wrt_0) -// CHECK-SIL: bb3: +// CHECK-SIL: bb2: // CHECK-SIL: [[BB2_PRED:%.*]] = enum $_AD__cond_bb2__Pred__src_0_wrt_0, #_AD__cond_bb2__Pred__src_0_wrt_0.bb0!enumelt.1, [[BB0_PB_STRUCT]] -// CHECK-SIL: br bb4([[BB2_PRED]] : $_AD__cond_bb2__Pred__src_0_wrt_0) - -// CHECK-SIL: bb4([[BB2_PRED_ARG:%.*]] : $_AD__cond_bb2__Pred__src_0_wrt_0) // CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = struct $_AD__cond_bb2__PB__src_0_wrt_0 // CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__cond_bb3__Pred__src_0_wrt_0, #_AD__cond_bb3__Pred__src_0_wrt_0.bb2!enumelt.1, [[BB2_PB_STRUCT]] -// CHECK-SIL: br bb5({{.*}} : $Float, [[BB3_PRED_PRED2]] : $_AD__cond_bb3__Pred__src_0_wrt_0) +// CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED2]] : $_AD__cond_bb3__Pred__src_0_wrt_0) -// CHECK-SIL: bb5([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : @owned $_AD__cond_bb3__Pred__src_0_wrt_0) +// CHECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : @owned $_AD__cond_bb3__Pred__src_0_wrt_0) // CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = struct $_AD__cond_bb3__PB__src_0_wrt_0 // CHECK-SIL: [[PULLBACK_REF:%.*]] = function_ref @AD__cond__pullback_src_0_wrt_0 // CHECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PULLBACK_REF]]([[BB3_PB_STRUCT]]) @@ -145,6 +139,70 @@ func loop_generic(_ x: T) -> T { return result } +// Test `switch_enum`. + +enum Enum { + case a(Float) + case b(Float, Float) +} +@differentiable +@_silgen_name("enum_notactive") +func enum_notactive(_ e: Enum, _ x: Float) -> Float { + switch e { + case let .a(a): return x * a + case let .b(b1, b2): return x * b1 * b2 + } +} + +// ECK-SIL-LABEL: sil hidden [ossa] @AD__cond__vjp_src_0_wrt_0 : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) { +// ECK-SIL: bb0([[INPUT_ARG:%.*]] : $Float): +// ECK-SIL: [[BB0_PB_STRUCT:%.*]] = struct $_AD__cond_bb0__PB__src_0_wrt_0 () +// ECK-SIL: cond_br {{%.*}}, bb1, bb2 + +// ECK-SIL: bb1: +// ECK-SIL: [[BB1_PRED:%.*]] = enum $_AD__cond_bb1__Pred__src_0_wrt_0, #_AD__cond_bb1__Pred__src_0_wrt_0.bb0!enumelt.1, [[BB0_PB_STRUCT]] +// ECK-SIL: [[BB1_PB_STRUCT:%.*]] = struct $_AD__cond_bb1__PB__src_0_wrt_0 +// ECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__cond_bb3__Pred__src_0_wrt_0, #_AD__cond_bb3__Pred__src_0_wrt_0.bb1!enumelt.1, [[BB1_PB_STRUCT]] +// ECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED1]] : $_AD__cond_bb3__Pred__src_0_wrt_0) + +// ECK-SIL: bb2: +// ECK-SIL: [[BB2_PRED:%.*]] = enum $_AD__cond_bb2__Pred__src_0_wrt_0, #_AD__cond_bb2__Pred__src_0_wrt_0.bb0!enumelt.1, [[BB0_PB_STRUCT]] +// ECK-SIL: [[BB2_PB_STRUCT:%.*]] = struct $_AD__cond_bb2__PB__src_0_wrt_0 +// ECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__cond_bb3__Pred__src_0_wrt_0, #_AD__cond_bb3__Pred__src_0_wrt_0.bb2!enumelt.1, [[BB2_PB_STRUCT]] +// ECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED2]] : $_AD__cond_bb3__Pred__src_0_wrt_0) + +// ECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : @owned $_AD__cond_bb3__Pred__src_0_wrt_0) +// ECK-SIL: [[BB3_PB_STRUCT:%.*]] = struct $_AD__cond_bb3__PB__src_0_wrt_0 +// ECK-SIL: [[PULLBACK_REF:%.*]] = function_ref @AD__cond__pullback_src_0_wrt_0 +// ECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PULLBACK_REF]]([[BB3_PB_STRUCT]]) +// ECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[ORIG_RES]] : $Float, [[PB]] : $@callee_guaranteed (Float) -> Float) +// ECK-SIL: return [[VJP_RESULT]] + +// CHECK-SIL-LABEL: sil hidden [ossa] @AD__enum_notactive__vjp_src_0_wrt_1 : $@convention(thin) (Enum, Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) { +// CHECK-SIL: bb0([[ENUM_ARG:%.*]] : $Enum, [[X_ARG:%.*]] : $Float): +// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb0__PB__src_0_wrt_1 () +// CHECK-SIL: switch_enum [[ENUM_ARG]] : $Enum, case #Enum.a!enumelt.1: bb1, case #Enum.b!enumelt.1: bb2 + +// CHECK-SIL: bb1([[ENUM_A:%.*]] : $Float): +// CHECK-SIL: [[BB1_PRED_PRED0:%.*]] = enum $_AD__enum_notactive_bb1__Pred__src_0_wrt_1, #_AD__enum_notactive_bb1__Pred__src_0_wrt_1.bb0!enumelt.1, %4 : $_AD__enum_notactive_bb0__PB__src_0_wrt_1 +// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb1__PB__src_0_wrt_1 ({{.*}}) +// CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__enum_notactive_bb3__Pred__src_0_wrt_1, #_AD__enum_notactive_bb3__Pred__src_0_wrt_1.bb1!enumelt.1, [[BB1_PB_STRUCT]] : $_AD__enum_notactive_bb1__PB__src_0_wrt_1 +// CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED1]] : $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) + +// CHECK-SIL: bb2([[ENUM_B:%.*]] : $(Float, Float)): +// CHECK-SIL: [[BB2_PRED_PRED0:%.*]] = enum $_AD__enum_notactive_bb2__Pred__src_0_wrt_1, #_AD__enum_notactive_bb2__Pred__src_0_wrt_1.bb0!enumelt.1, %4 : $_AD__enum_notactive_bb0__PB__src_0_wrt_1 +// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb2__PB__src_0_wrt_1 ({{.*}}) +// CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__enum_notactive_bb3__Pred__src_0_wrt_1, #_AD__enum_notactive_bb3__Pred__src_0_wrt_1.bb2!enumelt.1, [[BB2_PB_STRUCT]] : $_AD__enum_notactive_bb2__PB__src_0_wrt_1 +// CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED2]] : $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) + +// CHECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : @owned $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) +// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb3__PB__src_0_wrt_1 +// CHECK-SIL: [[PULLBACK_REF:%.*]] = function_ref @AD__enum_notactive__pullback_src_0_wrt_1 +// CHECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PULLBACK_REF]]([[BB3_PB_STRUCT]]) +// CHECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[ORIG_RES]] : $Float, [[PB]] : $@callee_guaranteed (Float) -> Float) +// CHECK-SIL: return [[VJP_RESULT]] +// CHECK-SIL: } + // Test control flow + tuple buffer. // Verify that pullback buffers are not allocated for address projections. From 28be9cb19c284a7e544ed385a07d24ca84d769ab Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 23 Nov 2019 19:19:30 -0800 Subject: [PATCH 282/283] [TF] Re-enable `-enable-ownership-stripping-after-serialization`. (#28460) SR-11336 was recently resolved, making it possible to re-enable the flag. Re-enable previously disabled SILOptimizer tests. Resolves TF-799. --- cmake/modules/SwiftSource.cmake | 8 ++------ test/DebugInfo/LoadableByAddress.swift | 1 - test/SILOptimizer/OSLogPrototypeCompileTest.swift | 4 ---- test/SILOptimizer/constant_evaluable_subset_test.swift | 4 ---- test/SILOptimizer/constant_propagation_diagnostics.swift | 4 ---- test/SILOptimizer/definite_init_diagnostics.swift | 4 ---- test/SILOptimizer/definite_init_diagnostics_objc.swift | 4 ---- .../definite_init_failable_initializers_diagnostics.swift | 4 ---- test/SILOptimizer/diagnostic_constant_propagation.swift | 4 ---- .../diagnostic_constant_propagation_int_arch64.swift | 4 ---- test/SILOptimizer/exclusivity_static_diagnostics.swift | 4 ---- .../exclusivity_static_diagnostics_objc.swift | 4 ---- test/SILOptimizer/generalized_accessors.swift | 4 ---- test/SILOptimizer/infinite_recursion.swift | 4 ---- test/SILOptimizer/invalid_escaping_captures.swift | 4 ---- test/SILOptimizer/mandatory_inlining.swift | 4 ---- test/SILOptimizer/noescape_param_exclusivity.swift | 4 ---- test/SILOptimizer/pound_assert.swift | 4 ---- utils/update_checkout/update-checkout-config.json | 2 +- 19 files changed, 3 insertions(+), 72 deletions(-) diff --git a/cmake/modules/SwiftSource.cmake b/cmake/modules/SwiftSource.cmake index 941505c8d284b..770bbe53261ee 100644 --- a/cmake/modules/SwiftSource.cmake +++ b/cmake/modules/SwiftSource.cmake @@ -237,12 +237,8 @@ function(_compile_swift_files NOT "${SWIFTFILE_MODULE_NAME}" STREQUAL "DifferentiationUnittest") list(APPEND swift_flags "-enable-library-evolution") endif() - # FIXME(SR-11336): `-enable-ownership-stripping-after-serialization` for - # swiftCore causes test/AutoDiff/array.swift to crash, related to - # Array and key paths. - # Disabling the flag for swiftCore requires disabling the flag for all - # other standard library modules. - # list(APPEND swift_flags "-Xfrontend" "-enable-ownership-stripping-after-serialization") + # SWIFT_ENABLE_TENSORFLOW END + list(APPEND swift_flags "-Xfrontend" "-enable-ownership-stripping-after-serialization") endif() if(SWIFT_STDLIB_USE_NONATOMIC_RC) diff --git a/test/DebugInfo/LoadableByAddress.swift b/test/DebugInfo/LoadableByAddress.swift index 540e48c5e3ada..34697d63e5305 100644 --- a/test/DebugInfo/LoadableByAddress.swift +++ b/test/DebugInfo/LoadableByAddress.swift @@ -1,4 +1,3 @@ -// SWIFT_ENABLE_TENSORFLOW // RUN: %target-swift-frontend %s -module-name A -emit-ir -g -o - | %FileCheck %s // REQUIRES: CPU=x86_64 public struct Continuation
{ diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index 6f927366eacbe..d0678c8af61c2 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -7,10 +7,6 @@ // whether specific compile-time constants such as the format string, // the size of the byte buffer etc. are literals after the mandatory pipeline. -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - import OSLogPrototype if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { diff --git a/test/SILOptimizer/constant_evaluable_subset_test.swift b/test/SILOptimizer/constant_evaluable_subset_test.swift index 12132fef53743..8e1b6fa33b592 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test.swift @@ -18,10 +18,6 @@ // // RUN: %FileCheck %s < %t/error-output-mandatory -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - // Test Swift code snippets that are expected to be constant evaluable and those // that are not. If any of the test here fails, it indicates a change in the // output of SILGen or the mandatory passes that affects the constant diff --git a/test/SILOptimizer/constant_propagation_diagnostics.swift b/test/SILOptimizer/constant_propagation_diagnostics.swift index e62498a46f860..de433d7a5a44c 100644 --- a/test/SILOptimizer/constant_propagation_diagnostics.swift +++ b/test/SILOptimizer/constant_propagation_diagnostics.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -emit-sil -sdk %S/../SILGen/Inputs %s -o /dev/null -verify // RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -emit-sil -sdk %S/../SILGen/Inputs %s -o /dev/null -verify -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - // enum with raw values that are too big are not diagnosed enum EnumWithTooLargeElements : UInt8 { case negativeOne = -1 // expected-error 2 {{negative integer '-1' overflows when stored into unsigned type 'UInt8'}} diff --git a/test/SILOptimizer/definite_init_diagnostics.swift b/test/SILOptimizer/definite_init_diagnostics.swift index 8db86c0d440ba..a563afdd18a74 100644 --- a/test/SILOptimizer/definite_init_diagnostics.swift +++ b/test/SILOptimizer/definite_init_diagnostics.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify // RUN: %target-swift-frontend -emit-sil -enable-ownership-stripping-after-serialization -primary-file %s -o /dev/null -verify -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - import Swift func markUsed(_ t: T) {} diff --git a/test/SILOptimizer/definite_init_diagnostics_objc.swift b/test/SILOptimizer/definite_init_diagnostics_objc.swift index 02c53017b956d..b1ecf92999b6e 100644 --- a/test/SILOptimizer/definite_init_diagnostics_objc.swift +++ b/test/SILOptimizer/definite_init_diagnostics_objc.swift @@ -2,10 +2,6 @@ // RUN: %target-swift-frontend -emit-sil -sdk %S/../SILGen/Inputs %s -I %S/../SILGen/Inputs -enable-source-import -parse-stdlib -o /dev/null -verify -enable-ownership-stripping-after-serialization // REQUIRES: objc_interop -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - import Swift import gizmo diff --git a/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift b/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift index 573b9c57e391b..125458ed212f6 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -emit-sil -disable-objc-attr-requires-foundation-module -verify %s // RUN: %target-swift-frontend -emit-sil -disable-objc-attr-requires-foundation-module -verify %s -enable-ownership-stripping-after-serialization -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - // High-level tests that DI rejects certain invalid idioms for early // return from initializers. diff --git a/test/SILOptimizer/diagnostic_constant_propagation.swift b/test/SILOptimizer/diagnostic_constant_propagation.swift index 4e556f4f3b5b2..a0fd24cc56eee 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation.swift @@ -9,10 +9,6 @@ // References: , // -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable RUN lines after SR-11336 is fixed. -// XFAIL: * - import StdlibUnittest func testArithmeticOverflow() { diff --git a/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift b/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift index c55e1058508a2..38104f2455167 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift @@ -13,10 +13,6 @@ // // FIXME: A false negative that happens only in REPL -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - import StdlibUnittest func testArithmeticOverflow_Int_64bit() { diff --git a/test/SILOptimizer/exclusivity_static_diagnostics.swift b/test/SILOptimizer/exclusivity_static_diagnostics.swift index 0a2b177b7b1d9..96afff273e1a5 100644 --- a/test/SILOptimizer/exclusivity_static_diagnostics.swift +++ b/test/SILOptimizer/exclusivity_static_diagnostics.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify // RUN: %target-swift-frontend -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - import Swift func takesTwoInouts(_ p1: inout T, _ p2: inout T) { } diff --git a/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift b/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift index a896756bb3c0d..a2403ab2d67c9 100644 --- a/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift +++ b/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift @@ -2,10 +2,6 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/optional_closure_bridging.h -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // REQUIRES: objc_interop -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - import Foundation class SomeClass { diff --git a/test/SILOptimizer/generalized_accessors.swift b/test/SILOptimizer/generalized_accessors.swift index 36c7715bf61a0..a0faa6159d7a8 100644 --- a/test/SILOptimizer/generalized_accessors.swift +++ b/test/SILOptimizer/generalized_accessors.swift @@ -3,10 +3,6 @@ // // Tests for yield-once diagnostics emitted for generalized accessors. -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - struct TestNoYield { var computed: Int { _read { diff --git a/test/SILOptimizer/infinite_recursion.swift b/test/SILOptimizer/infinite_recursion.swift index cf07919694c3f..00c1bd94b0178 100644 --- a/test/SILOptimizer/infinite_recursion.swift +++ b/test/SILOptimizer/infinite_recursion.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - func a() { // expected-warning {{all paths through this function will call itself}} a() } diff --git a/test/SILOptimizer/invalid_escaping_captures.swift b/test/SILOptimizer/invalid_escaping_captures.swift index 96883ea5e4cda..ae9ceb780ce3b 100644 --- a/test/SILOptimizer/invalid_escaping_captures.swift +++ b/test/SILOptimizer/invalid_escaping_captures.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -emit-sil %s -verify // RUN: %target-swift-frontend -emit-sil %s -verify -enable-ownership-stripping-after-serialization -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - func takesEscaping(_: @escaping () -> ()) {} func takesNonEscaping(_ fn: () -> ()) { fn() } diff --git a/test/SILOptimizer/mandatory_inlining.swift b/test/SILOptimizer/mandatory_inlining.swift index 3409e42aad096..7867ce7baee4f 100644 --- a/test/SILOptimizer/mandatory_inlining.swift +++ b/test/SILOptimizer/mandatory_inlining.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -sil-verify-all -primary-file %s -emit-sil -o - -verify | %FileCheck %s // RUN: %target-swift-frontend -sil-verify-all -primary-file %s -emit-sil -o - -verify -enable-ownership-stripping-after-serialization -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - // These tests are deliberately shallow, because I do not want to depend on the // specifics of SIL generation, which might change for reasons unrelated to this // pass diff --git a/test/SILOptimizer/noescape_param_exclusivity.swift b/test/SILOptimizer/noescape_param_exclusivity.swift index 9357626c9d540..ad7833937f279 100644 --- a/test/SILOptimizer/noescape_param_exclusivity.swift +++ b/test/SILOptimizer/noescape_param_exclusivity.swift @@ -1,10 +1,6 @@ // RUN: %target-swift-frontend -emit-sil %s -verify // RUN: %target-swift-frontend -emit-sil %s -verify -enable-ownership-stripping-after-serialization -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable test after SR-11336 is fixed. -// XFAIL: * - func test0(a: (() -> ()) -> (), b: () -> ()) { a(b) // expected-error {{passing a non-escaping function parameter 'b' to a call to a non-escaping function parameter can allow re-entrant modification of a variable}} } diff --git a/test/SILOptimizer/pound_assert.swift b/test/SILOptimizer/pound_assert.swift index a8ea5137bdfd2..acccdaf0e32b5 100644 --- a/test/SILOptimizer/pound_assert.swift +++ b/test/SILOptimizer/pound_assert.swift @@ -2,10 +2,6 @@ // RUN: %target-swift-frontend -enable-experimental-static-assert -enable-ownership-stripping-after-serialization -emit-sil %s -verify // REQUIRES: asserts -// SWIFT_ENABLE_TENSORFLOW -// TODO(TF-799): Re-enable RUN line after SR-11336 is fixed. -// XFAIL: * - //===----------------------------------------------------------------------===// // Basic function calls and control flow //===----------------------------------------------------------------------===// diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 1ff695032879e..c81ae4ab69154 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -250,7 +250,7 @@ "tensorflow": { "aliases": ["tensorflow"], "repos": { - "llvm-project": "b3338d3f55b4eba6433be56c5e05f5c04f79dcd5", + "llvm-project": "6d186f86de6fe0e6da9ca1972386c064514703e6", "swift": "tensorflow", "cmark": "swift-DEVELOPMENT-SNAPSHOT-2019-11-11-a", "llbuild": "swift-DEVELOPMENT-SNAPSHOT-2019-11-11-a", From c6b30e25c6eb7a76c117fa26118039d42167e612 Mon Sep 17 00:00:00 2001 From: marcrasi Date: Tue, 26 Nov 2019 11:44:27 -0800 Subject: [PATCH 283/283] Update update-checkout-config.json --- utils/update_checkout/update-checkout-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index c81ae4ab69154..d2207bbc0f9f7 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -250,7 +250,7 @@ "tensorflow": { "aliases": ["tensorflow"], "repos": { - "llvm-project": "6d186f86de6fe0e6da9ca1972386c064514703e6", + "llvm-project": "13eb6eb7bacb8e7302e77527fe477130a5a0885a", "swift": "tensorflow", "cmark": "swift-DEVELOPMENT-SNAPSHOT-2019-11-11-a", "llbuild": "swift-DEVELOPMENT-SNAPSHOT-2019-11-11-a",