From 3be83216754a731d2a90561479a759f8fcb14351 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 25 Apr 2019 10:32:36 -0500 Subject: [PATCH 001/140] [stdlib] Fix the example in LazySequenceProtocol docs. I'm not sure this code ever worked, but this new version does. Also includes some style revisions to the surrounding text. --- stdlib/public/core/LazySequence.swift | 177 +++++++++++++------------- 1 file changed, 88 insertions(+), 89 deletions(-) diff --git a/stdlib/public/core/LazySequence.swift b/stdlib/public/core/LazySequence.swift index 502d65cca9859..d0f2030f92c83 100644 --- a/stdlib/public/core/LazySequence.swift +++ b/stdlib/public/core/LazySequence.swift @@ -10,123 +10,122 @@ // //===----------------------------------------------------------------------===// -/// A sequence on which normally-eager operations such as `map` and -/// `filter` are implemented lazily. +/// A sequence on which normally-eager sequence operations are implemented +/// lazily. /// /// Lazy sequences can be used to avoid needless storage allocation /// and computation, because they use an underlying sequence for -/// storage and compute their elements on demand. For example, +/// storage and compute their elements on demand. For example, `doubled` in +/// this code sample is a sequence containing the values `2`, `4`, and `6`. /// -/// [1, 2, 3].lazy.map { $0 * 2 } +/// let doubled = [1, 2, 3].lazy.map { $0 * 2 } /// -/// is a sequence containing { `2`, `4`, `6` }. Each time an element -/// of the lazy sequence is accessed, an element of the underlying -/// array is accessed and transformed by the closure. +/// Each time an element of the lazy sequence `doubled` is accessed, an element +/// of the underlying array is accessed and transformed by the closure. /// -/// Sequence operations taking closure arguments, such as `map` and -/// `filter`, are normally eager: they use the closure immediately and -/// return a new array. Using the `lazy` property gives the standard +/// Sequence operations that take closure arguments, such as `map(_:)` and +/// `filter(_:)`, are normally eager: They use the closure immediately and +/// return a new array. When you use the `lazy` property, you give the standard /// library explicit permission to store the closure and the sequence /// in the result, and defer computation until it is needed. /// -/// To add new lazy sequence operations, extend this protocol with -/// methods that return lazy wrappers that are themselves -/// `LazySequenceProtocol`s. For example, given an eager `scan` -/// method defined as follows +/// To add a new lazy sequence operation, extend this protocol with +/// a method that returns a lazy wrapper that itself conforms to +/// `LazySequenceProtocol`. For example, an eager `scan(_:_:)` +/// method is defined as follows: /// /// extension Sequence { -/// /// Returns an array containing the results of -/// /// -/// /// p.reduce(initial, nextPartialResult) -/// /// -/// /// for each prefix `p` of `self`, in order from shortest to -/// /// longest. For example: -/// /// -/// /// (1..<6).scan(0, +) // [0, 1, 3, 6, 10, 15] -/// /// -/// /// - Complexity: O(n) -/// func scan( -/// _ initial: ResultElement, -/// _ nextPartialResult: (ResultElement, Element) -> ResultElement -/// ) -> [ResultElement] { -/// var result = [initial] -/// for x in self { -/// result.append(nextPartialResult(result.last!, x)) +/// /// Returns an array containing the results of +/// /// +/// /// p.reduce(initial, nextPartialResult) +/// /// +/// /// for each prefix `p` of `self`, in order from shortest to +/// /// longest. For example: +/// /// +/// /// (1..<6).scan(0, +) // [0, 1, 3, 6, 10, 15] +/// /// +/// /// - Complexity: O(n) +/// func scan( +/// _ initial: ResultElement, +/// _ nextPartialResult: (ResultElement, Element) -> ResultElement +/// ) -> [ResultElement] { +/// var result = [initial] +/// for x in self { +/// result.append(nextPartialResult(result.last!, x)) +/// } +/// return result /// } -/// return result -/// } /// } /// -/// we can build a sequence that lazily computes the elements in the -/// result of `scan`: +/// You can build a sequence type that lazily computes the elements in the +/// result of a scan: /// -/// struct LazyScanIterator -/// : IteratorProtocol { -/// mutating func next() -> ResultElement? { -/// return nextElement.map { result in -/// nextElement = base.next().map { nextPartialResult(result, $0) } -/// return result -/// } -/// } -/// private var nextElement: ResultElement? // The next result of next(). -/// private var base: Base // The underlying iterator. -/// private let nextPartialResult: (ResultElement, Base.Element) -> ResultElement -/// } -/// /// struct LazyScanSequence -/// : LazySequenceProtocol // Chained operations on self are lazy, too +/// : LazySequenceProtocol /// { -/// func makeIterator() -> LazyScanIterator { -/// return LazyScanIterator( -/// nextElement: initial, base: base.makeIterator(), nextPartialResult) -/// } -/// private let initial: ResultElement -/// private let base: Base -/// private let nextPartialResult: -/// (ResultElement, Base.Element) -> ResultElement +/// let initial: ResultElement +/// let base: Base +/// let nextPartialResult: +/// (ResultElement, Base.Element) -> ResultElement +/// +/// struct Iterator: IteratorProtocol { +/// var base: Base.Iterator +/// var nextElement: ResultElement? +/// let nextPartialResult: +/// (ResultElement, Base.Element) -> ResultElement +/// +/// mutating func next() -> ResultElement? { +/// return nextElement.map { result in +/// nextElement = base.next().map { +/// nextPartialResult(result, $0) +/// } +/// return result +/// } +/// } +/// } +/// +/// func makeIterator() -> Iterator { +/// return Iterator( +/// base: base.makeIterator(), +/// nextElement: initial as ResultElement?, +/// nextPartialResult: nextPartialResult) +/// } /// } /// -/// and finally, we can give all lazy sequences a lazy `scan` method: +/// Finally, you can give all lazy sequences a lazy `scan(_:_:)` method: /// /// extension LazySequenceProtocol { -/// /// Returns a sequence containing the results of -/// /// -/// /// p.reduce(initial, nextPartialResult) -/// /// -/// /// for each prefix `p` of `self`, in order from shortest to -/// /// longest. For example: -/// /// -/// /// Array((1..<6).lazy.scan(0, +)) // [0, 1, 3, 6, 10, 15] -/// /// -/// /// - Complexity: O(1) -/// func scan( -/// _ initial: ResultElement, -/// _ nextPartialResult: (ResultElement, Element) -> ResultElement -/// ) -> LazyScanSequence { -/// return LazyScanSequence( -/// initial: initial, base: self, nextPartialResult) -/// } +/// func scan( +/// _ initial: ResultElement, +/// _ nextPartialResult: @escaping (ResultElement, Element) -> ResultElement +/// ) -> LazyScanSequence { +/// return LazyScanSequence( +/// initial: initial, base: self, nextPartialResult: nextPartialResult) +/// } /// } /// -/// - See also: `LazySequence` +/// With this type and extension method, you can call `.lazy.scan(_:_:)` on any +/// sequence to create a lazily computed scan. The resulting `LazyScanSequence` +/// is itself lazy, too, so further sequence operations also defer computation. /// -/// - Note: The explicit permission to implement further operations -/// lazily applies only in contexts where the sequence is statically -/// known to conform to `LazySequenceProtocol`. Thus, side-effects such -/// as the accumulation of `result` below are never unexpectedly -/// dropped or deferred: +/// The explicit permission to implement operations lazily applies +/// only in contexts where the sequence is statically known to conform to +/// `LazySequenceProtocol`. In the following example, because the extension +/// applies only to `Sequence`, side-effects such as the accumulation of +/// `result` are never unexpectedly dropped or deferred: /// -/// extension Sequence where Element == Int { +/// extension Sequence where Element == Int { /// func sum() -> Int { -/// var result = 0 -/// _ = self.map { result += $0 } -/// return result +/// var result = 0 +/// _ = self.map { result += $0 } +/// return result /// } -/// } +/// } /// -/// [We don't recommend that you use `map` this way, because it -/// creates and discards an array. `sum` would be better implemented -/// using `reduce`]. +/// Don't actually use `map` for this purpose, however, since it creates +/// and discards a resulting array. Instead, use `reduce` for summing +/// operations, or `forEach` or a `for`-`in` loop for operations with side +/// effects. public protocol LazySequenceProtocol : Sequence { /// A `Sequence` that can contain the same elements as this one, /// possibly with a simpler type. From ab5846c8a990d417ada862cc9d5d82cd6bb1f014 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 10 May 2019 03:34:44 +0200 Subject: [PATCH 002/140] Shorten parameter name in doc example. --- stdlib/public/core/LazySequence.swift | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/stdlib/public/core/LazySequence.swift b/stdlib/public/core/LazySequence.swift index d0f2030f92c83..db875570d230f 100644 --- a/stdlib/public/core/LazySequence.swift +++ b/stdlib/public/core/LazySequence.swift @@ -45,10 +45,10 @@ /// /// (1..<6).scan(0, +) // [0, 1, 3, 6, 10, 15] /// /// /// /// - Complexity: O(n) -/// func scan( -/// _ initial: ResultElement, -/// _ nextPartialResult: (ResultElement, Element) -> ResultElement -/// ) -> [ResultElement] { +/// func scan( +/// _ initial: Result, +/// _ nextPartialResult: (Result, Element) -> Result +/// ) -> [Result] { /// var result = [initial] /// for x in self { /// result.append(nextPartialResult(result.last!, x)) @@ -60,21 +60,21 @@ /// You can build a sequence type that lazily computes the elements in the /// result of a scan: /// -/// struct LazyScanSequence +/// struct LazyScanSequence /// : LazySequenceProtocol /// { -/// let initial: ResultElement +/// let initial: Result /// let base: Base /// let nextPartialResult: -/// (ResultElement, Base.Element) -> ResultElement +/// (Result, Base.Element) -> Result /// /// struct Iterator: IteratorProtocol { /// var base: Base.Iterator -/// var nextElement: ResultElement? +/// var nextElement: Result? /// let nextPartialResult: -/// (ResultElement, Base.Element) -> ResultElement +/// (Result, Base.Element) -> Result /// -/// mutating func next() -> ResultElement? { +/// mutating func next() -> Result? { /// return nextElement.map { result in /// nextElement = base.next().map { /// nextPartialResult(result, $0) @@ -87,7 +87,7 @@ /// func makeIterator() -> Iterator { /// return Iterator( /// base: base.makeIterator(), -/// nextElement: initial as ResultElement?, +/// nextElement: initial as Result?, /// nextPartialResult: nextPartialResult) /// } /// } @@ -95,11 +95,11 @@ /// Finally, you can give all lazy sequences a lazy `scan(_:_:)` method: /// /// extension LazySequenceProtocol { -/// func scan( -/// _ initial: ResultElement, -/// _ nextPartialResult: @escaping (ResultElement, Element) -> ResultElement -/// ) -> LazyScanSequence { -/// return LazyScanSequence( +/// func scan( +/// _ initial: Result, +/// _ nextPartialResult: @escaping (Result, Element) -> Result +/// ) -> LazyScanSequence { +/// return LazyScanSequence( /// initial: initial, base: self, nextPartialResult: nextPartialResult) /// } /// } From 1cc1e58520fe45dc8af6f27911855744ac81dcec Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 8 Sep 2019 22:16:30 +0100 Subject: [PATCH 003/140] [Sema] Don't look through CoerceExprs in markDirectCallee Doing so prevented the coercion of a function with argument labels to a user-written function type, as the latter cannot contain argument labels. This commit changes the behaviour such that the referenced function is considered to be unapplied, meaning it has its argument labels stripped, therefore allowing the coercion. Resolves SR-11429. --- lib/Sema/TypeCheckConstraints.cpp | 6 ------ .../suppress-argument-labels-in-types.swift | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 0d148bd95d98c..397de12a71603 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -897,12 +897,6 @@ namespace { continue; } - // Coercions can be used for disambiguation. - if (auto coerce = dyn_cast(callee)) { - callee = coerce->getSubExpr(); - continue; - } - // We're done. break; } diff --git a/test/Sema/suppress-argument-labels-in-types.swift b/test/Sema/suppress-argument-labels-in-types.swift index 8fae44bb743df..5684a8e2a4799 100644 --- a/test/Sema/suppress-argument-labels-in-types.swift +++ b/test/Sema/suppress-argument-labels-in-types.swift @@ -204,3 +204,21 @@ class C0 { // Check diagnostics changes. let _ = min(Int(3), Float(2.5)) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}} + +// SR-11429 +func testIntermediateCoercions() { + _ = (f1 as (Int, Int) -> Int)(a: 0, b: 1) // expected-error {{extraneous argument labels 'a:b:' in call}} + _ = (f1 as (Int, Int) -> Int)(0, 1) + + typealias Magic = T + _ = (f1 as Magic)(a: 0, b: 1) // expected-error {{extraneous argument labels 'a:b:' in call}} + _ = (f1 as Magic)(0, 1) + + _ = (f4 as (Int, Int) -> Int)(0, 0) + _ = (f4 as (Double, Double) -> Double)(0, 0) + + func iuoReturning() -> Int! {} + _ = (iuoReturning as () -> Int?)() + _ = (iuoReturning as Magic)() + _ = (iuoReturning as () -> Int)() // expected-error {{'() -> Int?' is not convertible to '() -> Int'; did you mean to use 'as!' to force downcast?}} +} From 06102a9ffa7f964cafbde8efb9b95bfe52d39265 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 11 Sep 2019 10:09:55 -0500 Subject: [PATCH 004/140] Minor additional revisions --- stdlib/public/core/LazySequence.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/LazySequence.swift b/stdlib/public/core/LazySequence.swift index 61a5cf8d7d7a8..6c76cdb173de6 100644 --- a/stdlib/public/core/LazySequence.swift +++ b/stdlib/public/core/LazySequence.swift @@ -29,6 +29,8 @@ /// library explicit permission to store the closure and the sequence /// in the result, and defer computation until it is needed. /// +/// ## Adding New Lazy Operations +/// /// To add a new lazy sequence operation, extend this protocol with /// a method that returns a lazy wrapper that itself conforms to /// `LazySequenceProtocol`. For example, an eager `scan(_:_:)` @@ -123,7 +125,7 @@ /// } /// /// Don't actually use `map` for this purpose, however, since it creates -/// and discards a resulting array. Instead, use `reduce` for summing +/// and discards the resulting array. Instead, use `reduce` for summing /// operations, or `forEach` or a `for`-`in` loop for operations with side /// effects. public protocol LazySequenceProtocol: Sequence { From 70ad47332ece273599642faedc3a8718a9289459 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 09:23:21 -0700 Subject: [PATCH 005/140] [SILOptimizer] ProjectionTree can vend leaf types. Added getAllLeafTypes to ProjectionTree. The new method vends, via an out paramter, a vector containing the types of all the leaves in a projection tree in the order that they appear. The method relies uses a new convenience on ProjectionTreeNode, isLeaf to include only the types of those nodes which are leaves. Excerpted from @gottesm's https://github.com/apple/swift/pull/16756. --- include/swift/SIL/Projection.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 675e32aa8b03a..217d34388e378 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -761,6 +761,8 @@ class ProjectionTreeNode { return NonProjUsers; }; + bool isLeaf() const { return ChildProjections.empty(); } + SILType getType() const { return NodeType; } bool isRoot() const { @@ -914,6 +916,24 @@ class ProjectionTree { return false; } + void getAllLeafTypes(llvm::SmallVectorImpl &outArray) const { + llvm::SmallVector worklist; + worklist.push_back(getRoot()); + + while (!worklist.empty()) { + auto *node = worklist.pop_back_val(); + // If we have a leaf node, add its type. + if (node->isLeaf()) { + outArray.push_back(node->getType()); + continue; + } + + // Otherwise, add the nodes children to the worklist. + transform(node->getChildProjections(), std::back_inserter(worklist), + [&](unsigned idx) { return getNode(idx); }); + } + } + void getLiveLeafTypes(llvm::SmallVectorImpl &OutArray) const { for (unsigned LeafIndex : LiveLeafIndices) { const ProjectionTreeNode *Node = getNode(LeafIndex); From 9bd6fec6970799ee419cff712005b81e7dfd74b4 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 09:32:16 -0700 Subject: [PATCH 006/140] [SILOptimizer] Projection tree can vend users. Added getUsers to ProjectionTree. The new method vands, via an out parameter, a set of all the users of all of the nodes in the projection tree that are themselves not in the projection tree by way of getNonProjUsers. Took this opportunity to tweak getNonProjUsers to vend a const ArrayRef rather than a SmallVector. Excerpted from @gottesm's https://github.com/apple/swift/pull/16756. --- include/swift/SIL/Projection.h | 10 ++++++---- lib/SIL/Projection.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 217d34388e378..8380fb11c14e7 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -757,9 +757,9 @@ class ProjectionTreeNode { llvm::Optional &getProjection() { return Proj; } - llvm::SmallVector getNonProjUsers() const { - return NonProjUsers; - }; + const ArrayRef getNonProjUsers() const { + return llvm::makeArrayRef(NonProjUsers); + } bool isLeaf() const { return ChildProjections.empty(); } @@ -960,7 +960,9 @@ class ProjectionTree { void replaceValueUsesWithLeafUses(SILBuilder &B, SILLocation Loc, llvm::SmallVectorImpl &Leafs); - + + void getUsers(SmallPtrSetImpl &users) const; + private: void createRoot(SILType BaseTy) { assert(ProjectionTreeNodes.empty() && diff --git a/lib/SIL/Projection.cpp b/lib/SIL/Projection.cpp index 7e935989476a0..f40d7905fa5b8 100644 --- a/lib/SIL/Projection.cpp +++ b/lib/SIL/Projection.cpp @@ -1514,3 +1514,11 @@ replaceValueUsesWithLeafUses(SILBuilder &Builder, SILLocation Loc, NewNodes.clear(); } } + +void ProjectionTree::getUsers(SmallPtrSetImpl &users) const { + for (auto *node : ProjectionTreeNodes) { + for (auto *op : node->getNonProjUsers()) { + users.insert(op->getUser()); + } + } +} From 27fe8493aa00e21544c10ea5e18223d4ac2a0a9e Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 09:40:36 -0700 Subject: [PATCH 007/140] [SILOptimizer] Epilogue matcher can find partial post dom argument releases. Added getPartiallyPostDomReleaseSet to ConsumedArgToEpilogueReleaseMatcher. Given an argument, the new method returns the array of releases of the argument if there is an array thereof and if the releases therein do not jointly post-dominate the argument. Excerpted from @gottesm's https://github.com/apple/swift/pull/16756. --- include/swift/SILOptimizer/Analysis/ARCAnalysis.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/swift/SILOptimizer/Analysis/ARCAnalysis.h b/include/swift/SILOptimizer/Analysis/ARCAnalysis.h index 0103eb7e7192b..d0fabe87592be 100644 --- a/include/swift/SILOptimizer/Analysis/ARCAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/ARCAnalysis.h @@ -332,6 +332,17 @@ class ConsumedArgToEpilogueReleaseMatcher { return completeList.getValue(); } + Optional> + getPartiallyPostDomReleaseSet(SILArgument *arg) const { + auto iter = ArgInstMap.find(arg); + if (iter == ArgInstMap.end()) + return None; + auto partialList = iter->second.getPartiallyPostDomReleases(); + if (!partialList) + return None; + return partialList; + } + ArrayRef getReleasesForArgument(SILValue value) const { auto *arg = dyn_cast(value); if (!arg) From 851d9f44d0d8b338340aa5afd1ae56f111a51e8c Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 10:02:04 -0700 Subject: [PATCH 008/140] [Gardening] Minor cleanup to Projection.h. Replaced some namespace qualified references to ArrayRef and Optional with the unqualified type. Reordered the includes per clang-format. Excerpted from @gottesm's https://github.com/apple/swift/pull/16756. --- include/swift/SIL/Projection.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 8380fb11c14e7..1e4324e07b29f 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -22,11 +22,12 @@ #ifndef SWIFT_SIL_PROJECTION_H #define SWIFT_SIL_PROJECTION_H +#include "swift/AST/TypeAlignments.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/PointerIntEnum.h" -#include "swift/AST/TypeAlignments.h" -#include "swift/SIL/SILValue.h" +#include "swift/Basic/STLExtras.h" #include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" #include "llvm/ADT/Hashing.h" @@ -751,18 +752,18 @@ class ProjectionTreeNode { ~ProjectionTreeNode() = default; ProjectionTreeNode(const ProjectionTreeNode &) = default; - llvm::ArrayRef getChildProjections() { - return llvm::makeArrayRef(ChildProjections); + bool isLeaf() const { return ChildProjections.empty(); } + + ArrayRef getChildProjections() const { + return llvm::makeArrayRef(ChildProjections); } - llvm::Optional &getProjection() { return Proj; } + Optional &getProjection() { return Proj; } const ArrayRef getNonProjUsers() const { return llvm::makeArrayRef(NonProjUsers); } - bool isLeaf() const { return ChildProjections.empty(); } - SILType getType() const { return NodeType; } bool isRoot() const { From 782d6cd15c7e702bf5ca481028c05a570c10e00b Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 10:04:12 -0700 Subject: [PATCH 009/140] [Gardening] Documented ArgumentDescriptor method. Added brief doc for FunctionSignatureOpts' ArgumentDescriptor's method canOptimizeLiveArg and tweaked the style to add braces around the body of a single line if clause. --- .../FunctionSignatureTransforms/FunctionSignatureOpts.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h index cf0404392ad0b..3c2a467639d93 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h @@ -111,9 +111,13 @@ struct ArgumentDescriptor { return Arg->hasConvention(P); } + /// Returns true if all function signature opt passes are able to process + /// this. bool canOptimizeLiveArg() const { - if (Arg->getType().isObject()) + if (Arg->getType().isObject()) { return true; + } + // @in arguments of generic types can be processed. if (Arg->getType().hasArchetype() && Arg->getType().isAddress() && From 003876158ca5f92d98e5dd12e583c68ddc437676 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 10:08:46 -0700 Subject: [PATCH 010/140] [Gardening] Made FSOEnableGenerics option static. The command-line option for sil-fso-enable-generics was previously visible outside the FunctionSignatureOpts translation unit. It is not any longer. --- .../FunctionSignatureTransforms/FunctionSignatureOpts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 1fd6a2630522b..f2194670ec92f 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -65,7 +65,7 @@ using ArgumentIndexMap = llvm::SmallDenseMap; //===----------------------------------------------------------------------===// /// Set to true to enable the support for partial specialization. -llvm::cl::opt +static llvm::cl::opt FSOEnableGenerics("sil-fso-enable-generics", llvm::cl::init(true), llvm::cl::desc("Support function signature optimization " "of generic functions")); From c99f12ff40010c3266f68e07ba3612f0538053a1 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 20 Sep 2019 10:12:02 -0700 Subject: [PATCH 011/140] [SILOptimizer] Added option to force FSO for uncalled functions. The new flag -sil-fso-optimize-if-not-called forced function signature optimization to run even on functions which are not called. Doing so is helpful for tests to alleviate the burden of writing code to actually call a function in whose function signature optimization we are interested. --- .../FunctionSignatureOpts.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index f2194670ec92f..d41f99106ddc1 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -70,6 +70,12 @@ static llvm::cl::opt llvm::cl::desc("Support function signature optimization " "of generic functions")); +static llvm::cl::opt + FSOOptimizeIfNotCalled("sil-fso-optimize-if-not-called", + llvm::cl::init(false), + llvm::cl::desc("Optimize even if a function isn't " + "called. For testing only!")); + static bool isSpecializableRepresentation(SILFunctionTypeRepresentation Rep, bool OptForPartialApply) { switch (Rep) { @@ -632,6 +638,9 @@ bool FunctionSignatureTransform::run(bool hasCaller) { return false; } + // If we are asked to assume a caller for testing purposes, set the flag. + hasCaller |= FSOOptimizeIfNotCalled; + if (!hasCaller && (F->getDynamicallyReplacedFunction() || canBeCalledIndirectly(F->getRepresentation()))) { LLVM_DEBUG(llvm::dbgs() << " function has no caller -> abort\n"); From 4412a795bf3d98d4fc846f381376458c04e49734 Mon Sep 17 00:00:00 2001 From: Ewa Matejska <15254638+ematejska@users.noreply.github.com> Date: Sun, 22 Sep 2019 22:45:23 -0700 Subject: [PATCH 012/140] Using %sil-opt is inconsistent with the rest of the tests in this directory which use %target-sil-opt. This causes problems if some lit systems don't recognize %sil-opt and only recognize %target-sil-opt. Updating for consistency. --- test/SIL/Parser/ossa_needs_ownership_on_args.sil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SIL/Parser/ossa_needs_ownership_on_args.sil b/test/SIL/Parser/ossa_needs_ownership_on_args.sil index 5eab98574e1d0..da77193b17faf 100644 --- a/test/SIL/Parser/ossa_needs_ownership_on_args.sil +++ b/test/SIL/Parser/ossa_needs_ownership_on_args.sil @@ -1,4 +1,4 @@ -// RUN: not %sil-opt -verify %s +// RUN: not %target-sil-opt -verify %s // Make sure that we error if ossa functions do not parse unless the argument // has the proper ownership specifier on it. From 4203c2f9f3fd592c2c927b65d99fbcff46d7f237 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 17 Sep 2019 15:17:18 -0700 Subject: [PATCH 013/140] [Diagnostics] Store synthesized arguments in `missing arguments` diagnostic Since we are about to start diagnosing more than just closures, we need information about what synthesized arguments look like. --- lib/Sema/CSDiagnostics.cpp | 9 +++++---- lib/Sema/CSDiagnostics.h | 8 +++++--- lib/Sema/CSFix.cpp | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 69f82906ed587..fe5d50f153a69 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3598,14 +3598,15 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) { if (!funcType) return false; - auto diff = funcType->getNumParams() - NumSynthesized; + unsigned numSynthesized = SynthesizedArgs.size(); + auto diff = funcType->getNumParams() - numSynthesized; // If the closure didn't specify any arguments and it is in a context that // needs some, produce a fixit to turn "{...}" into "{ _,_ in ...}". if (diff == 0) { auto diag = emitDiagnostic(closure->getStartLoc(), - diag::closure_argument_list_missing, NumSynthesized); + diag::closure_argument_list_missing, numSynthesized); std::string fixText; // Let's provide fixits for up to 10 args. if (funcType->getNumParams() <= 10) { @@ -3649,9 +3650,9 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) { llvm::raw_svector_ostream OS(fixIt); OS << ","; - for (unsigned i = 0; i != NumSynthesized; ++i) { + for (unsigned i = 0; i != numSynthesized; ++i) { OS << ((onlyAnonymousParams) ? "_" : "<#arg#>"); - OS << ((i == NumSynthesized - 1) ? " " : ","); + OS << ((i == numSynthesized - 1) ? " " : ","); } diag.fixItInsertAfter(params->getEndLoc(), OS.str()); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index a0a4fdc180786..7c1192c80e330 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1186,12 +1186,14 @@ class ImplicitInitOnNonConstMetatypeFailure final class MissingArgumentsFailure final : public FailureDiagnostic { using Param = AnyFunctionType::Param; - unsigned NumSynthesized; + SmallVector SynthesizedArgs; public: MissingArgumentsFailure(Expr *root, ConstraintSystem &cs, - unsigned numSynthesized, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), NumSynthesized(numSynthesized) {} + ArrayRef synthesizedArgs, + ConstraintLocator *locator) + : FailureDiagnostic(root, cs, locator), + SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) {} bool diagnoseAsError() override; diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 625f4b9a79ff7..0ce51f4dab936 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -478,7 +478,8 @@ AllowClosureParamDestructuring::create(ConstraintSystem &cs, bool AddMissingArguments::diagnose(Expr *root, bool asNote) const { auto &cs = getConstraintSystem(); - MissingArgumentsFailure failure(root, cs, NumSynthesized, getLocator()); + MissingArgumentsFailure failure(root, cs, getSynthesizedArguments(), + getLocator()); return failure.diagnose(asNote); } From b15ef15a60c74f37bdf9098aa5fd79eb9ce575b8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 18 Sep 2019 16:49:58 -0700 Subject: [PATCH 014/140] [ConstraintSystem] Change `missingArgument` callback to produce an index of synthesized argument In diagnostic mode allow argument matcher to synthesize new arguments, which makes it much easier to diagnose problems related to missing arguments. --- lib/Sema/CSDiag.cpp | 3 ++- lib/Sema/CSSimplify.cpp | 5 ++++- lib/Sema/CalleeCandidateInfo.cpp | 3 ++- lib/Sema/ConstraintSystem.h | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 457bbc238b618..eed992a081b2a 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -2211,7 +2211,7 @@ class ArgumentMatcher : public MatchCallArgumentListener { Diagnosed = true; } - void missingArgument(unsigned missingParamIdx) override { + Optional missingArgument(unsigned missingParamIdx) override { auto ¶m = Parameters[missingParamIdx]; Identifier name = param.getLabel(); @@ -2341,6 +2341,7 @@ class ArgumentMatcher : public MatchCallArgumentListener { candidate.getDecl()->getFullName()); Diagnosed = true; + return None; } bool isPropertyWrapperImplicitInit() { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 45f2b858d3501..a76e073ee29d6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -36,7 +36,10 @@ MatchCallArgumentListener::~MatchCallArgumentListener() { } void MatchCallArgumentListener::extraArgument(unsigned argIdx) { } -void MatchCallArgumentListener::missingArgument(unsigned paramIdx) { } +Optional +MatchCallArgumentListener::missingArgument(unsigned paramIdx) { + return None; +} bool MatchCallArgumentListener::missingLabel(unsigned paramIdx) { return true; } bool MatchCallArgumentListener::extraneousLabel(unsigned paramIdx) { diff --git a/lib/Sema/CalleeCandidateInfo.cpp b/lib/Sema/CalleeCandidateInfo.cpp index 7a2f64ea75f73..1dc25bf3f0fde 100644 --- a/lib/Sema/CalleeCandidateInfo.cpp +++ b/lib/Sema/CalleeCandidateInfo.cpp @@ -284,8 +284,9 @@ CalleeCandidateInfo::ClosenessResultTy CalleeCandidateInfo::evaluateCloseness( void extraArgument(unsigned argIdx) override { result = CC_ArgumentCountMismatch; } - void missingArgument(unsigned paramIdx) override { + Optional missingArgument(unsigned paramIdx) override { result = CC_ArgumentCountMismatch; + return None; } bool missingLabel(unsigned paramIdx) override { result = CC_ArgumentLabelMismatch; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index f842c2998b4c0..c63182e768652 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3858,7 +3858,7 @@ class MatchCallArgumentListener { /// indices. /// /// \param paramIdx The index of the parameter that is missing an argument. - virtual void missingArgument(unsigned paramIdx); + virtual Optional missingArgument(unsigned paramIdx); /// Indicate that there was no label given when one was expected by parameter. /// From 65c03d5f065ae7a376d11a2f5bb4b2bb00372524 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 18 Sep 2019 16:55:48 -0700 Subject: [PATCH 015/140] [ConstraintSystem] Allow solver to synthesize missing arguments in diagnostic mode --- lib/Sema/CSSimplify.cpp | 95 +++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a76e073ee29d6..523f0f8c26bdf 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -590,7 +590,14 @@ matchCallArguments(ArrayRef args, if (paramInfo.hasDefaultArgument(paramIdx)) continue; - listener.missingArgument(paramIdx); + if (potentiallyOutOfOrder) + return true; + + if (auto newArgIdx = listener.missingArgument(paramIdx)) { + parameterBindings[paramIdx].push_back(*newArgIdx); + continue; + } + return true; } } @@ -790,20 +797,58 @@ getCalleeDeclAndArgs(ConstraintSystem &cs, class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintSystem &CS; - ArrayRef Arguments; + SmallVectorImpl &Arguments; ArrayRef Parameters; SmallVectorImpl &Bindings; ConstraintLocatorBuilder Locator; + unsigned NumSynthesizedArgs = 0; + public: ArgumentFailureTracker(ConstraintSystem &cs, - ArrayRef args, + SmallVectorImpl &args, ArrayRef params, SmallVectorImpl &bindings, ConstraintLocatorBuilder locator) : CS(cs), Arguments(args), Parameters(params), Bindings(bindings), Locator(locator) {} + ~ArgumentFailureTracker() override { + if (NumSynthesizedArgs > 0) { + ArrayRef argRef(Arguments); + + auto *fix = + AddMissingArguments::create(CS, argRef.take_back(NumSynthesizedArgs), + CS.getConstraintLocator(Locator)); + + // Not having an argument is the same impact as having a type mismatch. + (void)CS.recordFix(fix, /*impact=*/NumSynthesizedArgs * 2); + } + } + + Optional missingArgument(unsigned paramIdx) override { + if (!CS.shouldAttemptFixes()) + return None; + + const auto ¶m = Parameters[paramIdx]; + + unsigned newArgIdx = Arguments.size(); + auto argLoc = + Locator + .withPathElement( + LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx)) + .withPathElement(LocatorPathElt::SynthesizedArgument(newArgIdx)); + + auto *argType = CS.createTypeVariable( + CS.getConstraintLocator(argLoc), + TVO_CanBindToInOut | TVO_CanBindToLValue | TVO_CanBindToNoEscape); + + Arguments.push_back(param.withType(argType)); + ++NumSynthesizedArgs; + + return newArgIdx; + } + bool missingLabel(unsigned paramIndex) override { return !CS.shouldAttemptFixes(); } @@ -883,19 +928,19 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( // Match up the call arguments to the parameters. SmallVector parameterBindings; - ArgumentFailureTracker listener(cs, argsWithLabels, params, parameterBindings, - locator); - if (constraints::matchCallArguments(argsWithLabels, params, - paramInfo, - hasTrailingClosure, - cs.shouldAttemptFixes(), listener, - parameterBindings)) { - if (!cs.shouldAttemptFixes()) - return cs.getTypeMatchFailure(locator); + { + ArgumentFailureTracker listener(cs, argsWithLabels, params, + parameterBindings, locator); + if (constraints::matchCallArguments( + argsWithLabels, params, paramInfo, hasTrailingClosure, + cs.shouldAttemptFixes(), listener, parameterBindings)) { + if (!cs.shouldAttemptFixes()) + return cs.getTypeMatchFailure(locator); - if (AllowTupleSplatForSingleParameter::attempt(cs, argsWithLabels, params, - parameterBindings, locator)) - return cs.getTypeMatchFailure(locator); + if (AllowTupleSplatForSingleParameter::attempt( + cs, argsWithLabels, params, parameterBindings, locator)) + return cs.getTypeMatchFailure(locator); + } } // If this application is part of an operator, then we allow an implicit @@ -904,6 +949,15 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( auto *anchor = locator.getAnchor(); assert(anchor && "locator without anchor expression?"); + auto isSynthesizedArgument = [](const AnyFunctionType::Param &arg) -> bool { + if (auto *typeVar = arg.getPlainType()->getAs()) { + auto *locator = typeVar->getImpl().getLocator(); + return locator->isLastElement(ConstraintLocator::SynthesizedArgument); + } + + return false; + }; + for (unsigned paramIdx = 0, numParams = parameterBindings.size(); paramIdx != numParams; ++paramIdx){ // Skip unfulfilled parameters. There's nothing to do for them. @@ -918,10 +972,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( for (auto argIdx : parameterBindings[paramIdx]) { auto loc = locator.withPathElement(LocatorPathElt::ApplyArgToParam( argIdx, paramIdx, param.getParameterFlags())); - auto argTy = argsWithLabels[argIdx].getOldType(); + const auto &argument = argsWithLabels[argIdx]; + auto argTy = argument.getOldType(); bool matchingAutoClosureResult = param.isAutoClosure(); - if (param.isAutoClosure()) { + if (param.isAutoClosure() && !isSynthesizedArgument(argument)) { auto &ctx = cs.getASTContext(); auto *fnType = paramTy->castTo(); auto *argExpr = getArgumentExpr(locator.getAnchor(), argIdx); @@ -960,7 +1015,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( // If argument comes for declaration it should loose // `@autoclosure` flag, because in context it's used // as a function type represented by autoclosure. - assert(!argsWithLabels[argIdx].isAutoClosure()); + // + // Special case here are synthesized arguments because + // they mirror parameter flags to ease diagnosis. + assert(!argsWithLabels[argIdx].isAutoClosure() || + isSynthesizedArgument(argument)); cs.addConstraint( subKind, argTy, paramTy, From e30612e8cd257f9a49d200bfa3b7e26999a57286 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 18 Sep 2019 17:03:11 -0700 Subject: [PATCH 016/140] [Diagnostics] Diagnose missing argument(s) in argument positions Diagnose situation when parameter type expects N arguments but argument has `M` where `M` < `N`, e.g.: ```swift func foo(_: (Int) -> Void) {} func bar() -> Void {} foo(bar) // expected 1 argument, got 0 ``` --- lib/Sema/CSDiagnostics.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index fe5d50f153a69..ab9200780f768 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3581,6 +3581,25 @@ bool MissingArgumentsFailure::diagnoseAsError() { if (auto *closure = dyn_cast(anchor)) return diagnoseClosure(closure); + // This is a situation where function type is passed as an argument + // to a function type parameter and their argument arity is different. + // + // ``` + // func foo(_: (Int) -> Void) {} + // func bar() {} + // + // foo(bar) // `() -> Void` vs. `(Int) -> Void` + // ``` + if (locator->isLastElement(ConstraintLocator::ApplyArgToParam)) { + auto info = *getFunctionArgApplyInfo(locator); + + auto *argExpr = info.getArgExpr(); + emitDiagnostic(argExpr->getLoc(), diag::cannot_convert_argument_value, + info.getArgType(), info.getParamType()); + // TODO: It would be great so somehow point out which arguments are missing. + return true; + } + return false; } From 881f521958be6f19e1d3ef505765f77840b026b4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 18 Sep 2019 17:08:30 -0700 Subject: [PATCH 017/140] [Diagnostics] Diagnose missing arguments in relation to contextual type Function type has fewer arguments than expected by context: ```swift func foo() {} let _: (Int) -> Void = foo // expected 1 argument, got 0 ``` --- lib/Sema/CSDiagnostics.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ab9200780f768..a9591c5799c2c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3508,6 +3508,20 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { return true; } + // Function type has fewer arguments than expected by context: + // + // ``` + // func foo() {} + // let _: (Int) -> Void = foo + // ``` + if (locator->isLastElement(ConstraintLocator::ContextualType)) { + auto &cs = getConstraintSystem(); + emitDiagnostic(anchor->getLoc(), diag::cannot_convert_initializer_value, + getType(anchor), resolveType(cs.getContextualType())); + // TODO: It would be great so somehow point out which arguments are missing. + return true; + } + return false; } From b8528cd5750a95779411863747f4babbc8072164 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 19 Sep 2019 11:00:48 -0700 Subject: [PATCH 018/140] [ConstraintSystem] Short-circuit matching if missing arguments have been synthesized --- lib/Sema/CSSimplify.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 523f0f8c26bdf..39f96c3737d03 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -575,6 +575,7 @@ matchCallArguments(ArrayRef args, // If we have any unfulfilled parameters, check them now. if (haveUnfulfilledParams) { + bool hasSynthesizedArgs = false; for (paramIdx = 0; paramIdx != numParams; ++paramIdx) { // If we have a binding for this parameter, we're done. if (!parameterBindings[paramIdx].empty()) @@ -590,16 +591,19 @@ matchCallArguments(ArrayRef args, if (paramInfo.hasDefaultArgument(paramIdx)) continue; - if (potentiallyOutOfOrder) - return true; - if (auto newArgIdx = listener.missingArgument(paramIdx)) { parameterBindings[paramIdx].push_back(*newArgIdx); + hasSynthesizedArgs = true; continue; } return true; } + + // If all of the missing arguments have been synthesized, + // let's stop since we have found the problem. + if (hasSynthesizedArgs) + return false; } // If any arguments were provided out-of-order, check whether we have From ee8c78eef532c3802301fdd01142c5aa66e65834 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 19 Sep 2019 15:04:39 -0700 Subject: [PATCH 019/140] [Diagnostics] Tailored diagnostic when single tuple used instead of N distinct arguments Diagnose cases when instead of multiple distinct arguments call got a single tuple argument with expected arity/types: ```swift func foo(_: Int, _: Int) {} foo((0, 1)) // expected 2 arguments, got 1 tuple with 2 elements ``` --- include/swift/AST/DiagnosticsSema.def | 4 + lib/Sema/CSDiagnostics.cpp | 55 ++++++- lib/Sema/CSDiagnostics.h | 4 + lib/Sema/CSSimplify.cpp | 34 +++++ lib/Sema/ConstraintSystem.cpp | 11 +- test/Constraints/tuple_arguments.swift | 196 ++++++++++++------------- test/Misc/misc_diagnostics.swift | 4 +- 7 files changed, 202 insertions(+), 106 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ed0e5f7ff7faf..3f33afd5a11d5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3493,6 +3493,10 @@ ERROR(single_tuple_parameter_mismatch_normal,none, (DescriptiveDeclKind, DeclBaseName, Type, StringRef)) 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, + "%0 %select{%1 |}2expects %3 separate arguments" + "%select{|; remove extra parentheses to change tuple into separate arguments}4", + (DescriptiveDeclKind, DeclName, bool, unsigned, bool)) ERROR(enum_element_pattern_assoc_values_mismatch,none, "pattern with associated values does not match enum case %0", diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index a9591c5799c2c..73c65b059033e 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3522,6 +3522,9 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { return true; } + if (diagnoseInvalidTupleDestructuring()) + return true; + return false; } @@ -3585,7 +3588,8 @@ bool MissingArgumentsFailure::diagnoseAsError() { // TODO: Currently this is only intended to diagnose contextual failures. if (path.empty() || !(path.back().getKind() == ConstraintLocator::ApplyArgToParam || - path.back().getKind() == ConstraintLocator::ContextualType)) + path.back().getKind() == ConstraintLocator::ContextualType || + path.back().getKind() == ConstraintLocator::ApplyArgument)) return false; auto *anchor = getAnchor(); @@ -3694,6 +3698,55 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) { return true; } +bool MissingArgumentsFailure::diagnoseInvalidTupleDestructuring() const { + auto *locator = getLocator(); + if (!locator->isLastElement(ConstraintLocator::ApplyArgument)) + return false; + + if (SynthesizedArgs.size() < 2) + return false; + + auto *anchor = getAnchor(); + + Expr *argExpr = nullptr; + // Something like `foo(x: (1, 2))` + if (auto *TE = dyn_cast(anchor)) { + if (TE->getNumElements() == 1) + argExpr = TE->getElement(0); + } else { // or `foo((1, 2))` + argExpr = cast(anchor)->getSubExpr(); + } + + if (!(argExpr && getType(argExpr)->getRValueType()->is())) + return false; + + auto selectedOverload = getChoiceFor(locator); + if (!selectedOverload) + return false; + + auto *decl = selectedOverload->choice.getDeclOrNull(); + if (!decl) + return false; + + auto name = decl->getBaseName(); + auto diagnostic = + emitDiagnostic(anchor->getLoc(), + diag::cannot_convert_single_tuple_into_multiple_arguments, + decl->getDescriptiveKind(), name, name.isSpecial(), + SynthesizedArgs.size(), isa(argExpr)); + + // If argument is a literal tuple, let's suggest removal of parentheses. + if (auto *TE = dyn_cast(argExpr)) { + diagnostic.fixItRemove(TE->getLParenLoc()).fixItRemove(TE->getRParenLoc()); + } + + diagnostic.flush(); + + // Add a note which points to the overload choice location. + emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName()); + return true; +} + bool ClosureParamDestructuringFailure::diagnoseAsError() { auto *closure = cast(getAnchor()); auto params = closure->getParameters(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 7c1192c80e330..d9c1a9958eef4 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1201,6 +1201,10 @@ class MissingArgumentsFailure final : public FailureDiagnostic { /// If missing arguments come from a closure, /// let's produce tailored diagnostics. bool diagnoseClosure(ClosureExpr *closure); + + /// Diagnose cases when instead of multiple distinct arguments + /// call got a single tuple argument with expected arity/types. + bool diagnoseInvalidTupleDestructuring() const; }; class OutOfOrderArgumentFailure final : public FailureDiagnostic { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 39f96c3737d03..84fcfcb812087 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -930,6 +930,40 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( argsWithLabels.append(args.begin(), args.end()); AnyFunctionType::relabelParams(argsWithLabels, argLabels); + // Special case when a single tuple argument if used + // instead of N distinct arguments e.g.: + // + // func foo(_ x: Int, _ y: Int) {} + // foo((1, 2)) // expected 2 arguments, got a single tuple with 2 elements. + if (cs.shouldAttemptFixes() && argsWithLabels.size() == 1 && + llvm::count_if(indices(params), [&](unsigned paramIdx) { + return !paramInfo.hasDefaultArgument(paramIdx); + }) > 1) { + const auto &arg = argsWithLabels.front(); + auto argTuple = arg.getPlainType()->getRValueType()->getAs(); + // Don't explode a tuple in cases where first parameter is a tuple as + // well. That is a regular "missing argument case" even if their arity + // is different e.g. + // + // func foo(_: (Int, Int), _: Int) {} + // foo((1, 2)) // call is missing an argument for parameter #1 + if (argTuple && argTuple->getNumElements() == params.size() && + !params.front().getPlainType()->is()) { + argsWithLabels.pop_back(); + // Let's make sure that labels associated with tuple elements + // line up with what is expected by argument list. + for (const auto &arg : argTuple->getElements()) { + argsWithLabels.push_back( + AnyFunctionType::Param(arg.getType(), arg.getName())); + } + + (void)cs.recordFix( + AddMissingArguments::create(cs, argsWithLabels, + cs.getConstraintLocator(locator)), + /*impact=*/argsWithLabels.size() * 2); + } + } + // Match up the call arguments to the parameters. SmallVector parameterBindings; { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index d0db92464bcac..d3646dfedb139 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2913,11 +2913,12 @@ void constraints::simplifyLocator(Expr *&anchor, // Extract subexpression in parentheses. if (auto parenExpr = dyn_cast(anchor)) { - assert(elt.getArgIdx() == 0); - - anchor = parenExpr->getSubExpr(); - path = path.slice(1); - continue; + // This simplication request could be for a synthesized argument. + if (elt.getArgIdx() == 0) { + anchor = parenExpr->getSubExpr(); + path = path.slice(1); + continue; + } } break; } diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index 765018577cdf8..5bd8ebe144bba 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -18,7 +18,7 @@ do { // expected-error@-1 {{cannot convert value of type '(x: Int)' to expected argument type 'Int'}} concreteTwo(3, 4) - concreteTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + concreteTwo((3, 4)) // expected-error {{global function 'concreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} concreteTuple(3, 4) // expected-error {{global function 'concreteTuple' expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} concreteTuple((3, 4)) @@ -35,8 +35,8 @@ do { concrete(c) concreteTwo(a, b) - concreteTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - concreteTwo(d) // expected-error {{missing argument for parameter #2 in call}} + concreteTwo((a, b)) // expected-error {{global function 'concreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} + concreteTwo(d) // expected-error {{global function 'concreteTwo' expects 2 separate arguments}} concreteTuple(a, b) // expected-error {{global function 'concreteTuple' expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} concreteTuple((a, b)) @@ -54,8 +54,8 @@ do { concrete(c) concreteTwo(a, b) - concreteTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - concreteTwo(d) // expected-error {{missing argument for parameter #2 in call}} + concreteTwo((a, b)) // expected-error {{global function 'concreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} + concreteTwo(d) // expected-error {{global function 'concreteTwo' expects 2 separate arguments}} concreteTuple(a, b) // expected-error {{global function 'concreteTuple' expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} concreteTuple((a, b)) @@ -79,7 +79,7 @@ do { genericLabeled(x: (3, 4)) genericTwo(3, 4) - genericTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + genericTwo((3, 4)) // expected-error {{global function 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{14-15=}} {{19-20=}} genericTuple(3, 4) // expected-error {{global function 'genericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{16-16=(}} {{20-20=)}} genericTuple((3, 4)) @@ -99,8 +99,8 @@ do { generic(d) genericTwo(a, b) - genericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - genericTwo(d) // expected-error {{missing argument for parameter #2 in call}} + genericTwo((a, b)) // expected-error {{global function 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{14-15=}} {{19-20=}} + genericTwo(d) // expected-error {{global function 'genericTwo' expects 2 separate arguments}} genericTuple(a, b) // expected-error {{global function 'genericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{16-16=(}} {{20-20=)}} genericTuple((a, b)) @@ -121,8 +121,8 @@ do { generic(d) genericTwo(a, b) - genericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - genericTwo(d) // expected-error {{missing argument for parameter #2 in call}} + genericTwo((a, b)) // expected-error {{global function 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{14-15=}} {{19-20=}} + genericTwo(d) // expected-error {{global function 'genericTwo' expects 2 separate arguments}} genericTuple(a, b) // expected-error {{global function 'genericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{16-16=(}} {{20-20=)}} genericTuple((a, b)) @@ -138,7 +138,7 @@ do { function((3)) functionTwo(3, 4) - functionTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + functionTwo((3, 4)) // expected-error {{var 'functionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} functionTuple(3, 4) // expected-error {{var 'functionTuple' expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} functionTuple((3, 4)) @@ -155,8 +155,8 @@ do { function(c) functionTwo(a, b) - functionTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - functionTwo(d) // expected-error {{missing argument for parameter #2 in call}} + functionTwo((a, b)) // expected-error {{var 'functionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} + functionTwo(d) // expected-error {{var 'functionTwo' expects 2 separate arguments}} functionTuple(a, b) // expected-error {{var 'functionTuple' expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} functionTuple((a, b)) @@ -174,8 +174,8 @@ do { function(c) functionTwo(a, b) - functionTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - functionTwo(d) // expected-error {{missing argument for parameter #2 in call}} + functionTwo((a, b)) // expected-error {{var 'functionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} + functionTwo(d) // expected-error {{var 'functionTwo' expects 2 separate arguments}} functionTuple(a, b) // expected-error {{var 'functionTuple' expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} functionTuple((a, b)) @@ -197,7 +197,7 @@ do { s.concrete((3)) s.concreteTwo(3, 4) - s.concreteTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + s.concreteTwo((3, 4)) // expected-error {{instance method 'concreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{17-18=}} {{22-23=}} s.concreteTuple(3, 4) // expected-error {{instance method 'concreteTuple' expects a single parameter of type '(Int, Int)'}} {{19-19=(}} {{23-23=)}} s.concreteTuple((3, 4)) @@ -216,8 +216,8 @@ do { s.concrete(c) s.concreteTwo(a, b) - s.concreteTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.concreteTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.concreteTwo((a, b)) // expected-error {{instance method 'concreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{17-18=}} {{22-23=}} + s.concreteTwo(d) // expected-error {{instance method 'concreteTwo' expects 2 separate arguments}} s.concreteTuple(a, b) // expected-error {{instance method 'concreteTuple' expects a single parameter of type '(Int, Int)'}} {{19-19=(}} {{23-23=)}} s.concreteTuple((a, b)) @@ -237,8 +237,8 @@ do { s.concrete(c) s.concreteTwo(a, b) - s.concreteTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.concreteTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.concreteTwo((a, b)) // expected-error {{instance method 'concreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{17-18=}} {{22-23=}} + s.concreteTwo(d) // expected-error {{instance method 'concreteTwo' expects 2 separate arguments}} s.concreteTuple(a, b) // expected-error {{instance method 'concreteTuple' expects a single parameter of type '(Int, Int)'}} {{19-19=(}} {{23-23=)}} s.concreteTuple((a, b)) @@ -266,7 +266,7 @@ do { s.genericLabeled(x: (3, 4)) s.genericTwo(3, 4) - s.genericTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + s.genericTwo((3, 4)) // expected-error {{instance method 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} s.genericTuple(3, 4) // expected-error {{instance method 'genericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{18-18=(}} {{22-22=)}} s.genericTuple((3, 4)) @@ -287,8 +287,8 @@ do { s.generic(d) s.genericTwo(a, b) - s.genericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.genericTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.genericTwo((a, b)) // expected-error {{instance method 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} + s.genericTwo(d) // expected-error {{instance method 'genericTwo' expects 2 separate arguments}} s.genericTuple(a, b) // expected-error {{instance method 'genericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{18-18=(}} {{22-22=)}} s.genericTuple((a, b)) @@ -310,8 +310,8 @@ do { s.generic(d) s.genericTwo(a, b) - s.genericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.genericTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.genericTwo((a, b)) // expected-error {{instance method 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} + s.genericTwo(d) // expected-error {{instance method 'genericTwo' expects 2 separate arguments}} s.genericTuple(a, b) // expected-error {{instance method 'genericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{18-18=(}} {{22-22=)}} s.genericTuple((a, b)) @@ -331,7 +331,7 @@ do { s.mutatingConcrete((3)) s.mutatingConcreteTwo(3, 4) - s.mutatingConcreteTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingConcreteTwo((3, 4)) // expected-error {{instance method 'mutatingConcreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{25-26=}} {{30-31=}} s.mutatingConcreteTuple(3, 4) // expected-error {{instance method 'mutatingConcreteTuple' expects a single parameter of type '(Int, Int)'}} {{27-27=(}} {{31-31=)}} s.mutatingConcreteTuple((3, 4)) @@ -350,8 +350,8 @@ do { s.mutatingConcrete(c) s.mutatingConcreteTwo(a, b) - s.mutatingConcreteTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.mutatingConcreteTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingConcreteTwo((a, b)) // expected-error {{instance method 'mutatingConcreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{25-26=}} {{30-31=}} + s.mutatingConcreteTwo(d) // expected-error {{instance method 'mutatingConcreteTwo' expects 2 separate arguments}} s.mutatingConcreteTuple(a, b) // expected-error {{instance method 'mutatingConcreteTuple' expects a single parameter of type '(Int, Int)'}} {{27-27=(}} {{31-31=)}} s.mutatingConcreteTuple((a, b)) @@ -371,8 +371,8 @@ do { s.mutatingConcrete(c) s.mutatingConcreteTwo(a, b) - s.mutatingConcreteTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.mutatingConcreteTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingConcreteTwo((a, b)) // expected-error {{instance method 'mutatingConcreteTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{25-26=}} {{30-31=}} + s.mutatingConcreteTwo(d) // expected-error {{instance method 'mutatingConcreteTwo' expects 2 separate arguments}} s.mutatingConcreteTuple(a, b) // expected-error {{instance method 'mutatingConcreteTuple' expects a single parameter of type '(Int, Int)'}} {{27-27=(}} {{31-31=)}} s.mutatingConcreteTuple((a, b)) @@ -400,7 +400,7 @@ do { s.mutatingGenericLabeled(x: (3, 4)) s.mutatingGenericTwo(3, 4) - s.mutatingGenericTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingGenericTwo((3, 4)) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{29-30=}} s.mutatingGenericTuple(3, 4) // expected-error {{instance method 'mutatingGenericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{26-26=(}} {{30-30=)}} s.mutatingGenericTuple((3, 4)) @@ -421,8 +421,8 @@ do { s.mutatingGeneric(d) s.mutatingGenericTwo(a, b) - s.mutatingGenericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.mutatingGenericTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingGenericTwo((a, b)) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{29-30=}} + s.mutatingGenericTwo(d) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments}} s.mutatingGenericTuple(a, b) // expected-error {{instance method 'mutatingGenericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{26-26=(}} {{30-30=)}} s.mutatingGenericTuple((a, b)) @@ -444,8 +444,8 @@ do { s.mutatingGeneric(d) s.mutatingGenericTwo(a, b) - s.mutatingGenericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.mutatingGenericTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingGenericTwo((a, b)) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{29-30=}} + s.mutatingGenericTwo(d) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments}} s.mutatingGenericTuple(a, b) // expected-error {{instance method 'mutatingGenericTuple' expects a single parameter of type '(T, U)' [with T = Int, U = Int]}} {{26-26=(}} {{30-30=)}} s.mutatingGenericTuple((a, b)) @@ -454,7 +454,7 @@ do { extension Concrete { var function: (Int) -> () { return concrete } - var functionTwo: (Int, Int) -> () { return concreteTwo } + var functionTwo: (Int, Int) -> () { return concreteTwo } // expected-note 5 {{'functionTwo' declared here}} var functionTuple: ((Int, Int)) -> () { return concreteTuple } } @@ -465,7 +465,7 @@ do { s.function((3)) s.functionTwo(3, 4) - s.functionTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + s.functionTwo((3, 4)) // expected-error {{property 'functionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{17-18=}} {{22-23=}} s.functionTuple(3, 4) // expected-error {{property 'functionTuple' expects a single parameter of type '(Int, Int)'}} {{19-19=(}} {{23-23=)}} s.functionTuple((3, 4)) @@ -484,8 +484,8 @@ do { s.function(c) s.functionTwo(a, b) - s.functionTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.functionTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.functionTwo((a, b)) // expected-error {{property 'functionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{17-18=}} {{22-23=}} + s.functionTwo(d) // expected-error {{property 'functionTwo' expects 2 separate arguments}} s.functionTuple(a, b) // expected-error {{property 'functionTuple' expects a single parameter of type '(Int, Int)'}} {{19-19=(}} {{23-23=)}} @@ -506,8 +506,8 @@ do { s.function(c) s.functionTwo(a, b) - s.functionTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - s.functionTwo(d) // expected-error {{missing argument for parameter #2 in call}} + s.functionTwo((a, b)) // expected-error {{property 'functionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{17-18=}} {{22-23=}} + s.functionTwo(d) // expected-error {{property 'functionTwo' expects 2 separate arguments}} s.functionTuple(a, b) // expected-error {{property 'functionTuple' expects a single parameter of type '(Int, Int)'}} {{19-19=(}} {{23-23=)}} s.functionTuple((a, b)) @@ -528,7 +528,7 @@ struct InitLabeledTuple { do { _ = InitTwo(3, 4) - _ = InitTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + _ = InitTwo((3, 4)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} _ = InitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} _ = InitTuple((3, 4)) @@ -543,8 +543,8 @@ do { let c = (a, b) _ = InitTwo(a, b) - _ = InitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = InitTwo(c) // expected-error {{missing argument for parameter #2 in call}} + _ = InitTwo((a, b)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} + _ = InitTwo(c) // expected-error {{initializer expects 2 separate arguments}} _ = InitTuple(a, b) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} _ = InitTuple((a, b)) @@ -557,8 +557,8 @@ do { var c = (a, b) _ = InitTwo(a, b) - _ = InitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = InitTwo(c) // expected-error {{missing argument for parameter #2 in call}} + _ = InitTwo((a, b)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{15-16=}} {{20-21=}} + _ = InitTwo(c) // expected-error {{initializer expects 2 separate arguments}} _ = InitTuple(a, b) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{17-17=(}} {{21-21=)}} _ = InitTuple((a, b)) @@ -580,7 +580,7 @@ struct SubscriptLabeledTuple { do { let s1 = SubscriptTwo() _ = s1[3, 4] - _ = s1[(3, 4)] // expected-error {{missing argument for parameter #2 in call}} + _ = s1[(3, 4)] // expected-error {{subscript expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{10-11=}} {{15-16=}} let s2 = SubscriptTuple() _ = s2[3, 4] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} {{10-10=(}} {{14-14=)}} @@ -594,8 +594,8 @@ do { let s1 = SubscriptTwo() _ = s1[a, b] - _ = s1[(a, b)] // expected-error {{missing argument for parameter #2 in call}} - _ = s1[d] // expected-error {{missing argument for parameter #2 in call}} + _ = s1[(a, b)] // expected-error {{subscript expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{10-11=}} {{15-16=}} + _ = s1[d] // expected-error {{subscript expects 2 separate arguments}} let s2 = SubscriptTuple() _ = s2[a, b] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} {{10-10=(}} {{14-14=)}} @@ -615,8 +615,8 @@ do { var s1 = SubscriptTwo() _ = s1[a, b] - _ = s1[(a, b)] // expected-error {{missing argument for parameter #2 in call}} - _ = s1[d] // expected-error {{missing argument for parameter #2 in call}} + _ = s1[(a, b)] // expected-error {{subscript expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{10-11=}} {{15-16=}} + _ = s1[d] // expected-error {{subscript expects 2 separate arguments}} var s2 = SubscriptTuple() _ = s2[a, b] // expected-error {{subscript expects a single parameter of type '(Int, Int)'}} {{10-10=(}} {{14-14=)}} @@ -632,7 +632,7 @@ enum Enum { do { _ = Enum.two(3, 4) - _ = Enum.two((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + _ = Enum.two((3, 4)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} _ = Enum.two(3 > 4 ? 3 : 4) // expected-error {{missing argument for parameter #2 in call}} _ = Enum.tuple(3, 4) // expected-error {{enum case 'tuple' expects a single parameter of type '(Int, Int)'}} {{18-18=(}} {{22-22=)}} @@ -648,8 +648,8 @@ do { let c = (a, b) _ = Enum.two(a, b) - _ = Enum.two((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = Enum.two(c) // expected-error {{missing argument for parameter #2 in call}} + _ = Enum.two((a, b)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} + _ = Enum.two(c) // expected-error {{enum case 'two' expects 2 separate arguments}} _ = Enum.tuple(a, b) // expected-error {{enum case 'tuple' expects a single parameter of type '(Int, Int)'}} {{18-18=(}} {{22-22=)}} _ = Enum.tuple((a, b)) @@ -662,8 +662,8 @@ do { var c = (a, b) _ = Enum.two(a, b) - _ = Enum.two((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = Enum.two(c) // expected-error {{missing argument for parameter #2 in call}} + _ = Enum.two((a, b)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} + _ = Enum.two(c) // expected-error {{enum case 'two' expects 2 separate arguments}} _ = Enum.tuple(a, b) // expected-error {{enum case 'tuple' expects a single parameter of type '(Int, Int)'}} {{18-18=(}} {{22-22=)}} _ = Enum.tuple((a, b)) @@ -689,7 +689,7 @@ do { s.genericLabeled(x: (3.0)) s.genericTwo(3.0, 4.0) - s.genericTwo((3.0, 4.0)) // expected-error {{missing argument for parameter #2 in call}} + s.genericTwo((3.0, 4.0)) // expected-error {{instance method 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{25-26=}} s.genericTuple(3.0, 4.0) // expected-error {{instance method 'genericTuple' expects a single parameter of type '(Double, Double)'}} {{18-18=(}} {{26-26=)}} s.genericTuple((3.0, 4.0)) @@ -716,7 +716,7 @@ do { s.generic(c) s.genericTwo(a, b) - s.genericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.genericTwo((a, b)) // expected-error {{instance method 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} s.genericTuple(a, b) // expected-error {{instance method 'genericTuple' expects a single parameter of type '(Double, Double)'}} {{18-18=(}} {{22-22=)}} s.genericTuple((a, b)) @@ -741,7 +741,7 @@ do { s.generic(c) s.genericTwo(a, b) - s.genericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.genericTwo((a, b)) // expected-error {{instance method 'genericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} s.genericTuple(a, b) // expected-error {{instance method 'genericTuple' expects a single parameter of type '(Double, Double)'}} {{18-18=(}} {{22-22=)}} s.genericTuple((a, b)) @@ -770,7 +770,7 @@ do { s.mutatingGenericLabeled(x: (3.0)) s.mutatingGenericTwo(3.0, 4.0) - s.mutatingGenericTwo((3.0, 4.0)) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingGenericTwo((3.0, 4.0)) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {24-25=}} {{33-34=}} s.mutatingGenericTuple(3.0, 4.0) // expected-error {{instance method 'mutatingGenericTuple' expects a single parameter of type '(Double, Double)'}} {{26-26=(}} {{34-34=)}} s.mutatingGenericTuple((3.0, 4.0)) @@ -797,7 +797,7 @@ do { s.mutatingGeneric(c) s.mutatingGenericTwo(a, b) - s.mutatingGenericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingGenericTwo((a, b)) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {24-25=}} {{29-30=}} s.mutatingGenericTuple(a, b) // expected-error {{instance method 'mutatingGenericTuple' expects a single parameter of type '(Double, Double)'}} {{26-26=(}} {{30-30=)}} s.mutatingGenericTuple((a, b)) @@ -822,7 +822,7 @@ do { s.mutatingGeneric(c) s.mutatingGenericTwo(a, b) - s.mutatingGenericTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.mutatingGenericTwo((a, b)) // expected-error {{instance method 'mutatingGenericTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{29-30=}} s.mutatingGenericTuple(a, b) // expected-error {{instance method 'mutatingGenericTuple' expects a single parameter of type '(Double, Double)'}} {{26-26=(}} {{30-30=)}} s.mutatingGenericTuple((a, b)) @@ -836,7 +836,7 @@ do { extension Generic { var genericFunction: (T) -> () { return generic } - var genericFunctionTwo: (T, T) -> () { return genericTwo } + var genericFunctionTwo: (T, T) -> () { return genericTwo } // expected-note 3 {{'genericFunctionTwo' declared here}} var genericFunctionTuple: ((T, T)) -> () { return genericTuple } } @@ -847,7 +847,7 @@ do { s.genericFunction((3.0)) s.genericFunctionTwo(3.0, 4.0) - s.genericFunctionTwo((3.0, 4.0)) // expected-error {{missing argument for parameter #2 in call}} + s.genericFunctionTwo((3.0, 4.0)) // expected-error {{property 'genericFunctionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{33-34=}} s.genericFunctionTuple(3.0, 4.0) // expected-error {{property 'genericFunctionTuple' expects a single parameter of type '(Double, Double)'}} {{26-26=(}} {{34-34=)}} s.genericFunctionTuple((3.0, 4.0)) @@ -871,7 +871,7 @@ do { s.genericFunction(c) s.genericFunctionTwo(a, b) - s.genericFunctionTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.genericFunctionTwo((a, b)) // expected-error {{property 'genericFunctionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{29-30=}} s.genericFunctionTuple(a, b) // expected-error {{property 'genericFunctionTuple' expects a single parameter of type '(Double, Double)'}} {{26-26=(}} {{30-30=)}} s.genericFunctionTuple((a, b)) @@ -896,7 +896,7 @@ do { s.genericFunction(c) s.genericFunctionTwo(a, b) - s.genericFunctionTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.genericFunctionTwo((a, b)) // expected-error {{property 'genericFunctionTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{24-25=}} {{29-30=}} s.genericFunctionTuple(a, b) // expected-error {{property 'genericFunctionTuple' expects a single parameter of type '(Double, Double)'}} {{26-26=(}} {{30-30=)}} s.genericFunctionTuple((a, b)) @@ -936,7 +936,7 @@ do { _ = GenericInitLabeled(x: (3, 4)) _ = GenericInitTwo(3, 4) - _ = GenericInitTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericInitTwo((3, 4)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{22-23=}} {{27-28=}} _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(T, T)' [with T = Int]}} {{24-24=(}} {{28-28=)}} _ = GenericInitTuple((3, 4)) @@ -953,7 +953,7 @@ do { _ = GenericInitLabeled<(Int, Int)>(x: (3, 4)) _ = GenericInitTwo(3, 4) - _ = GenericInitTwo((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericInitTwo((3, 4)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{27-28=}} {{32-33=}} _ = GenericInitTuple(3, 4) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{29-29=(}} {{33-33=)}} _ = GenericInitTuple((3, 4)) @@ -972,8 +972,8 @@ do { _ = GenericInit(c) _ = GenericInitTwo(a, b) - _ = GenericInitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericInitTwo(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericInitTwo((a, b)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{22-23=}} {{27-28=}} + _ = GenericInitTwo(c) // expected-error {{initializer expects 2 separate arguments}} _ = GenericInitTuple(a, b) // expected-error {{initializer expects a single parameter of type '(T, T)' [with T = Int]}} {{24-24=(}} {{28-28=)}} _ = GenericInitTuple((a, b)) @@ -990,8 +990,8 @@ do { _ = GenericInit<(Int, Int)>(c) _ = GenericInitTwo(a, b) - _ = GenericInitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericInitTwo(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericInitTwo((a, b)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{27-28=}} {{32-33=}} + _ = GenericInitTwo(c) // expected-error {{initializer expects 2 separate arguments}} _ = GenericInitTuple(a, b) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{29-29=(}} {{33-33=)}} _ = GenericInitTuple((a, b)) @@ -1008,8 +1008,8 @@ do { _ = GenericInit(c) _ = GenericInitTwo(a, b) - _ = GenericInitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericInitTwo(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericInitTwo((a, b)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{22-23=}} {{27-28=}} + _ = GenericInitTwo(c) // expected-error {{initializer expects 2 separate arguments}} _ = GenericInitTuple(a, b) // expected-error {{initializer expects a single parameter of type '(T, T)' [with T = Int]}} {{24-24=(}} {{28-28=)}} _ = GenericInitTuple((a, b)) @@ -1026,8 +1026,8 @@ do { _ = GenericInit<(Int, Int)>(c) _ = GenericInitTwo(a, b) - _ = GenericInitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericInitTwo(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericInitTwo((a, b)) // expected-error {{initializer expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{27-28=}} {{32-33=}} + _ = GenericInitTwo(c) // expected-error {{initializer expects 2 separate arguments}} _ = GenericInitTuple(a, b) // expected-error {{initializer expects a single parameter of type '(Int, Int)'}} {{29-29=(}} {{33-33=)}} _ = GenericInitTuple((a, b)) @@ -1065,7 +1065,7 @@ do { let s2 = GenericSubscriptTwo() _ = s2[3.0, 4.0] - _ = s2[(3.0, 4.0)] // expected-error {{missing argument for parameter #2 in call}} + _ = s2[(3.0, 4.0)] // expected-error {{subscript expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{10-11=}} {{19-20=}} let s3 = GenericSubscriptTuple() _ = s3[3.0, 4.0] // expected-error {{subscript expects a single parameter of type '(Double, Double)'}} {{10-10=(}} {{18-18=)}} @@ -1088,8 +1088,8 @@ do { let s2 = GenericSubscriptTwo() _ = s2[a, b] - _ = s2[(a, b)] // expected-error {{missing argument for parameter #2 in call}} - _ = s2[d] // expected-error {{missing argument for parameter #2 in call}} + _ = s2[(a, b)] // expected-error {{subscript expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{10-11=}} {{15-16=}} + _ = s2[d] // expected-error {{subscript expects 2 separate arguments}} let s3 = GenericSubscriptTuple() _ = s3[a, b] // expected-error {{subscript expects a single parameter of type '(Double, Double)'}} {{10-10=(}} {{14-14=)}} @@ -1110,8 +1110,8 @@ do { var s2 = GenericSubscriptTwo() _ = s2[a, b] - _ = s2[(a, b)] // expected-error {{missing argument for parameter #2 in call}} - _ = s2[d] // expected-error {{missing argument for parameter #2 in call}} + _ = s2[(a, b)] // expected-error {{subscript expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{10-11=}} {{15-16=}} + _ = s2[d] // expected-error {{subscript expects 2 separate arguments}} var s3 = GenericSubscriptTuple() _ = s3[a, b] // expected-error {{subscript expects a single parameter of type '(Double, Double)'}} {{10-10=(}} {{14-14=)}} @@ -1136,7 +1136,7 @@ do { _ = GenericEnum.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) - _ = GenericEnum.two((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericEnum.two((3, 4)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{23-24=}} {{28-29=}} _ = GenericEnum.tuple(3, 4) // expected-error {{enum case 'tuple' expects a single parameter of type '(T, T)' [with T = Int]}} {{25-25=(}} {{29-29=)}} _ = GenericEnum.tuple((3, 4)) @@ -1152,7 +1152,7 @@ do { _ = GenericEnum<(Int, Int)>.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) - _ = GenericEnum.two((3, 4)) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericEnum.two((3, 4)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{28-29=}} {{33-34=}} _ = GenericEnum.tuple(3, 4) // expected-error {{enum case 'tuple' expects a single parameter of type '(Int, Int)'}} {{30-30=(}} {{34-34=)}} _ = GenericEnum.tuple((3, 4)) @@ -1168,8 +1168,8 @@ do { _ = GenericEnum.one(c) _ = GenericEnum.two(a, b) - _ = GenericEnum.two((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericEnum.two(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericEnum.two((a, b)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{23-24=}} {{28-29=}} + _ = GenericEnum.two(c) // expected-error {{enum case 'two' expects 2 separate arguments}} _ = GenericEnum.tuple(a, b) // expected-error {{enum case 'tuple' expects a single parameter of type '(T, T)' [with T = Int]}} {{25-25=(}} {{29-29=)}} _ = GenericEnum.tuple((a, b)) @@ -1186,8 +1186,8 @@ do { _ = GenericEnum<(Int, Int)>.one(c) _ = GenericEnum.two(a, b) - _ = GenericEnum.two((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericEnum.two(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericEnum.two((a, b)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{28-29=}} {{33-34=}} + _ = GenericEnum.two(c) // expected-error {{enum case 'two' expects 2 separate arguments}} _ = GenericEnum.tuple(a, b) // expected-error {{enum case 'tuple' expects a single parameter of type '(Int, Int)'}} {{30-30=(}} {{34-34=)}} _ = GenericEnum.tuple((a, b)) @@ -1204,8 +1204,8 @@ do { _ = GenericEnum.one(c) _ = GenericEnum.two(a, b) - _ = GenericEnum.two((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericEnum.two(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericEnum.two((a, b)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{23-24=}} {{28-29=}} + _ = GenericEnum.two(c) // expected-error {{enum case 'two' expects 2 separate arguments}} _ = GenericEnum.tuple(a, b) // expected-error {{enum case 'tuple' expects a single parameter of type '(T, T)' [with T = Int]}} {{25-25=(}} {{29-29=)}} _ = GenericEnum.tuple((a, b)) @@ -1222,8 +1222,8 @@ do { _ = GenericEnum<(Int, Int)>.one(c) _ = GenericEnum.two(a, b) - _ = GenericEnum.two((a, b)) // expected-error {{missing argument for parameter #2 in call}} - _ = GenericEnum.two(c) // expected-error {{missing argument for parameter #2 in call}} + _ = GenericEnum.two((a, b)) // expected-error {{enum case 'two' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{28-29=}} {{33-34=}} + _ = GenericEnum.two(c) // expected-error {{enum case 'two' expects 2 separate arguments}} _ = GenericEnum.tuple(a, b) // expected-error {{enum case 'tuple' expects a single parameter of type '(Int, Int)'}} {{30-30=(}} {{34-34=)}} _ = GenericEnum.tuple((a, b)) @@ -1255,7 +1255,7 @@ do { s.requirementLabeled(x: (3.0)) s.requirementTwo(3.0, 4.0) - s.requirementTwo((3.0, 4.0)) // expected-error {{missing argument for parameter #2 in call}} + s.requirementTwo((3.0, 4.0)) // expected-error {{instance method 'requirementTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{20-21=}} {{29-30=}} s.requirementTuple(3.0, 4.0) // expected-error {{instance method 'requirementTuple' expects a single parameter of type '(Double, Double)'}} {{22-22=(}} {{30-30=)}} s.requirementTuple((3.0, 4.0)) @@ -1282,7 +1282,7 @@ do { s.requirement(c) s.requirementTwo(a, b) - s.requirementTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.requirementTwo((a, b)) // expected-error {{instance method 'requirementTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{20-21=}} {{25-26=}} s.requirementTuple(a, b) // expected-error {{instance method 'requirementTuple' expects a single parameter of type '(Double, Double)'}} {{22-22=(}} {{26-26=)}} s.requirementTuple((a, b)) @@ -1307,7 +1307,7 @@ do { s.requirement(c) s.requirementTwo(a, b) - s.requirementTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}} + s.requirementTwo((a, b)) // expected-error {{instance method 'requirementTwo' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{20-21=}} {{25-26=}} s.requirementTuple(a, b) // expected-error {{instance method 'requirementTuple' expects a single parameter of type '(Double, Double)'}} {{22-22=(}} {{26-26=)}} s.requirementTuple((a, b)) @@ -1406,8 +1406,8 @@ func processArrayOfFunctions(f1: [((Bool, Bool)) -> ()], f2.forEach { block in // expected-note@-1 2{{'block' declared here}} - block(p) // expected-error {{missing argument for parameter #2 in call}} - block((c, c)) // expected-error {{missing argument for parameter #2 in call}} + block(p) // expected-error {{parameter 'block' expects 2 separate arguments}} + block((c, c)) // expected-error {{parameter 'block' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{11-12=}} {{16-17=}} block(c, c) } @@ -1420,8 +1420,8 @@ func processArrayOfFunctions(f1: [((Bool, Bool)) -> ()], f2.forEach { (block: (Bool, Bool) -> ()) in // expected-note@-1 2{{'block' declared here}} - block(p) // expected-error {{missing argument for parameter #2 in call}} - block((c, c)) // expected-error {{missing argument for parameter #2 in call}} + block(p) // expected-error {{parameter 'block' expects 2 separate arguments}} + block((c, c)) // expected-error {{parameter 'block' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{11-12=}} {{16-17=}} block(c, c) } } diff --git a/test/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index f0cd75768adf2..9dcd2e5b70051 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -137,9 +137,9 @@ func test20770032() { func tuple_splat1(_ a : Int, _ b : Int) { // expected-note 2 {{'tuple_splat1' declared here}} let x = (1,2) - tuple_splat1(x) // expected-error {{missing argument for parameter #2 in call}} + tuple_splat1(x) // expected-error {{global function 'tuple_splat1' expects 2 separate arguments}} tuple_splat1(1, 2) // Ok. - tuple_splat1((1, 2)) // expected-error {{missing argument for parameter #2 in call}} + tuple_splat1((1, 2)) // expected-error {{global function 'tuple_splat1' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{16-17=}} {{21-22=}} } // This take a tuple as a value, so it isn't a tuple splat. From 60bcc9457516cab412b06d9ef907e1ca46bf443e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 23 Sep 2019 12:00:10 -0700 Subject: [PATCH 020/140] [Diagnostics] Implement single missing argument diagnosis If call is missing a single argument we can provide a tailored diagnostic and suggest a fix-it to add it at the appropriate position. --- lib/Sema/CSDiagnostics.cpp | 169 +++++++++++++++++++++++++++++++++---- lib/Sema/CSDiagnostics.h | 2 + 2 files changed, 153 insertions(+), 18 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 73c65b059033e..1dc157e324491 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3508,23 +3508,6 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { return true; } - // Function type has fewer arguments than expected by context: - // - // ``` - // func foo() {} - // let _: (Int) -> Void = foo - // ``` - if (locator->isLastElement(ConstraintLocator::ContextualType)) { - auto &cs = getConstraintSystem(); - emitDiagnostic(anchor->getLoc(), diag::cannot_convert_initializer_value, - getType(anchor), resolveType(cs.getContextualType())); - // TODO: It would be great so somehow point out which arguments are missing. - return true; - } - - if (diagnoseInvalidTupleDestructuring()) - return true; - return false; } @@ -3585,7 +3568,6 @@ bool MissingArgumentsFailure::diagnoseAsError() { auto *locator = getLocator(); auto path = locator->getPath(); - // TODO: Currently this is only intended to diagnose contextual failures. if (path.empty() || !(path.back().getKind() == ConstraintLocator::ApplyArgToParam || path.back().getKind() == ConstraintLocator::ContextualType || @@ -3618,9 +3600,160 @@ bool MissingArgumentsFailure::diagnoseAsError() { return true; } + // Function type has fewer arguments than expected by context: + // + // ``` + // func foo() {} + // let _: (Int) -> Void = foo + // ``` + if (locator->isLastElement(ConstraintLocator::ContextualType)) { + auto &cs = getConstraintSystem(); + emitDiagnostic(anchor->getLoc(), diag::cannot_convert_initializer_value, + getType(anchor), resolveType(cs.getContextualType())); + // TODO: It would be great so somehow point out which arguments are missing. + return true; + } + + if (diagnoseInvalidTupleDestructuring()) + return true; + + if (diagnoseSingleMissingArgument()) + return true; + return false; } +bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { + auto &ctx = getASTContext(); + + auto *anchor = getRawAnchor(); + if (!(isa(anchor) || isa(anchor))) + return false; + + if (SynthesizedArgs.size() != 1) + return false; + + const auto &argument = SynthesizedArgs.front(); + auto *argType = argument.getPlainType()->castTo(); + auto *argLocator = argType->getImpl().getLocator(); + auto position = + argLocator->findLast()->getParamIdx(); + auto label = argument.getLabel(); + + SmallString<32> insertBuf; + llvm::raw_svector_ostream insertText(insertBuf); + + if (position != 0) + insertText << ", "; + + if (!label.empty()) + insertText << label.str() << ": "; + + // Explode inout type. + if (argument.isInOut()) + insertText << "&"; + + auto resolvedType = resolveType(argType); + // @autoclosure; the type should be the result type. + if (argument.isAutoClosure()) + resolvedType = resolvedType->castTo()->getResult(); + + insertText << "<#" << resolvedType << "#>"; + + unsigned insertableEndIdx = 0; + Expr *fnExpr = nullptr; + Expr *argExpr = nullptr; + if (auto *callExpr = dyn_cast(anchor)) { + fnExpr = callExpr->getFn(); + argExpr = callExpr->getArg(); + insertableEndIdx = callExpr->getNumArguments(); + if (callExpr->hasTrailingClosure()) + insertableEndIdx -= 1; + } else { + auto *SE = cast(anchor); + fnExpr = SE; + argExpr = SE->getIndex(); + insertableEndIdx = SE->getNumArguments(); + if (SE->hasTrailingClosure()) + insertableEndIdx -= 1; + } + + if (position == 0 && insertableEndIdx != 0) + insertText << ", "; + + SourceLoc insertLoc; + if (auto *TE = dyn_cast(argExpr)) { + // fn(): + // fn([argMissing]) + // fn(argX, argY): + // fn([argMissing, ]argX, argY) + // fn(argX[, argMissing], argY) + // fn(argX, argY[, argMissing]) + // fn(argX) { closure }: + // fn([argMissing, ]argX) { closure } + // fn(argX[, argMissing]) { closure } + // fn(argX[, closureLabel: ]{closure}[, argMissing)] // Not impl. + if (insertableEndIdx == 0) + insertLoc = TE->getRParenLoc(); + else if (position != 0) { + auto argPos = std::min(TE->getNumElements(), position) - 1; + insertLoc = Lexer::getLocForEndOfToken( + ctx.SourceMgr, TE->getElement(argPos)->getEndLoc()); + } else { + insertLoc = TE->getElementNameLoc(0); + if (insertLoc.isInvalid()) + insertLoc = TE->getElement(0)->getStartLoc(); + } + } else { + auto *PE = cast(argExpr); + if (PE->getRParenLoc().isValid()) { + // fn(argX): + // fn([argMissing, ]argX) + // fn(argX[, argMissing]) + // fn() { closure }: + // fn([argMissing]) {closure} + // fn([closureLabel: ]{closure}[, argMissing]) // Not impl. + if (insertableEndIdx == 0) + insertLoc = PE->getRParenLoc(); + else if (position == 0) + insertLoc = PE->getSubExpr()->getStartLoc(); + else + insertLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr, + PE->getSubExpr()->getEndLoc()); + } else { + // fn { closure }: + // fn[(argMissing)] { closure } + // fn[(closureLabel:] { closure }[, missingArg)] // Not impl. + assert(!isa(anchor) && "bracket less subscript"); + assert(PE->hasTrailingClosure() && + "paren less ParenExpr without trailing closure"); + insertBuf.insert(insertBuf.begin(), '('); + insertBuf.insert(insertBuf.end(), ')'); + insertLoc = + Lexer::getLocForEndOfToken(ctx.SourceMgr, fnExpr->getEndLoc()); + } + } + + if (insertLoc.isInvalid()) + return false; + + if (label.empty()) { + emitDiagnostic(insertLoc, diag::missing_argument_positional, position + 1) + .fixItInsert(insertLoc, insertText.str()); + } else { + emitDiagnostic(insertLoc, diag::missing_argument_named, label) + .fixItInsert(insertLoc, insertText.str()); + } + + if (auto selectedOverload = getChoiceFor(getLocator())) { + if (auto *decl = selectedOverload->choice.getDeclOrNull()) { + emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName()); + } + } + + return true; +} + bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) { auto &cs = getConstraintSystem(); FunctionType *funcType = nullptr; diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index d9c1a9958eef4..06eb7c671b2e3 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1198,6 +1198,8 @@ class MissingArgumentsFailure final : public FailureDiagnostic { bool diagnoseAsError() override; private: + bool diagnoseSingleMissingArgument() const; + /// If missing arguments come from a closure, /// let's produce tailored diagnostics. bool diagnoseClosure(ClosureExpr *closure); From 9406edc017f1be00e5411f3abcaccd7b53b15675 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 23 Sep 2019 12:59:07 -0700 Subject: [PATCH 021/140] [Diagnostics] Tailored diagnostic for missing argument in property wrapper init --- lib/Sema/CSDiagnostics.cpp | 21 +++++++++++++++++++++ lib/Sema/CSDiagnostics.h | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 1dc157e324491..5de1f19d013f5 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3740,6 +3740,10 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { if (label.empty()) { emitDiagnostic(insertLoc, diag::missing_argument_positional, position + 1) .fixItInsert(insertLoc, insertText.str()); + } else if (isPropertyWrapperInitialization()) { + auto *TE = cast(fnExpr); + emitDiagnostic(TE->getLoc(), diag::property_wrapper_missing_arg_init, label, + resolveType(TE->getInstanceType())->getString()); } else { emitDiagnostic(insertLoc, diag::missing_argument_named, label) .fixItInsert(insertLoc, insertText.str()); @@ -3880,6 +3884,23 @@ bool MissingArgumentsFailure::diagnoseInvalidTupleDestructuring() const { return true; } +bool MissingArgumentsFailure::isPropertyWrapperInitialization() const { + auto *call = dyn_cast(getRawAnchor()); + if (!(call && call->isImplicit())) + return false; + + auto TE = dyn_cast(call->getFn()); + if (!TE) + return false; + + auto instanceTy = TE->getInstanceType(); + if (!instanceTy) + return false; + + auto *NTD = resolveType(instanceTy)->getAnyNominal(); + return NTD && NTD->getAttrs().hasAttribute(); +} + bool ClosureParamDestructuringFailure::diagnoseAsError() { auto *closure = cast(getAnchor()); auto params = closure->getParameters(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 06eb7c671b2e3..4bf9b4aa524e4 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1207,6 +1207,11 @@ class MissingArgumentsFailure final : public FailureDiagnostic { /// Diagnose cases when instead of multiple distinct arguments /// call got a single tuple argument with expected arity/types. bool diagnoseInvalidTupleDestructuring() const; + + /// Determine whether missing arguments are associated with + /// an implicit call to a property wrapper initializer e.g. + /// `@Foo(answer: 42) var question = "ultimate question"` + bool isPropertyWrapperInitialization() const; }; class OutOfOrderArgumentFailure final : public FailureDiagnostic { From 73b6427f64b61e56707c9c7fd9bde0420406b5c7 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 23 Sep 2019 14:19:29 -0700 Subject: [PATCH 022/140] [ConstraintSystem] Store parameter flags in synthesized argument locator --- lib/Sema/CSSimplify.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 84fcfcb812087..93e635b4a16d3 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -839,8 +839,8 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { unsigned newArgIdx = Arguments.size(); auto argLoc = Locator - .withPathElement( - LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx)) + .withPathElement(LocatorPathElt::ApplyArgToParam( + newArgIdx, paramIdx, param.getParameterFlags())) .withPathElement(LocatorPathElt::SynthesizedArgument(newArgIdx)); auto *argType = CS.createTypeVariable( From 1e87dfcff9dc03ee306d0304ad3b12f7788535ce Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Mon, 23 Sep 2019 23:51:04 +0100 Subject: [PATCH 023/140] [Typechecker] Perform capture analysis in enums as well --- lib/Sema/TypeCheckDecl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index de19d1ee5de24..140579b91a845 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2589,6 +2589,8 @@ class DeclChecker : public DeclVisitor { checkAccessControl(TC, ED); + TC.checkPatternBindingCaptures(ED); + if (ED->hasRawType() && !ED->isObjC()) { // ObjC enums have already had their raw values checked, but pure Swift // enums haven't. From 9c97153b2a102b7186d9f5a6039be1458b0d399a Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Tue, 24 Sep 2019 00:00:07 +0100 Subject: [PATCH 024/140] [Test] Adds a test case --- .../compiler_crashers_2_fixed/sr11509.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/sr11509.swift diff --git a/validation-test/compiler_crashers_2_fixed/sr11509.swift b/validation-test/compiler_crashers_2_fixed/sr11509.swift new file mode 100644 index 0000000000000..a50a3aca15b83 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11509.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend %s -typecheck -verify + +@propertyWrapper +public struct Wrapper { + public init(someValue unused: Int) { + _ = unused + } + + public var wrappedValue: Value? +} + + +func functionScope() { + let scopedValue = 3 // expected-note {{'scopedValue' declared here}} + // expected-warning@-1 {{initialization of immutable value 'scopedValue' was never used; consider replacing with assignment to '_' or removing it}} + + enum StaticScope { // expected-note {{type declared here}} + @Wrapper(someValue: scopedValue) static var foo: String? // expected-error {{enum declaration cannot close over value 'scopedValue' defined in outer scope}} + } + + _ = StaticScope.foo +} From 7d65012f27414212cabb85d6a2e1df2ab0a1080e Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 23 Sep 2019 18:13:21 +0200 Subject: [PATCH 025/140] SILGen: use the correct forwarding substitutions for the setter of an assign_by_wrapper. Fixes a crash when a function, which assigns to a wrapped property has additional generic parameters. https://bugs.swift.org/browse/SR-11484 rdar://problem/55442328 --- lib/SILGen/SILGenLValue.cpp | 4 +--- test/SILOptimizer/di_property_wrappers.swift | 13 ++++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 5849093e0314b..90f026c680f9c 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1427,8 +1427,6 @@ namespace { // Create the allocating setter function. It captures the base address. auto setterInfo = SGF.getConstantInfo(setter); SILValue setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo); - auto setterSubs = SGF.getFunction().getForwardingSubstitutionMap(); - CanSILFunctionType setterTy = setterFRef->getType().castTo(); SILFunctionConventions setterConv(setterTy, SGF.SGM.M); @@ -1442,7 +1440,7 @@ namespace { PartialApplyInst *setterPAI = SGF.B.createPartialApply(loc, setterFRef, - setterSubs, { capturedBase }, + Substitutions, { capturedBase }, ParameterConvention::Direct_Guaranteed); ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI); diff --git a/test/SILOptimizer/di_property_wrappers.swift b/test/SILOptimizer/di_property_wrappers.swift index a5e507a791fa8..d4f572600ce15 100644 --- a/test/SILOptimizer/di_property_wrappers.swift +++ b/test/SILOptimizer/di_property_wrappers.swift @@ -61,6 +61,12 @@ struct IntStruct { } wrapped = 27 } + + // Check that we don't crash if the function has unrelated generic parameters. + // SR-11484 + mutating func setit(_ v: V) { + wrapped = 5 + } } final class IntClass { @@ -142,10 +148,15 @@ func testIntStruct() { // CHECK-NEXT: .. init 42 // CHECK-NEXT: .. set 27 - let t1 = IntStruct() + var t1 = IntStruct() // CHECK-NEXT: 27 print(t1.wrapped) + // CHECK-NEXT: .. set 5 + t1.setit(false) + // CHECK-NEXT: 5 + print(t1.wrapped) + // CHECK-NEXT: .. init 42 let t2 = IntStruct(conditional: false) // CHECK-NEXT: 42 From ab5d161c059ffb00e73e123ae2d154245b5e74d1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 20 Sep 2019 09:52:39 -0700 Subject: [PATCH 026/140] [SILGen] Separate the initialization of a wrapped property from a wrapped value Teach SILGen to emit a separate SIL function to capture the initialization of the backing storage type for a wrapped property based on the wrapped value. This eliminates manual code expansion at every use site. --- docs/ABI/Mangling.rst | 1 + include/swift/AST/ASTMangler.h | 3 ++ include/swift/Demangling/DemangleNodes.def | 1 + include/swift/SIL/SILDeclRef.h | 4 ++ lib/AST/ASTMangler.cpp | 13 ++++++ lib/AST/Decl.cpp | 3 +- lib/Demangling/Demangler.cpp | 5 +++ lib/Demangling/NodePrinter.cpp | 8 +++- lib/Demangling/OldRemangler.cpp | 5 +++ lib/Demangling/Remangler.cpp | 8 ++++ lib/IRGen/GenObjC.cpp | 1 + lib/ParseSIL/ParseSIL.cpp | 3 ++ lib/SIL/SILDeclRef.cpp | 5 +++ lib/SIL/SILFunctionType.cpp | 4 ++ lib/SIL/SILPrinter.cpp | 3 ++ lib/SIL/TypeLowering.cpp | 25 +++++++++++ lib/SILGen/SILGen.cpp | 17 +++++++ lib/SILGen/SILGen.h | 3 ++ lib/SILGen/SILGenConstructor.cpp | 52 +++++++--------------- lib/SILGen/SILGenExpr.cpp | 40 +++++++++++++++++ lib/SILGen/SILGenFunction.cpp | 43 ++++++++++++++++-- lib/SILGen/SILGenFunction.h | 6 +++ lib/SILGen/SILGenProlog.cpp | 1 - lib/SILGen/SILGenType.cpp | 9 ++++ test/Demangle/Inputs/manglings.txt | 1 + test/SILGen/property_wrappers.swift | 14 +++--- 26 files changed, 228 insertions(+), 50 deletions(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index ec0ee9c8a1dfb..4accbb8ddbb05 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -290,6 +290,7 @@ Entities entity-spec ::= type 'fu' INDEX // implicit anonymous closure entity-spec ::= 'fA' INDEX // default argument N+1 generator entity-spec ::= 'fi' // non-local variable initializer + entity-spec ::= 'fP' // property wrapper backing initializer entity-spec ::= 'fD' // deallocating destructor; untyped entity-spec ::= 'fd' // non-deallocating destructor; untyped entity-spec ::= 'fE' // ivar destroyer; untyped diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index da3b272f95379..5b877ca406421 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -124,6 +124,8 @@ class ASTMangler : public Mangler { SymbolKind SKind); std::string mangleInitializerEntity(const VarDecl *var, SymbolKind SKind); + std::string mangleBackingInitializerEntity(const VarDecl *var, + SymbolKind SKind); std::string mangleNominalType(const NominalTypeDecl *decl); @@ -311,6 +313,7 @@ class ASTMangler : public Mangler { void appendDefaultArgumentEntity(const DeclContext *ctx, unsigned index); void appendInitializerEntity(const VarDecl *var); + void appendBackingInitializerEntity(const VarDecl *var); CanType getDeclTypeForMangling(const ValueDecl *decl, GenericSignature *&genericSig, diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index da4d56ce7fad5..5fd70035ced93 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -157,6 +157,7 @@ NODE(PostfixOperator) NODE(PrefixOperator) NODE(PrivateDeclName) NODE(PropertyDescriptor) +CONTEXT_NODE(PropertyWrapperBackingInitializer) CONTEXT_NODE(Protocol) CONTEXT_NODE(ProtocolSymbolicReference) NODE(ProtocolConformance) diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 57bda0fdb7300..2993761a62bbe 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -133,6 +133,10 @@ struct SILDeclRef { /// routines have an ivar destroyer, which is emitted as /// .cxx_destruct. IVarDestroyer, + + /// References the wrapped value injection function used to initialize + /// the backing storage property from a wrapped value. + PropertyWrapperBackingInitializer, }; /// The ValueDecl or AbstractClosureExpr represented by this SILDeclRef. diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 0b9dfc8d31610..e225bf5273b85 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -155,6 +155,14 @@ std::string ASTMangler::mangleInitializerEntity(const VarDecl *var, return finalize(); } +std::string ASTMangler::mangleBackingInitializerEntity(const VarDecl *var, + SymbolKind SKind) { + beginMangling(); + appendBackingInitializerEntity(var); + appendSymbolKind(SKind); + return finalize(); +} + std::string ASTMangler::mangleNominalType(const NominalTypeDecl *decl) { beginMangling(); appendAnyGenericType(decl); @@ -2328,6 +2336,11 @@ void ASTMangler::appendInitializerEntity(const VarDecl *var) { appendOperator("fi"); } +void ASTMangler::appendBackingInitializerEntity(const VarDecl *var) { + appendEntity(var, "vp", var->isStatic()); + appendOperator("fP"); +} + /// Is this declaration a method for mangling purposes? If so, we'll leave the /// Self type out of its mangling. static bool isMethodDecl(const Decl *decl) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 53b120cffa52f..4f21bfe798a03 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5482,7 +5482,8 @@ bool VarDecl::hasAttachedPropertyWrapper() const { return !getAttachedPropertyWrappers().empty(); } -/// Whether all of the attached property wrappers have an init(initialValue:) initializer. +/// Whether all of the attached property wrappers have an init(wrappedValue:) +/// initializer. bool VarDecl::allAttachedPropertyWrappersHaveInitialValueInit() const { for (unsigned i : indices(getAttachedPropertyWrappers())) { if (!getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 7e661257cfd0c..04df4698c4e1e 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1577,6 +1577,7 @@ bool Demangle::nodeConsumesGenericArgs(Node *node) { case Node::Kind::ExplicitClosure: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Initializer: + case Node::Kind::PropertyWrapperBackingInitializer: return false; default: return true; @@ -2934,6 +2935,10 @@ NodePointer Demangler::demangleFunctionEntity() { case 'u': Args = TypeAndIndex; Kind = Node::Kind::ImplicitClosure; break; case 'A': Args = Index; Kind = Node::Kind::DefaultArgumentInitializer; break; case 'p': return demangleEntity(Node::Kind::GenericTypeParamDecl); + case 'P': + Args = None; + Kind = Node::Kind::PropertyWrapperBackingInitializer; + break; default: return nullptr; } diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 93e2825998ffb..768d033494e5d 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -398,6 +398,7 @@ class NodePrinter { case Node::Kind::InOut: case Node::Kind::InfixOperator: case Node::Kind::Initializer: + case Node::Kind::PropertyWrapperBackingInitializer: case Node::Kind::KeyPathGetterThunkHelper: case Node::Kind::KeyPathSetterThunkHelper: case Node::Kind::KeyPathEqualsThunkHelper: @@ -1125,6 +1126,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::Initializer: return printEntity(Node, asPrefixContext, TypePrinting::NoType, /*hasName*/false, "variable initialization expression"); + case Node::Kind::PropertyWrapperBackingInitializer: + return printEntity( + Node, asPrefixContext, TypePrinting::NoType, + /*hasName*/false, "property wrapper backing initializer"); case Node::Kind::DefaultArgumentInitializer: return printEntity(Node, asPrefixContext, TypePrinting::NoType, /*hasName*/false, "default argument ", @@ -2447,7 +2452,8 @@ printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, if (!asPrefixContext && PostfixContext) { // Print any left over context which couldn't be printed in prefix form. if (Entity->getKind() == Node::Kind::DefaultArgumentInitializer || - Entity->getKind() == Node::Kind::Initializer) { + Entity->getKind() == Node::Kind::Initializer || + Entity->getKind() == Node::Kind::PropertyWrapperBackingInitializer) { Printer << " of "; } else { Printer << " in "; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index ceb13bb0199fb..4a6a669683524 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -796,6 +796,11 @@ void Remangler::mangleInitializer(Node *node, EntityContext &ctx) { mangleSimpleEntity(node, 'I', "i", ctx); } +void Remangler::manglePropertyWrapperBackingInitializer(Node *node, + EntityContext &ctx) { + mangleSimpleEntity(node, 'I', "P", ctx); +} + void Remangler::mangleDefaultArgumentInitializer(Node *node, EntityContext &ctx) { mangleNamedEntity(node, 'I', "A", ctx); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 6f660946635b7..ecb972dcc0006 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -518,6 +518,7 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, case Node::Kind::ImplicitClosure: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Initializer: + case Node::Kind::PropertyWrapperBackingInitializer: if (!fullSubstitutionMap) break; @@ -1525,6 +1526,11 @@ void Remangler::mangleInitializer(Node *node) { Buffer << "fi"; } +void Remangler::manglePropertyWrapperBackingInitializer(Node *node) { + mangleChildNodes(node); + Buffer << "fP"; +} + void Remangler::mangleLazyProtocolWitnessTableAccessor(Node *node) { mangleChildNodes(node); Buffer << "Wl"; @@ -2482,6 +2488,7 @@ bool Demangle::isSpecialized(Node *node) { case Node::Kind::ExplicitClosure: case Node::Kind::ImplicitClosure: case Node::Kind::Initializer: + case Node::Kind::PropertyWrapperBackingInitializer: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Getter: case Node::Kind::Setter: @@ -2521,6 +2528,7 @@ NodePointer Demangle::getUnspecialized(Node *node, NodeFactory &Factory) { case Node::Kind::ExplicitClosure: case Node::Kind::ImplicitClosure: case Node::Kind::Initializer: + case Node::Kind::PropertyWrapperBackingInitializer: case Node::Kind::DefaultArgumentInitializer: NumToCopy = node->getNumChildren(); LLVM_FALLTHROUGH; diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 98ae4cfec3b95..82104a80e4a66 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -458,6 +458,7 @@ namespace { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::EnumElement: case SILDeclRef::Kind::GlobalAccessor: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: llvm_unreachable("Method does not have a selector"); case SILDeclRef::Kind::Destroyer: diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 5915ef23758e6..7e4ed5bb82b5b 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -1518,6 +1518,9 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, } else if (!ParseState && Id.str() == "propertyinit") { Kind = SILDeclRef::Kind::StoredPropertyInitializer; ParseState = 1; + } else if (!ParseState && Id.str() == "backinginit") { + Kind = SILDeclRef::Kind::PropertyWrapperBackingInitializer; + ParseState = 1; } else if (Id.str() == "foreign") { IsObjC = true; break; diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 3c088226ca21f..e7d570d653009 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -788,6 +788,11 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { case SILDeclRef::Kind::StoredPropertyInitializer: assert(!isCurried); return mangler.mangleInitializerEntity(cast(getDecl()), SKind); + + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + assert(!isCurried); + return mangler.mangleBackingInitializerEntity(cast(getDecl()), + SKind); } llvm_unreachable("bad entity kind!"); diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 5757e24f5ef94..b208ab2908681 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -1288,6 +1288,7 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: { auto conv = DefaultConventions(NormalParameterConvention::Guaranteed); @@ -1815,6 +1816,7 @@ static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: llvm_unreachable("Unexpected Kind of foreign SILDeclRef"); } @@ -2045,6 +2047,7 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return SILFunctionTypeRepresentation::Thin; case SILDeclRef::Kind::Func: @@ -2594,6 +2597,7 @@ static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: return nullptr; diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index e2139e61933ed..c0d8b3f8b3340 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -333,6 +333,9 @@ void SILDeclRef::print(raw_ostream &OS) const { case SILDeclRef::Kind::StoredPropertyInitializer: OS << "!propertyinit"; break; + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + OS << "!backinginit"; + break; } auto uncurryLevel = getParameterListCount() - 1; diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 5a9e2e9f48fd5..5d0360366de28 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -1807,6 +1807,27 @@ static CanAnyFunctionType getStoredPropertyInitializerInterfaceType( {}, resultTy); } +/// Get the type of a property wrapper backing initializer, +/// (property-type) -> backing-type. +static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( + TypeConverter &TC, + VarDecl *VD) { + CanType resultType = + VD->getPropertyWrapperBackingPropertyType()->getCanonicalType(); + + auto *DC = VD->getInnermostDeclContext(); + CanType inputType = + VD->getParentPattern()->getType()->mapTypeOutOfContext() + ->getCanonicalType(); + + auto sig = DC->getGenericSignatureOfContext(); + + AnyFunctionType::Param param( + inputType, Identifier(), + ParameterTypeFlags().withValueOwnership(ValueOwnership::Owned)); + return CanAnyFunctionType::get(getCanonicalSignatureOrNull(sig), {param}, + resultType); +} /// Get the type of a destructor function. static CanAnyFunctionType getDestructorInterfaceType(DestructorDecl *dd, bool isDeallocating, @@ -1941,6 +1962,9 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { return getDefaultArgGeneratorInterfaceType(c); case SILDeclRef::Kind::StoredPropertyInitializer: return getStoredPropertyInitializerInterfaceType(cast(vd)); + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + return getPropertyWrapperBackingInitializerInterfaceType(*this, + cast(vd)); case SILDeclRef::Kind::IVarInitializer: return getIVarInitDestroyerInterfaceType(cast(vd), c.isForeign, false); @@ -1979,6 +2003,7 @@ TypeConverter::getConstantGenericSignature(SILDeclRef c) { case SILDeclRef::Kind::EnumElement: case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return vd->getDeclContext()->getGenericSignatureOfContext(); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 20be4e6fdf8c7..9b26e625685f5 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1140,6 +1140,23 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) { }); } +void SILGenModule:: +emitPropertyWrapperBackingInitializer(VarDecl *var) { + SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperBackingInitializer); + emitOrDelayFunction(*this, constant, [this, constant, var](SILFunction *f) { + preEmitFunction(constant, var, f, var); + PrettyStackTraceSILFunction X( + "silgen emitPropertyWrapperBackingInitializer", f); + f->createProfiler(var, constant, ForDefinition); + auto varDC = var->getInnermostDeclContext(); + auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); + assert(wrapperInfo.initializeFromOriginal); + SILGenFunction SGF(*this, *f, varDC); + SGF.emitGeneratorFunction(constant, wrapperInfo.initializeFromOriginal); + postEmitFunction(constant, f); + }); +} + SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, PatternBindingDecl *binding, unsigned pbdEntry) { diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index b90effae74a0d..0acad1587419a 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -239,6 +239,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Emits the stored property initializer for the given pattern. void emitStoredPropertyInitialization(PatternBindingDecl *pd, unsigned i); + /// Emits the backing initializer for a property with an attached wrapper. + void emitPropertyWrapperBackingInitializer(VarDecl *var); + /// Emits default argument generators for the given parameter list. void emitDefaultArgGenerators(SILDeclRef::Loc decl, ParameterList *paramList); diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index a9cbe49fb156f..6c31bc3fe9c07 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -99,33 +99,24 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, } /// If the field has a property wrapper for which we will need to call the -/// wrapper type's init(wrapperValue:), set up that evaluation and call the -/// \c body with the expression to form the property wrapper instance from -/// the initial value type. -/// -/// \returns true if this was such a wrapper, \c false otherwise. -static bool maybeEmitPropertyWrapperInitFromValue( +/// wrapper type's init(wrappedValue:, ...), call the function that performs +/// that initialization and return the result. Otherwise, return \c arg. +static RValue maybeEmitPropertyWrapperInitFromValue( SILGenFunction &SGF, SILLocation loc, VarDecl *field, - RValue &&arg, - llvm::function_ref body) { + RValue &&arg) { auto originalProperty = field->getOriginalWrappedProperty(); if (!originalProperty || !originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) - return false; + return std::move(arg); auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo(); if (!wrapperInfo || !wrapperInfo.initializeFromOriginal) - return false; + return std::move(arg); - SILGenFunction::OpaqueValueRAII opaqueValue( - SGF, - wrapperInfo.underlyingValue, - std::move(arg).getAsSingleValue(SGF, loc)); - - body(wrapperInfo.initializeFromOriginal); - return true; + return SGF.emitApplyOfPropertyWrapperBackingInitializer(loc, originalProperty, + std::move(arg)); } static void emitImplicitValueConstructor(SILGenFunction &SGF, @@ -183,13 +174,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, "number of args does not match number of fields"); (void)eltEnd; FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); - if (!maybeEmitPropertyWrapperInitFromValue( - SGF, Loc, field, std::move(*elti), - [&](Expr *expr) { - SGF.emitExprInto(expr, init.get()); - })) { - std::move(*elti).forwardInto(SGF, Loc, init.get()); - } + maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, std::move(*elti)) + .forwardInto(SGF, Loc, init.get()); ++elti; } else { #ifndef NDEBUG @@ -221,14 +207,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); assert(elti != eltEnd && "number of args does not match number of fields"); (void)eltEnd; - if (!maybeEmitPropertyWrapperInitFromValue( - SGF, Loc, field, std::move(*elti), - [&](Expr *expr) { - v = SGF.emitRValue(expr) - .forwardAsSingleStorageValue(SGF, fieldTy, Loc); - })) { - v = std::move(*elti).forwardAsSingleStorageValue(SGF, fieldTy, Loc); - } + v = maybeEmitPropertyWrapperInitFromValue( + SGF, Loc, field, std::move(*elti)) + .forwardAsSingleStorageValue(SGF, fieldTy, Loc); ++elti; } else { // Otherwise, use its initializer. @@ -982,11 +963,8 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, auto originalVar = singleVar->getOriginalWrappedProperty(); if (originalVar && originalVar->isPropertyWrapperInitializedWithInitialValue()) { - (void)maybeEmitPropertyWrapperInitFromValue( - *this, init, singleVar, std::move(result), - [&](Expr *expr) { - result = emitRValue(expr); - }); + result = maybeEmitPropertyWrapperInitFromValue( + *this, init, singleVar, std::move(result)); } } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 8e1f13fc376e6..de1799e918afc 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2235,6 +2235,46 @@ RValue SILGenFunction::emitApplyOfStoredPropertyInitializer( subs, {}, calleeTypeInfo, ApplyOptions::None, C); } +RValue SILGenFunction::emitApplyOfPropertyWrapperBackingInitializer( + SILLocation loc, + VarDecl *var, + RValue &&originalValue, + SGFContext C) { + SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperBackingInitializer); + auto fnRef = ManagedValue::forUnmanaged(emitGlobalFunctionRef(loc, constant)); + auto fnType = fnRef.getType().castTo(); + + SubstitutionMap subs; + auto varDC = var->getInnermostDeclContext(); + if (auto genericSig = varDC->getGenericSignatureOfContext()) { + subs = SubstitutionMap::get( + genericSig, + [&](SubstitutableType *type) { + if (auto gp = type->getAs()) { + return F.mapTypeIntoContext(gp); + } + + return Type(type); + }, + LookUpConformanceInModule(varDC->getParentModule())); + } + + auto substFnType = fnType->substGenericArgs(SGM.M, subs); + + CanType resultType = + F.mapTypeIntoContext(var->getPropertyWrapperBackingPropertyType()) + ->getCanonicalType(); + AbstractionPattern origResultType(resultType); + CalleeTypeInfo calleeTypeInfo(substFnType, origResultType, resultType); + ResultPlanPtr resultPlan = + ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, C); + ArgumentScope argScope(*this, loc); + SmallVector args; + std::move(originalValue).getAll(args); + return emitApply(std::move(resultPlan), std::move(argScope), loc, fnRef, subs, + args, calleeTypeInfo, ApplyOptions::None, C); +} + RValue RValueEmitter::visitDestructureTupleExpr(DestructureTupleExpr *E, SGFContext C) { // Emit the sub-expression tuple and destructure it into elements. diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 65bed08759832..00833515db76c 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -22,6 +22,7 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/FileUnit.h" #include "swift/AST/Initializer.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILProfiler.h" @@ -127,6 +128,7 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) { case SILDeclRef::Kind::DefaultArgGenerator: return getMagicFunctionName(cast(ref.getDecl())); case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); case SILDeclRef::Kind::IVarInitializer: return getMagicFunctionName(cast(ref.getDecl())); @@ -666,6 +668,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, bool EmitProfilerIncrement) { + auto *dc = function.getDecl()->getInnermostDeclContext(); MagicFunctionName = SILGenModule::getMagicFunctionName(function); RegularLocation Loc(value); @@ -684,18 +687,52 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, } } + // For a property wrapper backing initializer, form a parameter list + // containing the wrapped value. + ParameterList *params = nullptr; + if (function.kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer) { + auto &ctx = getASTContext(); + auto param = new (ctx) ParamDecl(ParamDecl::Specifier::Owned, + SourceLoc(), SourceLoc(), + ctx.getIdentifier("$input_value"), + SourceLoc(), + ctx.getIdentifier("$input_value"), + dc); + param->setInterfaceType(function.getDecl()->getInterfaceType()); + + params = ParameterList::create(ctx, SourceLoc(), {param}, SourceLoc()); + } + CaptureInfo captureInfo; if (function.getAnyFunctionRef()) captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); - auto *dc = function.getDecl()->getInnermostDeclContext(); auto interfaceType = value->getType()->mapTypeOutOfContext(); - emitProlog(captureInfo, /*paramList=*/nullptr, /*selfParam=*/nullptr, + emitProlog(captureInfo, params, /*selfParam=*/nullptr, dc, interfaceType, /*throws=*/false, SourceLoc()); if (EmitProfilerIncrement) emitProfilerIncrement(value); prepareEpilog(value->getType(), false, CleanupLocation::get(Loc)); - emitReturnExpr(Loc, value); + + { + llvm::Optional opaqueValue; + + // For a property wrapper backing initializer, bind the opaque value used + // in the initializer expression to the given parameter. + if (function.kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer) { + auto var = cast(function.getDecl()); + auto wrappedInfo = var->getPropertyWrapperBackingPropertyInfo(); + auto param = params->get(0); + opaqueValue.emplace(*this, wrappedInfo.underlyingValue, + maybeEmitValueOfLocalVarDecl(param)); + + assert(value == wrappedInfo.initializeFromOriginal); + } + + emitReturnExpr(Loc, value); + } + emitEpilog(Loc); + mergeCleanupBlocks(); } void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 4187c86852c06..3b561daf238ca 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1460,6 +1460,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction AbstractionPattern origResultType, SGFContext C); + RValue emitApplyOfPropertyWrapperBackingInitializer( + SILLocation loc, + VarDecl *var, + RValue &&originalValue, + SGFContext C = SGFContext()); + /// A convenience method for emitApply that just handles monomorphic /// applications. RValue emitMonomorphicApply(SILLocation loc, diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 656ad9cdbf33c..3f0d8903b78c2 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -547,4 +547,3 @@ uint16_t SILGenFunction::emitProlog(ParameterList *paramList, return ArgNo; } - diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 4aa67bf9a18a2..b0cefba6ba0fe 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -25,6 +25,7 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/AST/SourceFile.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeMemberVisitor.h" @@ -1036,6 +1037,14 @@ class SILGenType : public TypeMemberVisitor { return; } + // If this variable has an attached property wrapper with an initialization + // function, emit the backing initializer function. + if (auto wrapperInfo = vd->getPropertyWrapperBackingPropertyInfo()) { + if (wrapperInfo.initializeFromOriginal && !vd->isStatic()) { + SGM.emitPropertyWrapperBackingInitializer(vd); + } + } + visitAbstractStorageDecl(vd); } diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index d40009dbd2cf7..ce1face21c57e 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -351,3 +351,4 @@ $s18keypaths_inlinable13KeypathStructV8computedSSvpACTKq ---> key path getter f $s18resilient_protocol24ResilientDerivedProtocolPxAA0c4BaseE0Tn --> associated conformance descriptor for resilient_protocol.ResilientDerivedProtocol.A: resilient_protocol.ResilientBaseProtocol $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF --> red.test(red.Res) -> red.Res $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> +$s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP --> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index 7fc2488e6ba1b..9154d52232668 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -97,7 +97,7 @@ func forceHasMemberwiseInit() { // CHECK-NOT: return // CHECK: function_ref @$s17property_wrappers17HasMemberwiseInitV2_y33_{{.*}}23WrapperWithInitialValueVyxGvpfi : $@convention(thin) <Ï„_0_0 where Ï„_0_0 : DefaultInit> () -> @out Ï„_0_0 // CHECK-NOT: return -// CHECK: function_ref @$s17property_wrappers23WrapperWithInitialValueV07wrappedF0ACyxGx_tcfC : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @thin WrapperWithInitialValue<Ï„_0_0>.Type) -> @out WrapperWithInitialValue<Ï„_0_0> +// CHECK: function_ref @$s17property_wrappers17HasMemberwiseInitV1yxvpfP : $@convention(thin) <Ï„_0_0 where Ï„_0_0 : DefaultInit> (@in Ï„_0_0) -> @out WrapperWithInitialValue<Ï„_0_0> // Initialization of z // CHECK-NOT: return @@ -229,8 +229,9 @@ struct UseLazy { @Lazy var wibble = [1, 2, 3] // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers7UseLazyV3foo3bar6wibbleACyxGSi_xSaySiGtcfC : $@convention(method) (Int, @in T, @owned Array, @thin UseLazy.Type) -> @out UseLazy - // CHECK: function_ref @$s17property_wrappers7UseLazyV4_foo33_{{.*}}AA0D0OySiGvpfiSiycfu_ : $@convention(thin) (@owned Int) -> Int - // CHECK: function_ref @$s17property_wrappers4LazyO12wrappedValueACyxGxyXA_tcfC : $@convention(method) <Ï„_0_0> (@owned @callee_guaranteed () -> @out Ï„_0_0, @thin Lazy<Ï„_0_0>.Type) -> @out Lazy<Ï„_0_0> + // CHECK: function_ref @$s17property_wrappers7UseLazyV3fooSivpfP : $@convention(thin) <Ï„_0_0 where Ï„_0_0 : DefaultInit> (Int) -> @owned Lazy + // CHECK: function_ref @$s17property_wrappers7UseLazyV3barxvpfP : $@convention(thin) <Ï„_0_0 where Ï„_0_0 : DefaultInit> (@in Ï„_0_0) -> @out Lazy<Ï„_0_0> + // CHECK: function_ref @$s17property_wrappers7UseLazyV6wibbleSaySiGvpfP : $@convention(thin) <Ï„_0_0 where Ï„_0_0 : DefaultInit> (@owned Array) -> @owned Lazy> } struct X { } @@ -325,9 +326,8 @@ struct CompositionMembers { // CHECK: %0 = string_literal utf8 "Hello" // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers18CompositionMembersV2p12p2ACSiSg_SSSgtcfC : $@convention(method) (Optional, @owned Optional, @thin CompositionMembers.Type) -> @owned CompositionMembers - // CHECK: function_ref @$s17property_wrappers8WrapperCV12wrappedValueACyxGxSg_tcfC - // CHECK: function_ref @$s17property_wrappers8WrapperBV12wrappedValueACyxGx_tcfC - // CHECK: function_ref @$s17property_wrappers8WrapperAV12wrappedValueACyxGx_tcfC + // CHECK: s17property_wrappers18CompositionMembersV3_p233_{{.*}}8WrapperAVyAA0N1BVyAA0N1CVySSGGGvpfi + } func testComposition() { @@ -388,7 +388,7 @@ struct ObservingTest { struct WithTuples { // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers10WithTuplesVACycfC : $@convention(method) (@thin WithTuples.Type) -> WithTuples { // CHECK: function_ref @$s17property_wrappers10WithTuplesV10_fractions33_F728088E0028E14D18C6A10CF68512E8LLAA07WrapperC12InitialValueVySd_S2dtGvpfi : $@convention(thin) () -> (Double, Double, Double) - // CHECK: function_ref @$s17property_wrappers23WrapperWithInitialValueV07wrappedF0ACyxGx_tcfC : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @thin WrapperWithInitialValue<Ï„_0_0>.Type) -> @out WrapperWithInitialValue<Ï„_0_0> + // CHECK: function_ref @$s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP : $@convention(thin) (Double, Double, Double) -> WrapperWithInitialValue<(Double, Double, Double)> @WrapperWithInitialValue var fractions = (1.3, 0.7, 0.3) static func getDefault() -> WithTuples { From 8613b05445bd9b54228072182d638295436180a8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 20 Sep 2019 15:51:51 -0700 Subject: [PATCH 027/140] [DI] Support definite initialization for composed property wrappers. Use the newly-introduced property wrapper backing initializer function in definite initialization (DI) to form the assign_by_wrapper instruction, rather than forming a reference to the (only) property wrapper's `init(wrappedValue:)`. This allows DI to work on properties that have multiple, composed property wrappers applied to them. --- lib/SILGen/SILGenApply.cpp | 24 ------ lib/SILGen/SILGenFunction.h | 6 -- lib/SILGen/SILGenLValue.cpp | 84 ++++++++++---------- test/SILOptimizer/di_property_wrappers.swift | 33 ++++++++ 4 files changed, 77 insertions(+), 70 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index f959d492c5e01..e52bb71d52532 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -5006,30 +5006,6 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, return emission.apply(C); } -RValue SILGenFunction::emitApplyPropertyWrapperAllocator(SILLocation loc, - SubstitutionMap subs, - SILDeclRef ctorRef, - Type wrapperTy, - CanAnyFunctionType funcTy) { - Callee callee = Callee::forDirect(*this, ctorRef, subs, loc); - - MetatypeType *MTty = MetatypeType::get(wrapperTy); - auto metatypeVal = B.createMetatype(loc, getLoweredType(MTty)); - ManagedValue mtManagedVal = ManagedValue::forUnmanaged(metatypeVal); - RValue metatypeRVal(*this, loc, MTty->getCanonicalType(), mtManagedVal); - - ArgumentSource ArgSrc(loc, std::move(metatypeRVal)); - FormalEvaluationScope writebacks(*this); - CallEmission emission(*this, std::move(callee), std::move(writebacks)); - - AnyFunctionType::Param selfParam((Type(MTty)), Identifier(), - ParameterTypeFlags()); - emission.addSelfParam(loc, std::move(ArgSrc), selfParam, funcTy.getResult()); - - RValue RV = emission.apply(); - return RV; -} - /// Emit a literal that applies the various initializers. RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { ConcreteDeclRef builtinInit; diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 3b561daf238ca..bc55917e75457 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1492,12 +1492,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ArgumentSource &&self, PreparedArguments &&args, SGFContext C); - RValue emitApplyPropertyWrapperAllocator(SILLocation loc, - SubstitutionMap subs, - SILDeclRef ctorRef, - Type wrapperTy, - CanAnyFunctionType funcTy); - CleanupHandle emitBeginApply(SILLocation loc, ManagedValue fn, SubstitutionMap subs, ArrayRef args, CanSILFunctionType substFnType, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 5849093e0314b..620257a8c51ec 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1295,29 +1295,26 @@ namespace { bool hasPropertyWrapper() const { if (auto *VD = dyn_cast(Storage)) { - // FIXME: Handle composition of property wrappers. - if (VD->getAttachedPropertyWrappers().size() == 1) { - auto wrapperInfo = VD->getAttachedPropertyWrapperTypeInfo(0); - - // If there is no init(wrapperValue:), we cannot rewrite an - // assignment into an initialization. - if (!wrapperInfo.wrappedValueInit) - return false; - - // If we have a nonmutating setter on a value type, the call - // captures all of 'self' and we cannot rewrite an assignment - // into an initialization. - if (!VD->isSetterMutating() && - VD->getDeclContext()->getSelfNominalTypeDecl() && - VD->isInstanceMember() && - !VD->getDeclContext()->getDeclaredInterfaceType() - ->hasReferenceSemantics()) { - return false; - } - - return true; + // If this is not a wrapper property that can be initialized from + // a value of the wrapped type, we can't perform the initialization. + auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo(); + if (!wrapperInfo.initializeFromOriginal) + return false; + + // If we have a nonmutating setter on a value type, the call + // captures all of 'self' and we cannot rewrite an assignment + // into an initialization. + if (!VD->isSetterMutating() && + VD->getDeclContext()->getSelfNominalTypeDecl() && + VD->isInstanceMember() && + !VD->getDeclContext()->getDeclaredInterfaceType() + ->hasReferenceSemantics()) { + return false; } + + return true; } + return false; } @@ -1404,25 +1401,32 @@ namespace { proj = std::move(SEC).project(SGF, loc, base); } - // Create the allocating initializer function. It captures the metadata. - // FIXME: Composition. - assert(field->getAttachedPropertyWrappers().size() == 1); - auto wrapperInfo = field->getAttachedPropertyWrapperTypeInfo(0); - auto ctor = wrapperInfo.wrappedValueInit; - SubstitutionMap subs = ValType->getMemberSubstitutionMap( - SGF.getModule().getSwiftModule(), ctor); - - Type ity = ctor->getInterfaceType(); - AnyFunctionType *substIty = - ity.subst(subs)->getCanonicalType()->castTo(); - - auto initRef = SILDeclRef(ctor, SILDeclRef::Kind::Allocator) - .asForeign(requiresForeignEntryPoint(ctor)); - RValue initFuncRV = - SGF.emitApplyPropertyWrapperAllocator(loc, subs,initRef, - ValType, - CanAnyFunctionType(substIty)); - ManagedValue initFn = std::move(initFuncRV).getAsSingleValue(SGF, loc); + // The property wrapper backing initializer forms an instance of + // the backing storage type from a wrapped value. + SILDeclRef initConstant( + field, SILDeclRef::Kind::PropertyWrapperBackingInitializer); + SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); + + SubstitutionMap initSubs; + if (auto genericSig = field->getInnermostDeclContext() + ->getGenericSignatureOfContext()) { + initSubs = SubstitutionMap::get( + genericSig, + [&](SubstitutableType *type) { + if (auto gp = type->getAs()) { + return SGF.F.mapTypeIntoContext(gp); + } + + return Type(type); + }, + LookUpConformanceInModule(SGF.SGM.M.getSwiftModule())); + } + + PartialApplyInst *initPAI = + SGF.B.createPartialApply(loc, initFRef, + initSubs, ArrayRef(), + ParameterConvention::Direct_Guaranteed); + ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI); // Create the allocating setter function. It captures the base address. auto setterInfo = SGF.getConstantInfo(setter); diff --git a/test/SILOptimizer/di_property_wrappers.swift b/test/SILOptimizer/di_property_wrappers.swift index a5e507a791fa8..f2b119bea7e3d 100644 --- a/test/SILOptimizer/di_property_wrappers.swift +++ b/test/SILOptimizer/di_property_wrappers.swift @@ -380,6 +380,38 @@ func testDefaultNilOptIntStruct() { // CHECK-NEXT: .. init nil } +@propertyWrapper +struct Wrapper2 { + var wrappedValue: T { + didSet { + print(" .. secondSet \(wrappedValue)") + } + } + + init(wrappedValue initialValue: T) { + print(" .. secondInit \(initialValue)") + self.wrappedValue = initialValue + } +} + +struct HasComposed { + @Wrapper @Wrapper2 var x: Int + + init() { + self.x = 17 + } +} + +func testComposed() { + // CHECK: ## Composed + print("\n## Composed") + _ = HasComposed() + + // CHECK-NEXT: .. secondInit 17 + // CHECK-NEXT: .. init Wrapper2(wrappedValue: 17) +} + + testIntStruct() testIntClass() testRefStruct() @@ -387,3 +419,4 @@ testGenericClass() testDefaultInit() testOptIntStruct() testDefaultNilOptIntStruct() +testComposed() From 5431efab71377a3eb94a4d01337bc53ab75d545b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Sep 2019 13:51:38 -0700 Subject: [PATCH 028/140] Add fixed test case from rdar://problem/53349209 --- test/decl/var/property_wrappers.swift | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index a684e6cc1a78f..69737ef311b02 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -1697,3 +1697,29 @@ struct SR_11381_W { struct SR_11381_S { @SR_11381_W var foo: Int = nil // expected-error {{'nil' is not compatible with expected argument type 'Int'}} } + +// rdar://problem/53349209 - regression in property wrapper inference +struct Concrete1: P {} + +@propertyWrapper struct ConcreteWrapper { + var wrappedValue: Concrete1 { get { fatalError() } } +} + +struct TestConcrete1 { + @ConcreteWrapper() var s1 + + func f() { + // Good: + let _: P = self.s1 + + // Bad: + self.g(s1: self.s1) + + // Ugly: + self.g(s1: self.s1 as P) + } + + func g(s1: P) { + // ... + } +} From 81c0a0ea936a8fa12228ad5d38ec6ec80cf9c6ee Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Sep 2019 20:51:19 -0700 Subject: [PATCH 029/140] [Property wrappers] Allow init(wrappedValue:) initializers with defaulted args Generalize the type checking when searching for "wrappedValue" initializers to also allow initializers that have other, defaulted parameters as well. --- include/swift/AST/DiagnosticsSema.def | 2 - include/swift/AST/PropertyWrappers.h | 11 +- lib/AST/TypeCheckRequests.cpp | 5 - lib/Sema/TypeCheckPropertyWrapper.cpp | 208 +++++++++++-------- test/SILGen/property_wrappers.swift | 39 +++- test/SILOptimizer/di_property_wrappers.swift | 6 +- test/decl/var/property_wrappers.swift | 8 +- 7 files changed, 174 insertions(+), 105 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ed0e5f7ff7faf..d714dfaad1a46 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4528,8 +4528,6 @@ ERROR(property_wrapper_wrong_initial_value_init, none, (DeclName, Type, Type)) ERROR(property_wrapper_failable_init, none, "%0 cannot be failable", (DeclName)) -ERROR(property_wrapper_ambiguous_initial_value_init, none, - "property wrapper type %0 has multiple initial-value initializers", (Type)) ERROR(property_wrapper_ambiguous_default_value_init, none, "property wrapper type %0 has multiple default-value initializers", (Type)) ERROR(property_wrapper_type_requirement_not_accessible,none, diff --git a/include/swift/AST/PropertyWrappers.h b/include/swift/AST/PropertyWrappers.h index d7e977e59262b..50e71d0a76fa8 100644 --- a/include/swift/AST/PropertyWrappers.h +++ b/include/swift/AST/PropertyWrappers.h @@ -35,12 +35,13 @@ struct PropertyWrapperTypeInfo { /// directed. VarDecl *valueVar = nullptr; - /// The initializer init(wrappedValue:) that will be called when the + /// Whether there is an init(wrappedValue:) that will be called when the /// initializing the property wrapper type from a value of the property type. - /// - /// This initializer is optional, but if present will be used for the `=` - /// initialization syntax. - ConstructorDecl *wrappedValueInit = nullptr; + enum { + NoWrappedValueInit = 0, + HasWrappedValueInit, + HasInitialValueInit + } wrappedValueInit = NoWrappedValueInit; /// The initializer `init()` that will be called to default-initialize a /// value with an attached property wrapper. diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index f8b17a4db6ea7..9ed6ff6a2d0f6 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -564,11 +564,6 @@ void swift::simple_display( out << propertyWrapper.valueVar->printRef(); else out << "null"; - out << ", "; - if (propertyWrapper.wrappedValueInit) - out << propertyWrapper.wrappedValueInit->printRef(); - else - out << "null"; out << " }"; } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 9d4b6894f5e88..5b97d564cb537 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -81,88 +81,121 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, /// Determine whether we have a suitable wrapped-value initializer within /// a property wrapper type. -static ConstructorDecl *findInitialValueInit(ASTContext &ctx, - NominalTypeDecl *nominal, - VarDecl *valueVar, - Identifier argumentLabel) { +static ConstructorDecl *findInitialValueInit( + ASTContext &ctx, + NominalTypeDecl *nominal, + VarDecl *valueVar, + Identifier argumentLabel) { + // Retrieve the type of the 'value' property. + Type valueVarType = valueVar->getValueInterfaceType(); + + enum class NonViableReason { + Failable, + ParameterTypeMismatch, + Inaccessible, + }; + SmallVector, 2> nonviable; SmallVector initialValueInitializers; - DeclName initName(ctx, DeclBaseName::createConstructor(), {argumentLabel}); + SmallVector decls; - nominal->lookupQualified(nominal, initName, NL_QualifiedDefault, decls); + nominal->lookupQualified(nominal, DeclBaseName::createConstructor(), + NL_QualifiedDefault, decls); for (const auto &decl : decls) { auto init = dyn_cast(decl); - if (!init || init->getDeclContext() != nominal) + if (!init || init->getDeclContext() != nominal || init->getGenericParams()) continue; - initialValueInitializers.push_back(init); - } - - switch (initialValueInitializers.size()) { - case 0: - return nullptr; + // Check whether every parameter meets one of the following criteria: + // (1) The parameter has a default argument, or + // (2) The parameter has the given argument label. + ParamDecl *wrappedValueParam = nullptr; + for (auto param : *init->getParameters()) { + // Recognize the first parameter with the requested argument label. + if (param->getArgumentName() == argumentLabel && !wrappedValueParam) { + wrappedValueParam = param; + continue; + } - case 1: - break; + if (param->getDefaultArgumentKind() != DefaultArgumentKind::None) + continue; - default: - // Diagnose ambiguous initializers. - nominal->diagnose(diag::property_wrapper_ambiguous_initial_value_init, - nominal->getDeclaredType()); - for (auto init : initialValueInitializers) { - init->diagnose(diag::kind_declname_declared_here, - init->getDescriptiveKind(), init->getFullName()); + // Forget we had a match. + wrappedValueParam = nullptr; + break; } - return nullptr; - } - // The initializer must be as accessible as the nominal type. - auto init = initialValueInitializers.front(); - if (init->getFormalAccess() < nominal->getFormalAccess()) { - init->diagnose(diag::property_wrapper_type_requirement_not_accessible, - init->getFormalAccess(), init->getDescriptiveKind(), - init->getFullName(), nominal->getDeclaredType(), - nominal->getFormalAccess()); - return nullptr; - } + if (!wrappedValueParam) + continue; - // Retrieve the type of the 'value' property. - Type valueVarType = valueVar->getValueInterfaceType(); + // Failable initializers cannot be used. + if (init->isFailable()) { + nonviable.push_back( + std::make_tuple(init, NonViableReason::Failable, Type())); + continue; + } - // Retrieve the parameter type of the initializer. - Type paramType; - if (auto *curriedInitType = - init->getInterfaceType()->getAs()) { - if (auto *initType = - curriedInitType->getResult()->getAs()) { - if (initType->getParams().size() == 1) { - const auto ¶m = initType->getParams()[0]; - if (!param.isInOut() && !param.isVariadic()) { - paramType = param.getPlainType(); - if (param.isAutoClosure()) { - if (auto *fnType = paramType->getAs()) - paramType = fnType->getResult(); - } - } + // Check accessibility. + if (init->getFormalAccess() < nominal->getFormalAccess()) { + nonviable.push_back( + std::make_tuple(init, NonViableReason::Inaccessible, Type())); + continue; + } + + Type paramType; + if (!wrappedValueParam->isInOut() && !wrappedValueParam->isVariadic()) { + paramType = wrappedValueParam->getInterfaceType(); + if (wrappedValueParam->isAutoClosure()) { + if (auto *fnType = paramType->getAs()) + paramType = fnType->getResult(); } } - } + + if (!paramType) + continue; - // The parameter type must be the same as the type of `valueVar` or an - // autoclosure thereof. - if (!paramType->isEqual(valueVarType)) { - init->diagnose(diag::property_wrapper_wrong_initial_value_init, initName, - paramType, valueVarType); - valueVar->diagnose(diag::decl_declared_here, valueVar->getFullName()); - return nullptr; + // The parameter type must be the same as the type of `valueVar` or an + // autoclosure thereof. + if (!paramType->isEqual(valueVarType)) { + nonviable.push_back( + std::make_tuple(init, NonViableReason::ParameterTypeMismatch, + paramType)); + continue; + } + + // Check the type + initialValueInitializers.push_back(init); } - // The initializer must not be failable. - if (init->isFailable()) { - init->diagnose(diag::property_wrapper_failable_init, initName); - return nullptr; + // If we found some nonviable candidates but no viable ones, complain. + if (initialValueInitializers.empty() && !nonviable.empty()) { + for (const auto &candidate : nonviable) { + auto init = std::get<0>(candidate); + auto reason = std::get<1>(candidate); + auto paramType = std::get<2>(candidate); + switch (reason) { + case NonViableReason::Failable: + init->diagnose(diag::property_wrapper_failable_init, + init->getFullName()); + break; + + case NonViableReason::Inaccessible: + init->diagnose(diag::property_wrapper_type_requirement_not_accessible, + init->getFormalAccess(), init->getDescriptiveKind(), + init->getFullName(), nominal->getDeclaredType(), + nominal->getFormalAccess()); + break; + + case NonViableReason::ParameterTypeMismatch: + init->diagnose(diag::property_wrapper_wrong_initial_value_init, + init->getFullName(), paramType, valueVarType); + valueVar->diagnose(diag::decl_declared_here, valueVar->getFullName()); + break; + } + } } - return init; + return initialValueInitializers.empty() ? nullptr + : initialValueInitializers.front(); } /// Determine whether we have a suitable init() within a property @@ -302,22 +335,23 @@ PropertyWrapperTypeInfoRequest::evaluate( PropertyWrapperTypeInfo result; result.valueVar = valueVar; - result.wrappedValueInit = - findInitialValueInit(ctx, nominal, valueVar, ctx.Id_wrappedValue); - - if (!result.wrappedValueInit) { - // Look for the older name init(initialValue:). - result.wrappedValueInit = - findInitialValueInit(ctx, nominal, valueVar, ctx.Id_initialValue); - if (result.wrappedValueInit && - result.wrappedValueInit->getLoc().isValid()) { - auto diag = result.wrappedValueInit->diagnose( - diag::property_wrapper_init_initialValue); - auto param = result.wrappedValueInit->getParameters()->get(0); - if (param->getArgumentNameLoc().isValid()) - diag.fixItReplace(param->getArgumentNameLoc(), "wrappedValue"); - else - diag.fixItInsert(param->getLoc(), "wrappedValue "); + if (findInitialValueInit(ctx, nominal, valueVar, ctx.Id_wrappedValue)) + result.wrappedValueInit = PropertyWrapperTypeInfo::HasWrappedValueInit; + else if (auto init = findInitialValueInit( + ctx, nominal, valueVar, ctx.Id_initialValue)) { + result.wrappedValueInit = PropertyWrapperTypeInfo::HasInitialValueInit; + + if (init->getLoc().isValid()) { + auto diag = init->diagnose(diag::property_wrapper_init_initialValue); + for (auto param : *init->getParameters()) { + if (param->getArgumentName() == ctx.Id_initialValue) { + if (param->getArgumentNameLoc().isValid()) + diag.fixItReplace(param->getArgumentNameLoc(), "wrappedValue"); + else + diag.fixItInsert(param->getLoc(), "wrappedValue "); + break; + } + } } } @@ -647,12 +681,18 @@ Expr *swift::buildPropertyWrapperInitialValueCall( // call `init(wrappedValue:)` directly. auto attr = wrapperAttrs[i]; if (!attr->getArg() || ignoreAttributeArgs) { - Identifier argName = ctx.Id_wrappedValue; - if (auto init - = var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { - argName = init->getFullName().getArgumentNames()[0]; + Identifier argName; + switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) { + case PropertyWrapperTypeInfo::HasInitialValueInit: + argName = ctx.Id_initialValue; + break; + + case PropertyWrapperTypeInfo::HasWrappedValueInit: + case PropertyWrapperTypeInfo::NoWrappedValueInit: + argName = ctx.Id_wrappedValue; + break; } - + auto endLoc = initializer->getEndLoc(); if (endLoc.isInvalid() && startLoc.isValid()) endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End; diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index 9154d52232668..e5a8802f51631 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -453,13 +453,48 @@ public class TestClass { @WrapperWithInitialValue var value: T // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers9TestClassC5value8protocolACyxGx_qd__tcAA0C8ProtocolRd__lufc - // CHECK: metatype $@thin WrapperWithInitialValue.Type - // CHECK: function_ref @$s17property_wrappers23WrapperWithInitialValueV07wrappedF0ACyxGx_tcfCTc + // CHECK: [[BACKING_INIT:%.*]] = function_ref @$s17property_wrappers9TestClassC5valuexvpfP : $@convention(thin) <Ï„_0_0> (@in Ï„_0_0) -> @out WrapperWithInitialValue<Ï„_0_0> + // CHECK-NEXT: partial_apply [callee_guaranteed] [[BACKING_INIT]]() init(value: T, protocol: U) { self.value = value } } +// Composition with wrappedValue initializers that have default values. +@propertyWrapper +struct Outer { + var wrappedValue: Value + + init(a: Int = 17, wrappedValue: Value, s: String = "hello") { + self.wrappedValue = wrappedValue + } +} + + +@propertyWrapper +struct Inner { + var wrappedValue: Value + + init(wrappedValue: @autoclosure @escaping () -> Value, d: Double = 3.14159) { + self.wrappedValue = wrappedValue() + } +} + +struct ComposedInit { + @Outer @Inner var value: Int + + // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers12ComposedInitV5valueSivpfP : $@convention(thin) (Int) -> Outer> { + // CHECK: function_ref @$s17property_wrappers12ComposedInitV6_value33_F728088E0028E14D18C6A10CF68512E8LLAA5OuterVyAA5InnerVySiGGvpfiSiycfu_ + // CHECK: function_ref @$s17property_wrappers5InnerV12wrappedValue1dACyxGxyXA_SdtcfcfA0_ + // CHECK: function_ref @$s17property_wrappers5InnerV12wrappedValue1dACyxGxyXA_SdtcfC + // CHECK: function_ref @$s17property_wrappers5OuterV1a12wrappedValue1sACyxGSi_xSStcfcfA_ + // CHECK: function_ref @$s17property_wrappers5OuterV1a12wrappedValue1sACyxGSi_xSStcfcfA1_ + // CHECK: function_ref @$s17property_wrappers5OuterV1a12wrappedValue1sACyxGSi_xSStcfC + init() { + self.value = 17 + } +} + // CHECK-LABEL: sil_vtable ClassUsingWrapper { // CHECK-NEXT: #ClassUsingWrapper.x!getter.1: (ClassUsingWrapper) -> () -> Int : @$s17property_wrappers17ClassUsingWrapperC1xSivg // ClassUsingWrapper.x.getter diff --git a/test/SILOptimizer/di_property_wrappers.swift b/test/SILOptimizer/di_property_wrappers.swift index f2b119bea7e3d..e3ae6f3de9625 100644 --- a/test/SILOptimizer/di_property_wrappers.swift +++ b/test/SILOptimizer/di_property_wrappers.swift @@ -388,8 +388,8 @@ struct Wrapper2 { } } - init(wrappedValue initialValue: T) { - print(" .. secondInit \(initialValue)") + init(before: Int = -10, wrappedValue initialValue: T, after: String = "end") { + print(" .. secondInit \(before), \(initialValue), \(after)") self.wrappedValue = initialValue } } @@ -407,7 +407,7 @@ func testComposed() { print("\n## Composed") _ = HasComposed() - // CHECK-NEXT: .. secondInit 17 + // CHECK-NEXT: .. secondInit -10, 17, end // CHECK-NEXT: .. init Wrapper2(wrappedValue: 17) } diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 69737ef311b02..0b1d4b1175da8 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -99,13 +99,13 @@ struct InitialValueTypeMismatch { } @propertyWrapper -struct MultipleInitialValues { // expected-error{{property wrapper type 'MultipleInitialValues' has multiple initial-value initializers}} - var wrappedValue: Value? = nil +struct MultipleInitialValues { + var wrappedValue: Value? = nil // expected-note 2{{'wrappedValue' declared here}} - init(wrappedValue initialValue: Int) { // expected-note{{initializer 'init(wrappedValue:)' declared here}} + init(wrappedValue initialValue: Int) { // expected-error{{'init(wrappedValue:)' parameter type ('Int') must be the same as its 'wrappedValue' property type ('Value?') or an @autoclosure thereof}} } - init(wrappedValue initialValue: Double) { // expected-note{{initializer 'init(wrappedValue:)' declared here}} + init(wrappedValue initialValue: Double) { // expected-error{{'init(wrappedValue:)' parameter type ('Double') must be the same as its 'wrappedValue' property type ('Value?') or an @autoclosure thereof}} } } From 4ba1833ca2cd37bced7e758696c6f1b5792afd9d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Sep 2019 21:01:10 -0700 Subject: [PATCH 030/140] [TBDGen] Emit property wrapper backing initializer symbol --- lib/TBDGen/TBDGen.cpp | 10 ++++++++++ test/TBD/property_wrapper.swift | 15 +++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test/TBD/property_wrapper.swift diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 8c400c28f18ce..45e2dbd6bcf94 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ASTVisitor.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/Basic/LLVM.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/FormalLinkage.h" @@ -309,8 +310,17 @@ void TBDGenVisitor::visitVarDecl(VarDecl *VD) { if (VD->isLazilyInitializedGlobal()) addSymbol(SILDeclRef(VD, SILDeclRef::Kind::GlobalAccessor)); } + } + // Wrapped non-static member properties may have a backing initializer. + if (auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo()) { + if (wrapperInfo.initializeFromOriginal && !VD->isStatic()) { + addSymbol( + SILDeclRef(VD, SILDeclRef::Kind::PropertyWrapperBackingInitializer)); + } + }; + visitAbstractStorageDecl(VD); } diff --git a/test/TBD/property_wrapper.swift b/test/TBD/property_wrapper.swift new file mode 100644 index 0000000000000..99ce36621e44b --- /dev/null +++ b/test/TBD/property_wrapper.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-frontend -emit-ir -o/dev/null -parse-as-library -module-name test -validate-tbd-against-ir=all %s + +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +public struct UseWrapper { + @Wrapper public var string = "hello" +} + From a1541a19df5bfdfea76e3e26fae2b56f65c3ea3e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Sep 2019 21:34:47 -0700 Subject: [PATCH 031/140] [TBDGen] Only emit backing initializer for non-resilient properties. --- lib/TBDGen/TBDGen.cpp | 15 +++++++-------- test/TBD/property_wrapper.swift | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 45e2dbd6bcf94..f4f18494cbf75 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -311,15 +311,14 @@ void TBDGenVisitor::visitVarDecl(VarDecl *VD) { addSymbol(SILDeclRef(VD, SILDeclRef::Kind::GlobalAccessor)); } - } - - // Wrapped non-static member properties may have a backing initializer. - if (auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo()) { - if (wrapperInfo.initializeFromOriginal && !VD->isStatic()) { - addSymbol( - SILDeclRef(VD, SILDeclRef::Kind::PropertyWrapperBackingInitializer)); + // Wrapped non-static member properties may have a backing initializer. + if (auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo()) { + if (wrapperInfo.initializeFromOriginal && !VD->isStatic()) { + addSymbol( + SILDeclRef(VD, SILDeclRef::Kind::PropertyWrapperBackingInitializer)); + } } - }; + } visitAbstractStorageDecl(VD); } diff --git a/test/TBD/property_wrapper.swift b/test/TBD/property_wrapper.swift index 99ce36621e44b..edec5f9408c7b 100644 --- a/test/TBD/property_wrapper.swift +++ b/test/TBD/property_wrapper.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-ir -o/dev/null -parse-as-library -module-name test -validate-tbd-against-ir=all %s +// RUN: %target-swift-frontend -emit-ir -o/dev/null -parse-as-library -module-name test -validate-tbd-against-ir=all -enable-library-evolution %s @propertyWrapper public struct Wrapper { From 2a74d0b821cd887a52249fa7eb3fd4540ab8970a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Sep 2019 07:19:52 -0700 Subject: [PATCH 032/140] Use ParamDecl::isDefaultArgument(). --- lib/Sema/TypeCheckPropertyWrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 5b97d564cb537..40eb5e9cb545c 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -116,7 +116,7 @@ static ConstructorDecl *findInitialValueInit( continue; } - if (param->getDefaultArgumentKind() != DefaultArgumentKind::None) + if (param->isDefaultArgument()) continue; // Forget we had a match. From 2120a31cf0eefb3b641aba7279c948718339d3ac Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 24 Sep 2019 10:24:16 -0700 Subject: [PATCH 033/140] [Diagnostics] Correctly diagnose misplaced missing argument Due to the fact that `matchCallArgument` can't and doesn't take types into consideration while matching arguments to parameters, when both arguments are un-labeled, it's impossible to say which one is missing: func foo(_: Int, _: String) {} foo("") In this case first argument is missing, but we end up with two fixes - argument mismatch (for #1) and missing argument (for #2), which is incorrect so it has to be handled specially. --- lib/Sema/CSDiagnostics.cpp | 98 ++++++++++++++++++++++++++++++++++++++ lib/Sema/CSDiagnostics.h | 27 ++++++++++- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 5de1f19d013f5..38edae8f5a5ad 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3565,6 +3565,7 @@ bool ImplicitInitOnNonConstMetatypeFailure::diagnoseAsError() { } bool MissingArgumentsFailure::diagnoseAsError() { + auto &cs = getConstraintSystem(); auto *locator = getLocator(); auto path = locator->getPath(); @@ -3574,6 +3575,11 @@ bool MissingArgumentsFailure::diagnoseAsError() { path.back().getKind() == ConstraintLocator::ApplyArgument)) return false; + // If this is a misplaced `missng argument` situation, it would be + // diagnosed by invalid conversion fix. + if (isMisplacedMissingArgument(cs, locator)) + return false; + auto *anchor = getAnchor(); if (auto *captureList = dyn_cast(anchor)) anchor = captureList->getClosureBody(); @@ -3901,6 +3907,68 @@ bool MissingArgumentsFailure::isPropertyWrapperInitialization() const { return NTD && NTD->getAttrs().hasAttribute(); } +bool MissingArgumentsFailure::isMisplacedMissingArgument( + ConstraintSystem &cs, ConstraintLocator *locator) { + auto *calleeLocator = cs.getCalleeLocator(locator); + auto overloadChoice = cs.findSelectedOverloadFor(calleeLocator); + if (!overloadChoice) + return false; + + auto *fnType = + cs.simplifyType(overloadChoice->ImpliedType)->getAs(); + if (!(fnType && fnType->getNumParams() == 2)) + return false; + + auto *anchor = locator->getAnchor(); + + auto hasFixFor = [&](FixKind kind, ConstraintLocator *locator) -> bool { + auto fix = llvm::find_if(cs.getFixes(), [&](const ConstraintFix *fix) { + return fix->getLocator() == locator; + }); + + if (fix == cs.getFixes().end()) + return false; + + return (*fix)->getKind() == kind; + }; + + auto *callLocator = + cs.getConstraintLocator(anchor, ConstraintLocator::ApplyArgument); + + auto argFlags = fnType->getParams()[0].getParameterFlags(); + auto *argLoc = cs.getConstraintLocator( + callLocator, LocatorPathElt::ApplyArgToParam(0, 0, argFlags)); + + if (!(hasFixFor(FixKind::AllowArgumentTypeMismatch, argLoc) && + hasFixFor(FixKind::AddMissingArguments, callLocator))) + return false; + + Expr *argExpr = nullptr; + if (auto *call = dyn_cast(anchor)) { + argExpr = call->getArg(); + } else if (auto *subscript = dyn_cast(anchor)) { + argExpr = subscript->getIndex(); + } else { + return false; + } + + Expr *argument = nullptr; + if (auto *PE = dyn_cast(argExpr)) { + argument = PE->getSubExpr(); + } else { + auto *tuple = cast(argExpr); + if (tuple->getNumElements() != 1) + return false; + argument = tuple->getElement(0); + } + + auto argType = cs.simplifyType(cs.getType(argument)); + auto paramType = fnType->getParams()[1].getPlainType(); + + auto &TC = cs.getTypeChecker(); + return TC.isConvertibleTo(argType, paramType, cs.DC); +} + bool ClosureParamDestructuringFailure::diagnoseAsError() { auto *closure = cast(getAnchor()); auto params = closure->getParameters(); @@ -4845,6 +4913,9 @@ void InOutConversionFailure::fixItChangeArgumentType() const { } bool ArgumentMismatchFailure::diagnoseAsError() { + if (diagnoseMisplacedMissingArgument()) + return true; + if (diagnoseConversionToBool()) return true; @@ -5074,6 +5145,33 @@ bool ArgumentMismatchFailure::diagnoseArchetypeMismatch() const { return true; } +bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const { + auto &cs = getConstraintSystem(); + auto *locator = getLocator(); + + if (!MissingArgumentsFailure::isMisplacedMissingArgument(cs, locator)) + return false; + + auto info = *getFunctionArgApplyInfo(locator); + + auto *argType = cs.createTypeVariable( + cs.getConstraintLocator(locator, LocatorPathElt::SynthesizedArgument(1)), + /*flags=*/0); + + // Assign new type variable to a type of a parameter. + auto *fnType = info.getFnType(); + const auto ¶m = fnType->getParams()[0]; + cs.assignFixedType(argType, param.getOldType()); + + auto *anchor = getRawAnchor(); + + MissingArgumentsFailure failure( + getParentExpr(), cs, {param.withType(argType)}, + cs.getConstraintLocator(anchor, ConstraintLocator::ApplyArgument)); + + return failure.diagnoseSingleMissingArgument(); +} + 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 4bf9b4aa524e4..4440e25bfc8cf 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1197,9 +1197,9 @@ class MissingArgumentsFailure final : public FailureDiagnostic { bool diagnoseAsError() override; -private: bool diagnoseSingleMissingArgument() const; +private: /// If missing arguments come from a closure, /// let's produce tailored diagnostics. bool diagnoseClosure(ClosureExpr *closure); @@ -1212,6 +1212,21 @@ class MissingArgumentsFailure final : public FailureDiagnostic { /// an implicit call to a property wrapper initializer e.g. /// `@Foo(answer: 42) var question = "ultimate question"` bool isPropertyWrapperInitialization() const; + +public: + /// Due to the fact that `matchCallArgument` can't and + /// doesn't take types into consideration while matching + /// arguments to parameters, for cases where both arguments + /// are un-labeled, it's impossible to say which one is missing: + /// + /// func foo(_: Int, _: String) {} + /// foo("") + /// + /// In this case first argument is missing, but we end up with + /// two fixes - argument mismatch (for #1) and missing argument + /// (for #2), which is incorrect so it has to be handled specially. + static bool isMisplacedMissingArgument(ConstraintSystem &cs, + ConstraintLocator *locator); }; class OutOfOrderArgumentFailure final : public FailureDiagnostic { @@ -1648,6 +1663,16 @@ class ArgumentMismatchFailure : public ContextualFailure { bool diagnoseUseOfReferenceEqualityOperator() const; protected: + + /// Situations like this: + /// + /// func foo(_: Int, _: String) {} + /// foo("") + /// + /// Are currently impossible to fix correctly, + /// so we have to attend to that in diagnostics. + bool diagnoseMisplacedMissingArgument() const; + SourceLoc getLoc() const { return getAnchor()->getLoc(); } ValueDecl *getDecl() const { From a3e26abd8afb4ebdf666bd5321e00bdb8d4a5db1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 24 Sep 2019 11:00:57 -0700 Subject: [PATCH 034/140] [ConstraintSystem] Mark each of the synthesized arguments as a "hole" --- lib/Sema/CSSimplify.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 93e635b4a16d3..0e8ca2a6af860 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -847,6 +847,11 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { CS.getConstraintLocator(argLoc), TVO_CanBindToInOut | TVO_CanBindToLValue | TVO_CanBindToNoEscape); + CS.recordHole(argType); + CS.addUnsolvedConstraint(Constraint::create( + CS, ConstraintKind::Defaultable, argType, CS.getASTContext().TheAnyType, + CS.getConstraintLocator(argLoc))); + Arguments.push_back(param.withType(argType)); ++NumSynthesizedArgs; From 888529cf97827d09bca13b6595e8663bfd7615cb Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Tue, 24 Sep 2019 11:39:54 -0700 Subject: [PATCH 035/140] [IDE] Fix ModelASTWalker passing syntax nodes before the corresponding AST nodes had been visited This was causing the tokens comprising image literals to be output separately, rather than as a single object literal. Resolves rdar://problem/55045797 --- lib/IDE/SyntaxModel.cpp | 8 +------- test/IDE/coloring.swift | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index 60746c1f8b375..a6925e8853af2 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -525,13 +525,7 @@ std::pair ModelASTWalker::walkToExprPre(Expr *E) { pushStructureNode(SN, E); } else if (auto *Tup = dyn_cast(E)) { auto *ParentE = Parent.getAsExpr(); - if (isCurrentCallArgExpr(Tup)) { - for (unsigned I = 0; I < Tup->getNumElements(); ++ I) { - SourceLoc NameLoc = Tup->getElementNameLoc(I); - if (NameLoc.isValid()) - passTokenNodesUntil(NameLoc, PassNodesBehavior::ExcludeNodeAtLocation); - } - } else if (!ParentE || !isa(ParentE)) { + if (!isCurrentCallArgExpr(Tup) && (!ParentE || !isa(ParentE))) { SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::TupleExpression; SN.Range = charSourceRangeFromSourceRange(SM, Tup->getSourceRange()); diff --git a/test/IDE/coloring.swift b/test/IDE/coloring.swift index 2bb6778cb7dd8..821af3a198f07 100644 --- a/test/IDE/coloring.swift +++ b/test/IDE/coloring.swift @@ -273,6 +273,8 @@ func bar(x: Int) -> (Int, Float) { // CHECK: #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) +// CHECK: test(#imageLiteral(resourceName: "test"), test: 0) +test(#imageLiteral(resourceName: "test"), test: 0) class GenC {} From fc8a2e6f8627f49c02b402563b3cebefc5dd2983 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 23 Sep 2019 11:20:50 -0700 Subject: [PATCH 036/140] [SyntaxParse] Parse associatedtype decl Along with inheritance clause. --- include/swift/Parse/ASTGen.h | 27 ++ include/swift/Parse/LibSyntaxGenerator.h | 2 +- include/swift/Parse/Parser.h | 19 +- include/swift/Parse/SyntaxParsingContext.h | 31 +- include/swift/Syntax/Syntax.h | 2 +- include/swift/Syntax/SyntaxData.h | 2 +- lib/Parse/ASTGen.cpp | 143 ++++++- lib/Parse/ParseDecl.cpp | 372 ++++++++++--------- lib/Parse/ParseGeneric.cpp | 5 +- lib/Syntax/Syntax.cpp | 2 +- test/Syntax/serialize_tupletype.swift.result | 48 +-- utils/gyb_syntax_support/AttributeNodes.py | 1 + utils/gyb_syntax_support/DeclNodes.py | 1 + 13 files changed, 438 insertions(+), 217 deletions(-) diff --git a/include/swift/Parse/ASTGen.h b/include/swift/Parse/ASTGen.h index 7cec25cbea816..06896bf6bc664 100644 --- a/include/swift/Parse/ASTGen.h +++ b/include/swift/Parse/ASTGen.h @@ -16,6 +16,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/TypeRepr.h" #include "swift/Parse/PersistentParserState.h" #include "swift/Syntax/SyntaxNodes.h" #include "llvm/ADT/DenseMap.h" @@ -43,6 +44,30 @@ class ASTGen { SourceLoc generate(const syntax::TokenSyntax &Tok, const SourceLoc Loc); + SourceLoc generateIdentifierDeclName(const syntax::TokenSyntax &Tok, + const SourceLoc, Identifier &Identifier); + +public: + //===--------------------------------------------------------------------===// + // Decls. + + Decl *generate(const syntax::DeclSyntax &Decl, const SourceLoc Loc); + TypeDecl *generate(const syntax::AssociatedtypeDeclSyntax &Decl, + const SourceLoc Loc); + + TrailingWhereClause *generate(const syntax::GenericWhereClauseSyntax &syntax, + const SourceLoc Loc); + MutableArrayRef + generate(const syntax::TypeInheritanceClauseSyntax &syntax, + const SourceLoc Loc, bool allowClassRequirement); + +private: + DeclAttributes + generateDeclAttributes(const syntax::DeclSyntax &D, + const Optional &attrs, + const Optional &modifiers, + SourceLoc Loc, bool includeComments); + public: //===--------------------------------------------------------------------===// // Expressions. @@ -97,6 +122,8 @@ class ASTGen { const SourceLoc Loc); TypeRepr *generate(const syntax::ImplicitlyUnwrappedOptionalTypeSyntax &Type, const SourceLoc Loc); + TypeRepr *generate(const syntax::ClassRestrictionTypeSyntax &Type, + const SourceLoc Loc); TypeRepr *generate(const syntax::CodeCompletionTypeSyntax &Type, const SourceLoc Loc); TypeRepr *generate(const syntax::UnknownTypeSyntax &Type, diff --git a/include/swift/Parse/LibSyntaxGenerator.h b/include/swift/Parse/LibSyntaxGenerator.h index 1333ed74af219..4f725904f2753 100644 --- a/include/swift/Parse/LibSyntaxGenerator.h +++ b/include/swift/Parse/LibSyntaxGenerator.h @@ -38,7 +38,7 @@ class LibSyntaxGenerator { assert(Node.isDeferredToken()); auto Kind = Node.getTokenKind(); - auto Range = Node.getDeferredTokenRangeWithTrivia(); + auto Range = Node.getDeferredTokenRange(); auto LeadingTriviaPieces = Node.getDeferredLeadingTriviaPieces(); auto TrailingTriviaPieces = Node.getDeferredTrailingTriviaPieces(); diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 840b0dac37ed1..2e36cc1abbff7 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1004,12 +1004,23 @@ class Parser { bool delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, IterableDeclContext *IDC); + ParsedSyntaxResult + parseTypeInheritanceClauseSyntax(bool allowClassRequirement, + bool allowAnyObject); + + ParsedSyntaxResult + parseDeclAssociatedTypeSyntax(ParseDeclOptions flags, + Optional attrs, + Optional modifiers); + ParserResult parseDeclTypeAlias(ParseDeclOptions Flags, - DeclAttributes &Attributes); + DeclAttributes &Attributes, + SourceLoc leadingLoc); ParserResult parseDeclAssociatedType(ParseDeclOptions Flags, - DeclAttributes &Attributes); - + DeclAttributes &Attributes, + SourceLoc leadingLoc); + /// Parse a #if ... #endif directive. /// Delegate callback function to parse elements in the blocks. ParserResult parseIfConfig( @@ -1091,7 +1102,7 @@ class Parser { ParserResult parseDeclImport(ParseDeclOptions Flags, DeclAttributes &Attributes); - ParserStatus parseInheritance(SmallVectorImpl &Inherited, + ParserStatus parseInheritance(MutableArrayRef &Inherited, bool allowClassRequirement, bool allowAnyObject); ParserStatus parseDeclItem(bool &PreviousHadSemi, diff --git a/include/swift/Parse/SyntaxParsingContext.h b/include/swift/Parse/SyntaxParsingContext.h index 6df76bebca037..96d4d57924429 100644 --- a/include/swift/Parse/SyntaxParsingContext.h +++ b/include/swift/Parse/SyntaxParsingContext.h @@ -279,17 +279,9 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { } /// Returns the topmost Syntax node. - template SyntaxNode topNode() { - ParsedRawSyntaxNode &TopNode = getStorage().back(); - if (TopNode.isRecorded()) { - OpaqueSyntaxNode OpaqueNode = TopNode.getOpaqueNode(); - return getSyntaxCreator().getLibSyntaxNodeFor(OpaqueNode); - } - return getSyntaxCreator().createNode(TopNode.copyDeferred()); - } + template SyntaxNode topNode(); - template - llvm::Optional popIf() { + template llvm::Optional popIf() { auto &Storage = getStorage(); if (Storage.size() <= Offset) return llvm::None; @@ -376,5 +368,24 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { "Only meant for use in the debugger"); }; +template +inline SyntaxNode SyntaxParsingContext::topNode() { + ParsedRawSyntaxNode &TopNode = getStorage().back(); + if (TopNode.isRecorded()) { + OpaqueSyntaxNode OpaqueNode = TopNode.getOpaqueNode(); + return getSyntaxCreator().getLibSyntaxNodeFor(OpaqueNode); + } + return getSyntaxCreator().createNode(TopNode.copyDeferred()); +} + +template <> inline TokenSyntax SyntaxParsingContext::topNode() { + ParsedRawSyntaxNode &TopNode = getStorage().back(); + if (TopNode.isRecorded()) { + OpaqueSyntaxNode OpaqueNode = TopNode.getOpaqueNode(); + return getSyntaxCreator().getLibSyntaxNodeFor(OpaqueNode); + } + return getSyntaxCreator().createToken(TopNode.copyDeferred()); +} + } // namespace swift #endif // SWIFT_SYNTAX_PARSING_CONTEXT_H diff --git a/include/swift/Syntax/Syntax.h b/include/swift/Syntax/Syntax.h index c4b2d8f227dc3..31fac98aa9a16 100644 --- a/include/swift/Syntax/Syntax.h +++ b/include/swift/Syntax/Syntax.h @@ -83,7 +83,7 @@ class Syntax { SyntaxKind getKind() const; /// Get the shared raw syntax. - RC getRaw() const; + const RC &getRaw() const; /// Get an ID for this node that is stable across incremental parses SyntaxNodeId getId() const { return getRaw()->getId(); } diff --git a/include/swift/Syntax/SyntaxData.h b/include/swift/Syntax/SyntaxData.h index aa6a69f1369ee..6159f2e01e2d8 100644 --- a/include/swift/Syntax/SyntaxData.h +++ b/include/swift/Syntax/SyntaxData.h @@ -186,7 +186,7 @@ class SyntaxData final CursorIndex IndexInParent = 0); /// Returns the raw syntax node for this syntax node. - const RC getRaw() const { + const RC &getRaw() const { return Raw; } diff --git a/lib/Parse/ASTGen.cpp b/lib/Parse/ASTGen.cpp index 8ff9a94f7f174..b9def85ea3a9f 100644 --- a/lib/Parse/ASTGen.cpp +++ b/lib/Parse/ASTGen.cpp @@ -12,6 +12,7 @@ #include "swift/Parse/ASTGen.h" +#include "DebuggerContextChange.h" #include "swift/Basic/SourceManager.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/Parser.h" @@ -23,6 +24,132 @@ SourceLoc ASTGen::generate(const TokenSyntax &Tok, const SourceLoc Loc) { return advanceLocBegin(Loc, Tok); } +SourceLoc ASTGen::generateIdentifierDeclName(const syntax::TokenSyntax &Tok, + const SourceLoc Loc, + Identifier &Id) { + StringRef text; + if (Tok.getText() == "Any") + // Special handle 'Any' because we don't want to accidantaly declare 'Any' + // type in any way. + text = "#Any"; + else + text = Tok.getIdentifierText(); + + Id = Context.getIdentifier(text); + return advanceLocBegin(Loc, Tok); +} + +Decl *ASTGen::generate(const DeclSyntax &D, const SourceLoc Loc) { + Decl *DeclAST = nullptr; + + if (auto associatedTypeDecl = D.getAs()) { + DeclAST = generate(*associatedTypeDecl, Loc); + } else { + llvm_unreachable("unsupported decl kind"); + } + + return DeclAST; +} + +DeclAttributes +ASTGen::generateDeclAttributes(const DeclSyntax &D, + const Optional &attrs, + const Optional &modifiers, + SourceLoc Loc, bool includeComments) { + SourceLoc attrsLoc; + if (attrs) { + attrsLoc = advanceLocBegin(Loc, *attrs->getFirstToken()); + } else if (modifiers) { + attrsLoc = advanceLocBegin(Loc, *modifiers->getFirstToken()); + } else { + // We might have comment attributes. + attrsLoc = advanceLocBegin(Loc, *D.getFirstToken()); + } + if (hasDeclAttributes(attrsLoc)) + return getDeclAttributes(attrsLoc); + return DeclAttributes(); +} + +MutableArrayRef +ASTGen::generate(const TypeInheritanceClauseSyntax &clause, SourceLoc Loc, + bool allowClassRequirement) { + SmallVector inherited; + + bool hasClass = false; + for (const auto elem : clause.getInheritedTypeCollection()) { + const auto &tySyntax = elem.getTypeName(); + if (tySyntax.is()) { + // Accept 'class' only if it's allowed and it's the first one. + if (!allowClassRequirement || hasClass) + continue; + hasClass = true; + } + if (auto ty = generate(tySyntax, Loc)) + inherited.emplace_back(ty); + } + + return Context.AllocateCopy(inherited); +} + +TypeDecl *ASTGen::generate(const AssociatedtypeDeclSyntax &D, + const SourceLoc Loc) { + if (!isa(P.CurDeclContext)) { + // This is already diagnosed in Parser. + return nullptr; + } + + auto idToken = D.getIdentifier(); + if (idToken.isMissing()) + return nullptr; + + auto keywordLoc = advanceLocBegin(Loc, D.getAssociatedtypeKeyword()); + auto name = Context.getIdentifier(idToken.getIdentifierText()); + auto nameLoc = advanceLocBegin(Loc, idToken); + + DeclAttributes attrs = + generateDeclAttributes(D, D.getAttributes(), D.getModifiers(), Loc, true); + + DebuggerContextChange DCC(P, name, DeclKind::AssociatedType); + + ArrayRef inherited; + if (const auto inheritanceClause = D.getInheritanceClause()) + inherited = + generate(*inheritanceClause, Loc, /*allowClassRequirement=*/true); + + TypeRepr *defaultTy = nullptr; + if (const auto init = D.getInitializer()) + defaultTy = generate(init->getValue(), Loc); + + TrailingWhereClause *trailingWhere = nullptr; + if (auto whereClause = D.getGenericWhereClause()) + trailingWhere = generate(*whereClause, Loc); + + auto assocType = new (Context) + AssociatedTypeDecl(P.CurDeclContext, keywordLoc, name, nameLoc, defaultTy, + trailingWhere); + assocType->getAttrs() = attrs; + if (!inherited.empty()) + assocType->setInherited(Context.AllocateCopy(inherited)); + addToScope(assocType); + return assocType; +} + +TrailingWhereClause *ASTGen::generate(const GenericWhereClauseSyntax &syntax, + const SourceLoc Loc) { + SourceLoc whereLoc = advanceLocBegin(Loc, syntax.getWhereKeyword()); + + SmallVector requirements; + requirements.reserve(syntax.getRequirementList().size()); + for (auto elem : syntax.getRequirementList()) { + if (auto req = generate(elem, Loc)) + requirements.push_back(*req); + } + + if (requirements.empty()) + return nullptr; + return TrailingWhereClause::create(Context, whereLoc, requirements); +} + Expr *ASTGen::generate(const IntegerLiteralExprSyntax &Expr, const SourceLoc Loc) { auto Digits = Expr.getDigits(); @@ -124,6 +251,8 @@ TypeRepr *ASTGen::generate(const TypeSyntax &Type, const SourceLoc Loc) { TypeAST = generate(*Unwrapped, Loc); else if (auto Attributed = Type.getAs()) TypeAST = generate(*Attributed, Loc); + else if (auto ClassRestriction = Type.getAs()) + TypeAST = generate(*ClassRestriction, Loc); else if (auto CompletionTy = Type.getAs()) TypeAST = generate(*CompletionTy, Loc); else if (auto Unknown = Type.getAs()) @@ -384,11 +513,6 @@ TypeRepr *ASTGen::generate(const SimpleTypeIdentifierSyntax &Type, auto AnyLoc = advanceLocBegin(Loc, Type.getName()); return CompositionTypeRepr::createEmptyComposition(Context, AnyLoc); } - if (Type.getName().getText() == "class") { - auto classLoc = advanceLocBegin(Loc, Type.getName()); - return new (Context) - SimpleIdentTypeRepr(classLoc, Context.getIdentifier("AnyObject")); - } return generateSimpleOrMemberIdentifier(Type, Loc); } @@ -448,6 +572,13 @@ TypeRepr *ASTGen::generate(const ImplicitlyUnwrappedOptionalTypeSyntax &Type, ImplicitlyUnwrappedOptionalTypeRepr(WrappedType, ExclamationLoc); } +TypeRepr * +ASTGen::generate(const ClassRestrictionTypeSyntax &Type, const SourceLoc Loc) { + auto classLoc = advanceLocBegin(Loc, Type); + return new (Context) + SimpleIdentTypeRepr(classLoc, Context.getIdentifier("AnyObject")); +} + TypeRepr *ASTGen::generate(const CodeCompletionTypeSyntax &Type, const SourceLoc Loc) { auto base = Type.getBase(); @@ -589,7 +720,7 @@ GenericParamList *ASTGen::generate(const GenericParameterClauseSyntax &clause, if (auto inherited = elem.getInheritedType()) { if (auto ty = generate(*inherited, Loc)) { - SmallVector constraints = {generate(*inherited, Loc)}; + SmallVector constraints = {ty}; param->setInherited(Context.AllocateCopy(constraints)); } } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 15ff71a34df9d..0a87abbbb67e8 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2692,6 +2692,7 @@ Parser::parseDecl(ParseDeclOptions Flags, SyntaxParsingContext DeclParsingContext(SyntaxContext, SyntaxContextKind::Decl); + SourceLoc leadingLoc = leadingTriviaLoc(); // Note that we're parsing a declaration. StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), @@ -2709,6 +2710,13 @@ Parser::parseDecl(ParseDeclOptions Flags, StaticSpellingKind StaticSpelling = StaticSpellingKind::None; parseDeclModifierList(Attributes, StaticLoc, StaticSpelling); + if (!Attributes.isEmpty()) { + auto startLoc = Attributes.getStartLoc(); + if (startLoc.isInvalid()) + startLoc = Tok.getLoc(); + Generator.addDeclAttributes(Attributes, startLoc); + } + // We emit diagnostics for 'try let ...' in parseDeclVar(). SourceLoc tryLoc; if (Tok.is(tok::kw_try) && peekToken().isAny(tok::kw_let, tok::kw_var)) @@ -2760,12 +2768,11 @@ Parser::parseDecl(ParseDeclOptions Flags, } case tok::kw_typealias: DeclParsingContext.setCreateSyntax(SyntaxKind::TypealiasDecl); - DeclResult = parseDeclTypeAlias(Flags, Attributes); + DeclResult = parseDeclTypeAlias(Flags, Attributes, leadingLoc); MayNeedOverrideCompletion = true; break; case tok::kw_associatedtype: - DeclParsingContext.setCreateSyntax(SyntaxKind::AssociatedtypeDecl); - DeclResult = parseDeclAssociatedType(Flags, Attributes); + DeclResult = parseDeclAssociatedType(Flags, Attributes, leadingLoc); break; case tok::kw_enum: DeclParsingContext.setCreateSyntax(SyntaxKind::EnumDecl); @@ -3304,100 +3311,107 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, /// 'class' /// type-identifier /// \endverbatim -ParserStatus Parser::parseInheritance(SmallVectorImpl &Inherited, - bool allowClassRequirement, - bool allowAnyObject) { - SyntaxParsingContext InheritanceContext(SyntaxContext, - SyntaxKind::TypeInheritanceClause); - Scope S(this, ScopeKind::InheritanceClause); - consumeToken(tok::colon); +ParsedSyntaxResult +Parser::parseTypeInheritanceClauseSyntax(bool allowClassRequirement, + bool allowAnyObject) { + ParsedTypeInheritanceClauseSyntaxBuilder builder(*SyntaxContext); + ParserStatus status; - SyntaxParsingContext TypeListContext(SyntaxContext, - SyntaxKind::InheritedTypeList); - SourceLoc classRequirementLoc; + builder.useColon(consumeTokenSyntax(tok::colon)); - ParserStatus Status; - SourceLoc prevComma; - bool HasNextType; + SourceLoc startLoc = Tok.getLoc(); + SourceLoc classRequirementLoc, prevCommaLoc; + bool hasNext = true; do { - SyntaxParsingContext TypeContext(SyntaxContext, SyntaxKind::InheritedType); - SWIFT_DEFER { - // Check for a ',', which indicates that there are more protocols coming. - HasNextType = consumeIf(tok::comma, prevComma); - }; + ParsedInheritedTypeSyntaxBuilder elemBuilder(*SyntaxContext); + // Parse the 'class' keyword for a class requirement. if (Tok.is(tok::kw_class)) { - SyntaxParsingContext ClassTypeContext(SyntaxContext, - SyntaxKind::ClassRestrictionType); - // If we aren't allowed to have a class requirement here, complain. - auto classLoc = consumeToken(); + auto classLoc = Tok.getLoc(); + auto classTok = consumeTokenSyntax(tok::kw_class); + auto restriction = ParsedSyntaxRecorder::makeClassRestrictionType( + std::move(classTok), *SyntaxContext); + elemBuilder.useTypeName(std::move(restriction)); + if (!allowClassRequirement) { + // If we aren't allowed to have a class requirement here, complain. diagnose(classLoc, diag::unexpected_class_constraint); - // Note that it makes no sense to suggest fixing - // 'struct S : class' to 'struct S : AnyObject' for - // example; in that case we just complain about - // 'class' being invalid here. + // Note that it makes no sense to suggest fixing 'struct S : class' to + // 'struct S : AnyObject' for example; in that case we just complain + // about 'class' being invalid here. if (allowAnyObject) { diagnose(classLoc, diag::suggest_anyobject) - .fixItReplace(classLoc, "AnyObject"); + .fixItReplace(classLoc, "AnyObject"); } - continue; - } - // If we already saw a class requirement, complain. - if (classRequirementLoc.isValid()) { - diagnose(classLoc, diag::redundant_class_requirement) + } else if (classRequirementLoc.isValid()) { + // If we already saw a class requirement, complain. + diagnose(Tok.getLoc(), diag::redundant_class_requirement) .highlight(classRequirementLoc) - .fixItRemove(SourceRange(prevComma, classLoc)); - continue; - } + .fixItRemove(SourceRange(prevCommaLoc, classLoc)); - // If the class requirement was not the first requirement, complain. - if (!Inherited.empty()) { - SourceLoc properLoc = Inherited[0].getSourceRange().Start; + } else if (prevCommaLoc.isValid()) { + // If the class requirement was not the first requirement, complain. diagnose(classLoc, diag::late_class_requirement) - .fixItInsert(properLoc, "class, ") - .fixItRemove(SourceRange(prevComma, classLoc)); + .fixItInsert(startLoc, "class, ") + .fixItRemove(SourceRange(prevCommaLoc, classLoc)); } // Record the location of the 'class' keyword. - classRequirementLoc = classLoc; + if (!classRequirementLoc.isValid()) + classRequirementLoc = classLoc; + } else { + // Parse inherited type. + auto inheritedType = parseTypeSyntax(); + status |= inheritedType.getStatus(); + if (!inheritedType.isNull()) + elemBuilder.useTypeName(inheritedType.get()); + else + elemBuilder.useTypeName( + ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); + } - // Add 'AnyObject' to the inherited list. - Inherited.push_back( - new (Context) SimpleIdentTypeRepr(classLoc, - Context.getIdentifier("AnyObject"))); - continue; + // Parse ','. + if (Tok.is(tok::comma)) { + prevCommaLoc = Tok.getLoc(); + elemBuilder.useTrailingComma(consumeTokenSyntax(tok::comma)); + } else { + hasNext = false; } - auto ParsedTypeResult = parseType(); - Status |= ParsedTypeResult; + builder.addInheritedTypeCollectionMember(elemBuilder.build()); + } while (hasNext); - // Record the type if its a single type. - if (ParsedTypeResult.isNonNull()) - Inherited.push_back(ParsedTypeResult.get()); - } while (HasNextType); + return makeParsedResult(builder.build(), status); +} - return Status; +ParserStatus Parser::parseInheritance(MutableArrayRef &Inherited, + bool allowClassRequirement, + bool allowAnyObject) { + auto leadingLoc = leadingTriviaLoc(); + auto parsed = parseTypeInheritanceClauseSyntax(allowClassRequirement, + allowAnyObject); + SyntaxContext->addSyntax(parsed.get()); + auto clause = SyntaxContext->topNode(); + Inherited = Generator.generate(clause, leadingLoc, allowClassRequirement); + return parsed.getStatus(); } -static ParserStatus -parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, - StringRef DeclKindName, - llvm::function_ref canRecover) { +static ParsedSyntaxResult +parseIdentifierDeclNameSyntax(Parser &P, StringRef DeclKindName, + llvm::function_ref canRecover) { if (P.Tok.is(tok::identifier)) { - Loc = P.consumeIdentifier(&Result); + auto text = P.Tok.getText(); + auto loc = P.Tok.getLoc(); - // We parsed an identifier for the declaration. If we see another - // identifier, it might've been a single identifier that got broken by a - // space or newline accidentally. + auto tok = P.consumeIdentifierSyntax(); if (P.Tok.isIdentifierOrUnderscore() && !P.Tok.isContextualDeclKeyword()) - P.diagnoseConsecutiveIDs(Result.str(), Loc, DeclKindName); + P.diagnoseConsecutiveIDs(text, loc, DeclKindName); // Return success anyway - return makeParserSuccess(); + return makeParsedResult(std::move(tok)); } P.checkForInputIncomplete(); @@ -3411,12 +3425,8 @@ parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, // Pretend this works as an identifier, which shouldn't be observable since // actual uses of it will hit random other errors, e.g. `1()` won't be // callable. - Result = P.Context.getIdentifier(P.Tok.getText()); - Loc = P.Tok.getLoc(); - P.consumeToken(); - - // We recovered, so this is a success. - return makeParserSuccess(); + P.Tok.setKind(tok::identifier); + return makeParsedResult(P.consumeTokenSyntax()); } if (P.Tok.isKeyword()) { @@ -3426,14 +3436,8 @@ parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, // Recover if the next token is one of the expected tokens. if (canRecover(P.peekToken())) { - llvm::SmallString<32> Name(P.Tok.getText()); - // Append an invalid character so that nothing can resolve to this name. - Name += "#"; - Result = P.Context.getIdentifier(Name.str()); - Loc = P.Tok.getLoc(); - P.consumeToken(); - // Return success because we recovered. - return makeParserSuccess(); + P.Tok.setKind(tok::identifier); + return makeParsedResult(P.consumeTokenSyntax()); } return makeParserError(); } @@ -3442,6 +3446,20 @@ parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, return makeParserError(); } +static ParserStatus +parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, + StringRef DeclKindName, + llvm::function_ref canRecover) { + auto leadingLoc = P.leadingTriviaLoc(); + auto parsed = parseIdentifierDeclNameSyntax(P, DeclKindName, canRecover); + if (!parsed.isNull()) { + P.SyntaxContext->addSyntax(parsed.get()); + auto syntax = P.SyntaxContext->topNode(); + Loc = P.Generator.generateIdentifierDeclName(syntax, leadingLoc, Result); + } + return parsed.getStatus(); +} + /// Add a fix-it to remove the space in consecutive identifiers. /// Add a camel-cased option if it is different than the first option. void Parser::diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc, @@ -3650,7 +3668,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { status |= extendedType; // Parse optional inheritance clause. - SmallVector Inherited; + MutableArrayRef Inherited; if (Tok.is(tok::colon)) status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, @@ -3677,7 +3695,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc, extendedType.getPtrOrNull(), - Context.AllocateCopy(Inherited), + Inherited, CurDeclContext, trailingWhereClause); ext->getAttrs() = Attributes; @@ -3947,8 +3965,9 @@ ParserStatus Parser::parseLineDirective(bool isLine) { /// decl-typealias: /// 'typealias' identifier generic-params? '=' type requirement-clause? /// \endverbatim -ParserResult Parser:: -parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { +ParserResult +Parser::parseDeclTypeAlias(Parser::ParseDeclOptions Flags, + DeclAttributes &Attributes, SourceLoc leadingLoc) { ParserPosition startPosition = getParserPosition(); llvm::Optional TmpCtxt; TmpCtxt.emplace(SyntaxContext); @@ -3967,7 +3986,7 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { TmpCtxt->setTransparent(); return nullptr; } - + DebuggerContextChange DCC(*this, Id, DeclKind::TypeAlias); Optional GenericsScope; @@ -3996,7 +4015,7 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { // If we're in a protocol and don't see an '=' this looks like leftover Swift 2 // code intending to be an associatedtype. backtrackToPosition(startPosition); - return parseDeclAssociatedType(Flags, Attributes); + return parseDeclAssociatedType(Flags, Attributes, leadingLoc); } TmpCtxt->setTransparent(); TmpCtxt.reset(); @@ -4055,95 +4074,116 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { /// Parse an associatedtype decl. /// -/// \verbatim /// decl-associatedtype: -/// 'associatedtype' identifier inheritance? ('=' type)? where-clause? -/// \endverbatim +/// 'associatedtype' identifier type-inheritance-clause? +/// ('=' type)? where-clause? +ParsedSyntaxResult +Parser::parseDeclAssociatedTypeSyntax(ParseDeclOptions flags, + Optional attrs, + Optional modifiers) { + ParsedAssociatedtypeDeclSyntaxBuilder builder(*SyntaxContext); + ParserStatus status; -ParserResult Parser::parseDeclAssociatedType(Parser::ParseDeclOptions Flags, - DeclAttributes &Attributes) { - SourceLoc AssociatedTypeLoc; - ParserStatus Status; - Identifier Id; - SourceLoc IdLoc; - - // Look for 'typealias' here and diagnose a fixit because parseDeclTypeAlias can - // ask us to fix up leftover Swift 2 code intending to be an associatedtype. + if (attrs) + builder.useAttributes(std::move(*attrs)); + if (modifiers) + builder.useModifiers(std::move(*modifiers)); + + // Parse 'associatedtype' keyword. + // Look for 'typealias' here and diagnose a fixit because parseDeclTypeAlias + // can ask us to fix up leftover Swift 2 code intending to be an + // associatedtype. + auto keywordLoc = Tok.getLoc(); if (Tok.is(tok::kw_typealias)) { - AssociatedTypeLoc = consumeToken(tok::kw_typealias); - diagnose(AssociatedTypeLoc, diag::typealias_inside_protocol_without_type) - .fixItReplace(AssociatedTypeLoc, "associatedtype"); + diagnose(Tok.getLoc(), diag::typealias_inside_protocol_without_type) + .fixItReplace(Tok.getLoc(), "associatedtype"); + ignoreToken(tok::kw_typealias); } else { - AssociatedTypeLoc = consumeToken(tok::kw_associatedtype); + builder.useAssociatedtypeKeyword( + consumeTokenSyntax(tok::kw_associatedtype)); } - Status = parseIdentifierDeclName( - *this, Id, IdLoc, "associatedtype", - [](const Token &next) { return next.isAny(tok::colon, tok::equal); }); - if (Status.isError()) - return nullptr; - - DebuggerContextChange DCC(*this, Id, DeclKind::AssociatedType); - - // Reject generic parameters with a specific error. + // Parse the name. + auto name = parseIdentifierDeclNameSyntax( + *this, "associatedtype", + [&](const Token &next) { return next.isAny(tok::colon, tok::equal); }); + if (name.isNull()) + return makeParsedResult(builder.build(), name.getStatus()); + assert(name.isSuccess()); + builder.useIdentifier(name.get()); + + // Diagnose generic parameters. if (startsWithLess(Tok)) { - // Introduce a throwaway scope to capture the generic parameters. We - // don't want them visible anywhere! - Scope S(this, ScopeKind::Generics); + auto loc = Tok.getLoc(); + ignoreToken(); + if (ignoreUntilGreaterInTypeList()) + ignoreToken(); - if (auto genericParams = parseGenericParameters().getPtrOrNull()) { - diagnose(genericParams->getLAngleLoc(), - diag::associated_type_generic_parameter_list) - .fixItRemove(genericParams->getSourceRange()); - } + diagnose(loc, diag::associated_type_generic_parameter_list) + .fixItRemove({loc, PreviousLoc}); } - + // Parse optional inheritance clause. - // FIXME: Allow class requirements here. - SmallVector Inherited; - if (Tok.is(tok::colon)) - Status |= parseInheritance(Inherited, - /*allowClassRequirement=*/false, - /*allowAnyObject=*/true); - - ParserResult UnderlyingTy; + if (Tok.is(tok::colon)) { + auto inheritance = parseTypeInheritanceClauseSyntax( + /*allowClassRequirement=*/false, /*allowAnyObject=*/true); + status |= inheritance.getStatus(); + if (!inheritance.isNull()) + builder.useInheritanceClause(inheritance.get()); + } + + // Parse optional default type. if (Tok.is(tok::equal)) { - SyntaxParsingContext InitContext(SyntaxContext, - SyntaxKind::TypeInitializerClause); - consumeToken(tok::equal); - UnderlyingTy = parseType(diag::expected_type_in_associatedtype); - Status |= UnderlyingTy; - if (UnderlyingTy.isNull()) - return Status; + ParsedTypeInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); + initBuilder.useEqual(consumeTokenSyntax(tok::equal)); + + // Parse type. + auto type = parseTypeSyntax(diag::expected_type_in_associatedtype); + status |= type.getStatus(); + if (!type.isNull()) + initBuilder.useValue(type.get()); + else + initBuilder.useValue( + ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); + + builder.useInitializer(initBuilder.build()); } - TrailingWhereClause *TrailingWhere = nullptr; - // Parse a 'where' clause if present. + // Parse optional 'where' clause. if (Tok.is(tok::kw_where)) { - auto whereStatus = parseProtocolOrAssociatedTypeWhereClause( - TrailingWhere, /*isProtocol=*/false); - Status |= whereStatus; - if (whereStatus.hasCodeCompletion() && !CodeCompletion) { - // Trigger delayed parsing, no need to continue. - return whereStatus; - } + bool firstTypeInComplete = false; + auto where = parseGenericWhereClauseSyntax(firstTypeInComplete); + status |= where.getStatus(); + if (!where.isNull()) + builder.useGenericWhereClause(where.get()); + } + + // Diagnose if it's not in protocol decl. + // TODO: Move this to ASTGen. + if (!flags.contains(PD_InProtocol)) { + diagnose(keywordLoc, diag::associatedtype_outside_protocol) + .fixItReplace(keywordLoc, "typealias"); + status.setIsParseError(); } - if (!Flags.contains(PD_InProtocol)) { - diagnose(AssociatedTypeLoc, diag::associatedtype_outside_protocol) - .fixItReplace(AssociatedTypeLoc, "typealias"); - Status.setIsParseError(); - return Status; - } + return makeParsedResult(builder.build(), status); +} - auto assocType = new (Context) - AssociatedTypeDecl(CurDeclContext, AssociatedTypeLoc, Id, IdLoc, - UnderlyingTy.getPtrOrNull(), TrailingWhere); - assocType->getAttrs() = Attributes; - if (!Inherited.empty()) - assocType->setInherited(Context.AllocateCopy(Inherited)); - addToScope(assocType); - return makeParserResult(Status, assocType); +ParserResult +Parser::parseDeclAssociatedType(Parser::ParseDeclOptions Flags, + DeclAttributes &Attributes, + SourceLoc leadingLoc) { + auto modifiers = SyntaxContext->popIf(); + auto attrs = SyntaxContext->popIf(); + + auto parsed = parseDeclAssociatedTypeSyntax(Flags, std::move(attrs), + std::move(modifiers)); + assert(!parsed.isNull()); + + SyntaxContext->addSyntax(parsed.get()); + auto syntax = SyntaxContext->topNode(); + auto result = Generator.generate(syntax, leadingLoc); + return makeParserResult(parsed.getStatus(), result); } /// This function creates an accessor function (with no body) for a computed @@ -5574,11 +5614,11 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the enum. if (Tok.is(tok::colon)) { - SmallVector Inherited; + MutableArrayRef Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); - ED->setInherited(Context.AllocateCopy(Inherited)); + ED->setInherited(Inherited); } diagnoseWhereClauseInGenericParamList(GenericParams); @@ -5860,11 +5900,11 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the struct. if (Tok.is(tok::colon)) { - SmallVector Inherited; + MutableArrayRef Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); - SD->setInherited(Context.AllocateCopy(Inherited)); + SD->setInherited(Inherited); } diagnoseWhereClauseInGenericParamList(GenericParams); @@ -5953,11 +5993,11 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the class. if (Tok.is(tok::colon)) { - SmallVector Inherited; + MutableArrayRef Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); - CD->setInherited(Context.AllocateCopy(Inherited)); + CD->setInherited(Inherited); // Parse python style inheritance clause and replace parentheses with a colon } else if (Tok.is(tok::l_paren)) { @@ -6059,7 +6099,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { DebuggerContextChange DCC (*this); // Parse optional inheritance clause. - SmallVector InheritedProtocols; + MutableArrayRef InheritedProtocols; SourceLoc colonLoc; if (Tok.is(tok::colon)) { colonLoc = Tok.getLoc(); @@ -6079,7 +6119,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { ProtocolDecl *Proto = new (Context) ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc, ProtocolName, - Context.AllocateCopy(InheritedProtocols), TrailingWhere); + InheritedProtocols, TrailingWhere); // No need to setLocalDiscriminator: protocols can't appear in local contexts. Proto->getAttrs() = Attributes; diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index ee00e32c6aad8..7e21c510cdfdd 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -104,9 +104,8 @@ Parser::parseGenericParameterClauseSyntax() { diagnose(Tok, diag::unexpected_class_constraint); diagnose(Tok, diag::suggest_anyobject) .fixItReplace(Tok.getLoc(), "AnyObject"); - Tok.setKind(tok::identifier); - auto ty = ParsedSyntaxRecorder::makeSimpleTypeIdentifier( - consumeTokenSyntax(), None, *SyntaxContext); + auto ty = ParsedSyntaxRecorder::makeClassRestrictionType( + consumeTokenSyntax(tok::kw_class), *SyntaxContext); paramBuilder.useInheritedType(std::move(ty)); } else { diagnose(Tok, diag::expected_generics_type_restriction, ident); diff --git a/lib/Syntax/Syntax.cpp b/lib/Syntax/Syntax.cpp index 57aa313597c7b..850fb9ecbf082 100644 --- a/lib/Syntax/Syntax.cpp +++ b/lib/Syntax/Syntax.cpp @@ -18,7 +18,7 @@ using namespace swift; using namespace swift::syntax; -RC Syntax::getRaw() const { +const RC &Syntax::getRaw() const { return Data->getRaw(); } diff --git a/test/Syntax/serialize_tupletype.swift.result b/test/Syntax/serialize_tupletype.swift.result index c186b51c93d6b..fcccca1cb3731 100644 --- a/test/Syntax/serialize_tupletype.swift.result +++ b/test/Syntax/serialize_tupletype.swift.result @@ -1,23 +1,23 @@ { - "id": 39, + "id": 40, "kind": "SourceFile", "layout": [ { - "id": 38, + "id": 39, "kind": "CodeBlockItemList", "layout": [ { - "id": 36, + "id": 37, "kind": "CodeBlockItem", "layout": [ { - "id": 35, + "id": 36, "kind": "TypealiasDecl", "layout": [ null, null, { - "id": 33, + "id": 34, "tokenKind": { "kind": "kw_typealias" }, @@ -48,7 +48,7 @@ "presence": "Present" }, { - "id": 34, + "id": 35, "tokenKind": { "kind": "identifier", "text": "x" @@ -64,11 +64,11 @@ }, null, { - "id": 32, + "id": 33, "kind": "TypeInitializerClause", "layout": [ { - "id": 1, + "id": 2, "tokenKind": { "kind": "equal" }, @@ -82,11 +82,11 @@ "presence": "Present" }, { - "id": 31, + "id": 32, "kind": "TupleType", "layout": [ { - "id": 18, + "id": 19, "tokenKind": { "kind": "l_paren" }, @@ -95,16 +95,16 @@ "presence": "Present" }, { - "id": 29, + "id": 30, "kind": "TupleTypeElementList", "layout": [ { - "id": 24, + "id": 25, "kind": "TupleTypeElement", "layout": [ null, { - "id": 19, + "id": 20, "tokenKind": { "kind": "identifier", "text": "b" @@ -115,7 +115,7 @@ }, null, { - "id": 20, + "id": 21, "tokenKind": { "kind": "colon" }, @@ -129,11 +129,11 @@ "presence": "Present" }, { - "id": 22, + "id": 23, "kind": "SimpleTypeIdentifier", "layout": [ { - "id": 21, + "id": 22, "tokenKind": { "kind": "identifier", "text": "Int" @@ -149,7 +149,7 @@ null, null, { - "id": 23, + "id": 24, "tokenKind": { "kind": "comma" }, @@ -166,12 +166,12 @@ "presence": "Present" }, { - "id": 28, + "id": 29, "kind": "TupleTypeElement", "layout": [ null, { - "id": 25, + "id": 26, "tokenKind": { "kind": "kw__" }, @@ -181,7 +181,7 @@ }, null, { - "id": 20, + "id": 21, "tokenKind": { "kind": "colon" }, @@ -195,11 +195,11 @@ "presence": "Present" }, { - "id": 27, + "id": 28, "kind": "SimpleTypeIdentifier", "layout": [ { - "id": 26, + "id": 27, "tokenKind": { "kind": "identifier", "text": "String" @@ -222,7 +222,7 @@ "presence": "Present" }, { - "id": 30, + "id": 31, "tokenKind": { "kind": "r_paren" }, @@ -249,7 +249,7 @@ "presence": "Present" }, { - "id": 37, + "id": 38, "tokenKind": { "kind": "eof", "text": "" diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index c185a1da30031..a663f92fae5d0 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -79,6 +79,7 @@ # attribute-list -> attribute attribute-list? Node('AttributeList', kind='SyntaxCollection', + omit_when_empty=True, element='Syntax', element_name='Attribute', element_choices=[ 'Attribute', diff --git a/utils/gyb_syntax_support/DeclNodes.py b/utils/gyb_syntax_support/DeclNodes.py index 6741725b51072..14111b3b45a7c 100644 --- a/utils/gyb_syntax_support/DeclNodes.py +++ b/utils/gyb_syntax_support/DeclNodes.py @@ -374,6 +374,7 @@ # | 'weak' # mutation-modifier -> 'mutating' | 'nonmutating' Node('ModifierList', kind='SyntaxCollection', + omit_when_empty=True, element='DeclModifier', element_name='Modifier'), From 061c036a233b45743a7ebe4645c66340cb48726c Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 23 Sep 2019 18:17:51 -0700 Subject: [PATCH 037/140] [IRGen] Convert `Builtin.UnknownObject` to `id` instead of `void *`. --- lib/IRGen/GenClangType.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index 3e0f19e90d9a2..501b3c8bbe92a 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -705,9 +705,8 @@ clang::CanQualType GenClangType::visitBuiltinFloatType( clang::CanQualType GenClangType::visitBuiltinUnknownObjectType( CanBuiltinUnknownObjectType type) { - auto &clangCtx = getClangASTContext(); - auto ptrTy = clangCtx.getObjCObjectPointerType(clangCtx.VoidTy); - return clangCtx.getCanonicalType(ptrTy); + // Builtin.UnknownObject == AnyObject, so it is also translated to 'id'. + return getClangIdType(getClangASTContext()); } clang::CanQualType GenClangType::visitArchetypeType(CanArchetypeType type) { From 77159c8be2e89e840c7ad83361ff3dfa089ef163 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 24 Sep 2019 12:40:53 -0700 Subject: [PATCH 038/140] [polymorphic-builtins] Fix test for rebranch/master-next. --- test/IRGen/polymorphic_builtins.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/IRGen/polymorphic_builtins.swift b/test/IRGen/polymorphic_builtins.swift index cda952f90b9e0..a9ad1e4d78a22 100644 --- a/test/IRGen/polymorphic_builtins.swift +++ b/test/IRGen/polymorphic_builtins.swift @@ -27,14 +27,14 @@ public func _isConcrete(type: T.Type) -> Bool { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s20polymorphic_builtins41calleeAddVectorsGenericTransparentGuardedyxx_xtlF"( // CHECK: br i1 false, label %[[CONCRETE_LABEL:[0-9][0-9]*]], label %[[NON_CONCRETE_LABEL:[0-9][0-9]*]] // -// CHECK: (_:) +sil [serialized] [always_inline] [_semantics "array.uninitialized_intrinsic"] @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <Ï„_0_0> (Builtin.Word) -> (@owned Array<Ï„_0_0>, Builtin.RawPointer) + +sil @interpretArrayInit : $@convention(thin) () -> @owned Array { +bb0: + %0 = metatype $@thin Array.Type + // function_ref Array.init() + %1 = function_ref @$sS2ayxGycfC : $@convention(method) <Ï„_0_0> (@thin Array<Ï„_0_0>.Type) -> @owned Array<Ï„_0_0> + %2 = apply %1(%0) : $@convention(method) <Ï„_0_0> (@thin Array<Ï„_0_0>.Type) -> @owned Array<Ï„_0_0> + return %2 : $Array +} // CHECK: Returns Array + // CHECK: size: 0 contents [] + +sil [ossa] @interpretEmptyArrayLiteral : $@convention(thin) () -> @owned Array { +bb0: + %0 = integer_literal $Builtin.Word, 0 + // function_ref _allocateUninitializedArray(_:) + %1 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <Ï„_0_0> (Builtin.Word) -> (@owned Array<Ï„_0_0>, Builtin.RawPointer) + %2 = apply %1(%0) : $@convention(thin) <Ï„_0_0> (Builtin.Word) -> (@owned Array<Ï„_0_0>, Builtin.RawPointer) + (%3, %4) = destructure_tuple %2 : $(Array, Builtin.RawPointer) + return %3 : $Array +} // CHECK: Returns Array + // CHECK: size: 0 contents [] + +sil [ossa] @initializeArrayWithLiterals : $@convention(thin) () -> @owned Array { +bb0: + %0 = integer_literal $Builtin.Int64, 11 // element 1 + %1 = struct $Int64 (%0 : $Builtin.Int64) + %2 = integer_literal $Builtin.Int64, 12 // element 2 + %3 = struct $Int64 (%2 : $Builtin.Int64) + %4 = integer_literal $Builtin.Int64, 14 // element 3 + %5 = struct $Int64 (%4 : $Builtin.Int64) + + %6 = integer_literal $Builtin.Word, 3 // array literal size + // function_ref _allocateUninitializedArray(_:) + %7 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <Ï„_0_0> (Builtin.Word) -> (@owned Array<Ï„_0_0>, Builtin.RawPointer) + %8 = apply %7(%6) : $@convention(thin) <Ï„_0_0> (Builtin.Word) -> (@owned Array<Ï„_0_0>, Builtin.RawPointer) + (%9, %10) = destructure_tuple %8 : $(Array, Builtin.RawPointer) + %11 = pointer_to_address %10 : $Builtin.RawPointer to [strict] $*Int64 + store %1 to [trivial] %11 : $*Int64 + %13 = integer_literal $Builtin.Word, 1 // Index: 1 + %14 = index_addr %11 : $*Int64, %13 : $Builtin.Word + store %3 to [trivial] %14 : $*Int64 + %16 = integer_literal $Builtin.Word, 2 // Index: 2 + %17 = index_addr %11 : $*Int64, %16 : $Builtin.Word + store %5 to [trivial] %17 : $*Int64 + return %9 : $Array +} + +sil [ossa] @interpretArrayLiteral : $@convention(thin) () -> @owned Array { +bb0: + %7 = function_ref @initializeArrayWithLiterals : $@convention(thin) () -> @owned Array + %8 = apply %7() : $@convention(thin) () -> @owned Array + return %8 : $Array +} // CHECK: Returns Array + // CHECK: size: 3 + // CHECK: agg: 1 elt: int: 11 + // CHECK: agg: 1 elt: int: 12 + // CHECK: agg: 1 elt: int: 14 + +// Array.append(_:) +sil [serialized] [_semantics "array.append_element"] @$sSa6appendyyxnF : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + +sil [ossa] @interpretArrayAppend : $@convention(thin) () -> @owned Array { + %0 = integer_literal $Builtin.Int64, 71 + %1 = struct $Int64 (%0 : $Builtin.Int64) + %2 = alloc_stack $Array, var, name "a" + %3 = metatype $@thin Array.Type + // function_ref Array.init() + %4 = function_ref @$sS2ayxGycfC : $@convention(method) <Ï„_0_0> (@thin Array<Ï„_0_0>.Type) -> @owned Array<Ï„_0_0> + %5 = apply %4(%3) : $@convention(method) <Ï„_0_0> (@thin Array<Ï„_0_0>.Type) -> @owned Array<Ï„_0_0> + store %5 to [init] %2 : $*Array + %10 = alloc_stack $Int64 + store %1 to [trivial] %10 : $*Int64 + // function_ref Array.append(_:) + %13 = function_ref @$sSa6appendyyxnF : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + %14 = apply %13(%10, %2) : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + dealloc_stack %10 : $*Int64 + %18 = load [copy] %2 : $*Array + destroy_addr %2 : $*Array + dealloc_stack %2 : $*Array + return %18 : $Array +} // CHECK: Returns Array + // CHECK: size: 1 + // CHECK: agg: 1 elt: int: 71 + +sil [ossa] @interpretArrayAppendNonEmpty : $@convention(thin) () -> @owned Array { +bb0: + %0 = integer_literal $Builtin.Int64, 100 + %1 = struct $Int64 (%0 : $Builtin.Int64) + %2 = alloc_stack $Array, var, name "a" + %3 = metatype $@thin Array.Type + %4 = function_ref @initializeArrayWithLiterals : $@convention(thin) () -> @owned Array + %5 = apply %4() : $@convention(thin) () -> @owned Array + store %5 to [init] %2 : $*Array + %10 = alloc_stack $Int64 + store %1 to [trivial] %10 : $*Int64 + // function_ref Array.append(_:) + %13 = function_ref @$sSa6appendyyxnF : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + %14 = apply %13(%10, %2) : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + dealloc_stack %10 : $*Int64 + %18 = load [copy] %2 : $*Array + destroy_addr %2 : $*Array + dealloc_stack %2 : $*Array + return %18 : $Array +} // CHECK: Returns Array + // CHECK: size: 4 + // CHECK: agg: 1 elt: int: 11 + // CHECK: agg: 1 elt: int: 12 + // CHECK: agg: 1 elt: int: 14 + // CHECK: agg: 1 elt: int: 100 + +/// Test appending of a static string to an array. The construction of a static +/// string is a bit complicated due to the use of instructions like "ptrtoint". +/// This tests that array append works with such complex constant values as well. +sil @interpretArrayAppendStaticString : $@convention(thin) () -> @owned Array { + %0 = string_literal utf8 "constant" // string to be appended. + + // Initialize an empty array + %2 = alloc_stack $Array, var, name "a" + %3 = metatype $@thin Array.Type + // function_ref Array.init() + %4 = function_ref @$sS2ayxGycfC : $@convention(method) <Ï„_0_0> (@thin Array<Ï„_0_0>.Type) -> @owned Array<Ï„_0_0> + %5 = apply %4(%3) : $@convention(method) <Ï„_0_0> (@thin Array<Ï„_0_0>.Type) -> @owned Array<Ï„_0_0> + store %5 to %2 : $*Array + + // Initialize a static string. + %6 = integer_literal $Builtin.Word, 8 + %7 = builtin "ptrtoint_Word"(%0 : $Builtin.RawPointer) : $Builtin.Word + %8 = integer_literal $Builtin.Int8, 2 + %9 = struct $StaticString (%7 : $Builtin.Word, %6 : $Builtin.Word, %8 : $Builtin.Int8) + + %10 = alloc_stack $StaticString + store %9 to %10 : $*StaticString + // function_ref Array.append(_:) + %13 = function_ref @$sSa6appendyyxnF : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + %14 = apply %13(%10, %2) : $@convention(method) <Ï„_0_0> (@in Ï„_0_0, @inout Array<Ï„_0_0>) -> () + dealloc_stack %10 : $*StaticString + %18 = load %2 : $*Array + destroy_addr %2 : $*Array + dealloc_stack %2 : $*Array + return %18 : $Array +} // CHECK: Returns Array + // CHECK: size: 1 + // CHECK: string: "constant" + diff --git a/test/SILOptimizer/pound_assert.swift b/test/SILOptimizer/pound_assert.swift index d09b59e7c9c82..d459c6cf587b4 100644 --- a/test/SILOptimizer/pound_assert.swift +++ b/test/SILOptimizer/pound_assert.swift @@ -668,3 +668,68 @@ func evaluate(addressOnlyEnum: AddressOnlyEnum) -> Int { #assert(evaluate(addressOnlyEnum: .double(IntContainer(value: 1))) == 2) #assert(evaluate(addressOnlyEnum: .triple(IntContainer(value: 1))) == 3) + +//===----------------------------------------------------------------------===// +// Arrays +//===----------------------------------------------------------------------===// + +// When the const-evaluator evaluates this struct, it forces evaluation of the +// `arr` value. +struct ContainsArray { + let x: Int + let arr: [Int] +} + +func arrayInitEmptyTopLevel() { + let c = ContainsArray(x: 1, arr: Array()) + #assert(c.x == 1) +} + +func arrayInitEmptyLiteralTopLevel() { + // TODO: More work necessary for array initialization using literals to work + // at the top level. + // expected-note@+1 {{cannot evaluate expression as constant here}} + let c = ContainsArray(x: 1, arr: []) + // expected-error @+1 {{#assert condition not constant}} + #assert(c.x == 1) +} + +func arrayInitLiteral() { + // TODO: More work necessary for array initialization using literals to work + // at the top level. + // expected-note @+1 {{cannot evaluate expression as constant here}} + let c = ContainsArray(x: 1, arr: [2, 3, 4]) + // expected-error @+1 {{#assert condition not constant}} + #assert(c.x == 1) +} + +func arrayInitNonConstantElementTopLevel(x: Int) { + // expected-note @+1 {{cannot evaluate expression as constant here}} + let c = ContainsArray(x: 1, arr: [x]) + // expected-error @+1 {{#assert condition not constant}} + #assert(c.x == 1) +} + +func arrayInitEmptyFlowSensitive() -> ContainsArray { + return ContainsArray(x: 1, arr: Array()) +} + +func invokeArrayInitEmptyFlowSensitive() { + #assert(arrayInitEmptyFlowSensitive().x == 1) +} + +func arrayInitEmptyLiteralFlowSensitive() -> ContainsArray { + return ContainsArray(x: 1, arr: []) +} + +func invokeArrayInitEmptyLiteralFlowSensitive() { + #assert(arrayInitEmptyLiteralFlowSensitive().x == 1) +} + +func arrayInitLiteralFlowSensitive() -> ContainsArray { + return ContainsArray(x: 1, arr: [2, 3, 4]) +} + +func invokeArrayInitLiteralFlowSensitive() { + #assert(arrayInitLiteralFlowSensitive().x == 1) +} From 1ba61d7615da9f5e8ba73e5126370871b3e4e9fb Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 24 Sep 2019 15:40:51 -0700 Subject: [PATCH 057/140] [Gardening] Sort the ASTTypeIDs --- include/swift/AST/ASTTypeIDZone.def | 32 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 9c925552c522a..1e5d2d0f2487a 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -14,25 +14,27 @@ // for use with the TypeID template. // //===----------------------------------------------------------------------===// -SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl) -SWIFT_TYPEID_NAMED(VarDecl *, VarDecl) -SWIFT_TYPEID_NAMED(ValueDecl *, ValueDecl) -SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl) -SWIFT_TYPEID_NAMED(Decl *, Decl) -SWIFT_TYPEID_NAMED(ModuleDecl *, ModuleDecl) -SWIFT_TYPEID(Type) -SWIFT_TYPEID(TypePair) + +SWIFT_TYPEID(AncestryFlags) +SWIFT_TYPEID(CtorInitializerKind) SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo) SWIFT_TYPEID(PropertyWrapperTypeInfo) -SWIFT_TYPEID(CtorInitializerKind) +SWIFT_TYPEID(Requirement) SWIFT_TYPEID(ResilienceExpansion) -SWIFT_TYPEID_NAMED(Optional, PropertyWrapperMutability) +SWIFT_TYPEID(Type) +SWIFT_TYPEID(TypePair) SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr) -SWIFT_TYPEID_NAMED(OperatorDecl *, OperatorDecl) -SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl) -SWIFT_TYPEID(AncestryFlags) +SWIFT_TYPEID_NAMED(Decl *, Decl) +SWIFT_TYPEID_NAMED(GenericParamList *, GenericParamList) SWIFT_TYPEID_NAMED(GenericSignature *, GenericSignature) SWIFT_TYPEID_NAMED(GenericTypeParamType *, GenericTypeParamType) -SWIFT_TYPEID(Requirement) SWIFT_TYPEID_NAMED(IterableDeclContext *, IterableDeclContext) -SWIFT_TYPEID_NAMED(GenericParamList *, GenericParamList) +SWIFT_TYPEID_NAMED(ModuleDecl *, ModuleDecl) +SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl) +SWIFT_TYPEID_NAMED(OperatorDecl *, OperatorDecl) +SWIFT_TYPEID_NAMED(Optional, + PropertyWrapperMutability) +SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl) +SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl) +SWIFT_TYPEID_NAMED(ValueDecl *, ValueDecl) +SWIFT_TYPEID_NAMED(VarDecl *, VarDecl) From 23a042207fd86a3462ef36bd3f3497eb8cef2a55 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 23 Sep 2019 08:54:42 -0700 Subject: [PATCH 058/140] [CallerAnalysis] foundAllCallers reqs nonexternal. Previously, CallerAnalysis::FunctionInfo.foundAllCallers(), which is documented to return true only when specialization of a function will not require a thunk, returned true for functions which are possibly used externally. Now, that member function only returns false for functions which may be used externally since dead code elimination will not be able to remove them. --- .../SILOptimizer/Analysis/CallerAnalysis.h | 7 +++- lib/SILOptimizer/Analysis/CallerAnalysis.cpp | 4 +- test/SILOptimizer/caller_analysis.sil | 38 +++++++++---------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/include/swift/SILOptimizer/Analysis/CallerAnalysis.h b/include/swift/SILOptimizer/Analysis/CallerAnalysis.h index 35ea773a3cb4b..b23570c2e49fa 100644 --- a/include/swift/SILOptimizer/Analysis/CallerAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/CallerAnalysis.h @@ -278,6 +278,10 @@ class CallerAnalysis::FunctionInfo { /// visibility of a protocol conformance or class. bool mayHaveIndirectCallers : 1; + /// Whether the function is sufficiently visible to be called by a different + /// module. + bool mayHaveExternalCallers : 1; + public: FunctionInfo(SILFunction *f); @@ -289,7 +293,8 @@ class CallerAnalysis::FunctionInfo { /// function (e.g. a specialized function) without needing to introduce a /// thunk since we can rewrite all of the callers to call the new function. bool foundAllCallers() const { - return hasOnlyCompleteDirectCallerSets() && !mayHaveIndirectCallers; + return hasOnlyCompleteDirectCallerSets() && !mayHaveIndirectCallers && + !mayHaveExternalCallers; } /// Returns true if this function has at least one direct caller. diff --git a/lib/SILOptimizer/Analysis/CallerAnalysis.cpp b/lib/SILOptimizer/Analysis/CallerAnalysis.cpp index ce0c4c70dee23..c2894f1072532 100644 --- a/lib/SILOptimizer/Analysis/CallerAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/CallerAnalysis.cpp @@ -33,7 +33,9 @@ CallerAnalysis::FunctionInfo::FunctionInfo(SILFunction *f) // TODO: Make this more aggressive by considering // final/visibility/etc. mayHaveIndirectCallers(f->getDynamicallyReplacedFunction() || - canBeCalledIndirectly(f->getRepresentation())) {} + canBeCalledIndirectly(f->getRepresentation())), + mayHaveExternalCallers(f->isPossiblyUsedExternally() || + f->isAvailableExternally()) {} //===----------------------------------------------------------------------===// // CallerAnalysis::ApplySiteFinderVisitor diff --git a/test/SILOptimizer/caller_analysis.sil b/test/SILOptimizer/caller_analysis.sil index 6d725031aeb92..441f231b8fe0d 100644 --- a/test/SILOptimizer/caller_analysis.sil +++ b/test/SILOptimizer/caller_analysis.sil @@ -12,7 +12,7 @@ import Builtin // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil hidden @dead_func : $@convention(thin) () -> () { +sil private @dead_func : $@convention(thin) () -> () { %2 = tuple () return %2 : $() } @@ -25,7 +25,7 @@ sil hidden @dead_func : $@convention(thin) () -> () { // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil hidden @call_top : $@convention(thin) () -> () { +sil private @call_top : $@convention(thin) () -> () { bb0: %0 = function_ref @call_middle : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -42,7 +42,7 @@ bb0: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - call_top // CHECK-NEXT: ... -sil hidden @call_middle : $@convention(thin) () -> () { +sil private @call_middle : $@convention(thin) () -> () { bb0: %0 = function_ref @call_bottom : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -59,7 +59,7 @@ bb0: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - call_middle // CHECK-NEXT: ... -sil hidden @call_bottom : $@convention(thin) () -> () { +sil private @call_bottom : $@convention(thin) () -> () { bb0: %0 = tuple () return %0 : $() @@ -74,7 +74,7 @@ bb0: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - self_recursive_func // CHECK-NEXT: ... -sil hidden @self_recursive_func : $@convention(thin) () -> () { +sil private @self_recursive_func : $@convention(thin) () -> () { bb0: %0 = function_ref @self_recursive_func : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -91,7 +91,7 @@ bb0: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - mutual_recursive_func2 // CHECK-NEXT: ... -sil hidden @mutual_recursive_func1 : $@convention(thin) () -> () { +sil private @mutual_recursive_func1 : $@convention(thin) () -> () { bb0: %0 = function_ref @mutual_recursive_func2 : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -108,7 +108,7 @@ bb0: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - mutual_recursive_func1 // CHECK-NEXT: ... -sil hidden @mutual_recursive_func2 : $@convention(thin) () -> () { +sil private @mutual_recursive_func2 : $@convention(thin) () -> () { bb0: %0 = function_ref @mutual_recursive_func1 : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -125,7 +125,7 @@ bb0: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - multi_calles // CHECK-NEXT: ... -sil hidden @multi_called : $@convention(thin) () -> () { +sil private @multi_called : $@convention(thin) () -> () { bb0: %2 = tuple () return %2 : $() @@ -139,7 +139,7 @@ bb0: // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil hidden @multi_calles : $@convention(thin) () -> () { +sil private @multi_calles : $@convention(thin) () -> () { bb0: %0 = function_ref @multi_called : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -165,7 +165,7 @@ bb3: // CHECK-NEXT: - multi_caller1 // CHECK-NEXT: - multi_caller2 // CHECK-NEXT: ... -sil hidden @multi_callers : $@convention(thin) () -> () { +sil private @multi_callers : $@convention(thin) () -> () { bb0: %2 = tuple () return %2 : $() @@ -179,7 +179,7 @@ bb0: // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil hidden @multi_caller1 : $@convention(thin) () -> () { +sil private @multi_caller1 : $@convention(thin) () -> () { bb0: %0 = function_ref @multi_callers : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -195,7 +195,7 @@ bb0: // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil hidden @multi_caller2 : $@convention(thin) () -> () { +sil private @multi_caller2 : $@convention(thin) () -> () { bb0: %0 = function_ref @multi_callers : $@convention(thin) () -> () %1 = apply %0() : $@convention(thin) () -> () @@ -243,7 +243,7 @@ bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32): // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil @partial_apply_one_arg : $@convention(thin) (Builtin.Int32) -> @owned @callee_owned (Builtin.Int32) -> Builtin.Int32 { +sil private @partial_apply_one_arg : $@convention(thin) (Builtin.Int32) -> @owned @callee_owned (Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32): %1 = function_ref @closure1 : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 %2 = partial_apply %1(%0) : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 @@ -258,7 +258,7 @@ bb0(%0 : $Builtin.Int32): // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil @partial_apply_two_args1 : $@convention(thin) (Builtin.Int32) -> @owned @callee_owned () -> Builtin.Int32 { +sil private @partial_apply_two_args1 : $@convention(thin) (Builtin.Int32) -> @owned @callee_owned () -> Builtin.Int32 { bb0(%0 : $Builtin.Int32): %1 = function_ref @closure1 : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 %2 = partial_apply %1(%0, %0) : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 @@ -273,7 +273,7 @@ bb0(%0 : $Builtin.Int32): // CHECK-NEXT: partialAppliers: // CHECK-NEXT: fullAppliers: // CHECK-NEXT: ... -sil @partial_apply_two_args2 : $@convention(thin) (Builtin.Int32) -> @owned @callee_owned () -> Builtin.Int32 { +sil private @partial_apply_two_args2 : $@convention(thin) (Builtin.Int32) -> @owned @callee_owned () -> Builtin.Int32 { bb0(%0 : $Builtin.Int32): %1 = function_ref @closure2 : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 %2 = partial_apply %1(%0, %0) : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 @@ -290,7 +290,7 @@ bb0(%0 : $Builtin.Int32): // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - partial_apply_that_is_applied // CHECK-NEXT: ... -sil @called_closure : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 { +sil private @called_closure : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32): return %0 : $Builtin.Int32 } @@ -317,7 +317,7 @@ bb0(%0 : $Builtin.Int32): // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - partial_apply_that_is_applied // CHECK-NEXT: ... -sil @called_closure_then_destroy : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 { +sil private @called_closure_then_destroy : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32): return %0 : $Builtin.Int32 } @@ -367,7 +367,7 @@ bb0(%0 : $Builtin.Int32): // CHECK-NEXT: fullAppliers: // CHECK-NEXT: - partial_apply_that_is_applied // CHECK-NEXT: ... -sil @called_closure_then_copy_destroy : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 { +sil private @called_closure_then_copy_destroy : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32): return %0 : $Builtin.Int32 } @@ -414,7 +414,7 @@ bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32): // CHECK-NEXT: - partial_apply_that_is_applied_and_passed_noescape // CHECK-NEXT: - thin_to_thick_is_applied_and_passed_noescape // CHECK-NEXT: ... -sil @noescape_caller : $@convention(thin) (@noescape @callee_owned () -> Builtin.Int32) -> () { +sil private @noescape_caller : $@convention(thin) (@noescape @callee_owned () -> Builtin.Int32) -> () { bb0(%0 : $@noescape @callee_owned () -> Builtin.Int32): %1 = apply %0() : $@noescape @callee_owned () -> Builtin.Int32 %9999 = tuple() From 9567bd434181a4084a4fedf90a5b7654878da8e9 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 23 Sep 2019 09:02:04 -0700 Subject: [PATCH 059/140] [SILOptimizer] Alter FSO arg explosion heuristic. The new rule is that an argument will be exploded if one of the following sets of conditions hold: (1) (a) Specializing the function will result in a thunk. That is, the thunk that is generated cannot be inlined everywhere. (b) The argument has dead non-trivial leaves. (c) The argument has fewer than three live leaves. (2) (a) Specializing the function will not result in a thunk. That is, the thunk that is generated will be inlined everywhere and eliminated as dead code. (b) The argument has dead potentially trivial leaves. (c) The argument has fewer than six live leaves. This change is based heavily on @gottesm's https://github.com/apple/swift/pull/16756 . rdar://problem/39957093 --- .../ArgumentExplosionTransform.cpp | 294 ++- .../FunctionSignatureOpts.cpp | 17 +- .../FunctionSignatureOpts.h | 32 +- .../funcsig_explode_heuristic.sil | 207 ++ .../funcsig_explode_heuristic_inline.sil | 90 + test/SILOptimizer/functionsigopts.sil | 25 +- test/SILOptimizer/functionsigopts_sroa.sil | 550 +++-- .../functionsigopts_string_fileprivate.swift | 22 + .../functionsigopts_string_internal.swift | 20 + .../functionsigopts_string_public.swift | 21 + test/SILOptimizer/functionsigopts_trivial.sil | 2110 +++++++++++++++++ 11 files changed, 3062 insertions(+), 326 deletions(-) create mode 100644 test/SILOptimizer/funcsig_explode_heuristic.sil create mode 100644 test/SILOptimizer/funcsig_explode_heuristic_inline.sil create mode 100644 test/SILOptimizer/functionsigopts_string_fileprivate.swift create mode 100644 test/SILOptimizer/functionsigopts_string_internal.swift create mode 100644 test/SILOptimizer/functionsigopts_string_public.swift create mode 100644 test/SILOptimizer/functionsigopts_trivial.sil diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp index 21a3ca467e497..37038e08da81e 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp @@ -9,6 +9,15 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +/// +/// \file +/// +/// This file contains an implementation of the partial dead argument +/// elimination optimization. We do this to attempt to remove non-trivial +/// arguments of callees to eliminate lifetime constraints of a large argument +/// on values in the caller. +/// +//===----------------------------------------------------------------------===// #define DEBUG_TYPE "fso-argument-explosion-transform" #include "FunctionSignatureOpts.h" @@ -25,43 +34,282 @@ static llvm::cl::opt FSODisableArgExplosion( // Utility //===----------------------------------------------------------------------===// +/// Whether the known-to-date upper bound on the live leaf count is high enough +/// so that argument explosion is possible. +static bool +mayExplodeGivenLiveLeafCountUpperBound(unsigned knownLiveLeafCountUpperBound) { + return knownLiveLeafCountUpperBound > 0; +} + +static unsigned maxExplosionSizeWhenSpecializationWillIntroduceThunk( + bool willSpecializationIntroduceThunk) { + // 3 is the heuristic max explosion size for a single argument when the + // specializing the function will introduce a thunk. If specializing the + // function may not introduce a thunk, then we rely on the maximum size + // imposed by shouldExpand. + return willSpecializationIntroduceThunk ? 3 : UINT_MAX; +} + +static bool shouldExplode(unsigned knownLiveLeafCountUpperBound, + bool hasKnownDeadLeaves, + bool hasKnownDeadNontrivialLeaves, + bool willSpecializationIntroduceThunk) { + unsigned maxExplosionSize = + maxExplosionSizeWhenSpecializationWillIntroduceThunk( + /*willSpecializationIntroduceThunk=*/ + willSpecializationIntroduceThunk); + bool isLiveLeafCountInExplodableRange = + mayExplodeGivenLiveLeafCountUpperBound(knownLiveLeafCountUpperBound) && + (knownLiveLeafCountUpperBound <= maxExplosionSize); + bool hasKnownDeadRelevantLeaves = willSpecializationIntroduceThunk + ? hasKnownDeadNontrivialLeaves + : hasKnownDeadLeaves; + return isLiveLeafCountInExplodableRange && hasKnownDeadRelevantLeaves; +} + /// Return true if it's both legal and a good idea to explode this argument. -static bool shouldExplode(ArgumentDescriptor &argDesc, - ConsumedArgToEpilogueReleaseMatcher &ERM) { - // We cannot optimize the argument. - if (!argDesc.canOptimizeLiveArg()) +/// +/// Our main interest here is to expose more opportunities for ARC. This means +/// that we are not interested in exploding (and partially DCEing) structs in +/// the following cases: +/// +/// 1. Completely dead arguments. This is handled by dead argument elimination. +/// +/// 2. Structs with many live leaf nodes. Our heuristic is to explode if there +/// are only 1-3 live leaf nodes for specializations and 1-6 live leaf nodes +/// (in fact, the number specified in shouldExpand). Otherwise again we run +/// into register pressure/spilling issues. +/// TODO: Improve the 1-3 heuristic by having FSO consider the total +/// resultant argument count. Currently, there is no consideration of +/// that, meaning we could end up with argument exploding even in the +/// case of long argument lists where it isn't beneficial. +/// +/// Perform argument exploding if one of the following sets of conditions hold: +/// +/// 1. a. The live leaf count is less than or equal to 3. +/// b. There is a dead non-trivial leaf. +/// 2. a. The live leaf count is less than or equal to 6. +/// b. There is a dead trivial leaf. +/// c. Specializing the function will not result in a thunk. +static bool +shouldExplode(FunctionSignatureTransformDescriptor &transformDesc, + ArgumentDescriptor &argDesc, + ConsumedArgToEpilogueReleaseMatcher &epilogueReleaseMatcher) { + // The method is structured as follows: + // + // First, do some basic checks and exit early. + // Then in three steps of increasing complexity, calculate data which could + // permit the heuristic to decide to explode the argument. These steps + // provide information of increasing expense and fidelity. Checking whether + // the heuristic allows explosion after each step unnecessary work to be + // avoided. + // + // In a bit more detail: + // + // 1) Do some basic checks and exit early, returning false. + // - that we can optimize the argument at all + // - that the argument has more than a single leaf node + // - that the module permits the type to be expanded + // 2) Gather some basic leaf counts. + // - calculate the unmodified (unmodified that is by the results of the + // owned-to-guaranteed transformation) live leaf count + // - calculate the total list of leaf types to obtain the total leaf count + // 3) Check whether the heuristic allows the argument to be exploded using + // only potentially-trivial leaf counts. At this point it is certainly not + // known that there are dead non-trivial leaves, so exiting early here + // is only possible if specializing the function will not result in a + // thunk. + // 4) Gather the counts of non-trivial leaves. + // - calculate the count of total non-trivial leaves by filtering the total + // list of leaf types from step 2) according to whether leaf is trivial + // - calculate an upper bound (upper bound because it doesn't consider the + // results of the owned-to-guaranteed transformation) on the count of + // live non-trivial leaves + // 5) Check whether the heuristic allows the argument to be exploded using the + // upper bound on live non-trivial leaves. + // 6) Dial in the upper bounds calculated in steps 2) and 4) by compensating + // for the effects of the owned-to-guaranteed transformation. + // 7) Check whether the heuristic allows the argument to be exploded using the + // actual count of live leaves, both trivial and non-trivial. + + // No passes can optimize this argument, so just bail. + if (!argDesc.canOptimizeLiveArg()) { + LLVM_DEBUG(llvm::dbgs() + << "The argument is of a type that cannot be exploded."); return false; + } - // See if the projection tree consists of potentially multiple levels of - // structs containing one field. In such a case, there is no point in - // exploding the argument. + // If the argument is a singleton, it will not be exploded. // - // Also, in case of a type can not be exploded, e.g an enum, we treat it - // as a singleton. - if (argDesc.ProjTree.isSingleton()) + // Explosion makes sense only if some but not all of the leaves are live. + // + // Note that ProjectionTree::isSingleton returns true for enums since they are + // sums and not products and so only have a single top-level node. + if (argDesc.ProjTree.isSingleton()) { + LLVM_DEBUG(llvm::dbgs() << "The argument's type is a singleton."); return false; + } - auto *arg = argDesc.Arg; - if (!shouldExpand(arg->getModule(), arg->getType().getObjectType())) { + auto *argument = argDesc.Arg; + auto &module = argument->getModule(); + auto type = argument->getType().getObjectType(); + + // If the global type expansion heuristic does not allow the type to be + // expanded, it will not be exploded. + if (!shouldExpand(module, type)) { + LLVM_DEBUG(llvm::dbgs() + << "The argument is of a type which should not be expanded."); return false; } - // If this argument is @owned and we can not find all the releases for it - // try to explode it, maybe we can find some of the releases and O2G some - // of its components. + bool willSpecializationIntroduceThunk = + transformDesc.willSpecializationIntroduceThunk(); + + unsigned const liveLeafCountUpperBound = argDesc.ProjTree.getLiveLeafCount(); + + // If we know already that we may not explode given the upper bound we have + // established on the live leaf count, exit early. + // + // If the argument is completely dead, it will not be exploded. // - // This is a potentially a very profitable optimization. Ignore other - // heuristics. - if (arg->hasConvention(SILArgumentConvention::Direct_Owned) && - ERM.hasSomeReleasesForArgument(arg)) + // Explosion makes sense only if some but not all of the leaves are live. The + // dead argument transformation will try to eliminate the argument. + if (!mayExplodeGivenLiveLeafCountUpperBound(liveLeafCountUpperBound)) { + LLVM_DEBUG(llvm::dbgs() << "The argument has no live leaves."); + return false; + } + + // To determine whether some but not all of the leaves are used, the total + // leaf count must be retrieved. + llvm::SmallVector allLeaves; + argDesc.ProjTree.getAllLeafTypes(allLeaves); + unsigned const leafCount = allLeaves.size(); + + assert( + liveLeafCountUpperBound <= leafCount && + "There should be no more *live* leaves than there are *total* leaves."); + + if (shouldExplode( + /*knownLifeLeafCount=*/liveLeafCountUpperBound, + /*hasKnownDeadLeaves=*/liveLeafCountUpperBound < leafCount, + /*hasKnownDeadNontrivialLeaves=*/false, + /*willSpecializationIntroduceThunk=*/ + willSpecializationIntroduceThunk)) { + LLVM_DEBUG( + llvm::dbgs() + << "Without considering the liveness of non-trivial leaves, it has " + "already been determined that there are already fewer (" + << liveLeafCountUpperBound + << ") live leaves of the relevant sort (trivial) than total leaves (" + << leafCount << ") and no more total live leaves (" + << liveLeafCountUpperBound << ") than the heuristic permits (" + << maxExplosionSizeWhenSpecializationWillIntroduceThunk( + /*willSpecializationIntroduceThunk=*/ + willSpecializationIntroduceThunk) + << "). Exploding."); + return true; + } + + auto *function = argument->getFunction(); + unsigned const nontrivialLeafCount = llvm::count_if( + allLeaves, [&](SILType type) { return !type.isTrivial(*function); }); + + llvm::SmallVector liveLeaves; + argDesc.ProjTree.getLiveLeafNodes(liveLeaves); + // NOTE: The value obtained here is an upper bound because the + // owned-to-guaranteed transformation may eliminate some live + // non-trivial leaves, leaving the count lower. + unsigned const liveNontrivialLeafCountUpperBound = + llvm::count_if(liveLeaves, [&](const ProjectionTreeNode *leaf) { + return !leaf->getType().isTrivial(*function); + }); + + assert(liveNontrivialLeafCountUpperBound <= nontrivialLeafCount && + "There should be no more *live* non-trivial leaves than there are " + "*total* non-trivial leaves."); + assert(nontrivialLeafCount <= leafCount && + "There should be no more *non-trivial* leaves than there are *total* " + "leaves."); + + // If it is known without taking the owned-to-guaranteed transformation into + // account both that exploding will reduce ARC traffic (because an upper bound + // for the number of live non-trivial leaves is less than the non-trivial + // leaf count) and also that the explosion will fit within the heuristic upper + // bound (because an upper bound for the total live leaf count falls within + // the limit imposed by the heuristic), then explode now. + bool shouldExplodeGivenUpperBounds = shouldExplode( + /*knownLiveLeafCount=*/liveLeafCountUpperBound, + /*hasKnownDeadLeaves=*/liveLeafCountUpperBound < leafCount, + /*hasKnownDeadNontrivialLeaves=*/liveNontrivialLeafCountUpperBound < + nontrivialLeafCount, + /*willSpecializationIntroduceThunk=*/willSpecializationIntroduceThunk); + if (shouldExplodeGivenUpperBounds) { + LLVM_DEBUG( + llvm::dbgs() + << "Without considering the expected results of the " + "owned-to-guaranteed transformation, there are already fewer (" + << liveNontrivialLeafCountUpperBound + << ") live non-trivial leaves than total leaves (" + << nontrivialLeafCount << ") and no more total live leaves (" + << liveLeafCountUpperBound << ") than the heuristic permits (" + << maxExplosionSizeWhenSpecializationWillIntroduceThunk( + /*willSpecializationIntroduceThunk=*/ + willSpecializationIntroduceThunk) + << "). Exploding."); return true; + } + + unsigned liveLeafCount = liveLeafCountUpperBound; + unsigned liveNontrivialLeafCount = liveNontrivialLeafCountUpperBound; + + // The upper bounds that have been established for the live leaf counts are + // too high to permit us to explode. That could be because it hasn't been + // established that any leaves are dead or alternatively that it hasn't been + // established that there are fewer total live leaves than the limit imposed + // by the heuristic. In either case, if some live leaves are eliminated, the + // number of live leaves may decrease such that exploding will be possible. + // The results of the owned-to-guaranteed transformation are predicated. If + // it is predicted that a leaf will be dead after the owned-to-guaranteed + // transformation, then the leaf count is decreased. + // + // The owned-to-guaranteed will only be applied to the argumehnt if its + // convention is Direct_Owned. Additionally, it only applies to non-trivial + // leaves, which it may kill, so if it is already known that there are no live + // non-trivial leaves, owned-to-guaranteed will not eliminate anything. + if (argDesc.hasConvention(SILArgumentConvention::Direct_Owned) && + liveNontrivialLeafCountUpperBound > 0) { + if (auto maybeReleases = + epilogueReleaseMatcher.getPartiallyPostDomReleaseSet(argument)) { + auto releases = maybeReleases.getValue(); + llvm::SmallPtrSet users; + users.insert(std::begin(releases), std::end(releases)); + + for (auto *leaf : liveLeaves) { + if (llvm::all_of(leaf->getNonProjUsers(), [&](Operand *operand) { + return users.count(operand->getUser()); + })) { + // Every non-projection user of the leaf is an epilogue release. The + // owned-to-guaranteed transformation will eliminate this usage. With + // the expectation of that usage being eliminated, stop considering + // this leaf to be live for the purposes of deciding whether the + // argument should be exploded. + --liveLeafCount; + --liveNontrivialLeafCount; + } + } + } + } - unsigned explosionSize = argDesc.ProjTree.getLiveLeafCount(); - return explosionSize >= 1 && explosionSize <= 3; + return shouldExplode( + /*knownLifeLeafCount=*/liveLeafCount, + /*hasKnownDeadLeaves=*/liveLeafCount < leafCount, + /*hasKnownDeadNontrivialLeaves=*/liveNontrivialLeafCount < + nontrivialLeafCount, + /*willSpecializationIntroduceThunk=*/willSpecializationIntroduceThunk); } //===----------------------------------------------------------------------===// -// Implementation +// Top Level Implementation //===----------------------------------------------------------------------===// bool FunctionSignatureTransform::ArgumentExplosionAnalyzeParameters() { @@ -95,7 +343,7 @@ bool FunctionSignatureTransform::ArgumentExplosionAnalyzeParameters() { continue; A.ProjTree.computeUsesAndLiveness(A.Arg); - A.Explode = shouldExplode(A, ArgToReturnReleaseMap); + A.Explode = shouldExplode(TransformDescriptor, A, ArgToReturnReleaseMap); // Modified self argument. if (A.Explode && Args[i]->isSelf()) { diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index d41f99106ddc1..6cfef091876fc 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -95,7 +95,7 @@ static bool isSpecializableRepresentation(SILFunctionTypeRepresentation Rep, llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch."); } -/// Returns true if F is a function which the pass know show to specialize +/// Returns true if F is a function which the pass knows how to specialize /// function signatures for. static bool canSpecializeFunction(SILFunction *F, const CallerAnalysis::FunctionInfo *FuncInfo, @@ -622,7 +622,11 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Run the optimization. bool FunctionSignatureTransform::run(bool hasCaller) { - bool Changed = false; + // We use a reference here on purpose so our transformations can know if we + // are going to make a thunk and thus should just optimize. + bool &Changed = TransformDescriptor.Changed; + bool hasOnlyDirectInModuleCallers = + TransformDescriptor.hasOnlyDirectInModuleCallers; SILFunction *F = TransformDescriptor.OriginalFunction; // Never repeat the same function signature optimization on the same function. @@ -657,7 +661,8 @@ bool FunctionSignatureTransform::run(bool hasCaller) { // Run DeadArgument elimination transformation. We only specialize // if this function has a caller inside the current module or we have // already created a thunk. - if ((hasCaller || Changed) && DeadArgumentAnalyzeParameters()) { + if ((hasCaller || Changed || hasOnlyDirectInModuleCallers) && + DeadArgumentAnalyzeParameters()) { Changed = true; LLVM_DEBUG(llvm::dbgs() << " remove dead arguments\n"); DeadArgumentTransformFunction(); @@ -675,7 +680,8 @@ bool FunctionSignatureTransform::run(bool hasCaller) { // In order to not miss any opportunity, we send the optimized function // to the passmanager to optimize any opportunities exposed by argument // explosion. - if ((hasCaller || Changed) && ArgumentExplosionAnalyzeParameters()) { + if ((hasCaller || Changed || hasOnlyDirectInModuleCallers) && + ArgumentExplosionAnalyzeParameters()) { Changed = true; } @@ -830,7 +836,8 @@ class FunctionSignatureOpts : public SILFunctionTransform { SILOptFunctionBuilder FuncBuilder(*this); // Owned to guaranteed optimization. FunctionSignatureTransform FST(FuncBuilder, F, RCIA, EA, Mangler, AIM, - ArgumentDescList, ResultDescList); + ArgumentDescList, ResultDescList, + FuncInfo.foundAllCallers()); bool Changed = false; if (OptForPartialApply) { diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h index 3c2a467639d93..84c1f62847afb 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h @@ -198,6 +198,13 @@ struct FunctionSignatureTransformDescriptor { /// will use during our optimization. MutableArrayRef ResultDescList; + /// Are we going to make a change to this function? + bool Changed; + + /// Does this function only have direct callers. In such a case we know that + /// all thunks we create will be eliminated so we can be more aggressive. + bool hasOnlyDirectInModuleCallers; + /// Return a function name based on the current state of ArgumentDescList and /// ResultDescList. /// @@ -218,6 +225,19 @@ struct FunctionSignatureTransformDescriptor { /// simply passes it through. void addThunkArgument(ArgumentDescriptor &AD, SILBuilder &Builder, SILBasicBlock *BB, SmallVectorImpl &NewArgs); + + /// Whether specializing the function will result in a thunk with the same + /// signature as the original function that calls through to the specialized + /// function. + /// + /// Such a thunk is necessary if there is (or could be) code that calls the + /// function which we are unable to specialize to match the function's + /// specialization. + bool willSpecializationIntroduceThunk() { + return !hasOnlyDirectInModuleCallers || + OriginalFunction->isPossiblyUsedExternally() || + OriginalFunction->isAvailableExternally(); + } }; class FunctionSignatureTransform { @@ -291,15 +311,17 @@ class FunctionSignatureTransform { public: /// Constructor. FunctionSignatureTransform( - SILOptFunctionBuilder &FunctionBuilder, - SILFunction *F, RCIdentityAnalysis *RCIA, EpilogueARCAnalysis *EA, + SILOptFunctionBuilder &FunctionBuilder, SILFunction *F, + RCIdentityAnalysis *RCIA, EpilogueARCAnalysis *EA, Mangle::FunctionSignatureSpecializationMangler &Mangler, llvm::SmallDenseMap &AIM, llvm::SmallVector &ADL, - llvm::SmallVector &RDL) + llvm::SmallVector &RDL, + bool hasOnlyDirectInModuleCallers) : FunctionBuilder(FunctionBuilder), - TransformDescriptor{F, nullptr, AIM, false, ADL, RDL}, RCIA(RCIA), - EA(EA) {} + TransformDescriptor{F, nullptr, AIM, false, + ADL, RDL, false, hasOnlyDirectInModuleCallers}, + RCIA(RCIA), EA(EA) {} /// Return the optimized function. SILFunction *getOptimizedFunction() { diff --git a/test/SILOptimizer/funcsig_explode_heuristic.sil b/test/SILOptimizer/funcsig_explode_heuristic.sil new file mode 100644 index 0000000000000..2aad732ec6ea5 --- /dev/null +++ b/test/SILOptimizer/funcsig_explode_heuristic.sil @@ -0,0 +1,207 @@ +// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all -function-signature-opts -sil-fso-disable-dead-argument -sil-fso-disable-owned-to-guaranteed -enable-expand-all -sil-fso-optimize-if-not-called %s | %FileCheck %s + +// *NOTE* We turn off all other fso optimizations including dead arg so we can +// make sure that we are not exploding those. + +sil_stage canonical + +import Builtin + +////////////////// +// Declarations // +////////////////// + +struct BigTrivial { + var x1: Builtin.Int32 + var x2: Builtin.Int32 + var x3: Builtin.Int32 + var x4: Builtin.Int32 + var x5: Builtin.Int32 + var x6: Builtin.Int32 +} + +class Klass {} + +struct LargeNonTrivialStructOneNonTrivialField { + var k1: Klass + var k2: Klass + var x1: Builtin.Int32 + var x2: Builtin.Int32 + var x3: Builtin.Int32 + var x4: Builtin.Int32 +} + +sil @int_user : $@convention(thin) (Builtin.Int32) -> () +sil @consuming_user : $@convention(thin) (@owned Klass) -> () +sil @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + +/////////// +// Tests // +/////////// + +// We should never optimize this. If we did this would become a thunk, so we +// know that just be checking NFC we have proven no optimization has occured. +// +// CHECK-LABEL: sil @never_explode_trivial : $@convention(thin) (BigTrivial) -> () { +// CHECK: } // end sil function 'never_explode_trivial' +sil @never_explode_trivial : $@convention(thin) (BigTrivial) -> () { +bb0(%0 : $BigTrivial): + %1 = struct_extract %0 : $BigTrivial, #BigTrivial.x1 + %intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%1) : $@convention(thin) (Builtin.Int32) -> () + %9999 = tuple() + return %9999 : $() +} + +// If a value is never used, do not touch it. We leave it for dead argument +// elimination. We have delibrately turned this off to test that behavior. +// +// CHECK-LABEL: sil @big_arg_with_no_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK-NOT: apply +// CHECK: } // end sil function 'big_arg_with_no_uses' +sil @big_arg_with_no_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %9999 = tuple() + return %9999 : $() +} + +// We are using a single non-trivial field of the struct. We should explode this +// so we eliminate the second non-trivial leaf. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField): +// CHECK: [[FUNC:%.*]] = function_ref @$s31big_arg_with_one_nontrivial_useTf4x_n +// CHECK: [[FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 +// CHECK: apply [[FUNC]]([[FIELD]]) +// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use' +sil @big_arg_with_one_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + apply %2(%1) : $@convention(thin) (@guaranteed Klass) -> () + %9999 = tuple() + return %9999 : $() +} + +// We are using a single non-trivial field and a single trivial field. We are +// willing to blow this up. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_one_trivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField): +// CHECK: [[FUNC:%.*]] = function_ref @$s032big_arg_with_one_nontrivial_use_d9_trivial_F0Tf4x_n : $@convention(thin) (@guaranteed Klass, Builtin.Int32) -> () +// CHECK: [[TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1 +// CHECK: [[NON_TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 +// CHECK: apply [[FUNC]]([[NON_TRIVIAL_FIELD]], [[TRIVIAL_FIELD]]) +// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_one_trivial_use' +sil @big_arg_with_one_nontrivial_use_one_trivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1 + %3 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%1) : $@convention(thin) (@guaranteed Klass) -> () + %intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> () + %9999 = tuple() + return %9999 : $() +} + +// We can still explode this, since our limit is 3 values. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_two_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField): +// CHECK: [[FUNC:%.*]] = function_ref @$s48big_arg_with_one_nontrivial_use_two_trivial_usesTf4x_n : $@convention(thin) +// CHECK: [[TRIVIAL_FIELD1:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2 +// CHECK: [[TRIVIAL_FIELD2:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1 +// CHECK: [[NON_TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 +// CHECK: apply [[FUNC]]([[NON_TRIVIAL_FIELD]], [[TRIVIAL_FIELD2]], [[TRIVIAL_FIELD1]]) +sil @big_arg_with_one_nontrivial_use_two_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1 + %3 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2 + %4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> () + %intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%3) : $@convention(thin) (Builtin.Int32) -> () + %9999 = tuple() + return %9999 : $() +} + +// We do not blow up the struct here since we have 4 uses, not 3. +// +// CHECK-LABEL: sil @big_arg_with_one_nontrivial_use_three_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +sil @big_arg_with_one_nontrivial_use_three_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1 + %3 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2 + %3a = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x3 + %4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> () + %intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%3) : $@convention(thin) (Builtin.Int32) -> () + apply %intfunc(%3a) : $@convention(thin) (Builtin.Int32) -> () + %9999 = tuple() + return %9999 : $() +} + +// In this case, we shouldn't blow up the struct since we have not reduced the +// number of non-trivial leaf nodes used. +// +// CHECK-LABEL: sil @big_arg_with_two_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +sil @big_arg_with_two_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2 + %3 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%1) : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> () + %9999 = tuple() + return %9999 : $() +} + +// If we have one non-trivial value that is live and only live because of a +// destroy, we can delete the argument after performing o2g. +// +// We are using a single non-trivial field of the struct. We should explode this +// so we eliminate the second non-trivial leaf. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_o2g_other_dead : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK-NOT: release_value +// CHECK: apply +// CHECK-NOT: release_value +// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_o2g_other_dead' +sil @big_arg_with_one_nontrivial_use_o2g_other_dead : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + release_value %1 : $Klass + %9999 = tuple() + return %9999 : $() +} + +// If we have two non-trivial values that are live and one is always dead and +// the other is kept alive due to a release, we can get rid of both since FSO +// reruns with o2g. Test here that we explode it appropriatel even though we +// aren't reducing the number of non-trivial uses. The +// funcsig_explode_heuristic_inline.sil test makes sure we in combination +// produce the appropriate SIL. +// +// We check that we can inline this correctly in the inline test. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField): +// CHECK: [[FUNC:%.*]] = function_ref @$s35big_arg_with_one_nontrivial_use_o2gTf4x_n : $@convention(thin) (@owned Klass, @owned Klass) -> () +// CHECK: apply [[FUNC]]( +// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_o2g' +sil @big_arg_with_one_nontrivial_use_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2 + %3 = function_ref @consuming_user : $@convention(thin) (@owned Klass) -> () + apply %3(%2) : $@convention(thin) (@owned Klass) -> () + release_value %1 : $Klass + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILOptimizer/funcsig_explode_heuristic_inline.sil b/test/SILOptimizer/funcsig_explode_heuristic_inline.sil new file mode 100644 index 0000000000000..9a21cea46ce90 --- /dev/null +++ b/test/SILOptimizer/funcsig_explode_heuristic_inline.sil @@ -0,0 +1,90 @@ +// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all -sil-inline-generics -inline -function-signature-opts -enable-expand-all %s | %FileCheck %s + +sil_stage canonical + +import Builtin + +////////////////// +// Declarations // +////////////////// + +class Klass {} + +struct LargeNonTrivialStructOneNonTrivialField { + var k1: Klass + var k2: Klass + var x1: Builtin.Int32 + var x2: Builtin.Int32 + var x3: Builtin.Int32 + var x4: Builtin.Int32 +} + +sil @consuming_user : $@convention(thin) (@owned Klass) -> () +sil @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + +// This test makes sure that if we have two non-trivial values that are live and +// one is always dead and the other is a value that we have a release for, we +// can get rid of the first argument and FSO the other. Test here that we +// explode it appropriately and do a partial o2g even though we aren't reducing +// the number of non-trivial uses. + +// CHECK-LABEL: sil @caller1 : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField): +// CHECK: [[FUNC:%.*]] = function_ref @partial_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () +// CHECK: apply [[FUNC]]([[ARG]]) : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () +// CHECK: } // end sil function 'caller1' +sil @caller1 : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = function_ref @partial_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () + apply %1(%0) : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () + %9999 = tuple() + return %9999 : $() +} + +// If we have two non-trivial values that are live and one is always dead and +// the other is kept alive due to a release, we can get rid of both since FSO +// reruns with o2g. Test here that we explode it appropriately and do a partial +// o2g even though we aren't reducing the number of non-trivial uses. +sil hidden [noinline] @partial_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2 + %3 = function_ref @consuming_user : $@convention(thin) (@owned Klass) -> () + apply %3(%2) : $@convention(thin) (@owned Klass) -> () + %4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> () + apply %4(%1) :$@convention(thin) (@guaranteed Klass) -> () + release_value %1 : $Klass + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil @caller2 : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField): +// CHECK: [[FIELD1:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2 +// CHECK: [[FIELD2:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 +// CHECK: [[FUNC:%.*]] = function_ref @$s23partiallydead_after_o2gTf4x_nTf4dn_n : $@convention(thin) (@owned Klass) -> () +// CHECK: apply [[FUNC]]([[FIELD1]]) : $@convention(thin) (@owned Klass) -> () +// CHECK: release_value [[FIELD2]] +// CHECK: } // end sil function 'caller2' +sil @caller2 : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = function_ref @partiallydead_after_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () + apply %1(%0) : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () + %9999 = tuple() + return %9999 : $() +} + +// If we have two non-trivial values that are live and one is always dead and +// the other is kept alive due to a release, we can get rid of both since FSO +// reruns with o2g. Test here that we explode it appropriately and do a partial +// o2g even though we aren't reducing the number of non-trivial uses. +sil hidden [noinline] @partiallydead_after_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () { +bb0(%0 : $LargeNonTrivialStructOneNonTrivialField): + %1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1 + %2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2 + %3 = function_ref @consuming_user : $@convention(thin) (@owned Klass) -> () + apply %3(%2) : $@convention(thin) (@owned Klass) -> () + release_value %1 : $Klass + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILOptimizer/functionsigopts.sil b/test/SILOptimizer/functionsigopts.sil index a25565cd1c35e..adec02118cef7 100644 --- a/test/SILOptimizer/functionsigopts.sil +++ b/test/SILOptimizer/functionsigopts.sil @@ -184,6 +184,7 @@ bb0(%0 : $boo): // CHECK: [[IN2:%.*]] = struct_extract %0 : $lotsoffield, #lotsoffield.b // CHECK: [[IN3:%.*]] = struct_extract %0 : $lotsoffield, #lotsoffield.a // CHECK: apply [[FN1]]([[IN3]], [[IN2]], [[IN1]]) +// CHECK-LABEL: } // end sil function 'dead_argument_due_to_only_release_user_but__exploded' sil @dead_argument_due_to_only_release_user_but__exploded : $@convention(thin) (@owned lotsoffield) -> (Int, Int, Int) { bb0(%0 : $lotsoffield): // make it a non-trivial function @@ -219,10 +220,16 @@ bb0(%0 : $lotsoffield): return %5 : $(Int, Int, Int) } -// Make sure argument is exploded and the baz part is not passed in as argument, as its only use -// is a release. +// Since this is a value that contains only a singular owned type, there is no +// point from an ARC perspective in splitting it up. We still want to perform +// owned to guaranteed though. +// // CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @dead_argument_due_to_more_than_release_user -// CHECK: [[FN1:%.*]] = function_ref @$s43dead_argument_due_to_more_than_release_userTf4gX_n : $@convention(thin) (@guaranteed baz, Int) -> (Int, Int) +// CHECK: bb{{[0-9]+}}([[BOO:%.*]] : $boo): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s43dead_argument_due_to_more_than_release_userTf4g_n : $@convention(thin) (@guaranteed boo) -> (Int, Int) +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[BOO]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'dead_argument_due_to_more_than_release_user' sil @dead_argument_due_to_more_than_release_user : $@convention(thin) (@owned boo) -> (Int, Int) { bb0(%0 : $boo): // make it a non-trivial function @@ -468,12 +475,13 @@ bb3(%4 : $boo): return %4 : $boo } -// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @single_owned_return_value_with_interfering_release +// The function is public (hence possibly used externally) and the full boo +// argument is live. Consequently, it is not specialized. +// CHECK-LABEL: sil @single_owned_return_value_with_interfering_release // CHECK: bb0([[INPUT_ARG0:%[0-9]+]] : $boo): -// CHECK: [[IN1:%.*]] = function_ref @$s50single_owned_return_value_with_interfering_releaseTf4x_nTf4gnn_n -// CHECK-NOT: retain_value // CHECK: return -sil @single_owned_return_value_with_interfering_release : $@convention(thin) (@owned boo) -> boo { +// CHECK-LABEL: } // end sil function 'single_owned_return_value_with_interfering_release' +sil @single_owned_return_value_with_interfering_release : $@convention(thin) (@owned boo) -> boo { bb0(%0 : $boo): // make it a non-trivial function %c1 = builtin "assert_configuration"() : $Builtin.Int32 @@ -1347,6 +1355,7 @@ bb0(%0 : $Optional<(foo, foo)>): // CHECK: [[F:%[0-9]+]] = function_ref @dont_explode_single_enum : $@convention(thin) (@owned Optional<(foo, foo)>) -> @owned foo // CHECK: apply [[F]](%0) // CHECK: return +// CHECK-LABEL: } // end sil function 'call_with_single_enum' sil @call_with_single_enum : $@convention(thin) (@owned Optional<(foo, foo)>) -> @owned foo { bb0(%0 : $Optional<(foo, foo)>): %f = function_ref @dont_explode_single_enum : $@convention(thin) (@owned Optional<(foo, foo)>) -> @owned foo @@ -1364,9 +1373,11 @@ bb0(%0 : $foo): } // CHECK-LABEL: sil @call_externally_available +// CHECK: bb{{[0-9]+}}([[FOO:%.*]] : $foo): // CHECK: [[F:%[0-9]+]] = function_ref @$s34externally_available_with_dead_argTf4d_n : $@convention(thin) () -> () // CHECK: apply [[F]]() // CHECK: return +// CHECK-LABEL: } // end sil function 'call_externally_available' sil @call_externally_available : $@convention(thin) (@guaranteed foo) -> () { bb0(%0 : $foo): %f = function_ref @externally_available_with_dead_arg : $@convention(thin) (@guaranteed foo) -> () diff --git a/test/SILOptimizer/functionsigopts_sroa.sil b/test/SILOptimizer/functionsigopts_sroa.sil index 6d03520f9bb0a..3a9ce57887c4d 100644 --- a/test/SILOptimizer/functionsigopts_sroa.sil +++ b/test/SILOptimizer/functionsigopts_sroa.sil @@ -54,6 +54,40 @@ struct S4 { var f1 : Builtin.NativeObject } +sil @s4_user : $@convention(thin) (S4) -> () +sil @s4_pointer_user : $@convention(thin) (@in S4) -> () +sil @s4_user_twice : $@convention(thin) (S4, S4) -> () + +struct S4Pair { + var f1 : S4 + var f2 : S4 +} + +struct S4Triple { + var f1 : S4 + var f2 : S4 + var f3 : S4 +} + +struct S4Octet { + var f1 : S4 + var f2 : S4 + var f3 : S4 + var f4 : S4 + var f5 : S4 + var f6 : S4 + var f7 : S4 + var f8 : S4 +} + +struct S4Fourtytet { + var f1 : S4Octet + var f2 : S4Octet + var f3 : S4Octet + var f4 : S4Octet + var f5 : S4Octet +} + struct S5 { var f1 : S4 var f2 : S1 @@ -134,13 +168,12 @@ struct SingleFieldLvl3 { // Tests // /////////// -/// This checks the case where we have a single level hierarchy and the root is -/// dead. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @single_level_dead_root_callee : $@convention(thin) (S1) -> Builtin.Int32 { +/// Verify that an argument is not exploded if it is entirely trivial even if it +/// is partly dead. +// CHECK-LABEL: sil [serialized] @single_level_dead_root_callee : $@convention(thin) (S1) -> Builtin.Int32 { // CHECK: bb0([[INPUT:%[0-9]+]] : $S1): -// CHECK: [[FN:%[0-9]+]] = function_ref @$s29single_level_dead_root_calleeTfq4x_n : $@convention(thin) (Builtin.Int32) -> Builtin.Int32 -// CHECK: [[ARG:%.*]] = struct_extract [[INPUT]] : $S1, #S1.f2 -// CHECK: apply [[FN]]([[ARG]]) +// CHECK: [[RESULT:%.*]] = struct_extract [[INPUT]] : $S1, #S1.f2 +// CHECK: return [[RESULT]] : $Builtin.Int32 sil [serialized] @single_level_dead_root_callee : $@convention(thin) (S1) -> Builtin.Int32 { bb0(%0 : $S1): // make it a non-trivial function @@ -173,7 +206,9 @@ bb0(%0 : $S1): // CHECK-LABEL: sil [serialized] @single_level_dead_root_caller : $@convention(thin) (S1) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $S1): -// CHECK: [[FN:%[0-9]+]] = function_ref @$s29single_level_dead_root_calleeTfq4x_n +// CHECK: [[FN:%[0-9]+]] = function_ref @single_level_dead_root_callee +// CHECK: apply [[FN]]([[INPUT]]) +// CHECK-LABEL: } // end sil function 'single_level_dead_root_caller' sil [serialized] @single_level_dead_root_caller : $@convention(thin) (S1) -> () { bb0(%0 : $S1): %1 = function_ref @single_level_dead_root_callee : $@convention(thin) (S1) -> Builtin.Int32 @@ -182,12 +217,14 @@ bb0(%0 : $S1): return %9999 : $() } -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @single_level_live_root_callee : $@convention(thin) (S1) -> Builtin.Int32 { +/// Verify that fully live trivial arguments are not exploded. +// CHECK-LABEL: sil [serialized] @single_level_live_root_callee : $@convention(thin) (S1) -> Builtin.Int32 { // CHECK: bb0([[INPUT:%[0-9]+]] : $S1): -// CHECK: [[FN:%[0-9]+]] = function_ref @$s29single_level_live_root_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> Builtin.Int32 -// CHECK: [[ARG2:%.*]] = struct_extract [[INPUT]] : $S1, #S1.f2 -// CHECK: [[ARG1:%.*]] = struct_extract [[INPUT]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[ARG1]], [[ARG2]]) +// CHECK: [[RESULT:%.*]] = struct_extract [[INPUT]] : $S1, #S1.f2 +// CHECK: [[FN:%.*]] = function_ref @s1_user : $@convention(thin) (S1) -> () +// CHECK: apply [[FN]]([[INPUT]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'single_level_live_root_callee' sil [serialized] @single_level_live_root_callee : $@convention(thin) (S1) -> Builtin.Int32 { bb0(%0 : $S1): // make it a non-trivial function @@ -222,7 +259,9 @@ bb0(%0 : $S1): // CHECK-LABEL: sil [serialized] @single_level_live_root_caller : $@convention(thin) (S1) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $S1): -// CHECK: [[FN:%[0-9]+]] = function_ref @$s29single_level_live_root_calleeTfq4x_n +// CHECK: [[FN:%[0-9]+]] = function_ref @single_level_live_root_callee +// CHECK: apply [[FN]]([[INPUT]]) +// CHECK-LABEL: } // end sil function 'single_level_live_root_caller' sil [serialized] @single_level_live_root_caller : $@convention(thin) (S1) -> () { bb0(%0 : $S1): %1 = function_ref @single_level_live_root_callee : $@convention(thin) (S1) -> Builtin.Int32 @@ -231,16 +270,16 @@ bb0(%0 : $S1): return %9999 : $() } -// This test checks where we have a multiple level hierarchy, the root is dead, -// but the root has all fields used. This means that we should extract -// everything, but we should not "reform" the aggregate. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @multiple_level_all_root_fields_used_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) { +/// Verify that a two-level aggregate argument which is entirely trivial, whose +/// root is dead but all of whose leaves are live will not be exploded. +// CHECK-LABEL: sil [serialized] @multiple_level_all_root_fields_used_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) { // CHECK: bb0([[INPUT:%.*]] : $S2): -// CHECK: [[FN:%.*]] = function_ref @$s42multiple_level_all_root_fields_used_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) -// CHECK: [[EXT1:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f1 -// CHECK: [[EXT3:%.*]] = struct_extract [[EXT2]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT3]], [[EXT1]]) +// CHECK: [[INPUTF1:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f1 +// CHECK: [[INPUTF1F1:%.*]] = struct_extract [[INPUTF1]] : $S1, #S1.f1 +// CHECK: [[INPUTF2:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f2 +// CHECK: [[OUT:%.*]] = tuple ([[INPUTF1F1]] : $Builtin.Int16, [[INPUTF2]] : $Builtin.Int64) +// CHECK: return [[OUT]] +// CHECK-LABEL: } // end sil function 'multiple_level_all_root_fields_used_callee' sil [serialized] @multiple_level_all_root_fields_used_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) { bb0(%0 : $S2): // make it a non-trivial function @@ -278,11 +317,9 @@ bb0(%0 : $S2): // CHECK-LABEL: sil [serialized] @multiple_level_all_root_fields_used_caller : $@convention(thin) (S2) -> () { // CHECK: bb0([[INPUT:%.*]] : $S2): -// CHECK: [[FN:%.*]] = function_ref @$s42multiple_level_all_root_fields_used_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) -// CHECK: [[EXT1:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f1 -// CHECK: [[EXT3:%.*]] = struct_extract [[EXT2]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT3]], [[EXT1]]) +// CHECK: [[FN:%.*]] = function_ref @multiple_level_all_root_fields_used_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) +// CHECK: apply [[FN]]([[INPUT]]) +// CHECK-LABEL: } // end sil function 'multiple_level_all_root_fields_used_caller' sil [serialized] @multiple_level_all_root_fields_used_caller : $@convention(thin) (S2) -> () { bb0(%0 : $S2): %1 = function_ref @multiple_level_all_root_fields_used_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) @@ -291,16 +328,17 @@ bb0(%0 : $S2): return %9999 : $() } -/// This test checks a multiple level hierarchy where the root has no fields used. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @multiple_level_no_root_fields_have_direct_uses_callee : $@convention(thin) (S3) -> (Builtin.Int16, Builtin.Int64) { -// CHECK: bb0([[IN:%.*]] : $S3): -// CHECK: [[FN:%.*]] = function_ref @$s53multiple_level_no_root_fields_have_direct_uses_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S3, #S3.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S3, #S3.f1 -// CHECK: [[EXT3:%.*]] = struct_extract [[EXT2]] : $S2, #S2.f2 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT2]] : $S2, #S2.f1 -// CHECK: [[EXT5:%.*]] = struct_extract [[EXT4]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT5]], [[EXT3]]) +/// Verify that a three-level aggregate argument which is entirely trivial, +/// whose first level nodes are dead but some of whose leaves are live will not +/// be exploded. +// CHECK-LABEL: sil [serialized] @multiple_level_no_root_fields_have_direct_uses_callee : $@convention(thin) (S3) -> (Builtin.Int16, Builtin.Int64) { +// CHECK: bb0([[INPUT:%.*]] : $S3): +// CHECK: [[INPUTF1:%.*]] = struct_extract [[INPUT]] : $S3, #S3.f1 +// CHECK: [[INPUTF1F1:%.*]] = struct_extract [[INPUTF1]] : $S2, #S2.f1 +// CHECK: [[INPUTF1F1F1:%.*]] = struct_extract [[INPUTF1F1]] : $S1, #S1.f1 +// CHECK: [[INPUTF1F2:%.*]] = struct_extract [[INPUTF1]] : $S2, #S2.f2 +// CHECK: [[RESULT:%.*]] = tuple ([[INPUTF1F1F1]] : $Builtin.Int16, [[INPUTF1F2]] : $Builtin.Int64) +// CHECK-LABEL: } // end sil function 'multiple_level_no_root_fields_have_direct_uses_callee' sil [serialized] @multiple_level_no_root_fields_have_direct_uses_callee : $@convention(thin) (S3) -> (Builtin.Int16, Builtin.Int64) { bb0(%0 : $S3): // make it a non-trivial function @@ -337,14 +375,10 @@ bb0(%0 : $S3): } // CHECK-LABEL: sil [serialized] @multiple_level_no_root_fields_have_direct_uses_caller : $@convention(thin) (S3) -> () { -// CHECK: bb0([[IN:%.*]] : $S3): -// CHECK: [[FN:%.*]] = function_ref @$s53multiple_level_no_root_fields_have_direct_uses_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S3, #S3.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S3, #S3.f1 -// CHECK: [[EXT3:%.*]] = struct_extract [[EXT2]] : $S2, #S2.f2 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT2]] : $S2, #S2.f1 -// CHECK: [[EXT5:%.*]] = struct_extract [[EXT4]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT5]], [[EXT3]]) +// CHECK: bb0([[INPUT:%.*]] : $S3): +// CHECK: [[FUNCTION:%.*]] = function_ref @multiple_level_no_root_fields_have_direct_uses_callee : $@convention(thin) (S3) -> (Builtin.Int16, Builtin.Int64) +// CHECK: apply [[FUNCTION]]([[INPUT]]) +// CHECK-LABEL: } // end sil function 'multiple_level_no_root_fields_have_direct_uses_caller' sil [serialized] @multiple_level_no_root_fields_have_direct_uses_caller : $@convention(thin) (S3) -> () { bb0(%0 : $S3): %1 = function_ref @multiple_level_no_root_fields_have_direct_uses_callee : $@convention(thin) (S3) -> (Builtin.Int16, Builtin.Int64) @@ -353,16 +387,17 @@ bb0(%0 : $S3): return %9999 : $() } -// This test checks a multiple level hierarchy where the root has its own use -// and needs to be reformed via a struct. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @multiple_level_root_must_be_reformed_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) { -// CHECK: bb0([[IN:%.*]] : $S2): -// CHECK: [[FN:%.*]] = function_ref @$s43multiple_level_root_must_be_reformed_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S2, #S2.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S2, #S2.f1 -// CHECK: [[EXT3:%.*]] = struct_extract [[EXT2]] : $S1, #S1.f2 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT2]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT4]], [[EXT3]], [[EXT1]]) +/// Verify that a two-level aggregate argument which is entirely trivial, some +/// of whose leaves are live and which itself is live will not be exploded. +// CHECK-LABEL: sil [serialized] @multiple_level_root_must_be_reformed_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) { +// CHECK: bb0([[INPUT:%.*]] : $S2): +// CHECK: [[INPUTF1:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f1 +// CHECK: [[INPUTF1F1:%.*]] = struct_extract [[INPUTF1]] : $S1, #S1.f1 +// CHECK: [[INPUTF2:%.*]] = struct_extract [[INPUT]] : $S2, #S2.f2 +// CHECK: [[OUT:%.*]] = tuple ([[INPUTF1F1]] : $Builtin.Int16, [[INPUTF2]] : $Builtin.Int64) +// CHECK: [[FN:%.*]] = function_ref @s2_user : $@convention(thin) (S2) -> () +// CHECK: apply [[FN]]([[INPUT]]) : $@convention(thin) (S2) -> () +// CHECK-LABEL: } // end sil function 'multiple_level_root_must_be_reformed_callee' sil [serialized] @multiple_level_root_must_be_reformed_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) { bb0(%0 : $S2): // make it a non-trivial function @@ -399,13 +434,10 @@ bb0(%0 : $S2): } // CHECK-LABEL: sil [serialized] @multiple_level_root_must_be_reformed_caller : $@convention(thin) (S2) -> () { -// CHECK: bb0([[IN:%.*]] : $S2): -// CHECK: [[FN:%.*]] = function_ref @$s43multiple_level_root_must_be_reformed_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S2, #S2.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S2, #S2.f1 -// CHECK: [[EXT3:%.*]] = struct_extract [[EXT2]] : $S1, #S1.f2 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT2]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT4]], [[EXT3]], [[EXT1]]) +// CHECK: bb0([[INPUT:%.*]] : $S2): +// CHECK: [[FUNCTION:%.*]] = function_ref @multiple_level_root_must_be_reformed_callee : $@convention(thin) (S2) -> (Builtin.Int16, Builtin.Int64) +// CHECK: apply [[FUNCTION]]([[INPUT]]) +// CHECK-LABEL: } // end sil function 'multiple_level_root_must_be_reformed_caller' sil [serialized] @multiple_level_root_must_be_reformed_caller : $@convention(thin) (S2) -> () { bb0(%0 : $S2): // make it a non-trivial function @@ -438,16 +470,16 @@ bb0(%0 : $S2): return %9999 : $() } -// This test checks if we can handle @owned structs correctly +/// Verify that an aggregate argument containing a single non-trivial live leaf +/// is not exploded. // CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @owned_struct_1_callee : $@convention(thin) (@owned S5, @owned S5) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) { -// CHECK: bb0([[IN1:%.*]] : $S5, [[IN2:%.*]] : $S5): -// CHECK: [[FN:%.*]] = function_ref @$s21owned_struct_1_calleeTfq4dgX_n : $@convention(thin) (@guaranteed S4, Builtin.Int16, Builtin.Int32) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN2]] : $S5, #S5.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN2]] : $S5, #S5.f1 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f2 -// CHECK: [[EXT5:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT2]], [[EXT5]], [[EXT4]]) : $@convention(thin) (@guaranteed S4, Builtin.Int16, Builtin.Int32) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) -// CHECK: release_value [[IN]] : $S5 +// CHECK: bb0({{%.*}} : $S5, [[INPUT:%.*]] : $S5): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s21owned_struct_1_calleeTfq4dg_n : $@convention(thin) (@guaranteed S5) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INPUT]]) +// CHECK: release_value [[INPUT]] : $S5 +// CHECK: release_value {{%.*}} : $S5 +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'owned_struct_1_callee' sil [serialized] @owned_struct_1_callee : $@convention(thin) (@owned S5, @owned S5) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) { bb0(%0 : $S5, %1 : $S5): // make it a non-trivial function @@ -488,14 +520,12 @@ bb0(%0 : $S5, %1 : $S5): } // CHECK-LABEL: sil [serialized] @owned_struct_1_caller : $@convention(thin) (S5) -> () { -// CHECK: bb0([[IN:%.*]] : $S5): -// CHECK: [[FN:%.*]] = function_ref @$s21owned_struct_1_calleeTfq4dgX_n : $@convention(thin) (@guaranteed S4, Builtin.Int16, Builtin.Int32) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S5, #S5.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S5, #S5.f1 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f2 -// CHECK: [[EXT5:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT2]], [[EXT5]], [[EXT4]]) : $@convention(thin) (@guaranteed S4, Builtin.Int16, Builtin.Int32) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) -// CHECK: release_value [[IN]] : $S5 +// CHECK: bb0([[INPUT:%.*]] : $S5): +// CHECK: [[INPUTF2:%.*]] = struct_extract [[INPUT]] : $S5, #S5.f2 +// CHECK: [[INPUTF2F1:%.*]] = struct_extract [[INPUTF2]] : $S1, #S1.f1 +// CHECK: [[INPUTF2F2:%.*]] = struct_extract [[INPUTF2]] : $S1, #S1.f2 +// CHECK: [[OUT:%.*]] = tuple ([[INPUTF2F1]] : $Builtin.Int16, [[INPUTF2F2]] : $Builtin.Int32, [[INPUTF2F1]] : $Builtin.Int16, [[INPUTF2F2]] : $Builtin.Int32) +// CHECK-LABEL: } // end sil function 'owned_struct_1_caller' sil [serialized] @owned_struct_1_caller : $@convention(thin) (S5) -> () { bb0(%0 : $S5): %1 = function_ref @owned_struct_1_callee : $@convention(thin) (@owned S5, @owned S5) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) @@ -504,17 +534,19 @@ bb0(%0 : $S5): return %9999 : $() } -// This test checks if we can properly insert arguments in between dead arguments. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @owned_struct_2_callee : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S5, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int256, [[IN2:%.*]] : $Builtin.Int256, [[IN3:%.*]] : $S5, [[IN4:%.*]] : $Builtin.Int128, [[IN5:%.*]] : $Builtin.Int128): -// CHECK: [[FN:%.*]] = function_ref @$s21owned_struct_2_calleeTfq4ndgXdn_n : $@convention(thin) (Builtin.Int256, @guaranteed S4, Builtin.Int16, Builtin.Int32, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN3]] : $S5, #S5.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN3]] : $S5, #S5.f1 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f2 -// CHECK: [[EXT5:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[IN1]], [[EXT2]], [[EXT5]], [[EXT4]], [[IN5]]) : $@convention(thin) (Builtin.Int256, @guaranteed S4, Builtin.Int16, Builtin.Int32, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) -sil [serialized] @owned_struct_2_callee : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S5, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) { -bb0(%0 : $Builtin.Int256, %1 : $Builtin.Int256, %2 : $S5, %3 : $Builtin.Int128, %4 : $Builtin.Int128): +/// Verify that argument explosion that increases argument count before and +/// after dead argument elimination behaves properly. +// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @owned_struct_2_callee : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S4Triple, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) { +// CHECK: bb0([[IN1:%.*]] : $Builtin.Int256, [[IN2:%.*]] : $Builtin.Int256, [[TRIPLE:%.*]] : $S4Triple, [[IN4:%.*]] : $Builtin.Int128, [[IN5:%.*]] : $Builtin.Int128): +// CHECK: [[FN:%.*]] = function_ref @$s21owned_struct_2_calleeTfq4ndgXdn_n : $@convention(thin) (Builtin.Int256, @guaranteed S4, @guaranteed S4, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) +// CHECK: [[TRIPLEF3:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f3 +// CHECK: [[TRIPLEF2:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f2 +// CHECK: [[RESULT:%.*]] = apply [[FN]]([[IN1]], [[TRIPLEF2]], [[TRIPLEF3]], [[IN5]]) : $@convention(thin) (Builtin.Int256, @guaranteed S4, @guaranteed S4, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) +// CHECK: release_value [[TRIPLE]] : $S4Triple +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'owned_struct_2_callee' +sil [serialized] @owned_struct_2_callee : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S4Triple, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) { +bb0(%0 : $Builtin.Int256, %1 : $Builtin.Int256, %triple : $S4Triple, %3 : $Builtin.Int128, %4 : $Builtin.Int128): // make it a non-trivial function %c1 = builtin "assert_configuration"() : $Builtin.Int32 %c2 = builtin "assert_configuration"() : $Builtin.Int32 @@ -539,42 +571,42 @@ bb0(%0 : $Builtin.Int256, %1 : $Builtin.Int256, %2 : $S5, %3 : $Builtin.Int128, %c21 = builtin "assert_configuration"() : $Builtin.Int32 %c22 = builtin "assert_configuration"() : $Builtin.Int32 - %5 = struct_extract %2 : $S5, #S5.f2 - %6 = struct_extract %5 : $S1, #S1.f1 - %7 = struct_extract %5 : $S1, #S1.f2 - %11 = function_ref @s5_user : $@convention(thin) (S5) -> () - %12 = apply %11(%2) : $@convention(thin) (S5) -> () - release_value %2 : $S5 - %13 = tuple(%0 : $Builtin.Int256, %6 : $Builtin.Int16, %7 : $Builtin.Int32, %4 : $Builtin.Int128) - return %13 : $(Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) -} - - -// CHECK-LABEL: sil [serialized] @owned_struct_2_caller : $@convention(thin) (Builtin.Int256, S5, Builtin.Int128) -> () { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int256, [[IN2:%.*]] : $S5, [[IN3:%.*]] : $Builtin.Int128): -// CHECK: [[FN:%.*]] = function_ref @$s21owned_struct_2_calleeTfq4ndgXdn_n : $@convention(thin) (Builtin.Int256, @guaranteed S4, Builtin.Int16, Builtin.Int32, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN2]] : $S5, #S5.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN2]] : $S5, #S5.f1 -// CHECK: [[EXT4:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f2 -// CHECK: [[EXT5:%.*]] = struct_extract [[EXT1]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[IN1]], [[EXT2]], [[EXT5]], [[EXT4]], [[IN3]]) : $@convention(thin) (Builtin.Int256, @guaranteed S4, Builtin.Int16, Builtin.Int32, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) -sil [serialized] @owned_struct_2_caller : $@convention(thin) (Builtin.Int256, S5, Builtin.Int128) -> () { -bb0(%0 : $Builtin.Int256, %1 : $S5, %2 : $Builtin.Int128): - %3 = function_ref @owned_struct_2_callee : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S5, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) - %4 = apply %3(%0, %0, %1, %2, %2) : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S5, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) + %triple_field2 = struct_extract %triple : $S4Triple, #S4Triple.f2 + %triple_field3 = struct_extract %triple : $S4Triple, #S4Triple.f3 + %func = function_ref @s4_user_twice : $@convention(thin) (S4, S4) -> () + %_ = apply %func(%triple_field2, %triple_field3) : $@convention(thin) (S4, S4) -> () + release_value %triple : $S4Triple + %result = tuple (%0 : $Builtin.Int256, %0 : $Builtin.Int256, %4 : $Builtin.Int128, %4 : $Builtin.Int128) + return %result : $(Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) +} + + +// CHECK-LABEL: sil [serialized] @owned_struct_2_caller : $@convention(thin) (Builtin.Int256, S4Triple, Builtin.Int128) -> () { +// CHECK: bb0([[IN1:%.*]] : $Builtin.Int256, [[TRIPLE:%.*]] : $S4Triple, [[IN3:%.*]] : $Builtin.Int128): +// CHECK: [[FN:%.*]] = function_ref @$s21owned_struct_2_calleeTfq4ndgXdn_n : $@convention(thin) (Builtin.Int256, @guaranteed S4, @guaranteed S4, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) +// CHECK: [[TRIPLEF3:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f3 +// CHECK: [[TRIPLEF2:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f2 +// CHECK: apply [[FN]]([[IN1]], [[TRIPLEF2]], [[TRIPLEF3]], [[IN3]]) : $@convention(thin) (Builtin.Int256, @guaranteed S4, @guaranteed S4, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) +// CHECK-LABEL: } // end sil function 'owned_struct_2_caller' +sil [serialized] @owned_struct_2_caller : $@convention(thin) (Builtin.Int256, S4Triple, Builtin.Int128) -> () { +bb0(%0 : $Builtin.Int256, %1 : $S4Triple, %2 : $Builtin.Int128): + %3 = function_ref @owned_struct_2_callee : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S4Triple, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) + %4 = apply %3(%0, %0, %1, %2, %2) : $@convention(thin) (Builtin.Int256, Builtin.Int256, @owned S4Triple, Builtin.Int128, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) %9999 = tuple() return %9999 : $() } -/// This test makes sure that we ignore pointer arguments for now. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @ignore_ptrs_callee : $@convention(thin) (@in S1, S1, S1) -> (Builtin.Int16, Builtin.Int16) { -// CHECK: bb0([[IN1:%.*]] : $*S1, [[IN2:%.*]] : $S1, [[IN3:%.*]] : $S1): -// CHECK: [[FN:%.*]] = function_ref @$s18ignore_ptrs_calleeTfq4nxx_n : $@convention(thin) (@in S1, Builtin.Int16, Builtin.Int16) -> (Builtin.Int16, Builtin.Int16) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN2]] : $S1, #S1.f1 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN3]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[IN1]], [[EXT1]], [[EXT2]]) : $@convention(thin) (@in S1, Builtin.Int16, Builtin.Int16) -> (Builtin.Int16, Builtin.Int16) -sil [serialized] @ignore_ptrs_callee : $@convention(thin) (@in S1, S1, S1) -> (Builtin.Int16, Builtin.Int16) { -bb0(%0 : $*S1, %1 : $S1, %2 : $S1): +/// Verify that pointer arguments are ignored for now. +// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @ignore_ptrs_callee : $@convention(thin) (@in S4Pair, S4Pair, S4Pair) -> (S4, S4) { +// CHECK: bb0([[POINTER:%.*]] : $*S4Pair, [[PAIR1:%.*]] : $S4Pair, [[PAIR2:%.*]] : $S4Pair): +// CHECK: [[FN:%.*]] = function_ref @$s18ignore_ptrs_calleeTfq4nxx_n : $@convention(thin) (@in S4Pair, S4, S4) -> (S4, S4) +// CHECK: [[VALUE1:%.*]] = struct_extract [[PAIR1]] : $S4Pair, #S4Pair.f1 +// CHECK: [[VALUE2:%.*]] = struct_extract [[PAIR2]] : $S4Pair, #S4Pair.f1 +// CHECK: [[RESULT:%.*]] = apply [[FN]]([[POINTER]], [[VALUE1]], [[VALUE2]]) : $@convention(thin) (@in S4Pair, S4, S4) -> (S4, S4) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'ignore_ptrs_callee' +sil [serialized] @ignore_ptrs_callee : $@convention(thin) (@in S4Pair, S4Pair, S4Pair) -> (S4, S4) { +bb0(%pointer : $*S4Pair, %pair1 : $S4Pair, %pair2 : $S4Pair): // make it a non-trivial function %c1 = builtin "assert_configuration"() : $Builtin.Int32 %c2 = builtin "assert_configuration"() : $Builtin.Int32 @@ -599,26 +631,27 @@ bb0(%0 : $*S1, %1 : $S1, %2 : $S1): %c21 = builtin "assert_configuration"() : $Builtin.Int32 %c22 = builtin "assert_configuration"() : $Builtin.Int32 - debug_value %1 : $S1 - debug_value %2 : $S1 - %3 = function_ref @s1_ptr_user : $@convention(thin) (@in S1) -> () - %4 = apply %3(%0) : $@convention(thin) (@in S1) -> () - %5 = struct_extract %1 : $S1, #S1.f1 - %6 = struct_extract %2 : $S1, #S1.f1 - %7 = tuple(%5 : $Builtin.Int16, %6 : $Builtin.Int16) - return %7 : $(Builtin.Int16, Builtin.Int16) -} - -// CHECK-LABEL: sil [serialized] @ignore_ptrs_caller : $@convention(thin) (@in S1, S1, S1) -> () { -// CHECK: bb0([[IN1:%.*]] : $*S1, [[IN2:%.*]] : $S1, [[IN3:%.*]] : $S1): -// CHECK: [[FN:%.*]] = function_ref @$s18ignore_ptrs_calleeTfq4nxx_n : $@convention(thin) (@in S1, Builtin.Int16, Builtin.Int16) -> (Builtin.Int16, Builtin.Int16) -// CHECK: [[EXT1:%.*]] = struct_extract [[IN2]] : $S1, #S1.f1 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN3]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[IN1]], [[EXT1]], [[EXT2]]) : $@convention(thin) (@in S1, Builtin.Int16, Builtin.Int16) -> (Builtin.Int16, Builtin.Int16) -sil [serialized] @ignore_ptrs_caller : $@convention(thin) (@in S1, S1, S1) -> () { -bb0(%0 : $*S1, %1 : $S1, %2 : $S1): - %3 = function_ref @ignore_ptrs_callee : $@convention(thin) (@in S1, S1, S1) -> (Builtin.Int16, Builtin.Int16) - %4 = apply %3(%0, %1, %2) : $@convention(thin) (@in S1, S1, S1) -> (Builtin.Int16, Builtin.Int16) + %func = function_ref @s4_user : $@convention(thin) (S4) -> () + %pointee = load %pointer : $*S4Pair + %first_pointee = struct_extract %pointee : $S4Pair, #S4Pair.f1 + %_ = apply %func(%first_pointee) : $@convention(thin) (S4) -> () + %first1 = struct_extract %pair1 : $S4Pair, #S4Pair.f1 + %first2 = struct_extract %pair2 : $S4Pair, #S4Pair.f1 + %result = tuple (%first1 : $S4, %first2 : $S4) + return %result : $(S4, S4) +} + +// CHECK-LABEL: sil [serialized] @ignore_ptrs_caller : $@convention(thin) (@in S4Pair, S4Pair, S4Pair) -> () { +// CHECK: bb0([[POINTER:%.*]] : $*S4Pair, [[PAIR1:%.*]] : $S4Pair, [[PAIR2:%.*]] : $S4Pair): +// CHECK: [[FN:%.*]] = function_ref @$s18ignore_ptrs_calleeTfq4nxx_n : $@convention(thin) (@in S4Pair, S4, S4) -> (S4, S4) +// CHECK: [[VALUE1:%.*]] = struct_extract [[PAIR1]] : $S4Pair, #S4Pair.f1 +// CHECK: [[VALUE2:%.*]] = struct_extract [[PAIR2]] : $S4Pair, #S4Pair.f1 +// CHECK: apply [[FN]]([[POINTER]], [[VALUE1]], [[VALUE2]]) : $@convention(thin) (@in S4Pair, S4, S4) -> (S4, S4) +// CHECK-LABEL: } // end sil function 'ignore_ptrs_caller' +sil [serialized] @ignore_ptrs_caller : $@convention(thin) (@in S4Pair, S4Pair, S4Pair) -> () { +bb0(%0 : $*S4Pair, %1 : $S4Pair, %2 : $S4Pair): + %3 = function_ref @ignore_ptrs_callee : $@convention(thin) (@in S4Pair, S4Pair, S4Pair) -> (S4, S4) + %4 = apply %3(%0, %1, %2) : $@convention(thin) (@in S4Pair, S4Pair, S4Pair) -> (S4, S4) %9999 = tuple() return %9999 : $() } @@ -628,6 +661,7 @@ bb0(%0 : $*S1, %1 : $S1, %2 : $S1): // CHECK: bb0([[IN1:%.*]] : $FakeStaticString, [[IN2:%.*]] : $FakeString, [[IN3:%.*]] : $FakeStaticString): // CHECK: [[FN:%.*]] = function_ref @fakestaticstring_user : $@convention(thin) (FakeStaticString) -> () // CHECK: apply [[FN]]([[IN1]]) : $@convention(thin) (FakeStaticString) -> () +// CHECK-LABEL: } // end sil function 'multiple_sroa_callee' sil [serialized] @multiple_sroa_callee : $@convention(thin) (FakeStaticString, @owned FakeString, FakeStaticString) -> () { bb0(%0 : $FakeStaticString, %1 : $FakeString, %2 : $FakeStaticString): // make it a non-trivial function @@ -666,6 +700,7 @@ bb0(%0 : $FakeStaticString, %1 : $FakeString, %2 : $FakeStaticString): // CHECK-LABEL: sil [serialized] @multiple_sroa_caller : $@convention(thin) (FakeStaticString, @owned FakeString, FakeStaticString) -> () { // CHECK: bb0([[IN1:%.*]] : $FakeStaticString, [[IN2:%.*]] : $FakeString, [[IN3:%.*]] : $FakeStaticString): // CHECK: [[FN:%.*]] = function_ref @multiple_sroa_callee : $@convention(thin) (FakeStaticString, @owned FakeString, FakeStaticString) -> () +// CHECK-LABEL: } // end sil function 'multiple_sroa_caller' sil [serialized] @multiple_sroa_caller : $@convention(thin) (FakeStaticString, @owned FakeString, FakeStaticString) -> () { bb0(%0 : $FakeStaticString, %1 : $FakeString, %2 : $FakeStaticString): %3 = function_ref @multiple_sroa_callee : $@convention(thin) (FakeStaticString, @owned FakeString, FakeStaticString) -> () @@ -674,17 +709,18 @@ bb0(%0 : $FakeStaticString, %1 : $FakeString, %2 : $FakeStaticString): return %9999 : $() } -// This test makes sure that we handle cases where the callee has field uses -// that are processed in a different order than the fields are layed out in the -// structure. -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @check_out_of_order_uses_callee : $@convention(thin) (S1) -> () { -// CHECK: bb0([[IN:%.*]] : $S1): -// CHECK: [[FN:%.*]] = function_ref @$s30check_out_of_order_uses_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> () -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S1, #S1.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT2]], [[EXT1]]) : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> () -sil [serialized] @check_out_of_order_uses_callee : $@convention(thin) (S1) -> () { -bb0(%0 : $S1): +/// Verify that in the face of an exploded argument the ordering of arguments is +/// based on ordering of fields in the original argument rather than ordering of +/// usage in the function being specialized. +// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @check_out_of_order_uses_callee : $@convention(thin) (S4Triple) -> () { +// CHECK: bb0([[TRIPLE:%.*]] : $S4Triple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s30check_out_of_order_uses_calleeTfq4x_n : $@convention(thin) (S4, S4) -> () +// CHECK: [[INPUT1F2:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f2 +// CHECK: [[INPUT1F1:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f1 +// CHECK: apply [[FUNCTION]]([[INPUT1F1]], [[INPUT1F2]]) : $@convention(thin) (S4, S4) -> () +// CHECK-LABEL: } // end sil function 'check_out_of_order_uses_callee' +sil [serialized] @check_out_of_order_uses_callee : $@convention(thin) (S4Triple) -> () { +only(%triple : $S4Triple): // make it a non-trivial function %c1 = builtin "assert_configuration"() : $Builtin.Int32 %c2 = builtin "assert_configuration"() : $Builtin.Int32 @@ -709,29 +745,28 @@ bb0(%0 : $S1): %c21 = builtin "assert_configuration"() : $Builtin.Int32 %c22 = builtin "assert_configuration"() : $Builtin.Int32 - debug_value %0 : $S1 - %1 = struct_extract %0 : $S1, #S1.f2 - %2 = function_ref @i32_user : $@convention(thin) (Builtin.Int32) -> () - apply %2(%1) : $@convention(thin) (Builtin.Int32) -> () - %3 = struct_extract %0 : $S1, #S1.f1 - %4 = function_ref @i16_user : $@convention(thin) (Builtin.Int16) -> () - apply %4(%3) : $@convention(thin) (Builtin.Int16) -> () - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [serialized] @check_out_of_order_uses_caller : $@convention(thin) (S1) -> () { -// CHECK: bb0([[IN:%.*]] : $S1): -// CHECK: [[FN:%.*]] = function_ref @$s30check_out_of_order_uses_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> () -// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S1, #S1.f2 -// CHECK: [[EXT2:%.*]] = struct_extract [[IN]] : $S1, #S1.f1 -// CHECK: apply [[FN]]([[EXT2]], [[EXT1]]) : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> () -sil [serialized] @check_out_of_order_uses_caller : $@convention(thin) (S1) -> () { -bb0(%0 : $S1): - %1 = function_ref @check_out_of_order_uses_callee : $@convention(thin) (S1) -> () - apply %1(%0) : $@convention(thin) (S1) -> () - %9999 = tuple() - return %9999 : $() + %second = struct_extract %triple : $S4Triple, #S4Triple.f2 + %func = function_ref @s4_user : $@convention(thin) (S4) -> () + apply %func(%second) : $@convention(thin) (S4) -> () + %first = struct_extract %triple : $S4Triple, #S4Triple.f1 + apply %func(%first) : $@convention(thin) (S4) -> () + %result = tuple() + return %result : $() +} + +// CHECK-LABEL: sil [serialized] @check_out_of_order_uses_caller : $@convention(thin) (S4Triple) -> () { +// CHECK: bb0([[TRIPLE:%.*]] : $S4Triple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s30check_out_of_order_uses_calleeTfq4x_n : $@convention(thin) (S4, S4) -> () +// CHECK: [[TRIPLEF2:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f2 +// CHECK: [[TRIPLEF1:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[TRIPLEF1]], [[TRIPLEF2]]) : $@convention(thin) (S4, S4) -> () +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'check_out_of_order_uses_caller' +sil [serialized] @check_out_of_order_uses_caller : $@convention(thin) (S4Triple) -> () { +bb0(%0 : $S4Triple): + %1 = function_ref @check_out_of_order_uses_callee : $@convention(thin) (S4Triple) -> () + %result = apply %1(%0) : $@convention(thin) (S4Triple) -> () + return %result : $() } // Make sure that we do not SROA classes. @@ -834,6 +869,7 @@ bb0(%0 : $*S1): // CHECK-NEXT: apply [[FN]] // CHECK-NEXT: tuple // CHECK-NEXT: return +// CHECK-LABEL: } // end sil function 'inarg_caller' sil [serialized] @inarg_caller : $@convention(thin) (@in S1) -> () { bb0(%0 : $*S1): %1 = function_ref @inarg_callee : $@convention(thin) (@in S1) -> () @@ -847,18 +883,19 @@ bb0(%0 : $*S1): // were not handling the possibility of an std::vector resize invalidated // references. This cause the this pointer to become invalidated and other // shenanigans. So just make sure we don't crash -// CHECK-LABEL: sil [serialized] @more_than_32_type_sized_caller : $@convention(thin) (ThirtySixFieldStruct) -> () { -sil [serialized] @more_than_32_type_sized_caller : $@convention(thin) (ThirtySixFieldStruct) -> () { -bb0(%0 : $ThirtySixFieldStruct): - %1 = function_ref @more_than_32_type_sized_callee : $@convention(thin) (ThirtySixFieldStruct) -> Builtin.Int32 - %2 = apply %1(%0) : $@convention(thin) (ThirtySixFieldStruct) -> Builtin.Int32 - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @more_than_32_type_sized_callee : $@convention(thin) (ThirtySixFieldStruct) -> Builtin.Int32 { -sil [serialized] @more_than_32_type_sized_callee : $@convention(thin) (ThirtySixFieldStruct) -> Builtin.Int32 { -bb0(%0 : $ThirtySixFieldStruct): +// CHECK-LABEL: sil [serialized] @more_than_32_type_sized_caller : $@convention(thin) (S4Fourtytet) -> S4 { +// CHECK-LABEL: } // end sil function 'more_than_32_type_sized_caller' +sil [serialized] @more_than_32_type_sized_caller : $@convention(thin) (S4Fourtytet) -> S4 { +only(%tet : $S4Fourtytet): + %func = function_ref @more_than_32_type_sized_callee : $@convention(thin) (S4Fourtytet) -> S4 + %result = apply %func(%tet) : $@convention(thin) (S4Fourtytet) -> S4 + return %result : $S4 +} + +// CHECK-LABEL: sil [serialized] [signature_optimized_thunk] [always_inline] @more_than_32_type_sized_callee : $@convention(thin) (S4Fourtytet) -> S4 { +// CHECK-LABEL: } // end sil function 'more_than_32_type_sized_callee' +sil [serialized] @more_than_32_type_sized_callee : $@convention(thin) (S4Fourtytet) -> S4 { +bb0(%tet : $S4Fourtytet): // make it a non-trivial function %c1 = builtin "assert_configuration"() : $Builtin.Int32 %c2 = builtin "assert_configuration"() : $Builtin.Int32 @@ -883,9 +920,9 @@ bb0(%0 : $ThirtySixFieldStruct): %c21 = builtin "assert_configuration"() : $Builtin.Int32 %c22 = builtin "assert_configuration"() : $Builtin.Int32 - %1 = struct_extract %0 : $ThirtySixFieldStruct, #ThirtySixFieldStruct.b1 - %2 = struct_extract %1 : $EightFieldStruct, #EightFieldStruct.a1 - return %2 : $Builtin.Int32 + %octet = struct_extract %tet : $S4Fourtytet, #S4Fourtytet.f1 + %result = struct_extract %octet : $S4Octet, #S4Octet.f1 + return %result : $S4 } // We should not specialize this since SingleFieldLvl1 is a struct that is layout compatible with its only leaf node, %0.s2.s3.s4 @@ -908,104 +945,45 @@ bb0(%0 : $SingleFieldLvl1): // Check Statements for generated code. -// CHECK-LABEL: sil shared [serialized] @$s29single_level_dead_root_calleeTfq4x_n : $@convention(thin) (Builtin.Int32) -> Builtin.Int32 { -// CHECK: bb0([[IN:%.*]] : $Builtin.Int32): -// CHECK: [[UN:%.*]] = struct $S1 (undef : $Builtin.Int16, [[IN]] : $Builtin.Int32) -// CHECK: struct_extract [[UN]] : $S1, #S1.f2 -// CHECK: return [[IN]] : $Builtin.Int32 -// CHECK-LABEL: sil shared [serialized] @$s29single_level_live_root_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> Builtin.Int32 { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int16, [[IN2:%.*]] : $Builtin.Int32): -// CHECK: [[STRUCT:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int32) -// CHECK: [[STRUCT2:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int32) -// CHECK: struct_extract [[STRUCT2]] : $S1, #S1.f2 -// CHECK: [[FN:%.*]] = function_ref @s1_user : $@convention(thin) (S1) -> () -// CHECK: apply [[FN]]([[STRUCT]]) -// CHECK: return [[IN2]] - - -// CHECK-LABEL: sil shared [serialized] @$s42multiple_level_all_root_fields_used_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int16, [[IN2:%.*]] : $Builtin.Int64): -// CHECK: [[STRUCT1:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, undef : $Builtin.Int32) -// CHECK: [[STRUCT2:%.*]] = struct $S2 (%2 : $S1, [[IN2]] : $Builtin.Int64) -// CHECK: debug_value [[STRUCT2]] : $S2 -// CHECK: [[STRUCT3:%.*]] = struct_extract [[STRUCT2]] : $S2, #S2.f1 -// CHECK: debug_value [[STRUCT3]] : $S1 -// CHECK: [[OUT:%.*]] = tuple ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int64) -// CHECK: return [[OUT]] - - -// CHECK-LABEL: sil shared [serialized] @$s53multiple_level_no_root_fields_have_direct_uses_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int16, [[IN2:%.*]] : $Builtin.Int64): -// CHECK: [[STRUCT1:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, undef : $Builtin.Int32) -// CHECK: [[STRUCT2:%.*]] = struct $S2 ([[STRUCT1]] : $S1, [[IN2]] : $Builtin.Int64) -// CHECK: [[STRUCT4:%.*]] = struct $S3 ([[STRUCT2]] : $S2, undef : $S1) -// CHECK: debug_value [[STRUCT4]] -// CHECK: [[OUT:%.*]] = tuple ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int64) -// CHECK: return [[OUT]] - - -// CHECK-LABEL: sil shared [serialized] @$s43multiple_level_root_must_be_reformed_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32, Builtin.Int64) -> (Builtin.Int16, Builtin.Int64) { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int16, [[IN2:%.*]] : $Builtin.Int32, [[IN3:%.*]] : $Builtin.Int64): -// CHECK: [[STRUCT1:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int32) -// CHECK: [[STRUCT3:%.*]] = struct $S2 ([[STRUCT1]] : $S1, [[IN3]] : $Builtin.Int64) -// CHECK: [[STRUCT4:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int32) -// CHECK: [[STRUCT5:%.*]] = struct $S2 ([[STRUCT4]] : $S1, [[IN3]] : $Builtin.Int64) -// CHECK: struct_extract [[STRUCT5]] : $S2, #S2.f1 -// CHECK: [[OUT:%.*]] = tuple ([[IN1]] : $Builtin.Int16, [[IN3]] : $Builtin.Int64) -// CHECK: [[FN:%.*]] = function_ref @s2_user : $@convention(thin) (S2) -> () -// CHECK: apply [[FN]]([[STRUCT3]]) : $@convention(thin) (S2) -> () -// CHECK: return [[OUT]] : $(Builtin.Int16, Builtin.Int64) - - - -// CHECK-LABEL: sil shared [serialized] @$s21owned_struct_1_calleeTfq4dgX_n : $@convention(thin) (@guaranteed S4, Builtin.Int16, Builtin.Int32) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) { -// CHECK: bb0([[IN1:%.*]] : $S4, [[IN2:%.*]] : $Builtin.Int16, [[IN3:%.*]] : $Builtin.Int32): -// CHECK: [[STRUCT1:%.*]] = struct $S1 ([[IN2]] : $Builtin.Int16, [[IN3]] : $Builtin.Int32) -// CHECK: [[STRUCT3:%.*]] = struct $S5 ([[IN1]] : $S4, [[STRUCT1]] : $S1) -// CHECK: [[STRUCT5:%.*]] = struct $S1 ([[IN2]] : $Builtin.Int16, [[IN3]] : $Builtin.Int32) -// CHECK: [[STRUCT6:%.*]] = struct $S5 ([[IN1]] : $S4, [[STRUCT5]] : $S1) -// CHECK: struct_extract [[STRUCT6]] : $S5, #S5.f2 -// CHECK: [[OUT:%.*]] = tuple ([[IN2]] : $Builtin.Int16, [[IN3]] : $Builtin.Int32, [[IN2]] : $Builtin.Int16, [[IN3]] : $Builtin.Int32) +// CHECK-LABEL: sil shared [serialized] @$s21owned_struct_1_calleeTfq4dg_n : $@convention(thin) (@guaranteed S5) -> (Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) { +// CHECK: bb0([[INPUT:%.*]] : $S5): +// CHECK: [[INPUTF2:%.*]] = struct_extract [[INPUT]] : $S5, #S5.f2 +// CHECK: [[INPUTF2F1:%.*]] = struct_extract [[INPUTF2]] : $S1, #S1.f1 +// CHECK: [[INPUTF2F2:%.*]] = struct_extract [[INPUTF2]] : $S1, #S1.f2 +// CHECK: [[OUT:%.*]] = tuple ([[INPUTF2F1]] : $Builtin.Int16, [[INPUTF2F2]] : $Builtin.Int32, [[INPUTF2F1]] : $Builtin.Int16, [[INPUTF2F2]] : $Builtin.Int32) // CHECK: return [[OUT]] : $(Builtin.Int16, Builtin.Int32, Builtin.Int16, Builtin.Int32) - -// CHECK-LABEL: sil shared [serialized] @$s21owned_struct_2_calleeTfq4ndgXdn_n : $@convention(thin) (Builtin.Int256, @guaranteed S4, Builtin.Int16, Builtin.Int32, Builtin.Int128) -> (Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int256, [[IN2:%.*]] : $S4, [[IN3:%.*]] : $Builtin.Int16, [[IN4:%.*]] : $Builtin.Int32, [[IN5:%.*]] : $Builtin.Int128): -// CHECK: [[STRUCT1:%.*]] = struct $S1 ([[IN3]] : $Builtin.Int16, [[IN4]] : $Builtin.Int32) -// CHECK: [[STRUCT3:%.*]] = struct $S5 ([[IN2]] : $S4, [[STRUCT1]] : $S1) -// CHECK: [[STRUCT5:%.*]] = struct $S1 ([[IN3]] : $Builtin.Int16, [[IN4]] : $Builtin.Int32) -// CHECK: [[STRUCT6:%.*]] = struct $S5 ([[IN2]] : $S4, [[STRUCT5]] : $S1) -// CHECK: struct_extract [[STRUCT6]] : $S5, #S5.f2 -// CHECK: [[FN:%.*]] = function_ref @s5_user : $@convention(thin) (S5) -> () -// CHECK: apply [[FN]]([[STRUCT3]]) : $@convention(thin) (S5) -> () -// CHECK: [[OUT:%.*]] = tuple ([[IN1]] : $Builtin.Int256, [[IN3]] : $Builtin.Int16, [[IN4]] : $Builtin.Int32, [[IN5]] : $Builtin.Int128) -// CHECK: [[OUT]] : $(Builtin.Int256, Builtin.Int16, Builtin.Int32, Builtin.Int128) - - -// CHECK-LABEL: sil shared [serialized] @$s18ignore_ptrs_calleeTfq4nxx_n : $@convention(thin) (@in S1, Builtin.Int16, Builtin.Int16) -> (Builtin.Int16, Builtin.Int16) { -// CHECK: bb0([[IN1:%.*]] : $*S1, [[IN2:%.*]] : $Builtin.Int16, [[IN3:%.*]] : $Builtin.Int16): -// CHECK: [[STRUCT2:%.*]] = struct $S1 ([[IN2]] : $Builtin.Int16, undef : $Builtin.Int32) -// CHECK: [[STRUCT1:%.*]] = struct $S1 ([[IN3]] : $Builtin.Int16, undef : $Builtin.Int32) -// CHECK: debug_value [[STRUCT2]] -// CHECK: debug_value [[STRUCT1]] -// CHECK: [[FN:%.*]] = function_ref @s1_ptr_user : $@convention(thin) (@in S1) -> () -// CHECK: apply [[FN]]([[IN1]]) : $@convention(thin) (@in S1) -> () -// CHECK: struct_extract [[STRUCT2]] : $S1, #S1.f1 -// CHECK: struct_extract [[STRUCT1]] : $S1, #S1.f1 -// CHECK: [[OUT:%.*]] = tuple ([[IN2]] : $Builtin.Int16, [[IN3]] : $Builtin.Int16) -// CHECK: return [[OUT]] : $(Builtin.Int16, Builtin.Int16) - -// CHECK-LABEL: sil shared [serialized] @$s30check_out_of_order_uses_calleeTfq4x_n : $@convention(thin) (Builtin.Int16, Builtin.Int32) -> () { -// CHECK: bb0([[IN1:%.*]] : $Builtin.Int16, [[IN2:%.*]] : $Builtin.Int32): -// CHECK: [[STRUCT0:%.*]] = struct $S1 ([[IN1]] : $Builtin.Int16, [[IN2]] : $Builtin.Int32) -// CHECK: debug_value [[STRUCT0]] -// CHECK: struct_extract [[STRUCT0]] : $S1, #S1.f2 -// CHECK: [[FN1:%.*]] = function_ref @i32_user : $@convention(thin) (Builtin.Int32) -> () -// CHECK: apply [[FN1]]([[IN2]]) : $@convention(thin) (Builtin.Int32) -> () -// CHECK: struct_extract [[STRUCT0]] : $S1, #S1.f1 -// CHECK: [[FN2:%.*]] = function_ref @i16_user : $@convention(thin) (Builtin.Int16) -> () -// CHECK: apply [[FN2]]([[IN1]]) : $@convention(thin) (Builtin.Int16) -> () +// CHECK-LABEL: } // end sil function '$s21owned_struct_1_calleeTfq4dg_n' + +// CHECK-LABEL: sil shared [serialized] @$s21owned_struct_2_calleeTfq4ndgXdn_n : $@convention(thin) (Builtin.Int256, @guaranteed S4, @guaranteed S4, Builtin.Int128) -> (Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) { +// CHECK: bb0([[O1:%.*]] : $Builtin.Int256, [[INPUT1:%.*]] : $S4, [[INPUT2:%.*]] : $S4, [[O4:%.*]] : $Builtin.Int128): +// CHECK: [[FUNCTION:%.*]] = function_ref @s4_user_twice : $@convention(thin) (S4, S4) -> () +// CHECK: apply [[FUNCTION]]([[INPUT1]], [[INPUT2]]) : $@convention(thin) (S4, S4) -> () +// CHECK: [[RESULT:%.*]] = tuple ([[O1]] : $Builtin.Int256, [[O1]] : $Builtin.Int256, [[O4]] : $Builtin.Int128, [[O4]] : $Builtin.Int128) +// CHECK: return [[RESULT]] : $(Builtin.Int256, Builtin.Int256, Builtin.Int128, Builtin.Int128) +// CHECK-LABEL: } // end sil function '$s21owned_struct_2_calleeTfq4ndgXdn_n' + + +// CHECK-LABEL: sil shared [serialized] @$s18ignore_ptrs_calleeTfq4nxx_n : $@convention(thin) (@in S4Pair, S4, S4) -> (S4, S4) { +// CHECK: bb0([[POINTER:%.*]] : $*S4Pair, [[VALUE1:%.*]] : $S4, [[VALUE2:%.*]] : $S4): +// CHECK: [[FUNCTION:%.*]] = function_ref @s4_user : $@convention(thin) (S4) -> () +// CHECK: [[POINTEE:%.*]] = load [[POINTER]] : $*S4Pair +// CHECK: [[VALUE:%.*]] = struct_extract [[POINTEE]] : $S4Pair, #S4Pair.f1 +// CHECK: apply [[FUNCTION]]([[VALUE]]) : $@convention(thin) (S4) -> () +// CHECK: [[OUT:%.*]] = tuple ([[VALUE1]] : $S4, [[VALUE2]] : $S4) +// CHECK: return [[OUT]] : $(S4, S4) +// CHECK-LABEL: } // end sil function '$s18ignore_ptrs_calleeTfq4nxx_n' + +// CHECK-LABEL: sil shared [serialized] @$s30check_out_of_order_uses_calleeTfq4x_n : $@convention(thin) (S4, S4) -> () { +// CHECK: bb{{[0-9]+}}([[FIRST:%.*]] : $S4, [[SECOND:%.*]] : $S4): +// CHECK: [[TRIPLE:%.*]] = struct $S4Triple ([[FIRST]] : $S4, [[SECOND]] : $S4, undef : $S4) +// CHECK: [[UNUSED_SECOND:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f2 +// CHECK: [[FUNCTION:%.*]] = function_ref @s4_user : $@convention(thin) (S4) -> () +// CHECK: apply [[FUNCTION]]([[IN2]]) : $@convention(thin) (S4) -> () +// CHECK: [[UNUSED_FIRST:%.*]] = struct_extract [[TRIPLE]] : $S4Triple, #S4Triple.f1 +// CHECK: apply [[FUNCTION]]([[IN1]]) : $@convention(thin) (S4) -> () +// CHECK-LABEL: } // end sil function '$s30check_out_of_order_uses_calleeTfq4x_n' // CHECK-LABEL: sil shared [serialized] @$s14class_callee_1Tfq4gn_n : $@convention(thin) (@guaranteed C1, Builtin.Int32) -> Builtin.Int32 { diff --git a/test/SILOptimizer/functionsigopts_string_fileprivate.swift b/test/SILOptimizer/functionsigopts_string_fileprivate.swift new file mode 100644 index 0000000000000..9e5707cf8c452 --- /dev/null +++ b/test/SILOptimizer/functionsigopts_string_fileprivate.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s | %FileCheck %s + +private var _storage: String? = nil + +// CHECK-LABEL: sil private [noinline] @$s34functionsigopts_string_fileprivate10setStorage33_{{[a-zA-Z0-9]+}}_tF : $@convention(thin) (@guaranteed String) -> () { +// CHECK-LABEL: } // end sil function '$s34functionsigopts_string_fileprivate10setStorage33_{{[a-zA-Z0-9]+}}_tF' +@inline(never) +fileprivate func setStorage(to newValue: String) { + _storage = newValue +} + +// CHECK-LABEL: sil private [noinline] @$s34functionsigopts_string_fileprivate12setStorageTo33_{{[a-zA-Z0-9]+}} : $@convention(thin) (@guaranteed String) -> () { +// CHECK: bb{{[0-9]+}}([[STRING:%.*]] : $String): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s34functionsigopts_string_fileprivate10setStorage33_{{[a-zA-Z0-9]+}}_tF +// CHECK: apply [[FUNCTION]]([[STRING]]) +// CHECK-LABEL: } // end sil function '$s34functionsigopts_string_fileprivate12setStorageTo33_{{[a-zA-Z0-9]+}}' +@inline(never) +fileprivate func setStorageTo(_ newValue: String) { + setStorage(to: newValue) +} + +setStorageTo("hi") diff --git a/test/SILOptimizer/functionsigopts_string_internal.swift b/test/SILOptimizer/functionsigopts_string_internal.swift new file mode 100644 index 0000000000000..981e0f3012061 --- /dev/null +++ b/test/SILOptimizer/functionsigopts_string_internal.swift @@ -0,0 +1,20 @@ +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s | %FileCheck %s + +private var _storage: String? = nil + +// CHECK-LABEL: sil hidden [noinline] @$s31functionsigopts_string_internal10setStorage2toySS_tF : $@convention(thin) (@guaranteed String) -> () { +// CHECK-LABEL: } // end sil function '$s31functionsigopts_string_internal10setStorage2toySS_tF' +@inline(never) +func setStorage(to newValue: String) { + _storage = newValue +} + +// CHECK-LABEL: sil hidden @$s31functionsigopts_string_internal12setStorageToyySSF : $@convention(thin) (@guaranteed String) -> () { +// CHECK: bb{{[0-9]+}}([[STRING:%.*]] : $String): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s31functionsigopts_string_internal10setStorage2toySS_tF +// CHECK: apply [[FUNCTION]]([[STRING]]) +// CHECK-LABEL: } // end sil function '$s31functionsigopts_string_internal12setStorageToyySSF' +func setStorageTo(_ newValue: String) { + setStorage(to: newValue) +} + diff --git a/test/SILOptimizer/functionsigopts_string_public.swift b/test/SILOptimizer/functionsigopts_string_public.swift new file mode 100644 index 0000000000000..3ef7497167baa --- /dev/null +++ b/test/SILOptimizer/functionsigopts_string_public.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s | %FileCheck %s + +private var _storage: String? = nil + + +// CHECK-LABEL: sil [noinline] @$s29functionsigopts_string_public10setStorage2toySS_tF : $@convention(thin) (@guaranteed String) -> () { +// CHECK-LABEL: } // end sil function '$s29functionsigopts_string_public10setStorage2toySS_tF' +@inline(never) +public func setStorage(to newValue: String) { + _storage = newValue +} + +// CHECK-LABEL: sil @$s29functionsigopts_string_public12setStorageToyySSF : $@convention(thin) (@guaranteed String) -> () { +// CHECK: bb{{[0-9]+}}([[STRING:%.*]] : $String): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s29functionsigopts_string_public10setStorage2toySS_tF +// CHECK: apply [[FUNCTION]]([[STRING]]) +// CHECK-LABEL: } // end sil function '$s29functionsigopts_string_public12setStorageToyySSF' +public func setStorageTo(_ newValue: String) { + setStorage(to: newValue) +} + diff --git a/test/SILOptimizer/functionsigopts_trivial.sil b/test/SILOptimizer/functionsigopts_trivial.sil new file mode 100644 index 0000000000000..e6341c02abe6d --- /dev/null +++ b/test/SILOptimizer/functionsigopts_trivial.sil @@ -0,0 +1,2110 @@ +// RUN: %target-sil-opt -sil-inline-generics -enable-sil-verify-all -inline -function-signature-opts %s | %FileCheck %s + +import Builtin +import Swift + +// ==========================================================================={{ +// ============================================================================= +// ===== SUPPORT ===== +// ============================================================================= +// ============================================================================= + +class Klass { + var value : Builtin.Int2048 +} + +struct Wrapper { + var klass: Klass + var int: Int +} + +struct Pair { + var klass1: Klass + var klass2: Klass +} + +struct Quintuple { + var klass1: Klass + var klass2: Klass + var klass3: Klass + var klass4: Klass + var klass5: Klass +} + +sil @take_nothing : $@convention(thin) () -> () +sil @take_klass : $@convention(thin) (Klass) -> () +sil @take_klass_and_klass : $@convention(thin) (Klass, Klass) -> () +sil @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +sil @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +sil @take_int : $@convention(thin) (Int) -> () + +// ============================================================================= +// ============================================================================= +// ===== SUPPORT ===== +// ============================================================================= +// ===========================================================================}} + + + +// ==========================================================================={{ +// ============================================================================= +// ===== TESTS ===== +// ============================================================================= +// ============================================================================= + + + +// ==========================================================================={{ +// = WRAPPER = +// ============================================================================= + +// struct Wrapper { +// var klass: Klass +// var int: Int +// } + +// = Wrapper, #Wrapper.klass ================================================={{ + + + +// TEST_WrKlPu: TYPE: Wrapper, FIELD: #Wrapper.klass, VISIBILITY: public +// VERIFY: NO-EXPLODE +// The function take_wrapper has a single argument of type Wrapper from which +// it uses only the klass field. The int field goes unused. Argument explosion +// should *not* result in a specialization in this case because (1) the +// function could be used outside this module (so a thunk would be generated if +// we specialized) and (2) specializing does not reduce ARC traffic because the +// stripped field is trivial. +// +// CHECK-LABEL: sil [noinline] @take_wrapper : $@convention(thin) (Wrapper) -> () { +// CHECK: [[KLASS:%.*]] = struct_extract {{%.*}} : $Wrapper, #Wrapper.klass +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper' +// CHECK-NOT: sil shared [noinline] @$s12take_wrapperTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK-NOT: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK-NOT: [[RESULT:%.*]] = apply [[FUNCTION]](%0) +// CHECK-NOT: return [[RESULT]] : $() +// CHECK-NOT: } // end sil function '$s12take_wrapperTf4x_n' +sil public [noinline] @take_wrapper : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %klass = struct_extract %wrapper : $Wrapper, #Wrapper.klass + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_wrapper' + +// CHECK-LABEL: sil @take_wrapper_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_wrapper +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[WRAPPER]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_caller' +sil @take_wrapper_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_caller' + + + +// TEST_WrKlPuEx: TYPE: Wrapper, FIELD: #Wrapper.klass, VISIBILITY: public_external +// VERIFY: NO-EXPLODE +// The function take_wrapper has a single argument of type Wrapper from which +// it uses only the klass field. The int field goes unused. Argument explosion +// should *not* result in a specialization in this case because (1) the +// function is definend outside this module (so a thunk would be generated if +// we specialized) and (2) specializing does not reduce ARC traffic because the +// stripped field is trivial. +// +// CHECK-LABEL: sil public_external [noinline] @take_wrapper_public_external : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[KLASS:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.klass +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_wrapper_public_external' +sil public_external [noinline] @take_wrapper_public_external : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %klass = struct_extract %wrapper : $Wrapper, #Wrapper.klass + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_wrapper_public_external' + +// CHECK-LABEL: sil @take_wrapper_public_external_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_wrapper_public_external +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[WRAPPER]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_wrapper_public_external_caller' +sil @take_wrapper_public_external_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_public_external : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_public_external_caller' + + + +// TEST_WrKlSh: TYPE: Wrapper, FIELD: #Wrapper.klass, VISIBILITY: shared +// VERIFY: EXPLODE: (Wrapper) => (#Wrapper.klass) +// The function take_wrapper_shared has a single argument of type Wrapper from +// which it uses only the klass field. The int field goes unused. Argument +// explosion should result in a specialization in this case because because +// there are dead leaves of the relevant sort, the relevant sort here being +// potentially trivial because specializing the function will not result in a +// thunk. +// +// CHECK-LABEL: sil shared [signature_optimized_thunk] [always_inline] @take_wrapper_shared : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s19take_wrapper_sharedTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_shared' +sil shared [noinline] @take_wrapper_shared : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %klass = struct_extract %wrapper : $Wrapper, #Wrapper.klass + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_wrapper_shared' + +// CHECK-LABEL: sil @take_wrapper_shared_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s19take_wrapper_sharedTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_shared_caller' +sil @take_wrapper_shared_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_shared : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_shared_caller' + + + +// TEST_WrKlHi: TYPE: Wrapper, FIELD: #Wrapper.klass, VISIBILITY: hidden +// VERIFY: NO-EXPLODE +// The function take_wrapper_hidden has a single argument of type Wrapper from +// which it uses only the klass field. The int field goes unused. Argument +// explosion should *not* result in a specialization in this case because, while +// all the non-trivial leaves are live, there is only a single non-trivial +// leaf, so there will be no downstream benefits from introducing a separate RC +// identities for each leaf. +// +// CHECK-LABEL: sil hidden [noinline] @take_wrapper_hidden : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[KLASS:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.klass +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_hidden' +sil hidden [noinline] @take_wrapper_hidden : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %klass = struct_extract %wrapper : $Wrapper, #Wrapper.klass + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_wrapper_hidden' + +// CHECK-LABEL: sil @take_wrapper_hidden_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_wrapper_hidden +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[WRAPPER]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_hidden_caller' +sil @take_wrapper_hidden_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_hidden : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil funcntion 'take_wrapper_hidden_caller' + + + +// TEST_WrKlPr: TYPE: Wrapper, FIELD: #Wrapper.klass, VISIBILITY: private +// VERIFY: EXPLODE: (Wrapper) => (#Wrapper.klass) +// The function take_wrapper_private has a single argument of type Wrapper from +// which it uses only the klass field. The int field goes unused. Argument +// explosion should result in a specialization in this case because, there are +// dead leaves of the relevant sort, that being potentially trivial because +// specializing the function will not result in a thunk. +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_wrapper_private : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_wrapper_privateTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_private' +sil private [noinline] @take_wrapper_private : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %klass = struct_extract %wrapper : $Wrapper, #Wrapper.klass + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_wrapper_private' + +// CHECK-LABEL: sil @take_wrapper_private_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_wrapper_privateTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_private_caller' +sil @take_wrapper_private_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_private : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_private_caller' + + + +// = END: Wrapper, #Wrapper.klass ============================================}} + + + +// = Wrapper, #Wrapper.int ==================================================={{ + + + +// TEST_WrInPu: TYPE: Wrapper, FIELD: #Wrapper.int, VISIBILITY: public +// VERIFY: EXPLODE (Wrapper) => (#Wrapper.int) +// The function take_wrapper_for_int has a single argument of type Wrapper from +// which it only uses the int field. The klass field goes unused. Without +// changes there would be needless ARC traffic around the klass field. So, +// argument explosion *should* result in a specialization in this case. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @take_wrapper_for_int : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_wrapper_for_intTf4x_n +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_for_int' +sil public [noinline] @take_wrapper_for_int : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %int = struct_extract %wrapper : $Wrapper, #Wrapper.int + %func = function_ref @take_int : $@convention(thin) (Int) -> () + %result = apply %func(%int) : $@convention(thin) (Int) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int' + +// CHECK-LABEL: sil @take_wrapper_for_int_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_wrapper_for_intTf4x_n +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_for_int_caller' +sil @take_wrapper_for_int_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_for_int : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_caller' + + + +// TEST_WrInPuEx: TYPE: Wrapper, FIELD: #Wrapper.int, VISIBILITY: public_external +// VERIFY: EXPLODE (Wrapper) => (#Wrapper.int) +// The function take_wrapper_for_int has a single argument of type Wrapper from +// which it only uses the int field. The klass field goes unused. Without +// changes there would be needless ARC traffic around the klass field. So, +// argument explosion *should* result in a specialization in this case. +// +// CHECK-LABEL: sil public_external [signature_optimized_thunk] [always_inline] @take_wrapper_for_int_public_external : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s36take_wrapper_for_int_public_externalTf4x_n +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_for_int_public_external' +sil public_external [noinline] @take_wrapper_for_int_public_external : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %int = struct_extract %wrapper : $Wrapper, #Wrapper.int + %func = function_ref @take_int : $@convention(thin) (Int) -> () + %result = apply %func(%int) : $@convention(thin) (Int) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_public_external' + +// CHECK-LABEL: sil @take_wrapper_for_int_public_external_caller : $@convention(thin) (Wrapper) -> () { +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s36take_wrapper_for_int_public_externalTf4x_n +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_wrapper_for_int_public_external_caller' +sil @take_wrapper_for_int_public_external_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_for_int_public_external : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_public_external_caller' + + + +// TEST_WrInHi: TYPE: Wrapper, FIELD: #Wrapper.int, VISIBILITY: hidden +// VERIFY: EXPLODE (Wrapper) => (#Wrapper.int) +// The function take_wrapper_for_int_hidden has a single argument of type +// Wrapper from which it only uses the int field. The klass field goes unused. +// Without changes there would be needless ARC traffic around the klass field. +// So, argument explosion *should* result in a specialization in this case. +// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @take_wrapper_for_int_hidden +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s27take_wrapper_for_int_hiddenTf4x_n : $@convention(thin) (Int) -> () +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: // end sil function 'take_wrapper_for_int_hidden' +sil hidden [noinline] @take_wrapper_for_int_hidden : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %int = struct_extract %wrapper : $Wrapper, #Wrapper.int + %func = function_ref @take_int : $@convention(thin) (Int) -> () + %result = apply %func(%int) : $@convention(thin) (Int) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_hidden' + +// CHECK-LABEL: sil @take_wrapper_for_int_hidden_caller +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s27take_wrapper_for_int_hiddenTf4x_n +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: // end sil function 'take_wrapper_for_int_hidden_caller' +sil @take_wrapper_for_int_hidden_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_for_int_hidden : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_hidden_caller' + + + +// TEST_WrInPr: TYPE: Wrapper, FIELD: #Wrapper.int, VISIBILITY: private +// VERIFY: EXPLODE (Wrapper) => (#Wrapper.int) +// The function take_wrapper_for_int_private has a single argument of type +// Wrapper from which it only uses the int field. The klass field goes unused. +// Without changes there would be needless ARC traffic around the klass field. +// So, argument explosion *should* result in a specialization in this case. +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_wrapper_for_int_private +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s28take_wrapper_for_int_privateTf4x_n : $@convention(thin) (Int) -> () +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: // end sil function 'take_wrapper_for_int_private' +sil private [noinline] @take_wrapper_for_int_private : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %int = struct_extract %wrapper : $Wrapper, #Wrapper.int + %func = function_ref @take_int : $@convention(thin) (Int) -> () + %result = apply %func(%int) : $@convention(thin) (Int) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_private' + +// CHECK-LABEL: sil @take_wrapper_for_int_private_caller +// CHECK: bb{{[0-9]*}}([[WRAPPER:%.*]] : $Wrapper) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s28take_wrapper_for_int_privateTf4x_n +// CHECK: [[INT:%.*]] = struct_extract [[WRAPPER]] : $Wrapper, #Wrapper.int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: // end sil function 'take_wrapper_for_int_private_caller' +sil @take_wrapper_for_int_private_caller : $@convention(thin) (Wrapper) -> () { +only(%wrapper : $Wrapper): + %func = function_ref @take_wrapper_for_int_private : $@convention(thin) (Wrapper) -> () + %result = apply %func(%wrapper) : $@convention(thin) (Wrapper) -> () + return %result : $() +} // end sil function 'take_wrapper_for_int_private_caller' + + + +// = END: Wrapper, #Wrapper.int ==============================================}} + + + +// ===========================================================================}} +// = END: WRAPPER = +// ============================================================================= + + + +// ==========================================================================={{ +// = PAIR = +// ============================================================================= + +// struct Pair { +// var klass1: Klass +// var klass2: Klass +// } + +// = Pair, No fields ========================================================={{ + + + +// TEST_PaPu: TYPE: Pair, FIELD: , VISIBILITY: public +// VERIFY: EXPLODE (Pair) => () +// The function take_pair_unused has a single argument of type Pair from which +// it uses no fields. Both the klass1 and klass2 fields go unused. Argument +// explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// fields are non-trivial. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @take_pair_unused : $@convention(thin) (Pair) -> () +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s16take_pair_unusedTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: // end sil function 'take_pair_unused' +sil public [noinline] @take_pair_unused : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_nothing : $@convention(thin) () -> () + %result = apply %func() : $@convention(thin) () -> () + return %result : $() +} // end sil function 'take_pair_unused' + +// CHECK-LABEL: sil @take_pair_unused_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s16take_pair_unusedTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_caller' +sil @take_pair_unused_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_unused : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_unused_caller' + + + +// TEST_PaPuEx: TYPE: Pair, FIELD: , VISIBILITY: public_external +// VERIFY: EXPLODE (Pair) => () +// The function take_pair_unused has a single argument of type Pair from which +// it uses no fields. Both the klass1 and klass2 fields go unused. Argument +// explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// fields are non-trivial. +// +// CHECK-LABEL: sil public_external [signature_optimized_thunk] [always_inline] @take_pair_unused_public_external : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s32take_pair_unused_public_externalTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_public_external' +sil public_external [noinline] @take_pair_unused_public_external : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_nothing : $@convention(thin) () -> () + %result = apply %func() : $@convention(thin) () -> () + return %result : $() +} // end sil function 'take_pair_unused_public_external' + +// CHECK-LABEL: sil @take_pair_unused_public_external_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s32take_pair_unused_public_externalTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_public_external_caller' +sil @take_pair_unused_public_external_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_unused_public_external : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_unused_public_external_caller' + + + +// TEST_PaHi: TYPE: Pair, FIELD: , VISIBILITY: hidden +// VERIFY: EXPLODE: (Pair) => () +// The function take_pair_unused_hidden has a single argument of type Pair from +// which it uses no fields. Both the klass1 and klass2 fields go unused. +// Argument explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// fields are non-trivial. +// +// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @take_pair_unused_hidden : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s23take_pair_unused_hiddenTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_hidden' +sil hidden [noinline] @take_pair_unused_hidden : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_nothing : $@convention(thin) () -> () + %result = apply %func() : $@convention(thin) () -> () + return %result : $() +} // end sil function 'take_pair_unused_hidden' + +// CHECK-LABEL: sil @take_pair_unused_hidden_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s23take_pair_unused_hiddenTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_hidden_caller' +sil @take_pair_unused_hidden_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_unused_hidden : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_unused_hidden_caller' + + + +// TEST_PaPr: TYPE: Pair, FIELD: , VISIBILITY: private +// VERIFY: EXPLODE: (Pair) => () +// The function take_pair_unused_private has a single argument of type Pair from +// which it uses no fields. Both the klass1 and klass2 fields go unused. +// Argument explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// fields are non-trivial. +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_pair_unused_private : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s24take_pair_unused_privateTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_private' +sil private [noinline] @take_pair_unused_private : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_nothing : $@convention(thin) () -> () + %result = apply %func() : $@convention(thin) () -> () + return %result : $() +} // end sil function 'take_pair_unused_private' + +// CHECK-LABEL: sil @take_pair_unused_private_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s24take_pair_unused_privateTf4d_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_unused_private_caller' +sil @take_pair_unused_private_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_unused_private : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_unused_private_caller' + + + +// = END: Pair, No fields ====================================================}} + + + +// = Pair, #Pair.klass1 ======================================================{{ + + + +// TEST_PaK1Pu: TYPE: Pair, FIELD: #Pair.klass1, VISIBILITY: public +// VERIFY: EXPLODE (Pair) => (#Pair.klass1) +// The function take_pair_for_klass1 has a single argument of type Pair +// from which it uses only the klass1 field. The klass2 field goes unused. +// Argument explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// field is non-trivial. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @take_pair_for_klass1 : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_pair_for_klass1Tf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1' +sil public [noinline] @take_pair_for_klass1 : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass1 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1' + +// CHECK-LABEL: sil @take_pair_for_klass1_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_pair_for_klass1Tf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract {{%.*}} : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_caller' +sil @take_pair_for_klass1_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1 : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_caller' + + + +// TEST_PaK1PuEx: TYPE: Pair, FIELD: #Pair.klass1, VISIBILITY: public_external +// VERIFY: EXPLODE (Pair) => (#Pair.klass1) +// The function take_pair_for_klass1 has a single argument of type Pair +// from which it uses only the klass1 field. The klass2 field goes unused. +// Argument explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// field is non-trivial. +// +// CHECK-LABEL: sil public_external [signature_optimized_thunk] [always_inline] @take_pair_for_klass1_public_external : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s36take_pair_for_klass1_public_externalTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_public_external' +sil public_external [noinline] @take_pair_for_klass1_public_external : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass1 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_public_external' + +// CHECK-LABEL: sil @take_pair_for_klass1_public_external_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s36take_pair_for_klass1_public_externalTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_public_external_caller' +sil @take_pair_for_klass1_public_external_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_public_external : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_public_external_caller' + + + +// TEST_PaK1Hi: TYPE: Pair, FIELD: #Pair.klass1, VISIBILITY: hidden +// VERIFY: EXPLODE: (Pair) => (#Pair.klass1) +// The function take_pair_for_klass1_hidden has a single argument of type Pair from +// which it uses only the klass1 field. The klass2 field goes unused. Argument +// explosion should result in a specialization in this case both because (1) the +// function can *not* be used outside this module (so no thunk +// will remain after dead code elimination) because it is hidden and because (2) +// avoiding passing the full Pair with its unused field klass2 avoids extraneous +// ARC traffic around that field (i.e. #Pair.klass2). +// +// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @take_pair_for_klass1_hidden : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s27take_pair_for_klass1_hiddenTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_hidden' +sil hidden [noinline] @take_pair_for_klass1_hidden : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass1 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_hidden' + +// CHECK-LABEL: sil @take_pair_for_klass1_hidden_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s27take_pair_for_klass1_hiddenTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_hidden_caller' +sil @take_pair_for_klass1_hidden_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_hidden : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_for_klass1_hidden_caller' + + + +// TEST_PaK1Pr: TYPE: Pair, FIELD: #Pair.klass1, VISIBILITY: private +// VERIFY: EXPLODE: (Pair) => (#Pair.klass1) +// The function take_pair_for_klass1_private has a single argument of type Pair from +// which it uses only the klass1 field. The klass2 field goes unused. Argument +// explosion should result in a specialization in this case both because (1) the +// function can *not* be used outside this module (so no thunk +// will remain after dead code elimination) because it is private and because +// (2) avoiding passing the full Pair with its unused field klass2 avoids +// extraneous ARC traffic around that field (i.e. #Pair.klass2). +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_pair_for_klass1_private : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s28take_pair_for_klass1_privateTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_private' +sil private [noinline] @take_pair_for_klass1_private : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass1 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_private' + +// CHECK-LABEL: sil @take_pair_for_klass1_private_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s28take_pair_for_klass1_privateTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_private_caller' +sil @take_pair_for_klass1_private_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_private : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_for_klass1_private_caller' + + + +// = END: Pair, #Pair.klass1 =================================================}} + + + +// = Pair, #Pair.klass2 ======================================================{{ + + + +// TEST_PaK2Pu: TYPE: Pair, FIELD: #Pair.klass2, VISIBILITY: public +// VERIFY: EXPLODE (Pair) => (#Pair.klass2) +// The function take_pair_for_klass2 has a single argument of type Pair +// from which it uses only the klass2 field. The klass1 field goes unused. +// Argument explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// field is non-trivial. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @take_pair_for_klass2 : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_pair_for_klass2Tf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2' +sil public [noinline] @take_pair_for_klass2 : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass2' + +// CHECK-LABEL: sil @take_pair_for_klass2_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s20take_pair_for_klass2Tf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract {{%.*}} : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_caller' +sil @take_pair_for_klass2_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass2 : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_for_klass2_caller' + + + +// TEST_PaK2PuEx: TYPE: Pair, FIELD: #Pair.klass2, VISIBILITY: public_external +// VERIFY: EXPLODE (Pair) => (#Pair.klass2) +// The function take_pair_for_klass2 has a single argument of type Pair +// from which it uses only the klass2 field. The klass1 field goes unused. +// Argument explosion *should* result in a specialization in this case because +// specializing reduces ARC traffic on account of the fact that the stripped +// field is non-trivial. +// +// CHECK-LABEL: sil public_external [signature_optimized_thunk] [always_inline] @take_pair_for_klass2_public_external : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s36take_pair_for_klass2_public_externalTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_public_external' +sil public_external [noinline] @take_pair_for_klass2_public_external : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass2_public_external' + +// CHECK-LABEL: sil @take_pair_for_klass2_public_external_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s36take_pair_for_klass2_public_externalTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_public_external_caller' +sil @take_pair_for_klass2_public_external_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass2_public_external : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_for_klass2_public_external_caller' + + + +// TEST_PaK2Hi: TYPE: Pair, FIELD: #Pair.klass2, VISIBILITY: hidden +// VERIFY: EXPLODE: (Pair) => (#Pair.klass2) +// The function take_pair_for_klass2_hidden has a single argument of type Pair from +// which it uses only the klass2 field. The klass1 field goes unused. Argument +// explosion should result in a specialization in this case both because (1) the +// function can *not* be used outside this module (so no thunk +// will remain after dead code elimination) because it is hidden and because (2) +// avoiding passing the full Pair with its unused field klass1 avoids extraneous +// ARC traffic around that field (i.e. #Pair.klass1). +// +// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @take_pair_for_klass2_hidden : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s27take_pair_for_klass2_hiddenTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_hidden' +sil hidden [noinline] @take_pair_for_klass2_hidden : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass2_hidden' + +// CHECK-LABEL: sil @take_pair_for_klass2_hidden_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s27take_pair_for_klass2_hiddenTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_hidden_caller' +sil @take_pair_for_klass2_hidden_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass2_hidden : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_for_klass2_hidden_caller' + + + +// TEST_PaK2Pr: TYPE: Pair, FIELD: #Pair.klass2, VISIBILITY: private +// VERIFY: EXPLODE: (Pair) => (#Pair.klass2) +// The function take_pair_for_klass2_private has a single argument of type Pair from +// which it uses only the klass2 field. The klass1 field goes unused. Argument +// explosion should result in a specialization in this case both because (1) the +// function can *not* be used outside this module (so no thunk +// will remain after dead code elimination) because it is private and because +// (2) avoiding passing the full Pair with its unused field klass1 avoids +// extraneous ARC traffic around that field (i.e. #Pair.klass1). +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_pair_for_klass2_private : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s28take_pair_for_klass2_privateTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_private' +sil private [noinline] @take_pair_for_klass2_private : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass : $@convention(thin) (Klass) -> () + %result = apply %func(%klass) : $@convention(thin) (Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass2_private' + +// CHECK-LABEL: sil @take_pair_for_klass2_private_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @$s28take_pair_for_klass2_privateTf4x_n +// CHECK: [[KLASS:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass2_private_caller' +sil @take_pair_for_klass2_private_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass2_private : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_for_klass2_private_caller' + + + +// = END: Pair, #Pair.klass2 =================================================}} + + + +// = Pair, #Pair.klass1, #Pair.klass2 ========================================{{ + + + +// TEST_PaK1K2Pu: TYPE: Pair, FIELD: #Pair.klass1 & #Pair.klass2, VISIBILITY: public +// VERIFY: NO-EXPLODE +// The function take_pair_for_klass1_and_klass2 has a single argument of type +// Pair from which it uses both the klass1 and klass2 fields. Argument +// explosion should *not* result in a specialization in this case because (1) +// specializing does not affect ARC traffic on account of the fact that no +// fields are stripped and (2) specializing increases code size. +// +// CHECK-LABEL: sil [noinline] @take_pair_for_klass1_and_klass2 : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[KLASS1:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_and_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2' +sil public [noinline] @take_pair_for_klass1_and_klass2 : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass1 = struct_extract %pair : $Pair, #Pair.klass1 + %klass2 = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass_and_klass : $@convention(thin) (Klass, Klass) -> () + %result = apply %func(%klass1, %klass2) : $@convention(thin) (Klass, Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_and_klass2' + +// CHECK-LABEL: sil @take_pair_for_klass1_and_klass2_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_pair_for_klass1_and_klass2 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[PAIR]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_caller' +sil @take_pair_for_klass1_and_klass2_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_and_klass2 : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_and_klass2_caller' + + + +// TEST_PaK1K2PuEx: TYPE: Pair, FIELD: #Pair.klass1 & #Pair.klass2, VISIBILITY: public_external +// VERIFY: NO-EXPLODE +// The function take_pair_for_klass1_and_klass2 has a single argument of type +// Pair from which it uses both the klass1 and klass2 fields. Argument +// explosion should *not* result in a specialization in this case because (1) +// specializing does not affect ARC traffic on account of the fact that no +// fields are stripped and (2) specializing increases code size. +// +// CHECK-LABEL: sil public_external [noinline] @take_pair_for_klass1_and_klass2_public_external : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[KLASS1:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_public_external' +sil public_external [noinline] @take_pair_for_klass1_and_klass2_public_external : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass1 = struct_extract %pair : $Pair, #Pair.klass1 + %klass2 = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass_and_klass : $@convention(thin) (Klass, Klass) -> () + %result = apply %func(%klass1, %klass2) : $@convention(thin) (Klass, Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_and_klass2_public_external' + +// CHECK-LABEL: sil @take_pair_for_klass1_and_klass2_public_external_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_pair_for_klass1_and_klass2_public_external +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[PAIR]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_public_external_caller' +sil @take_pair_for_klass1_and_klass2_public_external_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_and_klass2_public_external : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_and_klass2_public_external_caller' + + + +// TEST_PaK1K2Hi: TYPE: Pair, FIELD: #Pair.klass1 & #Pair.klass2, VISIBILITY: hidden +// VERIFY: NO-EXPLODE +// The function take_pair_for_klass1_and_klass2_hidden has a single argument of +// type Pair from which it uses both the klass1 and klass2 fields. Argument +// explosion should *not* result in a specialization in this case because there +// are no dead leaves. +// +// CHECK-LABEL: sil hidden [noinline] @take_pair_for_klass1_and_klass2_hidden : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[KLASS1:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_and_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_hidden' +sil hidden [noinline] @take_pair_for_klass1_and_klass2_hidden : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass1 = struct_extract %pair : $Pair, #Pair.klass1 + %klass2 = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass_and_klass : $@convention(thin) (Klass, Klass) -> () + %result = apply %func(%klass1, %klass2) : $@convention(thin) (Klass, Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_and_klass2_hidden' + +// CHECK-LABEL: sil @take_pair_for_klass1_and_klass2_hidden_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_pair_for_klass1_and_klass2_hidden +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[PAIR]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_hidden_caller' +sil @take_pair_for_klass1_and_klass2_hidden_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_and_klass2_hidden : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_for_klass1_and_klass2_hidden_caller' + + + +// TEST_PaK1K2Pr: TYPE: Pair, FIELD: #Pair.klass1 & #Pair.klass2, VISIBILITY: private +// VERIFY: NO-EXPLODE +// The function take_pair_for_klass1_and_klass2_private has a single argument of +// type Pair from which it uses both the klass1 and klass2 fields. Argument +// explosion should *not* result in a specialization in this case because there +// are no dead leaves. +// +// CHECK-LABEL: sil private [noinline] @take_pair_for_klass1_and_klass2_private : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair): +// CHECK: [[KLASS1:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[PAIR]] : $Pair, #Pair.klass2 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_and_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_private' +sil private [noinline] @take_pair_for_klass1_and_klass2_private : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %klass1 = struct_extract %pair : $Pair, #Pair.klass1 + %klass2 = struct_extract %pair : $Pair, #Pair.klass2 + %func = function_ref @take_klass_and_klass : $@convention(thin) (Klass, Klass) -> () + %result = apply %func(%klass1, %klass2) : $@convention(thin) (Klass, Klass) -> () + return %result : $() +} // end sil function 'take_pair_for_klass1_and_klass2_private' + +// CHECK-LABEL: sil @take_pair_for_klass1_and_klass2_private_caller : $@convention(thin) (Pair) -> () { +// CHECK: bb{{[0-9]*}}([[PAIR:%.*]] : $Pair) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_pair_for_klass1_and_klass2_private +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[PAIR]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_pair_for_klass1_and_klass2_private_caller' +sil @take_pair_for_klass1_and_klass2_private_caller : $@convention(thin) (Pair) -> () { +only(%pair : $Pair): + %func = function_ref @take_pair_for_klass1_and_klass2_private : $@convention(thin) (Pair) -> () + %result = apply %func(%pair) : $@convention(thin) (Pair) -> () + return %result : $() +} // end sil funcntion 'take_pair_for_klass1_and_klass2_private_caller' + + + +// = END: Pair, #Pair.klass1, #Pair.klass2 ===================================}} + + + +// ===========================================================================}} +// = END: PAIR = +// ============================================================================= + + + +// ==========================================================================={{ +// = QUINTUPLE = +// ============================================================================= + + + +// = Quintuple, #.klass1, #.klass2, #.klass3 ================================={{ + + + +// TEST_QuK1K2K3Pu: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3, VISIBILITY: public +// VERIFY: EXPLODE: (Quintuple) => (#Quintuple.klass1, #Quintuple.klass2, #Quintuple.klass3) +// The function take_quintuple_for_klass1_through_3 has a single argument of +// type Quintuple from which it uses the klass1, klass2, and klass3 fields. +// The klass4 and klass5 fields goes unused. Argument explosion *should* +// result in specialization because there are only three (the heuristic for max +// explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3 : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s35take_quintuple_for_klass1_through_3Tf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3' +sil public [noinline] @take_quintuple_for_klass1_through_3 : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_3_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s35take_quintuple_for_klass1_through_3Tf4x_n +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_caller' +sil @take_quintuple_for_klass1_through_3_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3 : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_caller' + +// TEST_QuK1K2K3PuEx: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3, VISIBILITY: public_external +// VERIFY: EXPLODE: (Quintuple) => (#Quintuple.klass1, #Quintuple.klass2, #Quintuple.klass3) +// The function take_quintuple_for_klass1_through_3 has a single argument of +// type Quintuple from which it uses the klass1, klass2, and klass3 fields. +// The klass4 and klass5 fields goes unused. Argument explosion *should* +// result in specialization because there are only three (the heuristic for max +// explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil public_external [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_public_external : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s51take_quintuple_for_klass1_through_3_public_externalTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_public_external' +sil public_external [noinline] @take_quintuple_for_klass1_through_3_public_external : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_public_external' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_3_public_external_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s51take_quintuple_for_klass1_through_3_public_externalTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_public_external_caller' +sil @take_quintuple_for_klass1_through_3_public_external_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_public_external : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_public_external_caller' + +// TEST_QuK1K2K3Sh: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3, VISIBILITY: shared +// VERIFY: NO-EXPLODE +// The function take_quintuple_for_klass1_through_3 has a single argument of +// type Quintuple from which it uses the klass1, klass2, and klass3 fields. +// The klass4 and klass5 fields goes unused. Argument explosion *should* +// result in specialization because there are only three (the heuristic for max +// explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil shared [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_shared : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s42take_quintuple_for_klass1_through_3_sharedTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_shared' +sil shared [noinline] @take_quintuple_for_klass1_through_3_shared : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_shared' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_3_shared_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s42take_quintuple_for_klass1_through_3_sharedTf4x_n +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_shared_caller' +sil @take_quintuple_for_klass1_through_3_shared_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_shared : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_shared_caller' + +// TEST_QuK1K2K3Hi: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3, VISIBILITY: hidden +// VERIFY: NO-EXPLODE +// The function take_quintuple_for_klass1_through_3 has a single argument of +// type Quintuple from which it uses the klass1, klass2, and klass3 fields. +// The klass4 and klass5 fields goes unused. Argument explosion *should* +// result in specialization because there are only three (the heuristic for max +// explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_hidden : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s42take_quintuple_for_klass1_through_3_hiddenTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_hidden' +sil hidden [noinline] @take_quintuple_for_klass1_through_3_hidden : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_hidden' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_3_hidden_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s42take_quintuple_for_klass1_through_3_hiddenTf4x_n +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_hidden_caller' +sil @take_quintuple_for_klass1_through_3_hidden_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_hidden : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_hidden_caller' + +// TEST_QuK1K2K3Pr: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3, VISIBILITY: private +// VERIFY: NO-EXPLODE +// The function take_quintuple_for_klass1_through_3 has a single argument of +// type Quintuple from which it uses the klass1, klass2, and klass3 fields. +// The klass4 and klass5 fields goes unused. Argument explosion *should* +// result in specialization because there are only three (the heuristic for max +// explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_private : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s43take_quintuple_for_klass1_through_3_privateTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_private' +sil private [noinline] @take_quintuple_for_klass1_through_3_private : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_private' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_3_private_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s43take_quintuple_for_klass1_through_3_privateTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_private_caller' +sil @take_quintuple_for_klass1_through_3_private_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_private : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_private_caller' + + + +// = END: Quintuple, #.klass1, #.klass2, #.klass3 ============================}} + + + +// = Quintuple, #.klass1, #.klass2, #.klass3, #.klass4 ======================={{ + + + +// TEST_QuK1K2K3K4Pu: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & #.klass4, VISIBILITY: public +// VERIFY: NO-EXPLODE +// The function take_quintuple_for_klass1_through_4 has a single argument of +// type Quintuple from which it uses the klass1, klass2, klass3, and klass4 +// fields. The klass5 field goes unused. Argument explosion should *not* +// result in specialization because there are more than three (the heuristic +// for max explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil [noinline] @take_quintuple_for_klass1_through_4 : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4' +sil public [noinline] @take_quintuple_for_klass1_through_4 : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %func = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3, %klass4) : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_4_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_quintuple_for_klass1_through_4 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[QUINTUPLE]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_caller' +sil @take_quintuple_for_klass1_through_4_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_4 : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_caller' + +// TEST_QuK1K2K3K4PuEx: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & #.klass4, VISIBILITY: public_external +// VERIFY: NO-EXPLODE +// The function take_quintuple_for_klass1_through_4 has a single argument of +// type Quintuple from which it uses the klass1, klass2, klass3, and klass4 +// fields. The klass5 field goes unused. Argument explosion should *not* +// result in specialization because there are more than three (the heuristic +// for max explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil public_external [noinline] @take_quintuple_for_klass1_through_4_public_external : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_public_external' +sil public_external [noinline] @take_quintuple_for_klass1_through_4_public_external : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %func = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3, %klass4) : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_public_external' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_4_public_external_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_quintuple_for_klass1_through_4_public_external +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[QUINTUPLE]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_public_external_caller' +sil @take_quintuple_for_klass1_through_4_public_external_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_4_public_external : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_public_external_caller' + +// TEST_QuK1K2K3K4Sh: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & #.klass4, VISIBILITY: shared +// VERIFY: EXPLODE (Quintuple) => (#Quintuple.klass1, #Quintuple.klass2, #Quintuple.klass3, #Quintuple.klass4) +// The function take_quintuple_for_klass1_through_4 has a single argument of +// type Quintuple from which it uses the klass1, klass2, klass3, and klass4 +// fields. The klass5 field goes unused. Argument explosion *should* +// result in specialization because there are dead leaves and there are fewer +// live than the limit imposed by the heuristic, six because the specializing +// the function will not result in a thunk. +// +// CHECK-LABEL: sil shared [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_4_shared : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s42take_quintuple_for_klass1_through_4_sharedTf4x_n : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_shared' +sil shared [noinline] @take_quintuple_for_klass1_through_4_shared : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %func = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3, %klass4) : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_shared' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_4_shared_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s42take_quintuple_for_klass1_through_4_sharedTf4x_n : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_shared_caller' +sil @take_quintuple_for_klass1_through_4_shared_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_4_shared : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_shared_caller' + + + +// TEST_QuK1K2K3K4Hi: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & #.klass4, VISIBILITY: hidden +// VERIFY: NO-EXPLODE +// The function take_quintuple_for_klass1_through_4 has a single argument of +// type Quintuple from which it uses the klass1, klass2, klass3, and klass4 +// fields. The klass5 field goes unused. Argument explosion should *not* +// result in specialization because there are more than three (the heuristic +// for max explosion size) live nodes in the type-tree. +// +// CHECK-LABEL: sil hidden [noinline] @take_quintuple_for_klass1_through_4_hidden : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_hidden' +sil hidden [noinline] @take_quintuple_for_klass1_through_4_hidden : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %func = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3, %klass4) : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_hidden' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_4_hidden_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_quintuple_for_klass1_through_4_hidden +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[QUINTUPLE]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_hidden_caller' +sil @take_quintuple_for_klass1_through_4_hidden_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_4_hidden : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_hidden_caller' + +// TEST_QuK1K2K3K4Pr: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & #.klass4, VISIBILITY: private +// VERIFY: EXPLODE (Quintuple) => (#Quintuple.klass1, #Quintuple.klass2, #Quintuple.klass3, #Quintuple.klass4) +// The function take_quintuple_for_klass1_through_4 has a single argument of +// type Quintuple from which it uses the klass1, klass2, klass3, and klass4 +// fields. The klass5 field goes unused. Argument explosion *should* +// result in specialization because there are dead leaves and there are fewer +// live than the limit imposed by the heuristic, six because the specializing +// the function will not result in a thunk. +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_4_private : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s43take_quintuple_for_klass1_through_4_privateTf4x_n : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_private' +sil private [noinline] @take_quintuple_for_klass1_through_4_private : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %func = function_ref @take_klass_four_times : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + %result = apply %func(%klass1, %klass2, %klass3, %klass4) : $@convention(thin) (Klass, Klass, Klass, Klass) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_private' + +// CHECK-LABEL: sil @take_quintuple_for_klass1_through_4_private_caller : $@convention(thin) (Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[FUNCTION:%.*]] = function_ref @$s43take_quintuple_for_klass1_through_4_privateTf4x_n : $@convention(thin) (Klass, Klass, Klass, Klass) -> () +// CHECK: [[KLASS4:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass4 +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]], [[KLASS4]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_4_private_caller' +sil @take_quintuple_for_klass1_through_4_private_caller : $@convention(thin) (Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_4_private : $@convention(thin) (Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_4_private_caller' + + + +// = END: Quintuple, #.klass1, #.klass2, #.klass3, #.klass4 ==================}} + + + +// = Quintuple, #.klass1, #.klass2, #.klass3; owned #.klass4, #.klass5 ======={{ + + + +// TEST_QuK1K2K3OwK4OwK5Pu: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & owned #.klass4 & owned #.klass5, VISIBILITY: public +// VERIFY: EXPLODE: (Quintuple) -> (#Quintuple.klass1, #Quintuple.klass2, #Quintuple.klaass3) +// The function take_quintuple_for_klass1_through_3_owned_klass4_through_5 has a +// single argument of type Quintuple from which it uses the klass1, klass2, +// klass3 fields and releases the klass4 and klass5 fields. Argument explosion +// *should* result in specialization because there are only three live leaves in +// the type tree after considering the owned-to-guaranteed transformation. +// +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5 : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E2_5Tf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5' +sil public [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5 : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %klass5 = struct_extract %quintuple : $Quintuple, #Quintuple.klass5 + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + strong_release %klass4 : $Klass + strong_release %klass5 : $Klass + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5' + +// CHECK-LABEL: sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_caller : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E2_5Tf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_caller' +sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_caller : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_owned_klass4_through_5 : $@convention(thin) (@owned Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (@owned Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_caller' + + + +// TEST_QuK1K2K3OwK4OwK5PuEx: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & owned #.klass4 & owned #.klass5, VISIBILITY: public_external +// VERIFY: EXPLODE: (Quintuple) -> (#Quintuple.klass1, #Quintuple.klass2, #Quintuple.klaass3) +// The function take_quintuple_for_klass1_through_3_owned_klass4_through_5 has a +// single argument of type Quintuple from which it uses the klass1, klass2, +// klass3 fields and releases the klass4 and klass5 fields. Argument explosion +// *should* result in specialization because there are only three live leaves in +// the type tree after considering the owned-to-guaranteed transformation. +// +// CHECK-LABEL: sil public_external [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E18_5_public_externalTf4x_nTf4nnndd_n : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external' +sil public_external [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %klass5 = struct_extract %quintuple : $Quintuple, #Quintuple.klass5 + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + strong_release %klass4 : $Klass + strong_release %klass5 : $Klass + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external' + +// CHECK-LABEL: sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external_caller : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E18_5_public_externalTf4x_nTf4nnndd_n : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external_caller' +sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external_caller : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external : $@convention(thin) (@owned Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (@owned Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_public_external_caller' + + + +// TEST_QuK1K2K3OwK4OwK5Sh: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & owned #.klass4 & owned #.klass5, VISIBILITY: shared +// VERIFY: EXPLODE +// The function +// take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared has a +// single argument of type Quintuple from which it uses the klass1, klass2, +// klass3 fields and releases the klass4 and klass5 fields. Argument explosion +// *should* result in specialization because there are only three live leaves +// in the type tree after considering the owned-to-guaranteed transformation. +// +// CHECK-LABEL: sil shared [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_sharedTf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared' +sil shared [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %klass5 = struct_extract %quintuple : $Quintuple, #Quintuple.klass5 + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + strong_release %klass4 : $Klass + strong_release %klass5 : $Klass + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared' + +// CHECK-LABEL: sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared_caller : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_sharedTf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared_caller' +sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared_caller : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared : $@convention(thin) (@owned Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (@owned Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_shared_caller' + + + +// TEST_QuK1K2K3OwK4OwK5Hi: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & owned #.klass4 & owned #.klass5, VISIBILITY: hidden +// VERIFY: EXPLODE +// The function +// take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden has a +// single argument of type Quintuple from which it uses the klass1, klass2, +// klass3 fields and releases the klass4 and klass5 fields. Argument explosion +// *should* result in specialization because there are only three live leaves +// in the type tree after considering the owned-to-guaranteed transformation. +// +// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_hiddenTf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden' +sil hidden [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %klass5 = struct_extract %quintuple : $Quintuple, #Quintuple.klass5 + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + strong_release %klass4 : $Klass + strong_release %klass5 : $Klass + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden' + +// CHECK-LABEL: sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden_caller : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_hiddenTf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden_caller' +sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden_caller : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden : $@convention(thin) (@owned Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (@owned Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_hidden_caller' + + + +// TEST_QuK1K2K3OwK4OwK5Pr: TYPE: Quintuple, FIELD: #.klass1 & #.klass2 & #.klass3 & owned #.klass4 & owned #.klass5, VISIBILITY: private +// VERIFY: EXPLODE +// The function +// take_quintuple_for_klass1_through_3_owned_klass4_through_5_private has a +// single argument of type Quintuple from which it uses the klass1, klass2, +// klass3 fields and releases the klass4 and klass5 fields. Argument explosion +// *should* result in specialization because there are only three live leaves +// in the type tree after considering the owned-to-guaranteed transformation. +// +// CHECK-LABEL: sil private [signature_optimized_thunk] [always_inline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_private : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E10_5_privateTf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_private' +sil private [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_private : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () + %klass1 = struct_extract %quintuple : $Quintuple, #Quintuple.klass1 + %klass2 = struct_extract %quintuple : $Quintuple, #Quintuple.klass2 + %klass3 = struct_extract %quintuple : $Quintuple, #Quintuple.klass3 + %klass4 = struct_extract %quintuple : $Quintuple, #Quintuple.klass4 + %klass5 = struct_extract %quintuple : $Quintuple, #Quintuple.klass5 + %result = apply %func(%klass1, %klass2, %klass3) : $@convention(thin) (Klass, Klass, Klass) -> () + strong_release %klass4 : $Klass + strong_release %klass5 : $Klass + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_private' + +// CHECK-LABEL: sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_private_caller : $@convention(thin) (@owned Quintuple) -> () { +// CHECK: bb{{[0-9]+}}([[QUINTUPLE:%.*]] : $Quintuple): +// CHECK: [[KLASS3:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass3 +// CHECK: [[KLASS2:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass2 +// CHECK: [[KLASS1:%.*]] = struct_extract [[QUINTUPLE]] : $Quintuple, #Quintuple.klass1 +// CHECK: [[FUNCTION:%.*]] = function_ref @$s049take_quintuple_for_klass1_through_3_owned_klass4_E10_5_privateTf4x_nTf4nnndd_n +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_private_caller' +sil [noinline] @take_quintuple_for_klass1_through_3_owned_klass4_through_5_private_caller : $@convention(thin) (@owned Quintuple) -> () { +only(%quintuple : $Quintuple): + %func = function_ref @take_quintuple_for_klass1_through_3_owned_klass4_through_5_private : $@convention(thin) (@owned Quintuple) -> () + %result = apply %func(%quintuple) : $@convention(thin) (@owned Quintuple) -> () + return %result : $() +} // end sil function 'take_quintuple_for_klass1_through_3_owned_klass4_through_5_private_caller' + + + +// = END: Quintuple, #.klass1, #.klass2, #.klass3; owned #.klass4, #.klass5 ==}} + + + +// ===========================================================================}} +// = END: QUINTUPLE = +// ============================================================================= + + +// ============================================================================= +// ============================================================================= +// ===== END: TESTS ===== +// ============================================================================= +// ===========================================================================}} + + + +// ==========================================================================={{ +// ============================================================================= +// ===== SPECIALIZATIONS ===== +// ===== All the specializations are added at the bottom of the output. ===== +// ===== The tests around them follow. They are tied back to the original ===== +// ===== code via a TEST_ identifier. ===== +// ============================================================================= +// ============================================================================= + + +// TEST_WrKlPu: +// Nothing to check. + +// TEST_WrKlPuEx: +// Nothig to check. + +// TEST_WrKlSh: +// CHECK: sil shared [noinline] @$s19take_wrapper_sharedTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]](%0) +// CHECK: return [[RESULT]] : $() +// CHECK: } // end sil function '$s19take_wrapper_sharedTf4x_n' + +// TEST_WrKlHi: +// CHECK-NOT: sil shared [noinline] @$s19take_wrapper_hiddenTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK-NOT: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK-NOT: [[RESULT:%.*]] = apply [[FUNCTION]](%0) +// CHECK-NOT: return [[RESULT]] : $() +// CHECK-NOT: } // end sil function '$s19take_wrapper_hiddenTf4x_n' + +// TEST_WrKlPr: +// CHECK: sil private [noinline] @$s20take_wrapper_privateTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]](%0) +// CHECK: return [[RESULT]] : $() +// CHECK: } // end sil function '$s20take_wrapper_privateTf4x_n' + +// TEST_WrInPu: +// CHECK-LABEL: sil shared [noinline] @$s20take_wrapper_for_intTf4x_n : $@convention(thin) (Int) -> () { +// CHECK: bb{{[0-9]*}}([[INT:%.*]] : $Int) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s20take_wrapper_for_intTf4x_n' + +// TEST_WrInPuEx: +// CHECK-LABEL: sil shared [noinline] @$s36take_wrapper_for_int_public_externalTf4x_n : $@convention(thin) (Int) -> () { +// CHECK: bb{{[0-9]*}}([[INT:%.*]] : $Int) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s36take_wrapper_for_int_public_externalTf4x_n' + +// TEST_WrInHi: +// CHECK-LABEL: sil shared [noinline] @$s27take_wrapper_for_int_hiddenTf4x_n : $@convention(thin) (Int) -> () { +// CHECK: bb{{[0-9]*}}([[INT:%.*]] : $Int) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s27take_wrapper_for_int_hiddenTf4x_n' + +// TEST_WrInPr: +// CHECK-LABEL: sil private [noinline] @$s28take_wrapper_for_int_privateTf4x_n : $@convention(thin) (Int) -> () { +// CHECK: bb{{[0-9]*}}([[INT:%.*]] : $Int) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_int +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[INT]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s28take_wrapper_for_int_privateTf4x_n' + +// TEST_PaPu: +// CHECK-LABEL: sil shared [noinline] @$s16take_pair_unusedTf4d_n : $@convention(thin) () -> () { +// CHECK: bb{{[0-9]*}} +// CHECK: [[FUNCTION:%.*]] = function_ref @take_nothing : $@convention(thin) () -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s16take_pair_unusedTf4d_n' + +// TEST_PaPuEx: +// CHECK-LABEL: sil shared [noinline] @$s32take_pair_unused_public_externalTf4d_n +// CHECK: bb{{[0-9]*}} +// CHECK: [[FUNCTION:%.*]] = function_ref @take_nothing : $@convention(thin) () -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s32take_pair_unused_public_externalTf4d_n' + +// TEST_PaHi: +// CHECK-LABEL: sil shared [noinline] @$s23take_pair_unused_hiddenTf4d_n : $@convention(thin) () -> () { +// CHECK: bb{{[0-9]*}} +// CHECK: [[FUNCTION:%.*]] = function_ref @take_nothing : $@convention(thin) () -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s23take_pair_unused_hiddenTf4d_n' + +// TEST_PaPr: +// CHECK-LABEL: sil private [noinline] @$s24take_pair_unused_privateTf4d_n : $@convention(thin) () -> () { +// CHECK: bb{{[0-9]*}} +// CHECK: [[FUNCTION:%.*]] = function_ref @take_nothing : $@convention(thin) () -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]() +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s24take_pair_unused_privateTf4d_n' + +// TEST_PaK1Pu: +// CHECK-LABEL: sil shared [noinline] @$s20take_pair_for_klass1Tf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}([[KLASS:%.*]] : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s20take_pair_for_klass1Tf4x_n' + +// TEST_PaK1PuEx: +// CHECK-LABEL: sil shared [noinline] @$s36take_pair_for_klass1_public_externalTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}([[KLASS:%.*]] : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s36take_pair_for_klass1_public_externalTf4x_n' + +// TEST_PaK1Hi: +// CHECK-LABEL: sil shared [noinline] @$s27take_pair_for_klass1_hiddenTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s27take_pair_for_klass1_hiddenTf4x_n' + +// TEST_PaK1Pr: +// CHECK-LABEL: sil private [noinline] @$s28take_pair_for_klass1_privateTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s28take_pair_for_klass1_privateTf4x_n' + +// TEST_PaK2Pu: +// CHECK-LABEL: sil shared [noinline] @$s20take_pair_for_klass2Tf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s20take_pair_for_klass2Tf4x_n' + +// TEST_PaK2PuEx: +// CHECK-LABEL: sil shared [noinline] @$s36take_pair_for_klass2_public_externalTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s36take_pair_for_klass2_public_externalTf4x_n' + +// TEST_PaK2Hi: +// CHECK-LABEL: sil shared [noinline] @$s27take_pair_for_klass2_hiddenTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s27take_pair_for_klass2_hiddenTf4x_n' + +// TEST_PaK2Pr: +// CHECK-LABEL: sil private [noinline] @$s28take_pair_for_klass2_privateTf4x_n : $@convention(thin) (Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass) +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass : $@convention(thin) (Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s28take_pair_for_klass2_privateTf4x_n' + +// TEST_PaK1K2Pu: +// Nothing to check. + +// TEST_PaK1K2PuEx: +// Nothing to check. + +// TEST_PaK1K2Hi: +// Nothing to check. + +// TEST_PaK1K2Pr: +// Nothing to check. + + +// TEST_QuK1K2K3Pu: +// CHECK-LABEL: sil shared [noinline] @$s35take_quintuple_for_klass1_through_3Tf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass, {{%.*}} : $Klass, {{%.*}} : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}, {{%.*}}, {{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s35take_quintuple_for_klass1_through_3Tf4x_n' +// Nothing to check. + +// TEST_QuK1K2K3PuEx: +// FIXME: THIS CHANGED + +// TEST_QuK1K2K3Sh: +// CHECK-LABEL: sil shared [noinline] @$s42take_quintuple_for_klass1_through_3_sharedTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass, {{%.*}} : $Klass, {{%.*}} : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}, {{%.*}}, {{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s42take_quintuple_for_klass1_through_3_sharedTf4x_n' + +// Nothing to check. + +// TEST_QuK1K2K3Hi: +// CHECK-LABEL: sil shared [noinline] @$s42take_quintuple_for_klass1_through_3_hiddenTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass, {{%.*}} : $Klass, {{%.*}} : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}, {{%.*}}, {{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s42take_quintuple_for_klass1_through_3_hiddenTf4x_n' + +// TEST_QuK1K2K3Pr: +// CHECK-LABEL: sil private [noinline] @$s43take_quintuple_for_klass1_through_3_privateTf4x_n : $@convention(thin) (Klass, Klass, Klass) -> () { +// CHECK: bb{{[0-9]*}}({{%.*}} : $Klass, {{%.*}} : $Klass, {{%.*}} : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]({{%.*}}, {{%.*}}, {{%.*}}) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s43take_quintuple_for_klass1_through_3_privateTf4x_n' + +// TEST_QuK1K2K3K4Pu: +// Nothing to check. + +// TEST_QuK1K2K3K4PuEx: +// Nothing to check. + +// TEST_QuK1K2K3K4Sh: +// FIXME: THIS CHANGED +// Nothing to check. + +// TEST_QuK1K2K3K4Hi: +// Nothing to check. + +// TEST_QuK1K2K3K4Pr: +// Nothing to check. + +// TEST_QuK1K2K3OwK4OwK5Pu: +// CHECK-LABEL: sil shared [noinline] @$s049take_quintuple_for_klass1_through_3_owned_klass4_E2_5Tf4x_nTf4nnndd_n : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () { +// CHECK: bb{{[0-9]*}}([[KLASS1:%.*]] : $Klass, [[KLASS2:%.*]] : $Klass, [[KLASS3:%.*]] : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s049take_quintuple_for_klass1_through_3_owned_klass4_E2_5Tf4x_nTf4nnndd_n' + +// TEST_QuK1K2K3OwK4OwK5PuEx: +// FIXME: THIS CHANGED + +// TEST_QuK1K2K3OwK4OwK5Sh: +// CHECK-LABEL: sil shared [noinline] @$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_sharedTf4x_nTf4nnndd_n : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () { +// CHECK: bb{{[0-9]*}}([[KLASS1:%.*]] : $Klass, [[KLASS2:%.*]] : $Klass, [[KLASS3:%.*]] : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_sharedTf4x_nTf4nnndd_n' + +// TEST_QuK1K2K3OwK4OwK5Hi: +// CHECK-LABEL: sil shared [noinline] @$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_hiddenTf4x_nTf4nnndd_n : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () { +// CHECK: bb{{[0-9]*}}([[KLASS1:%.*]] : $Klass, [[KLASS2:%.*]] : $Klass, [[KLASS3:%.*]] : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s049take_quintuple_for_klass1_through_3_owned_klass4_E9_5_hiddenTf4x_nTf4nnndd_n' + +// TEST_QuK1K2K3OwK4OwK5Pr: +// CHECK-LABEL: sil private [noinline] @$s049take_quintuple_for_klass1_through_3_owned_klass4_E10_5_privateTf4x_nTf4nnndd_n : $@convention(thin) (@owned Klass, @owned Klass, @owned Klass) -> () { +// CHECK: bb{{[0-9]*}}([[KLASS1:%.*]] : $Klass, [[KLASS2:%.*]] : $Klass, [[KLASS3:%.*]] : $Klass): +// CHECK: [[FUNCTION:%.*]] = function_ref @take_klass_three_times : $@convention(thin) (Klass, Klass, Klass) -> () +// CHECK: [[RESULT:%.*]] = apply [[FUNCTION]]([[KLASS1]], [[KLASS2]], [[KLASS3]]) +// CHECK: return [[RESULT]] : $() +// CHECK-LABEL: } // end sil function '$s049take_quintuple_for_klass1_through_3_owned_klass4_E10_5_privateTf4x_nTf4nnndd_n' + +// ============================================================================= +// ============================================================================= +// ===== END: SPECIALIZATIONS ===== +// ============================================================================= +// ===========================================================================}} From 7d2dac1272c0906fe3792cd04a9c7182b876d87b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 24 Sep 2019 17:45:14 -0700 Subject: [PATCH 060/140] Define Requests Define the LookupPrecedenceGroupRequest and OperatorPrecedenceGroupRequest for looking up an unvalidated precedence group declaration and retrieving then validating the precedence group associated with an operator. This allows us to drop both validateDecl overloads for these types out of the TypeChechecker --- include/swift/AST/ASTTypeIDZone.def | 2 + include/swift/AST/ASTTypeIDs.h | 2 + include/swift/AST/NameLookupRequests.h | 51 +++++++++++++++++++++ include/swift/AST/NameLookupTypeIDZone.def | 3 ++ include/swift/AST/TypeCheckRequests.h | 20 ++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 ++ lib/AST/NameLookupRequests.cpp | 19 ++++++++ lib/AST/TypeCheckRequests.cpp | 2 +- 8 files changed, 101 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 1e5d2d0f2487a..91764dd88f44d 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -28,12 +28,14 @@ SWIFT_TYPEID_NAMED(Decl *, Decl) SWIFT_TYPEID_NAMED(GenericParamList *, GenericParamList) SWIFT_TYPEID_NAMED(GenericSignature *, GenericSignature) SWIFT_TYPEID_NAMED(GenericTypeParamType *, GenericTypeParamType) +SWIFT_TYPEID_NAMED(InfixOperatorDecl *, InfixOperatorDecl) SWIFT_TYPEID_NAMED(IterableDeclContext *, IterableDeclContext) SWIFT_TYPEID_NAMED(ModuleDecl *, ModuleDecl) SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl) SWIFT_TYPEID_NAMED(OperatorDecl *, OperatorDecl) SWIFT_TYPEID_NAMED(Optional, PropertyWrapperMutability) +SWIFT_TYPEID_NAMED(PrecedenceGroupDecl *, PrecedenceGroupDecl) SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl) SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl) SWIFT_TYPEID_NAMED(ValueDecl *, ValueDecl) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index cc8a95ac71f2e..cb2850c5f9f95 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -26,10 +26,12 @@ class Decl; class GenericParamList; class GenericSignature; class GenericTypeParamType; +class InfixOperatorDecl; class IterableDeclContext; class ModuleDecl; class NominalTypeDecl; class OperatorDecl; +class PrecedenceGroupDecl; struct PropertyWrapperBackingPropertyInfo; struct PropertyWrapperTypeInfo; enum class CtorInitializerKind; diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 271a0632fd88c..5617ae7346908 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -19,6 +19,7 @@ #include "swift/AST/SimpleRequest.h" #include "swift/AST/ASTTypeIDs.h" #include "swift/Basic/Statistic.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/TinyPtrVector.h" namespace swift { @@ -273,6 +274,56 @@ class GenericParamListRequest : void cacheResult(GenericParamList *value) const; }; +struct PrecedenceGroupDescriptor { + DeclContext *dc; + Identifier ident; + SourceLoc nameLoc; + + SourceLoc getLoc() const; + + friend llvm::hash_code hash_value(const PrecedenceGroupDescriptor &owner) { + return hash_combine(llvm::hash_value(owner.dc), + llvm::hash_value(owner.ident.getAsOpaquePointer()), + llvm::hash_value(owner.nameLoc.getOpaquePointerValue())); + } + + friend bool operator==(const PrecedenceGroupDescriptor &lhs, + const PrecedenceGroupDescriptor &rhs) { + return lhs.dc == rhs.dc && + lhs.ident == rhs.ident && + lhs.nameLoc == rhs.nameLoc; + } + + friend bool operator!=(const PrecedenceGroupDescriptor &lhs, + const PrecedenceGroupDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, const PrecedenceGroupDescriptor &d); + +class LookupPrecedenceGroupRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, PrecedenceGroupDescriptor descriptor) const; + +public: + // Source location + SourceLoc getNearestLoc() const; + + // Separate caching. + bool isCached() const { return true; } +}; + #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 d7c9f832484b3..b33c93fee4bae 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -30,6 +30,9 @@ SWIFT_REQUEST(NameLookup, InheritedDeclsReferencedRequest, DirectlyReferencedTypeDecls( llvm::PointerUnion, unsigned), Uncached, HasNearestLocation) +SWIFT_REQUEST(NameLookup, LookupPrecedenceGroupRequest, + PrecedenceGroupDecl *(DeclContext *, Identifier, SourceLoc), + Cached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest, SelfBounds(llvm::PointerUnion), Uncached, NoLocationInfo) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 60f17ff3caed1..a6009209ada5b 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -33,6 +33,7 @@ class AbstractStorageDecl; class AccessorDecl; enum class AccessorKind; class GenericParamList; +class PrecedenceGroupDecl; struct PropertyWrapperBackingPropertyInfo; struct PropertyWrapperMutability; class RequirementRepr; @@ -1209,6 +1210,25 @@ class UnderlyingTypeRequest : void cacheResult(Type value) const; }; +class OperatorPrecedenceGroupRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, InfixOperatorDecl *PGD) const; + +public: + // Separate caching. + 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 7a8e4c96cb0d8..c539350de964a 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -85,6 +85,9 @@ SWIFT_REQUEST(TypeChecker, MangleLocalTypeDeclRequest, SWIFT_REQUEST(TypeChecker, OpaqueReadOwnershipRequest, OpaqueReadOwnership(AbstractStorageDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, OperatorPrecedenceGroupRequest, + PrecedenceGroupDecl *(PrecedenceGroupDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, OverriddenDeclsRequest, llvm::TinyPtrVector(ValueDecl *), SeparatelyCached, NoLocationInfo) diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index dce1e75fa83ab..3924cc4405e88 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -127,6 +127,25 @@ void GenericParamListRequest::cacheResult(GenericParamList *params) const { } +//----------------------------------------------------------------------------// +// LookupPrecedenceGroupRequest computation. +//----------------------------------------------------------------------------// + +SourceLoc LookupPrecedenceGroupRequest::getNearestLoc() const { + auto &desc = std::get<0>(getStorage()); + return desc.getLoc(); +} + +SourceLoc PrecedenceGroupDescriptor::getLoc() const { + return nameLoc; +} + +void swift::simple_display(llvm::raw_ostream &out, + const PrecedenceGroupDescriptor &desc) { + out << "precedence group " << desc.ident << " at "; + desc.nameLoc.print(out, desc.dc->getASTContext().SourceMgr); +} + // 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/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index f8b17a4db6ea7..35c304eb31630 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -832,7 +832,7 @@ void GenericSignatureRequest::cacheResult(GenericSignature *value) const { } //----------------------------------------------------------------------------// -// GenericSignatureRequest computation. +// InferredGenericSignatureRequest computation. //----------------------------------------------------------------------------// void InferredGenericSignatureRequest::noteCycleStep(DiagnosticEngine &d) const { From 5964ce47b042d66dc9732fc56338c5bbfcee67b0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 24 Sep 2019 17:45:50 -0700 Subject: [PATCH 061/140] Use OperatorPrecedenceGroupRequest to clean up InfixOperatorDecl a bit --- include/swift/AST/Decl.h | 14 +------------- lib/AST/Decl.cpp | 22 ++++++++++++++++++++++ lib/Serialization/Deserialization.cpp | 9 ++++++--- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index e1801d51450dd..315114a98b76c 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6909,7 +6909,6 @@ class OperatorDecl : public Decl { /// \endcode class InfixOperatorDecl : public OperatorDecl { SourceLoc ColonLoc; - PrecedenceGroupDecl *PrecedenceGroup = nullptr; public: InfixOperatorDecl(DeclContext *DC, SourceLoc operatorLoc, Identifier name, @@ -6920,14 +6919,6 @@ class InfixOperatorDecl : public OperatorDecl { identifiers, identifierLocs), ColonLoc(colonLoc) {} - InfixOperatorDecl(DeclContext *DC, SourceLoc operatorLoc, Identifier name, - SourceLoc nameLoc, SourceLoc colonLoc, - PrecedenceGroupDecl *precedenceGroup, - ArrayRef designatedNominalTypes) - : OperatorDecl(DeclKind::InfixOperator, DC, operatorLoc, name, nameLoc, - designatedNominalTypes), - ColonLoc(colonLoc), PrecedenceGroup(precedenceGroup) {} - SourceLoc getEndLoc() const { auto identifierLocs = getIdentifierLocs(); if (identifierLocs.empty()) @@ -6942,10 +6933,7 @@ class InfixOperatorDecl : public OperatorDecl { SourceLoc getColonLoc() const { return ColonLoc; } - PrecedenceGroupDecl *getPrecedenceGroup() const { return PrecedenceGroup; } - void setPrecedenceGroup(PrecedenceGroupDecl *PGD) { - PrecedenceGroup = PGD; - } + PrecedenceGroupDecl *getPrecedenceGroup() const; /// True if this decl's attributes conflict with those declared by another /// operator. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 53b120cffa52f..bb3f1b8f8d20b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7471,6 +7471,28 @@ PrecedenceGroupDecl::PrecedenceGroupDecl(DeclContext *dc, lowerThan.size() * sizeof(Relation)); } +llvm::Expected LookupPrecedenceGroupRequest::evaluate( + Evaluator &eval, PrecedenceGroupDescriptor descriptor) const { + auto *dc = descriptor.dc; + PrecedenceGroupDecl *group = nullptr; + if (auto sf = dc->getParentSourceFile()) { + bool cascading = dc->isCascadingContextForLookup(false); + group = sf->lookupPrecedenceGroup(descriptor.ident, cascading, + descriptor.nameLoc); + } else { + group = dc->getParentModule()->lookupPrecedenceGroup(descriptor.ident, + descriptor.nameLoc); + } + return group; +} + +PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const { + return evaluateOrDefault( + getASTContext().evaluator, + OperatorPrecedenceGroupRequest{const_cast(this)}, + nullptr); +} + bool FuncDecl::isDeferBody() const { return getName() == getASTContext().getIdentifier("$defer"); } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e6d37dbe6d690..2949820623d7b 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3294,9 +3294,12 @@ class swift::DeclDeserializer { auto result = MF.createDecl( DC, SourceLoc(), MF.getIdentifier(nameID), SourceLoc(), SourceLoc(), - cast_or_null(precedenceGroup.get()), - ctx.AllocateCopy(designatedNominalTypes)); - + ArrayRef{}, ArrayRef{}); + result->setDesignatedNominalTypes(ctx.AllocateCopy(designatedNominalTypes)); + ctx.evaluator.cacheOutput( + OperatorPrecedenceGroupRequest{result}, + std::move(cast_or_null(precedenceGroup.get()))); + declOrOffset = result; return result; } From 62266447f2c5907d1fd10e1149a9919a9e6a03db Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 24 Sep 2019 17:48:42 -0700 Subject: [PATCH 062/140] Drop the TypeChecker dependency from some helpers --- lib/Sema/CSApply.cpp | 18 ++++++------ lib/Sema/CSDiagnostics.h | 4 +-- lib/Sema/TypeCheckDecl.cpp | 23 ++++++++-------- lib/Sema/TypeCheckExpr.cpp | 56 +++++++++++++++++++------------------- lib/Sema/TypeChecker.h | 7 +++-- 5 files changed, 55 insertions(+), 53 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d2e1392f828ed..fdd6065ad83de 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7342,7 +7342,7 @@ bool swift::exprNeedsParensInsideFollowingOperator( TypeChecker &TC, DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG) { if (expr->isInfixOperator()) { - auto exprPG = TC.lookupPrecedenceGroupForInfixOperator(DC, expr); + auto exprPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, expr); if (!exprPG) return true; return TC.Context.associateInfixOperators(exprPG, followingPG) @@ -7376,7 +7376,8 @@ bool swift::exprNeedsParensOutsideFollowingOperator( return false; if (parent->isInfixOperator()) { - auto parentPG = TC.lookupPrecedenceGroupForInfixOperator(DC, parent); + auto parentPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, + parent); if (!parentPG) return true; // If the index is 0, this is on the LHS of the parent. @@ -7395,10 +7396,10 @@ bool swift::exprNeedsParensOutsideFollowingOperator( bool swift::exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC, DeclContext *DC, Expr *expr) { - auto asPG = - TC.lookupPrecedenceGroup(DC, DC->getASTContext().Id_NilCoalescingPrecedence, - SourceLoc()); - if (!asPG) return true; + auto asPG = TypeChecker::lookupPrecedenceGroup( + DC, DC->getASTContext().Id_NilCoalescingPrecedence, SourceLoc()); + if (!asPG) + return true; return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG); } @@ -7406,9 +7407,8 @@ bool swift::exprNeedsParensAfterAddingNilCoalescing(TypeChecker &TC, DeclContext *DC, Expr *expr, Expr *rootExpr) { - auto asPG = - TC.lookupPrecedenceGroup(DC, DC->getASTContext().Id_NilCoalescingPrecedence, - SourceLoc()); + auto asPG = TypeChecker::lookupPrecedenceGroup( + DC, DC->getASTContext().Id_NilCoalescingPrecedence, SourceLoc()); if (!asPG) return true; return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr, asPG); } diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index a0a4fdc180786..ca94841557484 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -794,7 +794,7 @@ class MissingExplicitConversionFailure final : public ContextualFailure { auto *DC = getDC(); auto &TC = getTypeChecker(); - auto asPG = TC.lookupPrecedenceGroup( + auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); if (!asPG) return true; @@ -805,7 +805,7 @@ class MissingExplicitConversionFailure final : public ContextualFailure { auto *DC = getDC(); auto &TC = getTypeChecker(); - auto asPG = TC.lookupPrecedenceGroup( + auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); if (!asPG) return true; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4a41da9127f37..007de8d4f1dd0 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1764,20 +1764,22 @@ void TypeChecker::validateDecl(PrecedenceGroupDecl *PGD) { bool isInvalid = false; + auto &Diags = PGD->getASTContext().Diags; + // Validate the higherThan relationships. bool addedHigherThan = false; for (auto &rel : PGD->getMutableHigherThan()) { if (rel.Group) continue; - auto group = lookupPrecedenceGroupPrimitive(PGD->getDeclContext(), - rel.Name, rel.NameLoc); + auto group = TypeChecker::lookupPrecedenceGroup(PGD->getDeclContext(), + rel.Name, rel.NameLoc); if (group) { rel.Group = group; validateDecl(group); addedHigherThan = true; } else if (!PGD->isInvalid()) { - diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name); - isInvalid = true; + Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name); + PGD->setInvalid(); } } @@ -1786,7 +1788,7 @@ void TypeChecker::validateDecl(PrecedenceGroupDecl *PGD) { if (rel.Group) continue; auto dc = PGD->getDeclContext(); - auto group = lookupPrecedenceGroupPrimitive(dc, rel.Name, rel.NameLoc); + auto group = TypeChecker::lookupPrecedenceGroup(dc, rel.Name, rel.NameLoc); if (group) { if (group->getDeclContext()->getParentModule() == dc->getParentModule()) { @@ -1828,14 +1830,14 @@ PrecedenceGroupDecl *TypeChecker::lookupPrecedenceGroup(DeclContext *dc, } static NominalTypeDecl *resolveSingleNominalTypeDecl( - DeclContext *DC, SourceLoc loc, Identifier ident, TypeChecker &tc, + DeclContext *DC, SourceLoc loc, Identifier ident, ASTContext &Ctx, TypeResolutionFlags flags = TypeResolutionFlags(0)) { - auto *TyR = new (tc.Context) SimpleIdentTypeRepr(loc, ident); + auto *TyR = new (Ctx) SimpleIdentTypeRepr(loc, ident); TypeLoc typeLoc = TypeLoc(TyR); TypeResolutionOptions options = TypeResolverContext::TypeAliasDecl; options |= flags; - if (TypeChecker::validateType(tc.Context, typeLoc, + if (TypeChecker::validateType(Ctx, typeLoc, TypeResolution::forInterface(DC), options)) return nullptr; @@ -1845,7 +1847,7 @@ static NominalTypeDecl *resolveSingleNominalTypeDecl( static bool checkDesignatedTypes(OperatorDecl *OD, ArrayRef identifiers, ArrayRef identifierLocs, - TypeChecker &TC) { + ASTContext &ctx) { assert(identifiers.size() == identifierLocs.size()); SmallVector designatedNominalTypes; @@ -1853,7 +1855,7 @@ static bool checkDesignatedTypes(OperatorDecl *OD, for (auto index : indices(identifiers)) { auto *decl = resolveSingleNominalTypeDecl(DC, identifierLocs[index], - identifiers[index], TC); + identifiers[index], ctx); if (!decl) return true; @@ -1861,7 +1863,6 @@ static bool checkDesignatedTypes(OperatorDecl *OD, designatedNominalTypes.push_back(decl); } - auto &ctx = TC.Context; OD->setDesignatedNominalTypes(ctx.AllocateCopy(designatedNominalTypes)); return false; } diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 8b93ac9a6be8d..aa3c557fc6575 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -124,64 +124,64 @@ Expr *TypeChecker::substituteInputSugarTypeForResult(ApplyExpr *E) { return E; } -/// Look up the builtin precedence group with the given name. -static PrecedenceGroupDecl * -getBuiltinPrecedenceGroup(TypeChecker &TC, DeclContext *DC, Identifier name, - SourceLoc loc) { - auto group = TC.lookupPrecedenceGroup(DC, name, - /*suppress diags*/ SourceLoc()); - if (!group) { - TC.diagnose(loc, diag::missing_builtin_precedence_group, name); - } - return group; -} - -static PrecedenceGroupDecl * -lookupPrecedenceGroupForOperator(TypeChecker &TC, DeclContext *DC, - Identifier name, SourceLoc loc) { +static PrecedenceGroupDecl *lookupPrecedenceGroupForOperator(DeclContext *DC, + Identifier name, + SourceLoc loc) { SourceFile *SF = DC->getParentSourceFile(); bool isCascading = DC->isCascadingContextForLookup(true); if (auto op = SF->lookupInfixOperator(name, isCascading, loc)) { - TC.validateDecl(op); return op->getPrecedenceGroup(); } else { - TC.diagnose(loc, diag::unknown_binop); + DC->getASTContext().Diags.diagnose(loc, diag::unknown_binop); } return nullptr; } PrecedenceGroupDecl * TypeChecker::lookupPrecedenceGroupForInfixOperator(DeclContext *DC, Expr *E) { + /// Look up the builtin precedence group with the given name. + + auto getBuiltinPrecedenceGroup = [](DeclContext *DC, Identifier name, + SourceLoc loc) { + auto group = TypeChecker::lookupPrecedenceGroup(DC, name, loc); + if (!group) { + DC->getASTContext().Diags.diagnose( + loc, diag::missing_builtin_precedence_group, name); + } + return group; + }; + + auto &Context = DC->getASTContext(); if (auto ifExpr = dyn_cast(E)) { // Ternary has fixed precedence. - return getBuiltinPrecedenceGroup(*this, DC, Context.Id_TernaryPrecedence, + return getBuiltinPrecedenceGroup(DC, Context.Id_TernaryPrecedence, ifExpr->getQuestionLoc()); } if (auto assignExpr = dyn_cast(E)) { // Assignment has fixed precedence. - return getBuiltinPrecedenceGroup(*this, DC, Context.Id_AssignmentPrecedence, + return getBuiltinPrecedenceGroup(DC, Context.Id_AssignmentPrecedence, assignExpr->getEqualLoc()); } if (auto castExpr = dyn_cast(E)) { // 'as' and 'is' casts have fixed precedence. - return getBuiltinPrecedenceGroup(*this, DC, Context.Id_CastingPrecedence, + return getBuiltinPrecedenceGroup(DC, Context.Id_CastingPrecedence, castExpr->getAsLoc()); } if (auto *DRE = dyn_cast(E)) { Identifier name = DRE->getDecl()->getBaseName().getIdentifier(); - return lookupPrecedenceGroupForOperator(*this, DC, name, DRE->getLoc()); + return lookupPrecedenceGroupForOperator(DC, name, DRE->getLoc()); } if (auto *OO = dyn_cast(E)) { Identifier name = OO->getDecls()[0]->getBaseName().getIdentifier(); - return lookupPrecedenceGroupForOperator(*this, DC, name, OO->getLoc()); + return lookupPrecedenceGroupForOperator(DC, name, OO->getLoc()); } if (auto arrowExpr = dyn_cast(E)) { - return getBuiltinPrecedenceGroup(*this, DC, + return getBuiltinPrecedenceGroup(DC, Context.Id_FunctionArrowPrecedence, arrowExpr->getArrowLoc()); } @@ -198,13 +198,13 @@ TypeChecker::lookupPrecedenceGroupForInfixOperator(DeclContext *DC, Expr *E) { if (auto *MRE = dyn_cast(E)) { Identifier name = MRE->getDecl().getDecl()->getBaseName().getIdentifier(); - return lookupPrecedenceGroupForOperator(*this, DC, name, MRE->getLoc()); + return lookupPrecedenceGroupForOperator(DC, name, MRE->getLoc()); } // If E is already an ErrorExpr, then we've diagnosed it as invalid already, // otherwise emit an error. if (!isa(E)) - diagnose(E->getLoc(), diag::unknown_binop); + Context.Diags.diagnose(E->getLoc(), diag::unknown_binop); return nullptr; } @@ -217,7 +217,7 @@ TypeChecker::lookupPrecedenceGroupForInfixOperator(DeclContext *DC, Expr *E) { /// 'findLHS(DC, expr, "<<")' returns 'B'. /// 'findLHS(DC, expr, '==')' returns nullptr. Expr *TypeChecker::findLHS(DeclContext *DC, Expr *E, Identifier name) { - auto right = lookupPrecedenceGroupForOperator(*this, DC, name, E->getEndLoc()); + auto right = lookupPrecedenceGroupForOperator(DC, name, E->getEndLoc()); if (!right) return nullptr; @@ -445,7 +445,7 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, Expr *op = S[0]; // If the operator's precedence is lower than the minimum, stop here. - auto opPrecedence = TC.lookupPrecedenceGroupForInfixOperator(DC, op); + auto opPrecedence = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, op); if (!precedenceBound.shouldConsider(TC, opPrecedence)) return {nullptr, nullptr}; return {op, opPrecedence}; @@ -476,7 +476,7 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, } // Pull out the next binary operator. - Op op2 = { S[0], TC.lookupPrecedenceGroupForInfixOperator(DC, S[0]) }; + Op op2{ S[0], TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, S[0]) }; // If the second operator's precedence is lower than the // precedence bound, break out of the loop. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 7a7285ba62933..df542203004cd 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1635,11 +1635,12 @@ class TypeChecker final : public LazyResolver { /// Given an expression that's known to be an infix operator, /// look up its precedence group. - PrecedenceGroupDecl * + static PrecedenceGroupDecl * lookupPrecedenceGroupForInfixOperator(DeclContext *dc, Expr *op); - PrecedenceGroupDecl *lookupPrecedenceGroup(DeclContext *dc, Identifier name, - SourceLoc nameLoc); + static PrecedenceGroupDecl *lookupPrecedenceGroup(DeclContext *dc, + Identifier name, + SourceLoc nameLoc); /// Given an pre-folded expression, find LHS from the expression if a binary /// operator \c name appended to the expression. From 1cce12f20c283d980d017f12324eb42b6f9bf298 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 24 Sep 2019 13:54:41 -0700 Subject: [PATCH 063/140] Add an Array-based NSMutableArray subclass --- stdlib/public/core/BridgeObjectiveC.swift | 18 +- stdlib/public/core/Runtime.swift.gyb | 12 ++ stdlib/public/core/SwiftNativeNSArray.swift | 196 +++++++++++++++++- .../public/stubs/SwiftNativeNSXXXBase.mm.gyb | 4 +- ...dlib-abi.asserts.additional.swift.expected | 2 + .../SwiftNativeNSBase/SwiftNativeNSBase.m | 1 + 6 files changed, 224 insertions(+), 9 deletions(-) diff --git a/stdlib/public/core/BridgeObjectiveC.swift b/stdlib/public/core/BridgeObjectiveC.swift index ee4cd366e137b..654bc0fdd84ac 100644 --- a/stdlib/public/core/BridgeObjectiveC.swift +++ b/stdlib/public/core/BridgeObjectiveC.swift @@ -85,10 +85,7 @@ public protocol _ObjectiveCBridgeable { #if _runtime(_ObjC) -@available(macOS, introduced: 9999, deprecated) -@available(iOS, introduced: 9999, deprecated) -@available(watchOS, introduced: 9999, deprecated) -@available(tvOS, introduced: 9999, deprecated) +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) @available(*, deprecated) @_cdecl("_SwiftCreateBridgedArray") @usableFromInline @@ -101,6 +98,19 @@ internal func _SwiftCreateBridgedArray( return Unmanaged.passRetained(bridged) } +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) +@available(*, deprecated) +@_cdecl("_SwiftCreateBridgedMutableArray") +@usableFromInline +internal func _SwiftCreateBridgedMutableArray( + values: UnsafePointer, + numValues: Int +) -> Unmanaged { + let bufPtr = UnsafeBufferPointer(start: values, count: numValues) + let bridged = _SwiftNSMutableArray(Array(bufPtr)) + return Unmanaged.passRetained(bridged) +} + @_silgen_name("swift_stdlib_connectNSBaseClasses") internal func _connectNSBaseClasses() -> Bool diff --git a/stdlib/public/core/Runtime.swift.gyb b/stdlib/public/core/Runtime.swift.gyb index 8248898c5a2d9..90788227a8541 100644 --- a/stdlib/public/core/Runtime.swift.gyb +++ b/stdlib/public/core/Runtime.swift.gyb @@ -276,6 +276,18 @@ internal class __SwiftNativeNSArray { deinit {} } +@_fixed_layout +@usableFromInline +@objc @_swift_native_objc_runtime_base(__SwiftNativeNSMutableArrayBase) +internal class _SwiftNativeNSMutableArray { + @inlinable + @nonobjc + internal init() {} +// @objc public init(coder: AnyObject) {} + @inlinable + deinit {} +} + @_fixed_layout @usableFromInline @objc @_swift_native_objc_runtime_base(__SwiftNativeNSDictionaryBase) diff --git a/stdlib/public/core/SwiftNativeNSArray.swift b/stdlib/public/core/SwiftNativeNSArray.swift index a7b7715e85b8d..79ce494d5aea5 100644 --- a/stdlib/public/core/SwiftNativeNSArray.swift +++ b/stdlib/public/core/SwiftNativeNSArray.swift @@ -61,14 +61,16 @@ internal class __SwiftNativeNSArrayWithContiguousStorage } } +private let NSNotFound: Int = .max + // Implement the APIs required by NSArray extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { @objc internal var count: Int { return withUnsafeBufferOfObjects { $0.count } } - @objc(objectAtIndex:) - internal func objectAt(_ index: Int) -> AnyObject { + @inline(__always) + @nonobjc private func _objectAt(_ index: Int) -> AnyObject { return withUnsafeBufferOfObjects { objects in _precondition( @@ -77,6 +79,16 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { return objects[index] } } + + @objc(objectAtIndexedSubscript:) + dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject { + return _objectAt(index) + } + + @objc(objectAtIndex:) + dynamic internal func objectAt(_ index: Int) -> AnyObject { + return _objectAt(index) + } @objc internal func getObjects( _ aBuffer: UnsafeMutablePointer, range: _SwiftNSRange @@ -131,6 +143,172 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { } } +@_fixed_layout +@usableFromInline +@objc internal final class _SwiftNSMutableArray : + _SwiftNativeNSMutableArray, _NSArrayCore +{ + internal var contents: [AnyObject] + + internal init(_ array: [AnyObject]) { + contents = array + super.init() + } + + @objc internal var count: Int { + return contents.count + } + + @objc(objectAtIndexedSubscript:) + dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject { + //TODO: exception instead of precondition, once that's possible + return contents[index] + } + + @objc(objectAtIndex:) + dynamic internal func objectAt(_ index: Int) -> AnyObject { + //TODO: exception instead of precondition, once that's possible + return contents[index] + } + + @objc internal func getObjects( + _ aBuffer: UnsafeMutablePointer, range: _SwiftNSRange + ) { + return contents.withContiguousStorageIfAvailable { objects in + //TODO: exceptions instead of preconditions, once that's possible + + _precondition( + _isValidArrayIndex(range.location, count: objects.count), + "Array index out of range") + + _precondition( + _isValidArrayIndex( + range.location + range.length, count: objects.count), + "Array index out of range") + + if objects.isEmpty { return } + + // These objects are "returned" at +0, so treat them as pointer values to + // avoid retains. Copy bytes via a raw pointer to circumvent reference + // counting while correctly aliasing with all other pointer types. + UnsafeMutableRawPointer(aBuffer).copyMemory( + from: objects.baseAddress! + range.location, + byteCount: range.length * MemoryLayout.stride) + }! + } + + @objc(countByEnumeratingWithState:objects:count:) + internal func countByEnumerating( + with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, + objects: UnsafeMutablePointer?, count: Int + ) -> Int { + var enumerationState = state.pointee + + if enumerationState.state != 0 { + return 0 + } + + return contents.withContiguousStorageIfAvailable { + objects in + enumerationState.mutationsPtr = _fastEnumerationStorageMutationsPtr + enumerationState.itemsPtr = + AutoreleasingUnsafeMutablePointer(objects.baseAddress) + enumerationState.state = 1 + state.pointee = enumerationState + return objects.count + }! + } + + @objc(copyWithZone:) + dynamic internal func copy(with _: _SwiftNSZone?) -> AnyObject { + return contents._bridgeToObjectiveCImpl() + } + + @objc(insertObject:atIndex:) + dynamic internal func insert(_ anObject: AnyObject, at index: Int) { + contents.insert(anObject, at: index) + } + + @objc(removeObjectAtIndex:) + dynamic internal func removeObject(at index: Int) { + contents.remove(at: index) + } + + @objc(addObject:) + dynamic internal func add(_ anObject: AnyObject) { + contents.append(anObject) + } + + @objc(removeLastObject) + dynamic internal func removeLastObject() { + contents.removeLast() + } + + @objc(replaceObjectAtIndex:withObject:) + dynamic internal func replaceObject(at index: Int, with anObject: AnyObject) { + //enforces bounds, unlike set equivalent, which can append + contents[index] = anObject + } + + //Non-core methods overridden for performance + + @objc(exchangeObjectAtIndex:withObjectAtIndex:) + dynamic internal func exchange(at index: Int, with index2: Int) { + swap(&contents[index], &contents[index2]) + } + + @objc(replaceObjectsInRange:withObjects:count:) + dynamic internal func replaceObjects(in range: _SwiftNSRange, + with objects: UnsafePointer, + count: Int) { + let range = range.location ..< range.location + range.length + let buf = UnsafeBufferPointer(start: objects, count: count) + contents.replaceSubrange(range, with: buf) + } + + @objc(insertObjects:count:atIndex:) + dynamic internal func insertObjects(_ objects: UnsafePointer, + count: Int, + at index: Int) { + let buf = UnsafeBufferPointer(start: objects, count: count) + contents.insert(contentsOf: buf, at: index) + } + + @objc(indexOfObjectIdenticalTo:) + dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int { + return contents.firstIndex { $0 === object } ?? NSNotFound + } + + @objc(removeObjectsInRange:) + dynamic internal func removeObjects(in range: _SwiftNSRange) { + let range = range.location ..< range.location + range.length + contents.replaceSubrange(range, with: []) + } + + @objc(removeAllObjects) + dynamic internal func removeAllObjects() { + contents = [] + } + + @objc(setObject:atIndex:) + dynamic internal func setObject(_ anObject: AnyObject, at index: Int) { + if index == contents.count { + contents.append(anObject) + } else { + contents[index] = anObject + } + } + + @objc(setObject:atIndexedSubscript:) dynamic + internal func setObjectSubscript(_ anObject: AnyObject, at index: Int) { + if index == contents.count { + contents.append(anObject) + } else { + contents[index] = anObject + } + } +} + /// An `NSArray` whose contiguous storage is created and filled, upon /// first access, by bridging the elements of a Swift `Array`. /// @@ -292,6 +470,18 @@ internal class __ContiguousArrayStorageBase _internalInvariantFailure( "Concrete subclasses must implement _getNonVerbatimBridgingBuffer") } + + @objc(mutableCopyWithZone:) + dynamic internal func mutableCopy(with _: _SwiftNSZone?) -> AnyObject { + let arr = Array(_ContiguousArrayBuffer(self)) + return _SwiftNSMutableArray(arr) + } + + @objc(indexOfObjectIdenticalTo:) + dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int { + let arr = Array(_ContiguousArrayBuffer(self)) + return arr.firstIndex { $0 === object } ?? NSNotFound + } #endif @inlinable @@ -306,7 +496,7 @@ internal class __ContiguousArrayStorageBase _internalInvariantFailure( "Concrete subclasses must implement staticElementType") } - + @inlinable deinit { _internalInvariant( diff --git a/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb b/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb index cc711eb44e28f..9016bb1c97d2b 100644 --- a/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb +++ b/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb @@ -50,7 +50,7 @@ using namespace swift; // NOTE: older runtimes called these _SwiftNativeNSXXXBase. The two must // coexist, so these were renamed. The old names must not be used in the new // runtime. -% for Class in ('Array', 'Dictionary', 'Set', 'String', 'Enumerator'): +% for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'): SWIFT_RUNTIME_STDLIB_API @interface __SwiftNativeNS${Class}Base : NSObject { @@ -120,7 +120,7 @@ swift_stdlib_NSObject_isEqual(id lhs, SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI bool swift_stdlib_connectNSBaseClasses() { -% for Class in ('Array', 'Dictionary', 'Set', 'String', 'Enumerator'): +% for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'): Class NS${Class}Super = objc_lookUpClass("NS${Class}"); if (!NS${Class}Super) return false; Class NS${Class}OurClass = objc_lookUpClass("__SwiftNativeNS${Class}Base"); diff --git a/test/api-digester/Outputs/stability-stdlib-abi.asserts.additional.swift.expected b/test/api-digester/Outputs/stability-stdlib-abi.asserts.additional.swift.expected index 04ccbba339084..2893ee31fcbdc 100644 --- a/test/api-digester/Outputs/stability-stdlib-abi.asserts.additional.swift.expected +++ b/test/api-digester/Outputs/stability-stdlib-abi.asserts.additional.swift.expected @@ -1,3 +1,5 @@ +Class _SwiftNSMutableArray is a new API without @available attribute +Class _SwiftNativeNSMutableArray is a new API without @available attribute Func _collectReferencesInsideObject(_:) is a new API without @available attribute Func _loadDestroyTLSCounter() is a new API without @available attribute Protocol _RuntimeFunctionCountersStats is a new API without @available attribute diff --git a/test/stdlib/Inputs/SwiftNativeNSBase/SwiftNativeNSBase.m b/test/stdlib/Inputs/SwiftNativeNSBase/SwiftNativeNSBase.m index 85afa78ed9f99..175443aac95cd 100644 --- a/test/stdlib/Inputs/SwiftNativeNSBase/SwiftNativeNSBase.m +++ b/test/stdlib/Inputs/SwiftNativeNSBase/SwiftNativeNSBase.m @@ -65,6 +65,7 @@ BOOL TestSwiftNativeNSBase_UnwantedCdtors() NSMutableSet *expectedClasses = [NSMutableSet setWithObjects: @"__SwiftNativeNSArrayBase", + @"__SwiftNativeNSMutableArrayBase", @"__SwiftNativeNSDictionaryBase", @"__SwiftNativeNSSetBase", @"__SwiftNativeNSStringBase", From 8f96e8f463d409338131cfb3289a0a6680cb2b2e Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Tue, 24 Sep 2019 18:16:32 -0700 Subject: [PATCH 064/140] [Constant Evaluator][Test] Add a couple of tests to check whether the evaluator can detect fatal errors at compile time. --- .../ConstantEvaluableSubsetChecker.cpp | 18 ++++++++++--- .../constant_evaluable_subset_test.swift | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp index 54ba6d88c4057..d12e9b016a2e8 100644 --- a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp +++ b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp @@ -74,10 +74,6 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { if (isa(inst)) break; - assert(!previousEvaluationHadFatalError && - "cannot continue evaluation of test driver as previous call " - "resulted in non-skippable evaluation error."); - auto *applyInst = dyn_cast(inst); SILFunction *callee = nullptr; if (applyInst) { @@ -89,6 +85,16 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { if (!applyInst || !callee || !callee->hasSemanticsAttr(constantEvaluableSemanticsAttr)) { + + // Ignore these instructions if we had a fatal error already. + if (previousEvaluationHadFatalError) { + if (isa(inst)) { + assert(false && "non-constant control flow in the test driver"); + } + ++currI; + continue; + } + std::tie(nextInstOpt, errorVal) = stepEvaluator.tryEvaluateOrElseMakeEffectsNonConstant(currI); if (!nextInstOpt) { @@ -100,6 +106,10 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { continue; } + assert(!previousEvaluationHadFatalError && + "cannot continue evaluation of test driver as previous call " + "resulted in non-skippable evaluation error."); + // Here, a function annotated as "constant_evaluable" is called. llvm::errs() << "@" << demangleSymbolName(callee->getName()) << "\n"; std::tie(nextInstOpt, errorVal) = diff --git a/test/SILOptimizer/constant_evaluable_subset_test.swift b/test/SILOptimizer/constant_evaluable_subset_test.swift index dd1dff4b390f6..2fa791f0804aa 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test.swift @@ -350,6 +350,19 @@ func interpretDivideByZero() -> Int { return testDivideByZero(127, 0) } +// CHECK-LABEL: @testDividingFullWidthByZero +// CHECK: error: not constant evaluable +@_semantics("constant_evaluable") +func testDividingFullWidthByZero(_ x: Int, _ y: Int, _ z: UInt) -> Int { + return x.dividingFullWidth((y, z)).1 +} // CHECK: note: {{.*}}: Division by zero + // CHECK: note: operation performed during this call traps + +@_semantics("test_driver") +func interpretDividingFullWidthByZero() -> Int { + return testDividingFullWidthByZero(0, 1, 1) +} + // CHECK-LABEL: @testDivideOverflow // CHECK: error: not constant evaluable @_semantics("constant_evaluable") @@ -364,6 +377,20 @@ func interpretDivideOverflow() -> Int8 { return testDivideOverflow(-128, -1) } +// CHECK-LABEL: @testDistance +// CHECK: error: not constant evaluable +@_semantics("constant_evaluable") +func testDistance(_ x: UInt, _ y: UInt) -> Int { + return x.distance(to: y) + // CHECK: note: {{.*}}: Distance is not representable in Int + // CHECK: note: operation performed during this call traps +} + +@_semantics("test_driver") +func interpretDistanceTest() -> Int { + return testDistance(0, UInt.max) +} + // CHECK-LABEL: @testInOut // CHECK-NOT: error: @_semantics("constant_evaluable") From 80e646642f5672506397ee28fcecf2a1374c6042 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 24 Sep 2019 18:39:03 -0700 Subject: [PATCH 065/140] [ClangImporter] importMethodType -> importMethodParamsAndReturnType (#27349) And similar for importFunctionParamsAndReturnType and importAccessorParamsAndReturnType. In all cases the return type isn't a FunctionType, and there's also a ParameterList out-parameter. No functionality change. --- lib/ClangImporter/ImportDecl.cpp | 11 ++++---- lib/ClangImporter/ImportType.cpp | 8 +++--- lib/ClangImporter/ImporterImpl.h | 45 ++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index fb3eecc5b6a24..8c30866260929 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3724,7 +3724,7 @@ namespace { } else { // Import the function type. If we have parameters, make sure their // names get into the resulting function type. - importedType = Impl.importFunctionType( + importedType = Impl.importFunctionParamsAndReturnType( dc, decl, {decl->param_begin(), decl->param_size()}, decl->isVariadic(), isInSystemModule(dc), name, bodyParams); @@ -4288,10 +4288,11 @@ namespace { prop->getSetterMethodDecl() != decl) return nullptr; importedType = - Impl.importAccessorMethodType(dc, prop, decl, isInSystemModule(dc), - importedName, &bodyParams); + Impl.importAccessorParamsAndReturnType(dc, prop, decl, + isInSystemModule(dc), + importedName, &bodyParams); } else { - importedType = Impl.importMethodType( + importedType = Impl.importMethodParamsAndReturnType( dc, decl, decl->parameters(), decl->isVariadic(), isInSystemModule(dc), &bodyParams, importedName, errorConvention, kind); @@ -6252,7 +6253,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( // Import the type that this method will have. Optional errorConvention; ParameterList *bodyParams; - auto importedType = Impl.importMethodType( + auto importedType = Impl.importMethodParamsAndReturnType( dc, objcMethod, args, variadic, isInSystemModule(dc), &bodyParams, importedName, errorConvention, SpecialMethodKind::Constructor); if (!importedType) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index a3feabe58fc2e..3f5dd28ce2129 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1579,7 +1579,7 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( OptionalityOfReturn); } -ImportedType ClangImporter::Implementation::importFunctionType( +ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, bool isFromSystemModule, DeclName name, ParameterList *¶meterList) { @@ -1871,7 +1871,7 @@ static Type mapGenericArgs(const DeclContext *fromDC, return type.subst(subs); } -ImportedType ClangImporter::Implementation::importMethodType( +ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( const DeclContext *dc, const clang::ObjCMethodDecl *clangDecl, ArrayRef params, bool isVariadic, bool isFromSystemModule, ParameterList **bodyParams, @@ -2165,7 +2165,7 @@ ImportedType ClangImporter::Implementation::importMethodType( importedType.isImplicitlyUnwrapped()}; } -ImportedType ClangImporter::Implementation::importAccessorMethodType( +ImportedType ClangImporter::Implementation::importAccessorParamsAndReturnType( const DeclContext *dc, const clang::ObjCPropertyDecl *property, const clang::ObjCMethodDecl *clangDecl, bool isFromSystemModule, ImportedName functionName, swift::ParameterList **params) { @@ -2184,7 +2184,7 @@ ImportedType ClangImporter::Implementation::importAccessorMethodType( // The member was defined in 'origDC', but is being imported into 'dc'. // 'dc' must be a subclass or a type conforming to protocol. - // FIXME: Duplicated from importMethodType. + // FIXME: Duplicated from importMethodParamsAndReturnType. DeclContext *origDC = importDeclContextOf(property, property->getDeclContext()); assert(origDC); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 5e5fc64330657..2a2218b0f9612 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1038,11 +1038,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// to system APIs. /// \param name The name of the function. /// \param[out] parameterList The parameters visible inside the function body. - ImportedType importFunctionType(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool isFromSystemModule, - DeclName name, ParameterList *¶meterList); + ImportedType + importFunctionParamsAndReturnType(DeclContext *dc, + const clang::FunctionDecl *clangDecl, + ArrayRef params, + bool isVariadic, bool isFromSystemModule, + DeclName name, + ParameterList *¶meterList); /// Import the given function return type. /// @@ -1093,7 +1095,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// the return type of this method. /// /// Note that this is not appropriate to use for property accessor methods. - /// Use #importAccessorMethodType instead. + /// Use #importAccessorParamsAndReturnType instead. /// /// \param dc The context the method is being imported into. /// \param clangDecl The underlying declaration. @@ -1104,20 +1106,22 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// to system APIs. /// \param[out] bodyParams The patterns visible inside the function body. /// \param importedName How to import the name of the method. - /// \param[out] errorConvention Whether and how the method throws NSErrors. + /// \param[out] errorConv Whether and how the method throws NSErrors. /// \param kind Controls whether we're building a type for a method that /// needs special handling. /// /// \returns the imported result type, or null if the type cannot be /// imported. ImportedType - importMethodType(const DeclContext *dc, - const clang::ObjCMethodDecl *clangDecl, - ArrayRef params, bool isVariadic, - bool isFromSystemModule, ParameterList **bodyParams, - importer::ImportedName importedName, - Optional &errorConvention, - SpecialMethodKind kind); + importMethodParamsAndReturnType(const DeclContext *dc, + const clang::ObjCMethodDecl *clangDecl, + ArrayRef params, + bool isVariadic, + bool isFromSystemModule, + ParameterList **bodyParams, + importer::ImportedName importedName, + Optional &errorConv, + SpecialMethodKind kind); /// Import the type of an Objective-C method that will be imported as an /// accessor for \p property. @@ -1137,12 +1141,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// \returns the imported result type, or null if the type cannot be /// imported. - ImportedType importAccessorMethodType(const DeclContext *dc, - const clang::ObjCPropertyDecl *property, - const clang::ObjCMethodDecl *clangDecl, - bool isFromSystemModule, - importer::ImportedName importedName, - ParameterList **params); + ImportedType + importAccessorParamsAndReturnType(const DeclContext *dc, + const clang::ObjCPropertyDecl *property, + const clang::ObjCMethodDecl *clangDecl, + bool isFromSystemModule, + importer::ImportedName importedName, + ParameterList **params); /// Determine whether the given typedef-name is "special", meaning /// that it has performed some non-trivial mapping of its underlying type From ccd534d139d6170548f617aa61ca6bc3b9a1aeeb Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 24 Sep 2019 18:48:29 -0700 Subject: [PATCH 066/140] [TBD] Ensure swift-abi-version is kept in sync with IRGen (#27347) The TAPI_SWIFT_ABI_VERSION macro was never updated in sync with IRGen::swiftVersion, so just expose that value through irgen::getSwiftABIVersion() and use it in TBDGen. Fixes rdar://55643763 --- include/swift/IRGen/IRGenPublic.h | 3 +++ include/swift/TBDGen/TBDGen.h | 3 --- lib/IRGen/IRGenModule.cpp | 5 +++++ lib/TBDGen/TBDGen.cpp | 3 ++- test/TBD/abi-version.swift | 21 +++++++++++++++++++++ 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 test/TBD/abi-version.swift diff --git a/include/swift/IRGen/IRGenPublic.h b/include/swift/IRGen/IRGenPublic.h index a5857a6995379..3d1c11c487787 100644 --- a/include/swift/IRGen/IRGenPublic.h +++ b/include/swift/IRGen/IRGenPublic.h @@ -36,6 +36,9 @@ createIRGenModule(SILModule *SILMod, StringRef OutputFilename, /// Delete the IRGenModule and IRGenerator obtained by the above call. void deleteIRGenModule(std::pair &Module); +/// Gets the ABI version number that'll be set as a flag in the module. +uint32_t getSwiftABIVersion(); + } // end namespace irgen } // end namespace swift diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index 7419e60d3c469..363109c5964d6 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -24,9 +24,6 @@ namespace swift { class FileUnit; class ModuleDecl; -/// The current ABI version of Swift, as tapi labels it. -const uint8_t TAPI_SWIFT_ABI_VERSION = 5; - /// Options for controlling the exact set of symbols included in the TBD /// output. struct TBDGenOptions { diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index ebe565371ee50..042aad6265643 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -22,6 +22,7 @@ #include "swift/Basic/Dwarf.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/ClangImporter/ClangImporter.h" +#include "swift/IRGen/IRGenPublic.h" #include "swift/IRGen/Linking.h" #include "swift/Runtime/RuntimeFnWrappersGen.h" #include "swift/Runtime/Config.h" @@ -1355,6 +1356,10 @@ IRGenModule *IRGenerator::getGenModule(SILFunction *f) { return getPrimaryIGM(); } +uint32_t swift::irgen::getSwiftABIVersion() { + return IRGenModule::swiftVersion; +} + llvm::Triple IRGenerator::getEffectiveClangTriple() { auto CI = static_cast( &*SIL.getASTContext().getClangModuleLoader()); diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 3104ddf629004..f2a61de409409 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/Basic/LLVM.h" +#include "swift/IRGen/IRGenPublic.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/SILDeclRef.h" @@ -609,7 +610,7 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, file.setCurrentVersion(convertToPacked(opts.CurrentVersion)); file.setCompatibilityVersion(convertToPacked(opts.CompatibilityVersion)); file.setTwoLevelNamespace(); - file.setSwiftABIVersion(TAPI_SWIFT_ABI_VERSION); + file.setSwiftABIVersion(irgen::getSwiftABIVersion()); file.setPlatform(tapi::internal::mapToSinglePlatform(target)); auto arch = tapi::internal::getArchType(target.getArchName()); file.setArch(arch); diff --git a/test/TBD/abi-version.swift b/test/TBD/abi-version.swift new file mode 100644 index 0000000000000..60ec951b802c9 --- /dev/null +++ b/test/TBD/abi-version.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) + +// This test ensures that we see the same Swift ABI Version flag in the LLVM IR +// and in the TBD. + +// 1. Emit IR and a TBD for this file + +// RUN: %target-swift-frontend -emit-ir -o %t/test.ll %s -emit-tbd-path %t/test.tbd + +// 2. Concatenate them and FileCheck them both in the same file, so we can capture +// the ABI version in a variable. + +// RUN: cat %t/test.ll %t/test.tbd | %FileCheck %s + +// 3. Look in the IR for the Swift Version flag + +// CHECK: !"Swift Version", i32 [[VERSION:[0-9]+]] + +// 4. Look in the TBD for the same version listed + +// CHECK: swift-abi-version: [[VERSION]] From 88bc5b8bf5bfeb135c9b19b8a79e73b5d0811019 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 24 Sep 2019 19:29:33 -0700 Subject: [PATCH 067/140] [ClangImporter] Detangle an index-based loop into two for-each loops (#27348) No functionality change. --- lib/ClangImporter/ImportType.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 3f5dd28ce2129..a2261024629eb 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -870,20 +870,23 @@ namespace { unsigned typeParamCount = imported->getGenericParams()->size(); auto typeArgs = type->getObjectType()->getTypeArgs(); assert(typeArgs.empty() || typeArgs.size() == typeParamCount); - llvm::SmallVector importedTypeArgs; - for (unsigned i = 0; i < typeParamCount; i++) { - Type importedTypeArg; - auto typeParam = imported->getGenericParams()->getParams()[i]; - if (!typeArgs.empty()) { - auto subresult = Visit(typeArgs[i]); - if (!subresult) { + SmallVector importedTypeArgs; + importedTypeArgs.reserve(typeParamCount); + if (!typeArgs.empty()) { + for (auto typeArg : typeArgs) { + Type importedTypeArg = Visit(typeArg).AbstractType; + if (!importedTypeArg) return nullptr; + importedTypeArgs.push_back(importedTypeArg); + } + } else { + for (auto typeParam : imported->getGenericParams()->getParams()) { + if (typeParam->getSuperclass() && + typeParam->getConformingProtocols().empty()) { + importedTypeArgs.push_back(typeParam->getSuperclass()); + continue; } - importedTypeArg = subresult.AbstractType; - } else if (typeParam->getSuperclass() && - typeParam->getConformingProtocols().empty()) { - importedTypeArg = typeParam->getSuperclass(); - } else { + SmallVector memberTypes; if (auto superclassType = typeParam->getSuperclass()) @@ -896,11 +899,11 @@ namespace { if (memberTypes.empty()) hasExplicitAnyObject = true; - importedTypeArg = ProtocolCompositionType::get( + Type importedTypeArg = ProtocolCompositionType::get( Impl.SwiftContext, memberTypes, hasExplicitAnyObject); + importedTypeArgs.push_back(importedTypeArg); } - importedTypeArgs.push_back(importedTypeArg); } assert(importedTypeArgs.size() == typeParamCount); importedType = BoundGenericClassType::get( From 645fc20df72623e34a617c869cb268cc2bcf662d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 24 Sep 2019 18:39:10 -0400 Subject: [PATCH 068/140] AST: TypeAliasDecl::get{Structural,Underlying}Type() always return non-empty Type Return an ErrorType on circularity instead of Type(). --- lib/AST/Decl.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8db92f43a4c2a..d9d6b163aeee8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3551,9 +3551,10 @@ void TypeAliasDecl::computeType() { } Type TypeAliasDecl::getUnderlyingType() const { - return evaluateOrDefault(getASTContext().evaluator, + auto &ctx = getASTContext(); + return evaluateOrDefault(ctx.evaluator, UnderlyingTypeRequest{const_cast(this)}, - Type()); + ErrorType::get(ctx)); } void TypeAliasDecl::setUnderlyingType(Type underlying) { @@ -3583,10 +3584,11 @@ UnboundGenericType *TypeAliasDecl::getUnboundGenericType() const { } Type TypeAliasDecl::getStructuralType() const { - auto &context = getASTContext(); + auto &ctx = getASTContext(); return evaluateOrDefault( - context.evaluator, - StructuralTypeRequest{const_cast(this)}, Type()); + ctx.evaluator, + StructuralTypeRequest{const_cast(this)}, + ErrorType::get(ctx)); } Type AbstractTypeParamDecl::getSuperclass() const { From edd5105452abaf702e176c261f018f101d6c345e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 20 Sep 2019 19:02:34 -0400 Subject: [PATCH 069/140] AST: Clean up usages of TypeAliasDecl::getStructuralType() This also gets rid of a usage of TypeAliasType::get(). --- lib/AST/GenericSignatureBuilder.cpp | 47 ++++++++++------------- lib/Sema/TypeCheckType.cpp | 14 +++---- test/Generics/requirement_inference.swift | 2 +- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 05a7f3e5229c1..c45eda95bae47 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -3784,16 +3784,17 @@ PotentialArchetype *GenericSignatureBuilder::realizePotentialArchetype( return pa; } -static Type getStructuralType(TypeDecl *typeDecl) { +static Type getStructuralType(TypeDecl *typeDecl, bool keepSugar) { if (auto typealias = dyn_cast(typeDecl)) { - // When we're computing requirement signatures, the structural type - // suffices. Otherwise we'll potentially try to validate incomplete - // requirements. - auto *proto = dyn_cast_or_null( - typealias->getDeclContext()->getAsDecl()); - if (proto && proto->isComputingRequirementSignature()) - return typealias->getStructuralType(); - return typealias->getUnderlyingType(); + if (typealias->getUnderlyingTypeRepr() != nullptr) { + auto type = typealias->getStructuralType(); + if (!keepSugar) + if (auto *aliasTy = cast(type.getPointer())) + return aliasTy->getSinglyDesugaredType(); + return type; + } + if (!keepSugar) + return typealias->getUnderlyingType(); } return typeDecl->getDeclaredInterfaceType(); @@ -3804,43 +3805,35 @@ static Type substituteConcreteType(GenericSignatureBuilder &builder, TypeDecl *concreteDecl) { assert(concreteDecl); - auto *proto = concreteDecl->getDeclContext()->getSelfProtocolDecl(); + auto *dc = concreteDecl->getDeclContext(); + auto *proto = dc->getSelfProtocolDecl(); // Form an unsubstituted type referring to the given type declaration, // for use in an inferred same-type requirement. - auto type = getStructuralType(concreteDecl); + auto type = getStructuralType(concreteDecl, /*keepSugar=*/true); if (!type) return Type(); - Type parentType; SubstitutionMap subMap; if (proto) { // Substitute in the type of the current PotentialArchetype in // place of 'Self' here. - parentType = basePA->getDependentType(builder.getGenericParams()); + auto parentType = basePA->getDependentType(builder.getGenericParams()); subMap = SubstitutionMap::getProtocolSubstitutions( proto, parentType, ProtocolConformanceRef(proto)); - - type = type.subst(subMap); } else { // Substitute in the superclass type. auto parentPA = basePA->getEquivalenceClassIfPresent(); - parentType = + auto parentType = parentPA->concreteType ? parentPA->concreteType : parentPA->superclass; auto parentDecl = parentType->getAnyNominal(); - subMap = parentType->getMemberSubstitutionMap(parentDecl->getParentModule(), - concreteDecl); - type = type.subst(subMap); - } - - // If we had a typealias, form a sugared type. - if (auto *typealias = dyn_cast(concreteDecl)) { - type = TypeAliasType::get(typealias, parentType, subMap, type); + subMap = parentType->getContextSubstitutionMap( + parentDecl->getParentModule(), dc); } - return type; + return type.subst(subMap); }; ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass( @@ -4215,10 +4208,10 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( // An inferred same-type requirement between the two type declarations // within this protocol or a protocol it inherits. auto addInferredSameTypeReq = [&](TypeDecl *first, TypeDecl *second) { - Type firstType = getStructuralType(first); + Type firstType = getStructuralType(first, /*keepSugar=*/false); if (!firstType) return; - Type secondType = getStructuralType(second); + Type secondType = getStructuralType(second, /*keepSugar=*/false); if (!secondType) return; auto inferredSameTypeSource = diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index c2875613df411..9d653f88557c0 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -534,9 +534,8 @@ Type TypeChecker::resolveTypeInContext(TypeDecl *typeDecl, DeclContext *foundDC, dyn_cast(unboundGeneric->getAnyGeneric())) { if (ugAliasDecl == aliasDecl) { if (resolution.getStage() == TypeResolutionStage::Structural && - !aliasDecl->hasInterfaceType()) { - return resolution.mapTypeIntoContext( - aliasDecl->getStructuralType()); + aliasDecl->getUnderlyingTypeRepr() != nullptr) { + return aliasDecl->getStructuralType(); } return resolution.mapTypeIntoContext( aliasDecl->getDeclaredInterfaceType()); @@ -550,9 +549,8 @@ Type TypeChecker::resolveTypeInContext(TypeDecl *typeDecl, DeclContext *foundDC, dyn_cast(extendedType.getPointer())) { if (aliasType->getDecl() == aliasDecl) { if (resolution.getStage() == TypeResolutionStage::Structural && - !aliasDecl->hasInterfaceType()) { - return resolution.mapTypeIntoContext( - aliasDecl->getStructuralType()); + aliasDecl->getUnderlyingTypeRepr() != nullptr) { + return aliasDecl->getStructuralType(); } return resolution.mapTypeIntoContext( aliasDecl->getDeclaredInterfaceType()); @@ -577,8 +575,8 @@ Type TypeChecker::resolveTypeInContext(TypeDecl *typeDecl, DeclContext *foundDC, // Otherwise, return the appropriate type. if (resolution.getStage() == TypeResolutionStage::Structural && - !aliasDecl->hasInterfaceType()) { - return resolution.mapTypeIntoContext(aliasDecl->getStructuralType()); + aliasDecl->getUnderlyingTypeRepr() != nullptr) { + return aliasDecl->getStructuralType(); } return resolution.mapTypeIntoContext( aliasDecl->getDeclaredInterfaceType()); diff --git a/test/Generics/requirement_inference.swift b/test/Generics/requirement_inference.swift index 883ee40ec34ae..60afcd73d7e59 100644 --- a/test/Generics/requirement_inference.swift +++ b/test/Generics/requirement_inference.swift @@ -383,7 +383,7 @@ struct X28 : P2 { } // CHECK-LABEL: .P28@ -// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Requirement signature: // CHECK-NEXT: Canonical requirement signature: <Ï„_0_0 where Ï„_0_0 : P3, Ï„_0_0.P3Assoc == X28> protocol P28: P3 { typealias P3Assoc = X28 // expected-warning{{typealias overriding associated type}} From 040be292e536012418b4edd5833bf977e8925c18 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 24 Sep 2019 19:26:22 -0400 Subject: [PATCH 070/140] AST: TypeDecl::getDeclaredInterfaceType() always returns a non-empty Type In the case of circular type aliases, this now returns ErrorType instead of Type(). --- lib/AST/Decl.cpp | 9 +++------ lib/AST/GenericSignatureBuilder.cpp | 5 ----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d9d6b163aeee8..0630189b2ba61 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3248,13 +3248,10 @@ Type TypeDecl::getDeclaredInterfaceType() const { } Type interfaceType = getInterfaceType(); - if (interfaceType.isNull() || interfaceType->is()) - return interfaceType; - - if (isa(this)) - return interfaceType; + if (!interfaceType) + return ErrorType::get(getASTContext()); - return interfaceType->castTo()->getInstanceType(); + return interfaceType->getMetatypeInstanceType(); } int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) { diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index c45eda95bae47..737a44e5a0057 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -3811,8 +3811,6 @@ static Type substituteConcreteType(GenericSignatureBuilder &builder, // Form an unsubstituted type referring to the given type declaration, // for use in an inferred same-type requirement. auto type = getStructuralType(concreteDecl, /*keepSugar=*/true); - if (!type) - return Type(); SubstitutionMap subMap; if (proto) { @@ -4209,10 +4207,7 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( // within this protocol or a protocol it inherits. auto addInferredSameTypeReq = [&](TypeDecl *first, TypeDecl *second) { Type firstType = getStructuralType(first, /*keepSugar=*/false); - if (!firstType) return; - Type secondType = getStructuralType(second, /*keepSugar=*/false); - if (!secondType) return; auto inferredSameTypeSource = FloatingRequirementSource::viaProtocolRequirement( From bbce38409c24d772aa2c3cfbb31b3f60e21f730e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 24 Sep 2019 22:40:29 -0400 Subject: [PATCH 071/140] Sema: Decouple availability checking from the TypeChecker --- lib/Sema/MiscDiagnostics.cpp | 2 +- lib/Sema/ResilienceDiagnostics.cpp | 13 +- lib/Sema/TypeCheckAvailability.cpp | 201 ++++++++++++++++------------- lib/Sema/TypeCheckAvailability.h | 5 +- lib/Sema/TypeCheckType.cpp | 5 +- lib/Sema/TypeChecker.h | 68 +++++----- 6 files changed, 153 insertions(+), 141 deletions(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 68e2e7c832c4a..1c8bcaa796edd 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -3961,7 +3961,7 @@ void swift::performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E, if (!TC.Context.isSwiftVersionAtLeast(5)) diagnoseDeprecatedWritableKeyPath(TC, E, DC); if (!TC.getLangOpts().DisableAvailabilityChecking) - diagAvailability(TC, E, const_cast(DC)); + diagAvailability(E, const_cast(DC)); if (TC.Context.LangOpts.EnableObjCInterop) diagDeprecatedObjCSelectors(TC, DC, E); } diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index b77ac07675e34..e6a83b67642ee 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -143,6 +143,8 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, TreatUsableFromInlineAsPublic).isPublic()) return false; + auto &Context = DC->getASTContext(); + // Dynamic declarations were mistakenly not checked in Swift 4.2. // Do enforce the restriction even in pre-Swift-5 modes if the module we're // building is resilient, though. @@ -194,18 +196,19 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::resilience_decl_unavailable_warn; - diagnose(loc, diagID, + Context.Diags.diagnose( + loc, diagID, D->getDescriptiveKind(), diagName, D->getFormalAccessScope().accessLevelForDiagnostics(), static_cast(Kind), isAccessor); if (TreatUsableFromInlineAsPublic) { - diagnose(D, diag::resilience_decl_declared_here, - D->getDescriptiveKind(), diagName, isAccessor); + Context.Diags.diagnose(D, diag::resilience_decl_declared_here, + D->getDescriptiveKind(), diagName, isAccessor); } else { - diagnose(D, diag::resilience_decl_declared_here_public, - D->getDescriptiveKind(), diagName, isAccessor); + Context.Diags.diagnose(D, diag::resilience_decl_declared_here_public, + D->getDescriptiveKind(), diagName, isAccessor); } return (downgradeToWarning == DowngradeToWarning::No); diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 2b48f3e8b470a..2edca51e0f175 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -72,7 +72,7 @@ class TypeRefinementContextBuilder : private ASTWalker { }; std::vector ContextStack; - TypeChecker &TC; + ASTContext &Context; /// A mapping from abstract storage declarations with accessors to /// to the type refinement contexts for those declarations. We refer to @@ -93,8 +93,8 @@ class TypeRefinementContextBuilder : private ASTWalker { } public: - TypeRefinementContextBuilder(TypeRefinementContext *TRC, TypeChecker &TC) - : TC(TC) { + TypeRefinementContextBuilder(TypeRefinementContext *TRC, ASTContext &Context) + : Context(Context) { assert(TRC); pushContext(TRC, ParentTy()); } @@ -171,11 +171,11 @@ class TypeRefinementContextBuilder : private ASTWalker { // the declared availability of the declaration and the potential versions // of its lexical context. AvailabilityContext DeclInfo = - swift::AvailabilityInference::availableRange(D, TC.Context); + swift::AvailabilityInference::availableRange(D, Context); DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo()); TypeRefinementContext *NewTRC = - TypeRefinementContext::createForDecl(TC.Context, D, getCurrentTRC(), + TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(), DeclInfo, refinementSourceRangeForDecl(D)); @@ -199,7 +199,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // No need to introduce a context if the declaration does not have an // availability attribute. - if (!hasActiveAvailableAttribute(D, TC.Context)) { + if (!hasActiveAvailableAttribute(D, Context)) { return false; } @@ -295,10 +295,10 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the Then branch and traverse it in that new // context. auto *ThenTRC = - TypeRefinementContext::createForIfStmtThen(TC.Context, IS, + TypeRefinementContext::createForIfStmtThen(Context, IS, getCurrentTRC(), ThenRange.getValue()); - TypeRefinementContextBuilder(ThenTRC, TC).build(IS->getThenStmt()); + TypeRefinementContextBuilder(ThenTRC, Context).build(IS->getThenStmt()); } else { build(IS->getThenStmt()); } @@ -318,10 +318,10 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the Then branch and traverse it in that new // context. auto *ElseTRC = - TypeRefinementContext::createForIfStmtElse(TC.Context, IS, + TypeRefinementContext::createForIfStmtElse(Context, IS, getCurrentTRC(), ElseRange.getValue()); - TypeRefinementContextBuilder(ElseTRC, TC).build(ElseStmt); + TypeRefinementContextBuilder(ElseTRC, Context).build(ElseStmt); } else { build(IS->getElseStmt()); } @@ -339,8 +339,8 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the body and traverse it in the new // context. auto *BodyTRC = TypeRefinementContext::createForWhileStmtBody( - TC.Context, WS, getCurrentTRC(), BodyRange.getValue()); - TypeRefinementContextBuilder(BodyTRC, TC).build(WS->getBody()); + Context, WS, getCurrentTRC(), BodyRange.getValue()); + TypeRefinementContextBuilder(BodyTRC, Context).build(WS->getBody()); } else { build(WS->getBody()); } @@ -370,9 +370,9 @@ class TypeRefinementContextBuilder : private ASTWalker { if (Stmt *ElseBody = GS->getBody()) { if (ElseRange.hasValue()) { auto *TrueTRC = TypeRefinementContext::createForGuardStmtElse( - TC.Context, GS, getCurrentTRC(), ElseRange.getValue()); + Context, GS, getCurrentTRC(), ElseRange.getValue()); - TypeRefinementContextBuilder(TrueTRC, TC).build(ElseBody); + TypeRefinementContextBuilder(TrueTRC, Context).build(ElseBody); } else { build(ElseBody); } @@ -386,7 +386,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the fallthrough. auto *FallthroughTRC = - TypeRefinementContext::createForGuardStmtFallthrough(TC.Context, GS, + TypeRefinementContext::createForGuardStmtFallthrough(Context, GS, ParentBrace, getCurrentTRC(), FallthroughRange.getValue()); pushContext(FallthroughTRC, ParentBrace); @@ -445,9 +445,10 @@ class TypeRefinementContextBuilder : private ASTWalker { // We couldn't find an appropriate spec for the current platform, // so rather than refining, emit a diagnostic and just use the current // TRC. - TC.Diags.diagnose(Query->getLoc(), - diag::availability_query_required_for_platform, - platformString(targetPlatform(TC.getLangOpts()))); + Context.Diags.diagnose( + Query->getLoc(), + diag::availability_query_required_for_platform, + platformString(targetPlatform(Context.LangOpts))); continue; } @@ -469,7 +470,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // the range for the spec, then a version query can never be false, so the // spec is useless. If so, report this. if (CurrentInfo.isContainedIn(NewConstraint)) { - DiagnosticEngine &Diags = TC.Diags; + DiagnosticEngine &Diags = Context.Diags; // Some availability checks will always pass because the minimum // deployment target guarantees they will never be false. We don't // diagnose these checks as useless because the source file may @@ -481,7 +482,7 @@ class TypeRefinementContextBuilder : private ASTWalker { if (CurrentTRC->getReason() != TypeRefinementContext::Reason::Root) { Diags.diagnose(Query->getLoc(), diag::availability_query_useless_enclosing_scope, - platformString(targetPlatform(TC.getLangOpts()))); + platformString(targetPlatform(Context.LangOpts))); Diags.diagnose(CurrentTRC->getIntroductionLoc(), diag::availability_query_useless_enclosing_scope_here); } @@ -499,7 +500,7 @@ class TypeRefinementContextBuilder : private ASTWalker { FalseFlow.unionWith(CurrentInfo); auto *TRC = TypeRefinementContext::createForConditionFollowingQuery( - TC.Context, Query, LastElement, CurrentTRC, NewConstraint); + Context, Query, LastElement, CurrentTRC, NewConstraint); pushContext(TRC, ParentTy()); NestedCount++; @@ -550,7 +551,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // properly. For example, on the OSXApplicationExtension platform // we want to chose the OS X spec unless there is an explicit // OSXApplicationExtension spec. - if (isPlatformActive(VersionSpec->getPlatform(), TC.getLangOpts())) { + if (isPlatformActive(VersionSpec->getPlatform(), Context.LangOpts)) { return VersionSpec; } } @@ -589,20 +590,20 @@ void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF, // already have a root type refinement context. assert(StartElem == 0 || RootTRC); - ASTContext &AC = SF.getASTContext(); + ASTContext &Context = SF.getASTContext(); if (!RootTRC) { // The root type refinement context reflects the fact that all parts of // the source file are guaranteed to be executing on at least the minimum // platform version. - auto MinPlatformReq = AvailabilityContext::forDeploymentTarget(AC); + auto MinPlatformReq = AvailabilityContext::forDeploymentTarget(Context); RootTRC = TypeRefinementContext::createRoot(&SF, MinPlatformReq); SF.setTypeRefinementContext(RootTRC); } // Build refinement contexts, if necessary, for all declarations starting // with StartElem. - TypeRefinementContextBuilder Builder(RootTRC, *this); + TypeRefinementContextBuilder Builder(RootTRC, Context); for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { Builder.build(D); } @@ -624,6 +625,7 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, const DeclContext *DC, const TypeRefinementContext **MostRefined) { SourceFile *SF = DC->getParentSourceFile(); + auto &Context = DC->getASTContext(); // If our source location is invalid (this may be synthesized code), climb // the decl context hierarchy until we find a location that is valid, @@ -676,6 +678,7 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, bool TypeChecker::isDeclAvailable(const Decl *D, SourceLoc referenceLoc, const DeclContext *referenceDC, AvailabilityContext &OutAvailableInfo) { + ASTContext &Context = referenceDC->getASTContext(); AvailabilityContext safeRangeUnderApprox{ AvailabilityInference::availableRange(D, Context)}; @@ -698,6 +701,7 @@ bool TypeChecker::isDeclAvailable(const Decl *D, SourceLoc referenceLoc, Optional TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, const DeclContext *referenceDC) { + ASTContext &Context = referenceDC->getASTContext(); if (Context.LangOpts.DisableAvailabilityChecking) { return None; } @@ -1137,15 +1141,15 @@ static void findAvailabilityFixItNodes(SourceRange ReferenceRange, /// on the given declaration for the given version range. static void fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D, const VersionRange &RequiredRange, - TypeChecker &TC) { + ASTContext &Context) { assert(D); // Don't suggest adding an @available() to a declaration where we would // emit a diagnostic saying it is not allowed. - if (TC.diagnosticIfDeclCannotBePotentiallyUnavailable(D).hasValue()) + if (TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(D).hasValue()) return; - if (getActiveAvailableAttribute(D, TC.Context)) { + if (getActiveAvailableAttribute(D, Context)) { // For QoI, in future should emit a fixit to update the existing attribute. return; } @@ -1180,11 +1184,10 @@ static void fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D, return; StringRef OriginalIndent = - Lexer::getIndentationForLine(TC.Context.SourceMgr, InsertLoc); - PlatformKind Target = targetPlatform(TC.getLangOpts()); + Lexer::getIndentationForLine(Context.SourceMgr, InsertLoc); + PlatformKind Target = targetPlatform(Context.LangOpts); - TC.diagnose(D, diag::availability_add_attribute, - KindForDiagnostic) + D->diagnose(diag::availability_add_attribute, KindForDiagnostic) .fixItInsert(InsertLoc, diag::insert_available_attr, platformString(Target), RequiredRange.getLowerEndpoint().getAsString(), @@ -1200,12 +1203,12 @@ static bool fixAvailabilityByNarrowingNearbyVersionCheck( SourceRange ReferenceRange, const DeclContext *ReferenceDC, const VersionRange &RequiredRange, - TypeChecker &TC, + ASTContext &Context, InFlightDiagnostic &Err) { const TypeRefinementContext *TRC = nullptr; AvailabilityContext RunningOSOverApprox = - TC.overApproximateAvailabilityAtLocation(ReferenceRange.Start, - ReferenceDC, &TRC); + TypeChecker::overApproximateAvailabilityAtLocation(ReferenceRange.Start, + ReferenceDC, &TRC); VersionRange RunningRange = RunningOSOverApprox.getOSVersion(); if (RunningRange.hasLowerEndpoint() && RequiredRange.hasLowerEndpoint() && @@ -1217,7 +1220,7 @@ static bool fixAvailabilityByNarrowingNearbyVersionCheck( // or disagreement on a subminor-or-less version for macOS. auto RunningVers = RunningRange.getLowerEndpoint(); auto RequiredVers = RequiredRange.getLowerEndpoint(); - auto Platform = targetPlatform(TC.Context.LangOpts); + auto Platform = targetPlatform(Context.LangOpts); if (RunningVers.getMajor() != RequiredVers.getMajor()) return false; if ((Platform == PlatformKind::OSX || @@ -1243,7 +1246,7 @@ static bool fixAvailabilityByNarrowingNearbyVersionCheck( /// that checks for the given version range around the given node. static void fixAvailabilityByAddingVersionCheck( ASTNode NodeToWrap, const VersionRange &RequiredRange, - SourceRange ReferenceRange, TypeChecker &TC) { + SourceRange ReferenceRange, ASTContext &Context) { SourceRange RangeToWrap = NodeToWrap.getSourceRange(); if (RangeToWrap.isInvalid()) return; @@ -1251,19 +1254,19 @@ static void fixAvailabilityByAddingVersionCheck( SourceLoc ReplaceLocStart = RangeToWrap.Start; StringRef ExtraIndent; StringRef OriginalIndent = Lexer::getIndentationForLine( - TC.Context.SourceMgr, ReplaceLocStart, &ExtraIndent); + Context.SourceMgr, ReplaceLocStart, &ExtraIndent); std::string IfText; { llvm::raw_string_ostream Out(IfText); SourceLoc ReplaceLocEnd = - Lexer::getLocForEndOfToken(TC.Context.SourceMgr, RangeToWrap.End); + Lexer::getLocForEndOfToken(Context.SourceMgr, RangeToWrap.End); std::string GuardedText = - TC.Context.SourceMgr.extractText(CharSourceRange(TC.Context.SourceMgr, - ReplaceLocStart, - ReplaceLocEnd)).str(); + Context.SourceMgr.extractText(CharSourceRange(Context.SourceMgr, + ReplaceLocStart, + ReplaceLocEnd)).str(); std::string NewLine = "\n"; std::string NewLineReplacement = (NewLine + ExtraIndent).str(); @@ -1277,7 +1280,7 @@ static void fixAvailabilityByAddingVersionCheck( StartAt += NewLine.length(); } - PlatformKind Target = targetPlatform(TC.getLangOpts()); + PlatformKind Target = targetPlatform(Context.LangOpts); Out << "if #available(" << platformString(Target) << " " << RequiredRange.getLowerEndpoint().getAsString() @@ -1292,7 +1295,8 @@ static void fixAvailabilityByAddingVersionCheck( Out << OriginalIndent << "}"; } - TC.diagnose(ReferenceRange.Start, diag::availability_guard_with_version_check) + Context.Diags.diagnose( + ReferenceRange.Start, diag::availability_guard_with_version_check) .fixItReplace(RangeToWrap, IfText); } @@ -1301,7 +1305,7 @@ static void fixAvailabilityByAddingVersionCheck( static void fixAvailability(SourceRange ReferenceRange, const DeclContext *ReferenceDC, const VersionRange &RequiredRange, - TypeChecker &TC) { + ASTContext &Context) { if (ReferenceRange.isInvalid()) return; @@ -1309,30 +1313,33 @@ static void fixAvailability(SourceRange ReferenceRange, const Decl *FoundMemberDecl = nullptr; const Decl *FoundTypeLevelDecl = nullptr; - findAvailabilityFixItNodes(ReferenceRange, ReferenceDC, TC.Context.SourceMgr, + findAvailabilityFixItNodes(ReferenceRange, ReferenceDC, Context.SourceMgr, NodeToWrapInVersionCheck, FoundMemberDecl, FoundTypeLevelDecl); // Suggest wrapping in if #available(...) { ... } if possible. if (NodeToWrapInVersionCheck.hasValue()) { fixAvailabilityByAddingVersionCheck(NodeToWrapInVersionCheck.getValue(), - RequiredRange, ReferenceRange, TC); + RequiredRange, ReferenceRange, Context); } // Suggest adding availability attributes. if (FoundMemberDecl) { - fixAvailabilityForDecl(ReferenceRange, FoundMemberDecl, RequiredRange, TC); + fixAvailabilityForDecl(ReferenceRange, FoundMemberDecl, RequiredRange, + Context); } if (FoundTypeLevelDecl) { fixAvailabilityForDecl(ReferenceRange, FoundTypeLevelDecl, RequiredRange, - TC); + Context); } } void TypeChecker::diagnosePotentialOpaqueTypeUnavailability( SourceRange ReferenceRange, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason) { + ASTContext &Context = ReferenceDC->getASTContext(); + // We only emit diagnostics for API unavailability, not for explicitly // weak-linked symbols. if (Reason.getReasonKind() != @@ -1343,22 +1350,24 @@ void TypeChecker::diagnosePotentialOpaqueTypeUnavailability( auto RequiredRange = Reason.getRequiredOSVersionRange(); { auto Err = - diagnose(ReferenceRange.Start, diag::availability_opaque_types_only_version_newer, + Context.Diags.diagnose( + ReferenceRange.Start, diag::availability_opaque_types_only_version_newer, prettyPlatformString(targetPlatform(Context.LangOpts)), Reason.getRequiredOSVersionRange().getLowerEndpoint()); // Direct a fixit to the error if an existing guard is nearly-correct if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange, ReferenceDC, - RequiredRange, *this, Err)) + RequiredRange, Context, Err)) return; } - fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, *this); + fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context); } void TypeChecker::diagnosePotentialUnavailability( const Decl *D, DeclName Name, SourceRange ReferenceRange, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason) { + ASTContext &Context = ReferenceDC->getASTContext(); // We only emit diagnostics for API unavailability, not for explicitly // weak-linked symbols. @@ -1370,24 +1379,27 @@ void TypeChecker::diagnosePotentialUnavailability( auto RequiredRange = Reason.getRequiredOSVersionRange(); { auto Err = - diagnose(ReferenceRange.Start, diag::availability_decl_only_version_newer, + Context.Diags.diagnose( + ReferenceRange.Start, diag::availability_decl_only_version_newer, Name, prettyPlatformString(targetPlatform(Context.LangOpts)), Reason.getRequiredOSVersionRange().getLowerEndpoint()); // Direct a fixit to the error if an existing guard is nearly-correct if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange, ReferenceDC, - RequiredRange, *this, Err)) + RequiredRange, Context, Err)) return; } - fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, *this); + fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context); } void TypeChecker::diagnosePotentialAccessorUnavailability( const AccessorDecl *Accessor, SourceRange ReferenceRange, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason, bool ForInout) { + ASTContext &Context = ReferenceDC->getASTContext(); + assert(Accessor->isGetterOrSetter()); const AbstractStorageDecl *ASD = Accessor->getStorage(); @@ -1399,7 +1411,8 @@ void TypeChecker::diagnosePotentialAccessorUnavailability( auto RequiredRange = Reason.getRequiredOSVersionRange(); { auto Err = - diagnose(ReferenceRange.Start, diag, + Context.Diags.diagnose( + ReferenceRange.Start, diag, static_cast(Accessor->getAccessorKind()), Name, prettyPlatformString(targetPlatform(Context.LangOpts)), Reason.getRequiredOSVersionRange().getLowerEndpoint()); @@ -1408,11 +1421,11 @@ void TypeChecker::diagnosePotentialAccessorUnavailability( // Direct a fixit to the error if an existing guard is nearly-correct if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange, ReferenceDC, - RequiredRange, *this, Err)) + RequiredRange, Context, Err)) return; } - fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, *this); + fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context); } const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) { @@ -1937,6 +1950,7 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, return; } + auto &Context = ReferenceDC->getASTContext(); if (!Context.LangOpts.DisableAvailabilityChecking) { AvailabilityContext RunningOSVersions = overApproximateAvailabilityAtLocation(ReferenceRange.Start,ReferenceDC); @@ -1959,7 +1973,8 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, DeprecatedVersion = Attr->Deprecated.getValue(); if (Attr->Message.empty() && Attr->Rename.empty()) { - diagnose(ReferenceRange.Start, diag::availability_deprecated, + Context.Diags.diagnose( + ReferenceRange.Start, diag::availability_deprecated, RawAccessorKind, Name, Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(), DeprecatedVersion, /*message*/ StringRef()) @@ -1974,7 +1989,8 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, if (!Attr->Message.empty()) { EncodedDiagnosticMessage EncodedMessage(Attr->Message); - diagnose(ReferenceRange.Start, diag::availability_deprecated, + Context.Diags.diagnose( + ReferenceRange.Start, diag::availability_deprecated, RawAccessorKind, Name, Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(), DeprecatedVersion, EncodedMessage.Message) @@ -1982,7 +1998,8 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, } else { unsigned rawReplaceKind = static_cast( replacementDeclKind.getValueOr(ReplacementDeclKind::None)); - diagnose(ReferenceRange.Start, diag::availability_deprecated_rename, + Context.Diags.diagnose( + ReferenceRange.Start, diag::availability_deprecated_rename, RawAccessorKind, Name, Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(), DeprecatedVersion, replacementDeclKind.hasValue(), rawReplaceKind, newName) @@ -1990,7 +2007,8 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, } if (!Attr->Rename.empty() && !isa(DeprecatedDecl)) { - auto renameDiag = diagnose(ReferenceRange.Start, + auto renameDiag = Context.Diags.diagnose( + ReferenceRange.Start, diag::note_deprecated_rename, newName); fixItAvailableAttrRename(renameDiag, ReferenceRange, DeprecatedDecl, @@ -2274,7 +2292,7 @@ class AvailabilityWalker : public ASTWalker { InOut }; - TypeChecker &TC; + ASTContext &Context; DeclContext *DC; MemberAccessContext AccessContext = MemberAccessContext::Getter; SmallVector ExprStack; @@ -2292,16 +2310,15 @@ class AvailabilityWalker : public ASTWalker { init->isImplicit() && init->getParameters()->size() == 1 && init->getParameters()->get(0)->getArgumentName() == - TC.Context.Id_rawValue; + Context.Id_rawValue; } public: - AvailabilityWalker( - TypeChecker &TC, DeclContext *DC) : TC(TC), DC(DC) { + AvailabilityWalker(DeclContext *DC) : Context(DC->getASTContext()), DC(DC) { Expansion = DC->getResilienceExpansion(); if (Expansion == ResilienceExpansion::Minimal) std::tie(FragileKind, TreatUsableFromInlineAsPublic) - = TC.getFragileFunctionKind(DC); + = TypeChecker::getFragileFunctionKind(DC); } // FIXME: Remove this @@ -2502,7 +2519,7 @@ class AvailabilityWalker : public ASTWalker { void maybeDiagStorageAccess(const ValueDecl *VD, SourceRange ReferenceRange, const DeclContext *ReferenceDC) const { - if (TC.getLangOpts().DisableAvailabilityChecking) + if (Context.LangOpts.DisableAvailabilityChecking) return; auto *D = dyn_cast(VD); @@ -2581,8 +2598,8 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, if (FragileKind) if (R.isValid()) - if (TC.diagnoseInlinableDeclRef(R.Start, declRef, DC, *FragileKind, - TreatUsableFromInlineAsPublic)) + if (TypeChecker::diagnoseInlinableDeclRef(R.Start, declRef, DC, *FragileKind, + TreatUsableFromInlineAsPublic)) return true; if (diagnoseExplicitUnavailability(D, R, DC, call)) @@ -2595,7 +2612,7 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, // Diagnose for deprecation if (!isAccessorWithDeprecatedStorage) - TC.diagnoseIfDeprecated(R, DC, D, call); + TypeChecker::diagnoseIfDeprecated(R, DC, D, call); if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailable)) return false; @@ -2605,15 +2622,15 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, return false; // Diagnose (and possibly signal) for potential unavailability - auto maybeUnavail = TC.checkDeclarationAvailability(D, R.Start, DC); + auto maybeUnavail = TypeChecker::checkDeclarationAvailability(D, R.Start, DC); if (maybeUnavail.hasValue()) { if (accessor) { bool forInout = Flags.contains(DeclAvailabilityFlag::ForInout); - TC.diagnosePotentialAccessorUnavailability(accessor, R, DC, - maybeUnavail.getValue(), - forInout); + TypeChecker::diagnosePotentialAccessorUnavailability(accessor, R, DC, + maybeUnavail.getValue(), + forInout); } else { - TC.diagnosePotentialUnavailability(D, R, DC, maybeUnavail.getValue()); + TypeChecker::diagnosePotentialUnavailability(D, R, DC, maybeUnavail.getValue()); } if (!Flags.contains(DeclAvailabilityFlag::ContinueOnPotentialUnavailability)) return true; @@ -2625,13 +2642,11 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, /// Return true if the specified type looks like an integer of floating point /// type. static bool isIntegerOrFloatingPointType(Type ty, DeclContext *DC, - TypeChecker &TC) { + ASTContext &Context) { auto integerType = - TC.getProtocol(SourceLoc(), - KnownProtocolKind::ExpressibleByIntegerLiteral); + Context.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); auto floatingType = - TC.getProtocol(SourceLoc(), - KnownProtocolKind::ExpressibleByFloatLiteral); + Context.getProtocol(KnownProtocolKind::ExpressibleByFloatLiteral); if (!integerType || !floatingType) return false; return @@ -2667,12 +2682,12 @@ AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, // If the expression type is integer or floating point, then we can rewrite it // to "lvalue += 1". std::string replacement; - if (isIntegerOrFloatingPointType(call->getType(), DC, TC)) + if (isIntegerOrFloatingPointType(call->getType(), DC, Context)) replacement = isInc ? " += 1" : " -= 1"; else { // Otherwise, it must be an index type. Rewrite to: // "lvalue = lvalue.successor()". - auto &SM = TC.Context.SourceMgr; + auto &SM = Context.SourceMgr; auto CSR = Lexer::getCharSourceRangeFromSourceRange(SM, call->getArg()->getSourceRange()); replacement = " = " + SM.extractText(CSR).str(); @@ -2685,9 +2700,10 @@ AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D); // If we emit a deprecation diagnostic, produce a fixit hint as well. - auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable, - RawAccessorKind, Name, true, "", - "it has been removed in Swift 3"); + auto diag = Context.Diags.diagnose( + R.Start, diag::availability_decl_unavailable, + RawAccessorKind, Name, true, "", + "it has been removed in Swift 3"); if (isa(call)) { // Prefix: remove the ++ or --. diag.fixItRemove(call->getFn()->getSourceRange()); @@ -2737,8 +2753,9 @@ AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, EncodedDiagnosticMessage EncodedMessage(Attr->Message); auto diag = - TC.diagnose(R.Start, diag::availability_decl_unavailable, RawAccessorKind, - Name, true, "", EncodedMessage.Message); + Context.Diags.diagnose( + R.Start, diag::availability_decl_unavailable, RawAccessorKind, + Name, true, "", EncodedMessage.Message); diag.highlight(R); auto subject = args->getSubExpr(); @@ -2778,9 +2795,8 @@ AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, } /// Diagnose uses of unavailable declarations. -void swift::diagAvailability(TypeChecker &TC, const Expr *E, - DeclContext *DC) { - AvailabilityWalker walker(TC, DC); +void swift::diagAvailability(const Expr *E, DeclContext *DC) { + AvailabilityWalker walker(DC); const_cast(E)->walk(walker); } @@ -2788,12 +2804,11 @@ void swift::diagAvailability(TypeChecker &TC, const Expr *E, /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool swift::diagnoseDeclAvailability(const ValueDecl *Decl, - TypeChecker &TC, DeclContext *DC, SourceRange R, DeclAvailabilityFlags Flags) { - AvailabilityWalker AW(TC, DC); + AvailabilityWalker AW(DC); return AW.diagAvailability(const_cast(Decl), R, nullptr, Flags); } diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 2166b166851ec..29d3f385abfc8 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -27,13 +27,11 @@ namespace swift { class DeclContext; class Expr; class InFlightDiagnostic; - class TypeChecker; class Decl; class ValueDecl; /// Diagnose uses of unavailable declarations. -void diagAvailability(TypeChecker &TC, const Expr *E, - DeclContext *DC); +void diagAvailability(const Expr *E, DeclContext *DC); enum class DeclAvailabilityFlag : uint8_t { /// Do not diagnose uses of protocols in versions before they were introduced. @@ -59,7 +57,6 @@ using DeclAvailabilityFlags = OptionSet; /// context, but for non-expr contexts such as TypeDecls referenced from /// TypeReprs. bool diagnoseDeclAvailability(const ValueDecl *Decl, - TypeChecker &TC, DeclContext *DC, SourceRange R, DeclAvailabilityFlags Options); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 9d653f88557c0..2f044162cfa28 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1630,13 +1630,10 @@ static bool diagnoseAvailability(IdentTypeRepr *IdType, DeclAvailabilityFlag::ContinueOnPotentialUnavailability; if (AllowPotentiallyUnavailableProtocol) flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; - ASTContext &ctx = DC->getASTContext(); auto componentRange = IdType->getComponentRange(); for (auto comp : componentRange) { if (auto *typeDecl = comp->getBoundDecl()) { - assert(ctx.getLazyResolver() && "Must have a type checker!"); - TypeChecker &tc = static_cast(*ctx.getLazyResolver()); - if (diagnoseDeclAvailability(typeDecl, tc, DC, comp->getIdLoc(), flags)) { + if (diagnoseDeclAvailability(typeDecl, DC, comp->getIdLoc(), flags)) { return true; } } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 7a7285ba62933..7599f5ba32c86 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1727,25 +1727,25 @@ class TypeChecker final : public LazyResolver { PropertyInitializer }; - bool diagnoseInlinableDeclRef(SourceLoc loc, ConcreteDeclRef declRef, - const DeclContext *DC, - FragileFunctionKind Kind, - bool TreatUsableFromInlineAsPublic); + static bool diagnoseInlinableDeclRef(SourceLoc loc, ConcreteDeclRef declRef, + const DeclContext *DC, + FragileFunctionKind Kind, + bool TreatUsableFromInlineAsPublic); Expr *buildDefaultInitializer(Type type); private: - bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, - const DeclContext *DC, - FragileFunctionKind Kind, - bool TreatUsableFromInlineAsPublic); + static bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, + const DeclContext *DC, + FragileFunctionKind Kind, + bool TreatUsableFromInlineAsPublic); /// Given that a declaration is used from a particular context which /// exposes it in the interface of the current module, diagnose if it cannot /// reasonably be shared. - bool diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, - const DeclContext *DC, - FragileFunctionKind fragileKind); + static bool diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, + const DeclContext *DC, + FragileFunctionKind fragileKind); /// Given that a type is used from a particular context which /// exposes it in the interface of the current module, diagnose if its @@ -1787,7 +1787,7 @@ class TypeChecker final : public LazyResolver { /// that could the passed-in location could be executing upon for /// the target platform. If MostRefined != nullptr, set to the most-refined /// TRC found while approximating. - AvailabilityContext + static AvailabilityContext overApproximateAvailabilityAtLocation(SourceLoc loc, const DeclContext *DC, const TypeRefinementContext **MostRefined=nullptr); @@ -1795,18 +1795,18 @@ class TypeChecker final : public LazyResolver { /// /// \param StartElem Where to start for incremental building of refinement /// contexts - void buildTypeRefinementContextHierarchy(SourceFile &SF, - unsigned StartElem); + static void buildTypeRefinementContextHierarchy(SourceFile &SF, + unsigned StartElem); /// Build the hierarchy of TypeRefinementContexts for the entire /// source file, if it has not already been built. Returns the root /// TypeRefinementContext for the source file. - TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF); + static TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF); /// Returns a diagnostic indicating why the declaration cannot be annotated /// with an @available() attribute indicating it is potentially unavailable /// or None if this is allowed. - Optional> + static Optional> diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D); /// Checks whether a declaration is available when referred to at the given @@ -1816,15 +1816,15 @@ class TypeChecker final : public LazyResolver { /// If the declaration is not available, return false and write the /// declaration's availability info to the out parameter /// \p OutAvailableRange. - bool isDeclAvailable(const Decl *D, SourceLoc referenceLoc, - const DeclContext *referenceDC, - AvailabilityContext &OutAvailableRange); + static bool isDeclAvailable(const Decl *D, SourceLoc referenceLoc, + const DeclContext *referenceDC, + AvailabilityContext &OutAvailableRange); /// Checks whether a declaration should be considered unavailable when /// referred to at the given location and, if so, returns the reason why the /// declaration is unavailable. Returns None is the declaration is /// definitely available. - Optional + static Optional checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc, const DeclContext *referenceDC); @@ -1836,26 +1836,26 @@ class TypeChecker final : public LazyResolver { // Emits a diagnostic, if necessary, for a reference to a declaration // that is potentially unavailable at the given source location. - void diagnosePotentialUnavailability(const ValueDecl *D, - SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - const UnavailabilityReason &Reason); + static void diagnosePotentialUnavailability(const ValueDecl *D, + SourceRange ReferenceRange, + const DeclContext *ReferenceDC, + const UnavailabilityReason &Reason); // Emits a diagnostic, if necessary, for a reference to a declaration // that is potentially unavailable at the given source location, using // Name as the diagnostic name. - void diagnosePotentialUnavailability(const Decl *D, DeclName Name, - SourceRange ReferenceRange, - const DeclContext *ReferenceDC, - const UnavailabilityReason &Reason); + static void diagnosePotentialUnavailability(const Decl *D, DeclName Name, + SourceRange ReferenceRange, + const DeclContext *ReferenceDC, + const UnavailabilityReason &Reason); - void diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange, + static void diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason); /// Emits a diagnostic for a reference to a storage accessor that is /// potentially unavailable. - void diagnosePotentialAccessorUnavailability( + static void diagnosePotentialAccessorUnavailability( const AccessorDecl *Accessor, SourceRange ReferenceRange, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason, bool ForInout); @@ -1867,10 +1867,10 @@ class TypeChecker final : public LazyResolver { /// Emits a diagnostic for a reference to a declaration that is deprecated. /// Callers can provide a lambda that adds additional information (such as a /// fixit hint) to the deprecation diagnostic, if it is emitted. - void diagnoseIfDeprecated(SourceRange SourceRange, - const DeclContext *ReferenceDC, - const ValueDecl *DeprecatedDecl, - const ApplyExpr *Call); + static void diagnoseIfDeprecated(SourceRange SourceRange, + const DeclContext *ReferenceDC, + const ValueDecl *DeprecatedDecl, + const ApplyExpr *Call); /// @} /// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl From 5e4900242b5f9c26c9d34bba328fc345e365dc09 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 24 Sep 2019 22:37:27 -0400 Subject: [PATCH 072/140] Sema: Remove outdated comment --- lib/Sema/TypeCheckGeneric.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index dd2b6e00b365f..688e32e8932db 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -585,12 +585,6 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator, Requirement(RequirementKind::Conformance, self, PD->getDeclaredType()); auto *sig = GenericSignature::get({self}, {req}); - // The requirement signature is created lazily by - // ProtocolDecl::getRequirementSignature(). - // The generic signature and environment is created lazily by - // GenericContext::getGenericSignature(), so there is nothing we - // need to do. - // Debugging of the generic signature builder and generic signature // generation. if (GC->getASTContext().LangOpts.DebugGenericSignatures) { From cc2c868522b538d921726e4820ab886cb87e6238 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 24 Sep 2019 17:37:02 -0700 Subject: [PATCH 073/140] [Diagnostics] Produce a tailored diagnostic for multiple missing arguments --- include/swift/AST/DiagnosticsSema.def | 2 + lib/Sema/CSDiagnostics.cpp | 138 ++++++++++++++++++++------ lib/Sema/CSDiagnostics.h | 14 ++- 3 files changed, 122 insertions(+), 32 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3f33afd5a11d5..a1bca2b166c2e 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1116,6 +1116,8 @@ ERROR(missing_argument_named,none, "missing argument for parameter %0 in call", (Identifier)) ERROR(missing_argument_positional,none, "missing argument for parameter #%0 in call", (unsigned)) +ERROR(missing_arguments_in_call,none, + "missing arguments for parameters %0 in call", (StringRef)) ERROR(extra_argument_named,none, "extra argument %0 in call", (Identifier)) ERROR(extra_argument_positional,none, diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 38edae8f5a5ad..c54f1bbde9fa3 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3623,17 +3623,75 @@ bool MissingArgumentsFailure::diagnoseAsError() { if (diagnoseInvalidTupleDestructuring()) return true; - if (diagnoseSingleMissingArgument()) - return true; + if (SynthesizedArgs.size() == 1) + return diagnoseSingleMissingArgument(); - return false; + // At this point we know that this is a situation when + // there are multiple arguments missing, so let's produce + // a diagnostic which lists all of them and a fix-it + // to add arguments at appropriate positions. + + SmallString<32> diagnostic; + llvm::raw_svector_ostream arguments(diagnostic); + + interleave( + SynthesizedArgs, + [&](const AnyFunctionType::Param &arg) { + if (arg.hasLabel()) { + arguments << "'" << arg.getLabel().str() << "'"; + } else { + auto *typeVar = arg.getPlainType()->castTo(); + auto *locator = typeVar->getImpl().getLocator(); + auto paramIdx = locator->findLast() + ->getParamIdx(); + + arguments << "#" << (paramIdx + 1); + } + }, + [&] { arguments << ", "; }); + + auto diag = emitDiagnostic(anchor->getLoc(), diag::missing_arguments_in_call, + arguments.str()); + + Expr *fnExpr = nullptr; + Expr *argExpr = nullptr; + unsigned numArguments = 0; + bool hasTrailingClosure = false; + + std::tie(fnExpr, argExpr, numArguments, hasTrailingClosure) = + getCallInfo(getRawAnchor()); + + // TODO(diagnostics): We should be able to suggest this fix-it + // unconditionally. + if (argExpr && numArguments == 0) { + SmallString<32> scratch; + llvm::raw_svector_ostream fixIt(scratch); + interleave( + SynthesizedArgs, + [&](const AnyFunctionType::Param &arg) { forFixIt(fixIt, arg); }, + [&] { fixIt << ", "; }); + + auto *tuple = cast(argExpr); + diag.fixItInsertAfter(tuple->getLParenLoc(), fixIt.str()); + } + + diag.flush(); + + if (auto selectedOverload = getChoiceFor(locator)) { + if (auto *decl = selectedOverload->choice.getDeclOrNull()) { + emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName()); + } + } + + return true; } bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { auto &ctx = getASTContext(); auto *anchor = getRawAnchor(); - if (!(isa(anchor) || isa(anchor))) + if (!(isa(anchor) || isa(anchor) || + isa(anchor))) return false; if (SynthesizedArgs.size() != 1) @@ -3652,37 +3710,21 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { if (position != 0) insertText << ", "; - if (!label.empty()) - insertText << label.str() << ": "; + forFixIt(insertText, argument); - // Explode inout type. - if (argument.isInOut()) - insertText << "&"; + Expr *fnExpr = nullptr; + Expr *argExpr = nullptr; + unsigned insertableEndIdx = 0; + bool hasTrailingClosure = false; - auto resolvedType = resolveType(argType); - // @autoclosure; the type should be the result type. - if (argument.isAutoClosure()) - resolvedType = resolvedType->castTo()->getResult(); + std::tie(fnExpr, argExpr, insertableEndIdx, hasTrailingClosure) = + getCallInfo(anchor); - insertText << "<#" << resolvedType << "#>"; + if (!argExpr) + return false; - unsigned insertableEndIdx = 0; - Expr *fnExpr = nullptr; - Expr *argExpr = nullptr; - if (auto *callExpr = dyn_cast(anchor)) { - fnExpr = callExpr->getFn(); - argExpr = callExpr->getArg(); - insertableEndIdx = callExpr->getNumArguments(); - if (callExpr->hasTrailingClosure()) - insertableEndIdx -= 1; - } else { - auto *SE = cast(anchor); - fnExpr = SE; - argExpr = SE->getIndex(); - insertableEndIdx = SE->getNumArguments(); - if (SE->hasTrailingClosure()) - insertableEndIdx -= 1; - } + if (hasTrailingClosure) + insertableEndIdx -= 1; if (position == 0 && insertableEndIdx != 0) insertText << ", "; @@ -3969,6 +4011,40 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument( return TC.isConvertibleTo(argType, paramType, cs.DC); } +std::tuple +MissingArgumentsFailure::getCallInfo(Expr *anchor) const { + if (auto *call = dyn_cast(anchor)) { + return std::make_tuple(call->getFn(), call->getArg(), + call->getNumArguments(), call->hasTrailingClosure()); + } else if (auto *UME = dyn_cast(anchor)) { + return std::make_tuple(UME, UME->getArgument(), UME->getNumArguments(), + UME->hasTrailingClosure()); + } else if (auto *SE = dyn_cast(anchor)) { + return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(), + SE->hasTrailingClosure()); + } + + return std::make_tuple(nullptr, nullptr, 0, false); +} + +void MissingArgumentsFailure::forFixIt( + llvm::raw_svector_ostream &out, + const AnyFunctionType::Param &argument) const { + if (argument.hasLabel()) + out << argument.getLabel().str() << ": "; + + // Explode inout type. + if (argument.isInOut()) + out << "&"; + + auto resolvedType = resolveType(argument.getPlainType()); + // @autoclosure; the type should be the result type. + if (argument.isAutoClosure()) + resolvedType = resolvedType->castTo()->getResult(); + + out << "<#" << resolvedType << "#>"; +} + bool ClosureParamDestructuringFailure::diagnoseAsError() { auto *closure = cast(getAnchor()); auto params = closure->getParameters(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 4440e25bfc8cf..ce0d0bcbd72ac 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1193,7 +1193,9 @@ class MissingArgumentsFailure final : public FailureDiagnostic { ArrayRef synthesizedArgs, ConstraintLocator *locator) : FailureDiagnostic(root, cs, locator), - SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) {} + SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) { + assert(!SynthesizedArgs.empty() && "No missing arguments?!"); + } bool diagnoseAsError() override; @@ -1213,6 +1215,16 @@ class MissingArgumentsFailure final : public FailureDiagnostic { /// `@Foo(answer: 42) var question = "ultimate question"` bool isPropertyWrapperInitialization() const; + /// Gather informatioin associated with expression that represents + /// a call - function, arguments, # of arguments and whether it has + /// a trailing closure. + std::tuple getCallInfo(Expr *anchor) const; + + /// Transform given argument into format suitable for a fix-it + /// text e.g. `[