diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 6440c3b76c9..4d4dd1c9ac3 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -212,6 +212,7 @@ set(SWIFT_BENCH_MODULES single-source/SuperChars single-source/TaskGroups single-source/TaskLocalGet + single-source/ToddCoxeter single-source/TwoSum single-source/TypeFlood single-source/UTF8Decode diff --git a/benchmark/single-source/ToddCoxeter.swift b/benchmark/single-source/ToddCoxeter.swift new file mode 100644 index 00000000000..576ae05e13e --- /dev/null +++ b/benchmark/single-source/ToddCoxeter.swift @@ -0,0 +1,580 @@ +// +// This file implements an algorithm to construct the Cayley graph for a +// finite monoid. Specifically, this implements the "modified Felsch +// strategy" for congruence enumeration, described in this paper: +// +// "The Todd–Coxeter algorithm for semigroups and monoids" +// https://link.springer.com/article/10.1007/s00233-024-10431-z +// +// A few things you would need for a "real" implementation are missing: +// +// 1) There is no garbage collection for nodes in the graph, so when two nodes +// are merged, the dead node is not reused. Fixing this would decrease +// memory pressure. +// 2) Other strategies, such as the HLT strategy, could be implemented, since +// alternating Flesch with HLT gives better performance on some workloads. +// 3) The constructed word graph is not standardized, and the only thing this +// implementation does with it is count the number of elements. +// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo(name: "ToddCoxeter", runFunction: run_ToddCoxeter, tags: [.miniapplication]) +] + +func run(rules: [[Word]], maxNodes: Int, maxLinks: Int) + throws(CongruenceError) -> Int { + let alphabet = 2 + let rules = rules.map { Rule(lhs: $0[0], rhs: $0[1]) } + let p = Presentation(alphabet: alphabet, rules: rules) + + var c = Congruence(presentation: p, maxNodes: maxNodes, maxLinks: maxLinks) + return try c.enumerate() +} + +let problems: [([[Word]], Int, Int, Int)] = [ + // has 1083 elements. This takes about half a millisecond + // on an Apple MacBook Pro with M4 chip. This runs by default. + ([[[0, 0, 0], []], [[0, 1, 1, 1, 1, 0], [1]]], 2000, 3000, 1083), + + // has 10,587 elements. This takes about 9 milliseconds + // on an Apple MacBook Pro with M4 chip. + ([[[0, 0, 0], []], [[0, 1, 0, 1, 1, 1, 0], [1]]], 30000, 30000, 10587), + + // has 2,361,964 elements. This takes about 22 seconds + // on an Apple MacBook Pro with M4 chip. + ([[[0, 0, 0, 0], []], [[0, 1, 0, 1, 1, 0], [1]]], 40000000, 40000000, 2361964), +] + +// Change 0 to 1 or 2 to run the longer workloads. +let problem = problems[0] + +func run_ToddCoxeter(_ n: Int) { + let (rules, maxNodes, maxLinks, expected) = problem + for _ in 0 ..< n { + let result = try! run(rules: rules, maxNodes: maxNodes, maxLinks: maxLinks) + precondition(result == expected) + } +} + +typealias Symbol = UInt8 + +typealias Word = [Symbol] + +struct Rule { + var lhs: Word + var rhs: Word +} + +struct Presentation { + var alphabet: Int + var rules: [Rule] +} + +/// Utility function. This actually only gets called with an input value of '2' +/// so it doesn't need to be optimized. +func nextPowerOfTwo(_ x: Int) -> Int { + precondition(x > 0) + var y = 1 + while y < x { + y *= 2 + } + return y +} + +enum CongruenceError: Error { + case tooManyNodes + case tooManyLinks +} + +/// A tree: +/// - The nodes in the tree correspond to the factors of the words that appear +/// in the monoid's defining relations. +/// - For each letter 'a' in the alphabet, there is an edge joniing each node +/// 'x' with the node 'ax'. +/// - Each node then stores the index of each defining relation where one of the +/// two sides has the node's word as a prefix. +/// - The nodes are stored in a flat array, with the root appearing at index 0. +struct FelschTree { + typealias Node = Int16 + + static let none: Node = -1 + + var alphabet: Int + + /// Mapping from node index to list of defining relations that have this node's + /// word as a prefix. + var values: [[Int]] = [] + + /// An array storing the mth child of the nth node at index 'n * alphabet + m'. + var children: [Node] = [] + + init(alphabet: Int) { + self.alphabet = alphabet + + let root = createNode() + precondition(root == 0) + } + + func child(_ node: Node, _ s: Symbol) -> Int { + return Int(node) * alphabet + Int(s) + } + + func follow(_ node: Node, _ s: Symbol) -> Node { + return children[child(node, s)] + } + + mutating func setValue(_ node: Node, _ value: [Int]) { + precondition(values[Int(node)].isEmpty) + values[Int(node)] = value + } + + mutating func createNode() -> Node { + let node = Node(values.count) + values.append([]) + children.append(contentsOf: Array(repeating: Self.none, count: alphabet)) + return node + } + + mutating func insert(word: Word, from: Int, value: [Int]) { + precondition(from > 0) + + var from = from + var j: Node = 0 + + while from > 0 { + let s = word[from - 1] + var next = children[child(j, s)] + if next == Self.none { + next = createNode() + children[child(j, s)] = next + } + j = next + from -= 1 + } + + setValue(j, value) + } + + mutating func insert(word: Word, value: [Int]) { + insert(word: word, from: word.count, value: value) + } +} + +extension Presentation { + func buildFelschTree() -> FelschTree { + // Collect unique factors of the defining relations. + var factors: [Word] = [] + var unique: Set = [] + + for rule in rules { + for side in [rule.lhs, rule.rhs] { + if side.isEmpty { + continue + } + for i in 0 ... side.count { + for j in i ..< side.count { + let factor = Word(side[i ... j]) + if unique.insert(factor).inserted { + factors.append(factor) + } + } + } + } + } + + // Build the tree. + var result = FelschTree(alphabet: alphabet) + for factor in factors { + let value = rules.indices.filter { + return rules[$0].lhs.starts(with: factor) || + rules[$0].rhs.starts(with: factor) + } + + if !value.isEmpty { + // Add a node for each factor that is a prefix of at least one side of + // at least one defining relation. + result.insert(word: factor, value: value) + } + } + + return result + } +} + +struct Congruence: ~Copyable { + typealias Node = Int32 + typealias Link = Int32 + + static let none: Int32 = -1 + + let p: Presentation + + // Base-2 logarithm of the alphabet rounded up to the next power of two. + // Shifting by ashift converts between indices into the 'values' and + // 'parents' arrays below. + let ashift: Int + + let tree: FelschTree + + // Counters. + var iterations = 0 + var merged = 0 + var checked = 0 + var nodes = 0 + + // First node we have not yet performed "TC1" on. + var cursor: Node = 0 + + // The word graph is represented as an array of nodes. Nodes are identified + // by their index in this array. Nodes are sized to the alphabet, rounded up + // to the next power of two, so the index is always a multiple of the node + // size. The mth child of the nth node is stored at index 'm + n'. This is the + // destination of the edge labeled by the nth letter of the alphabet. + var values: [Node] + + // Union-find map, the parent of the nth node is stored at index 'n >> ashift'. + var parents: [Node] + + // Next word graph nodes that is unused. + var freeNode: Node = 0 + + // For each node, we also store a linked list of predecessors. The head of the + // mth list of predecessors for the nth node is stored at index 'm + n' in this + // array. This list points at all nodes whose mth child is n. + var heads: [Link] + + // A list of linked list nodes + var links: [(Node, Link)] + + // Next list node that is unused. + var freeLink: Link = 0 + + // When we record an edge from 'x' to 'y' labeled by 's', we add (x, s) to + // this list. + var recentList: [(Node, Symbol)] = [] + + // Work list for steps "PD1" and "PD2" in the modified Felsch strategy. + var workList: [(Node, FelschTree.Node)] = [] + + // Work list for step "TC3". + var mergeList: [(Node, Node)] = [] + + init(presentation: Presentation, maxNodes: Int, maxLinks: Int) { + self.p = presentation + ashift = nextPowerOfTwo(p.alphabet) + tree = p.buildFelschTree() + values = Array(repeating: Self.none, count: maxNodes << ashift) + parents = Array(repeating: Self.none, count: maxNodes) + heads = Array(repeating: Self.none, count: maxNodes << ashift) + links = Array(repeating: (Self.none, Self.none), + count: maxLinks << ashift) + } + + mutating func find(_ node: Node) -> Node { + let index = Int(node) >> ashift + let parent = parents[index] + if node != parent { + let canonical = find(parent) + if canonical != parent { + parents[index] = canonical + } + return canonical + } + + return node + } + + /// Allocate a graph node. + mutating func createNode() throws(CongruenceError) -> Node { + if freeNode == values.count { + throw CongruenceError.tooManyNodes + } + + let node = freeNode + freeNode += (1 << ashift) + + nodes += 1 + parents[Int(node) >> ashift] = node + + return node + } + + /// Allocate a linked list node. + mutating func createLink() throws(CongruenceError) -> Link { + if freeLink == links.count { + throw CongruenceError.tooManyLinks + } + + let link = freeLink + freeLink += 1 + return link + } + + /// Record a predecessor of a node in the reverse index. + mutating func recordLink(from: Node, label: Symbol, to: Node) + throws(CongruenceError) { + let link = try createLink() + let index = Int(to) + Int(label) + links[Int(link)] = (from, heads[index]) + heads[index] = link + } + + func rawEdge(from: Node, label: Symbol) -> Node { + let index = Int(from) + Int(label) + return values[index] + } + + func edge(from: Node, label: Symbol) -> Node { + let next = rawEdge(from: from, label: label) + if next == Self.none { + return next + } + precondition(parents[Int(next) >> ashift] == next) + return next + } + + func follow(from: Node, word: some Sequence) -> Node { + var node = from + for s in word { + node = edge(from: node, label: s) + if node == Self.none { + break + } + } + return node + } + + mutating func setEdge(from: Node, label: Symbol, to: Node, + overwrite: Bool=false) throws(CongruenceError) { + precondition(parents[Int(from) >> ashift] == from) + precondition(parents[Int(to) >> ashift] == to) + + let i = Int(from) + Int(label) + precondition(overwrite != (values[i] == Self.none)) + + values[i] = to + + try recordLink(from: from, label: label, to: to) + + recentList.append((from, label)) + } + + /// TC1: Define a new node. + mutating func expand(from: Node) throws(CongruenceError) -> Bool { + var changed = false + + for s in 0 ..< Symbol(p.alphabet) { + let next = edge(from: from, label: s) + if next == Self.none { + let to = try createNode() + try setEdge(from: from, label: s, to: to) + changed = true + } + } + + return changed + } + + /// TC2: Follow paths defined by a relation. + mutating func relation(from: Node, rule: Rule) throws(CongruenceError) { + checked += 1 + + if !rule.lhs.isEmpty && !rule.rhs.isEmpty { + let lhsP = rule.lhs.dropLast() + let xP = follow(from: from, word: lhsP) + if xP == Self.none { + return + } + let lhsS = rule.lhs.last! + let x = edge(from: xP, label: lhsS) + + let rhsP = rule.rhs.dropLast() + let yP = follow(from: from, word: rhsP) + if yP == Self.none { + return + } + let rhsS = rule.rhs.last! + let y = edge(from: yP, label: rhsS) + + if x == y { + return + } else if x == Self.none { + precondition(y != Self.none) + try setEdge(from: xP, label: lhsS, to: y) + return + } else if y == Self.none { + precondition(x != Self.none) + try setEdge(from: yP, label: rhsS, to: x) + return + } else { + precondition(x != y) + mergeList.append((x, y)) + return + } + } else if !rule.lhs.isEmpty && rule.rhs.isEmpty { + let lhsP = rule.lhs.dropLast() + + let xP = follow(from: from, word: lhsP) + if xP == Self.none { + return + } + + let lhsS = rule.lhs.last! + let x = edge(from: xP, label: lhsS) + + if x == from { + return + } else if x == Self.none { + try setEdge(from: xP, label: lhsS, to: from) + return + } else { + mergeList.append((x, from)) + return + } + } else if rule.lhs.isEmpty && !rule.rhs.isEmpty { + let rhsP = rule.rhs.dropLast() + let yP = follow(from: from, word: rhsP) + if yP == Self.none { + return + } + + let rhsS = rule.rhs.last! + let y = edge(from: yP, label: rhsS) + + if y == from { + return + } else if y == Self.none { + try setEdge(from: yP, label: rhsS, to: from) + return + } else { + mergeList.append((y, from)) + return + } + } + + /// The if statements above should be exhaustive. + fatalError() + } + + /// Reverse index walk using Felsch tree. Given a recently-added edge, + /// walk all nodes that possibly changed as a result, and re-apply "TC2" + /// to each node. + mutating func closure(from: Node, label: Symbol) throws(CongruenceError) { + let node = tree.follow(0, label) + precondition(node != FelschTree.none) + + precondition(workList.isEmpty) + workList.append((from, node)) + + while !workList.isEmpty { + let (from, node) = workList.removeLast() + precondition(parents[Int(from) >> ashift] == from) + + for i in tree.values[Int(node)] { + try relation(from: from, rule: p.rules[i]) + } + + for s in 0 ..< Symbol(p.alphabet) { + let child = tree.follow(node, s) + if child != FelschTree.none { + var link = heads[Int(from) + Int(s)] + while link != Self.none { + let pred = find(links[Int(link)].0) + workList.append((pred, child)) + link = links[Int(link)].1 + } + } + } + } + } + + /// Step "PD2" in modified Felsch strategy. + mutating func closure() throws(CongruenceError) { + while !recentList.isEmpty { + let (node, s) = recentList.removeLast() + if parents[Int(node) >> ashift] == node { + try closure(from: node, label: s) + } + + try merge() + } + } + + /// TC3: Process coincidences or a determination. + mutating func merge() throws(CongruenceError) { + func mergeLinks(_ x: Node, _ y: Node) throws(CongruenceError) { + for s in 0 ..< Symbol(p.alphabet) { + let index = Int(x) + Int(s) + var link = heads[index] + while link != Self.none { + let ref = links[Int(link)].0 + if parents[Int(ref) >> ashift] == ref { + let next = rawEdge(from: ref, label: s) + if next != y { + try setEdge(from: ref, label: s, to: y, overwrite: true) + } + } + link = links[Int(link)].1 + } + } + } + + func union(_ x: Node, _ y: Node) throws(CongruenceError) { + var x = find(x) + var y = find(y) + if x == y { + return + } else if x < y { + (x, y) = (y, x) + } + + parents[Int(x) >> ashift] = y + + try mergeLinks(x, y) + + merged += 1 + + for i in 0 ..< Symbol(p.alphabet) { + let xx = edge(from: x, label: i) + let yy = edge(from: y, label: i) + + if xx != yy && xx != Self.none && yy != Self.none { + mergeList.append((xx, yy)) + } else if yy == Self.none && xx != Self.none { + try setEdge(from: y, label: i, to: xx) + } + } + } + + while !mergeList.isEmpty { + let (x, y) = mergeList.removeLast() + try union(x, y) + } + } + + /// Run the congruence enumeration. + mutating func enumerate() throws(CongruenceError) -> Int { + let root = try! createNode() + precondition(root == 0) + + repeat { + try closure() + + while Int(cursor) < values.count { + if parents[Int(cursor) >> ashift] == cursor { + if try expand(from: cursor) { + break + } + } + + cursor += 1 + } + + precondition(mergeList.isEmpty) + } while !recentList.isEmpty + + return nodes - merged + } +} diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index 8c2a30900ca..dc5500c71b8 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -217,6 +217,7 @@ import Suffix import SuperChars import TaskGroups import TaskLocalGet +import ToddCoxeter import TwoSum import TypeFlood import UTF8Decode @@ -433,6 +434,7 @@ register(Suffix.benchmarks) register(SuperChars.benchmarks) register(TaskGroups.benchmarks) register(TaskLocalGet.benchmarks) +register(ToddCoxeter.benchmarks) register(TwoSum.benchmarks) register(TypeFlood.benchmarks) register(UTF8Decode.benchmarks) diff --git a/include/swift/ABI/InvertibleProtocols.h b/include/swift/ABI/InvertibleProtocols.h index 895d199f41a..48af490dc09 100644 --- a/include/swift/ABI/InvertibleProtocols.h +++ b/include/swift/ABI/InvertibleProtocols.h @@ -40,7 +40,7 @@ class InvertibleProtocolSet { StorageType bits; /// Retrieve the mask for this bit. - static StorageType getMask(InvertibleProtocolKind kind) { + static constexpr StorageType getMask(InvertibleProtocolKind kind) { return 1 << static_cast(kind); } @@ -48,7 +48,7 @@ public: explicit constexpr InvertibleProtocolSet(StorageType bits) : bits(bits) {} constexpr InvertibleProtocolSet() : bits(0) {} - InvertibleProtocolSet( + constexpr InvertibleProtocolSet( std::initializer_list elements ) : bits(0) { for (auto element : elements) @@ -56,51 +56,51 @@ public: } /// Retrieve the raw bits that describe this set. - StorageType rawBits() const { return bits; } + constexpr StorageType rawBits() const { return bits; } /// Whether the set contains no protocols. - bool empty() const { return bits == 0; } + constexpr bool empty() const { return bits == 0; } /// Check whether the set contains the specific invertible protocol. - bool contains(InvertibleProtocolKind kind) const { + constexpr bool contains(InvertibleProtocolKind kind) const { return bits & getMask(kind); } /// Insert the invertible protocol into the set. - void insert(InvertibleProtocolKind kind) { + constexpr void insert(InvertibleProtocolKind kind) { bits = bits | getMask(kind); } /// Insert all of the invertible protocols from the other set into this /// one. - void insertAll(InvertibleProtocolSet other) { + constexpr void insertAll(InvertibleProtocolSet other) { bits |= other.bits; } /// Remove all of the invertible protocols from this one not present /// in the other set. - void intersect(InvertibleProtocolSet other) { + constexpr void intersect(InvertibleProtocolSet other) { bits &= other.bits; } /// Remove the given invertible protocol from the set. - void remove(InvertibleProtocolKind kind) { + constexpr void remove(InvertibleProtocolKind kind) { uint16_t mask = getMask(kind); bits = bits & ~mask; } /// Clear out all of the protocols from the set. - void clear() { bits = 0; } + constexpr void clear() { bits = 0; } #define INVERTIBLE_PROTOCOL(Name, Bit) \ - bool contains##Name() const { \ + constexpr bool contains##Name() const { \ return contains(InvertibleProtocolKind::Name); \ } #include "swift/ABI/InvertibleProtocols.def" /// Produce a invertible protocol set containing all known invertible /// protocols. - static InvertibleProtocolSet allKnown() { + static constexpr InvertibleProtocolSet allKnown() { InvertibleProtocolSet result; #define INVERTIBLE_PROTOCOL(Name, Bit) \ result.insert(InvertibleProtocolKind::Name); @@ -114,7 +114,7 @@ public: /// or mangled names generated by a newer compiler that has introduced /// another kind of invertible protocol. The Swift runtime will need to /// step lightly around protocol sets with unknown protocols. - bool hasUnknownProtocols() const { + constexpr bool hasUnknownProtocols() const { return !(*this - allKnown()).empty(); } @@ -198,32 +198,32 @@ public: iterator begin() const { return iterator(rawBits()); } iterator end() const { return iterator(0); } - friend bool operator==( + friend constexpr bool operator==( InvertibleProtocolSet lhs, InvertibleProtocolSet rhs) { return lhs.bits == rhs.bits; } - friend bool operator!=( + friend constexpr bool operator!=( InvertibleProtocolSet lhs, InvertibleProtocolSet rhs) { return lhs.bits != rhs.bits; } - friend InvertibleProtocolSet operator-( + friend constexpr InvertibleProtocolSet operator-( InvertibleProtocolSet lhs, InvertibleProtocolSet rhs) { return InvertibleProtocolSet(lhs.bits & ~rhs.bits); } - InvertibleProtocolSet &operator-=(InvertibleProtocolSet rhs) { + constexpr InvertibleProtocolSet &operator-=(InvertibleProtocolSet rhs) { bits = bits & ~rhs.bits; return *this; } - friend InvertibleProtocolSet operator|( + friend constexpr InvertibleProtocolSet operator|( InvertibleProtocolSet lhs, InvertibleProtocolSet rhs) { return InvertibleProtocolSet(lhs.bits | rhs.bits); } - InvertibleProtocolSet &operator|=(InvertibleProtocolSet rhs) { + constexpr InvertibleProtocolSet &operator|=(InvertibleProtocolSet rhs) { bits |= rhs.bits; return *this; } diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 90c6ae857e6..e7226ba34d3 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -911,6 +911,12 @@ BridgedAllowFeatureSuppressionAttr_createParsed(BridgedASTContext cContext, bool inverted, BridgedArrayRef cFeatures); +SWIFT_NAME("BridgedPreInverseGenericsAttr.createParsed(_:atLoc:range:)") +BridgedPreInverseGenericsAttr +BridgedPreInverseGenericsAttr_createParsed(BridgedASTContext cContext, + swift::SourceLoc atLoc, + swift::SourceRange range); + SWIFT_NAME( "BridgedBackDeployedAttr.createParsed(_:atLoc:range:platform:version:)") BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed( diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 90620d66f5e..eb222e0378a 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -16,6 +16,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/FreestandingMacroExpansion.h" +#include "swift/ABI/InvertibleProtocols.h" #include "swift/AST/SILThunkKind.h" #include "swift/AST/Types.h" #include "swift/Basic/Mangler.h" @@ -81,8 +82,8 @@ protected: /// If enabled, marker protocols can be encoded in the mangled name. bool AllowMarkerProtocols = true; - /// If enabled, inverses will not be mangled into generic signatures. - bool AllowInverses = true; + /// The set of inverses allowed to be mangled into generic signatures. + InvertibleProtocolSet AllowedInverses = InvertibleProtocolSet::allKnown(); /// If enabled, @isolated(any) can be encoded in the mangled name. /// Suppressing type attributes this way is generally questionable --- @@ -560,8 +561,8 @@ protected: BaseEntitySignature(const Decl *decl); }; - static bool inversesAllowed(const Decl *decl); - static bool inversesAllowedIn(const DeclContext *ctx); + static InvertibleProtocolSet inversesAllowed(const Decl *decl); + static InvertibleProtocolSet inversesAllowedIn(const DeclContext *ctx); void appendContextOf(const ValueDecl *decl, BaseEntitySignature &base); void appendContextualInverses(const GenericTypeDecl *contextDecl, diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index ec5531ef24c..e44fa8750f5 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -16,6 +16,7 @@ #ifndef SWIFT_ATTR_H #define SWIFT_ATTR_H +#include "swift/ABI/InvertibleProtocols.h" #include "swift/AST/ASTAllocated.h" #include "swift/AST/AttrKind.h" #include "swift/AST/AutoDiff.h" @@ -3616,6 +3617,56 @@ public: Decl *attachedTo) const; }; +/// The @_preInverseGenerics attribute +class PreInverseGenericsAttr : public DeclAttribute { + /// The potentially unresolved 'ExceptType'. + TypeRepr *ExceptTypeRepr; + + /// A ProtocolCompositionType whose contained inverses are those that should + /// be KEPT in the mangling of the decl to which this attribute is attached. + /// + /// A bare `@_preInverseGenerics` is semantically equivalent to + /// @_preInverseGenerics(except: Any) because `Any` contains no inverses, thus + /// such an attribute will resolve `ExceptType` to `Any`. + Type ExceptType; + + friend class ResolvePreInverseGenericsRequest; + +public: + PreInverseGenericsAttr(SourceLoc AtLoc, SourceRange Range, + TypeRepr *exceptRepr = nullptr, + Type exceptType = Type()); + + /// If the 'except:' argument was present, this may still be null in the case + /// of a deserialized attribute. The `hasExcept` query is more reliable. + TypeRepr *getExceptTypeRepr() const { return ExceptTypeRepr; } + + /// \returns a ProtocolCompositionType whose inverses represent those that + /// must be kept when mangling. + Type getResolvedExceptType(const Decl *attachedTo) const; + + /// True if this attribute was written with an `except:` argument. + bool hasExcept(const Decl *attachedTo) const { + return getExceptTypeRepr() != nullptr || + !getAllowedInverses(attachedTo).empty(); + } + + /// \returns the set of inverses allowed to be mangled. + InvertibleProtocolSet getAllowedInverses(const Decl *attachedTo) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::PreInverseGenerics; + } + + PreInverseGenericsAttr *clone(ASTContext &ctx) const { + return new (ctx) + PreInverseGenericsAttr(AtLoc, Range, ExceptTypeRepr, ExceptType); + } + + bool isEquivalent(const PreInverseGenericsAttr *other, + Decl *attachedTo) const; +}; + /// Defines the @abi attribute. class ABIAttr : public DeclAttribute { friend class DeclAttribute; diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index de1600a6a80..36338ff81d4 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -832,7 +832,7 @@ DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression, 157) DECL_ATTR_ALIAS(_disallowFeatureSuppression, AllowFeatureSuppression) -SIMPLE_DECL_ATTR(_preInverseGenerics, PreInverseGenerics, +DECL_ATTR(_preInverseGenerics, PreInverseGenerics, OnAbstractFunction | OnSubscript | OnVar | OnExtension, UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 158) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 8751e58020b..b1c68078402 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1902,6 +1902,9 @@ ERROR(attr_rawlayout_expected_integer_alignment,none, ERROR(attr_rawlayout_expected_params,none, "expected %1 argument after %0 argument in '@_rawLayout'", (StringRef, StringRef)) +ERROR(attr_pre_inverse_generics_expected_except,none, + "expected 'except:' argument in '@_preInverseGenerics'", ()) + ERROR(attr_extern_expected_label,none, "expected %0 argument to '@_extern'", (StringRef)) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a2b9819be0c..acdcf02b8ea 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4462,6 +4462,16 @@ ERROR(attr_rawlayout_invalid_count_type,none, "'@_rawLayout' count must either be integer literal or a generic " "argument", ()) +ERROR(attr_pre_inverse_generics_invalid_except,none, + "'except' argument to '@_preInverseGenerics' must consist only of " + "inverse constraints such as '~Copyable' or '~Escapable'", ()) +WARNING(attr_pre_inverse_generics_except_all,none, + "'@_preInverseGenerics' that excepts all inverse constraints is " + "equivalent to not using the attribute", ()) +WARNING(attr_pre_inverse_generics_on_extension,none, + "'@_preInverseGenerics' has no effect on an extension; place it on " + "individual members instead", ()) + // lazy ERROR(lazy_not_on_let,none, "'lazy' cannot be used on a let", ()) diff --git a/include/swift/AST/ExistentialLayout.h b/include/swift/AST/ExistentialLayout.h index ad45c33a5e3..11f56536b6d 100644 --- a/include/swift/AST/ExistentialLayout.h +++ b/include/swift/AST/ExistentialLayout.h @@ -126,10 +126,11 @@ struct ExistentialLayout { /// is relevant for the mangler to mangle as a symbolic link where possible /// and for IRGen directly emitting some existentials. /// - /// If 'allowInverses' is false, then regardless of if this existential layout - /// has inverse requirements those will not influence the need for having a - /// shape. - bool needsExtendedShape(bool allowInverses = true) const; + /// If 'allowedInverses' is empty, then regardless of if this existential + /// layout has inverse requirements those will not influence the need for + /// having a shape. + bool needsExtendedShape(InvertibleProtocolSet allowedInverses = + InvertibleProtocolSet::allKnown()) const; private: SmallVector protocols; diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index d1af822138f..0a5aadc2ddc 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -418,6 +418,21 @@ public: uncurry(ASTContext &ctx, ArrayRef inner, unsigned numInnerParams, unsigned numOuterParams); + /// Compute the lifetime dependencies for the result of a partial application + /// of a SIL function with the given lifetimes and number of parameters, + /// binding the given number of arguments. + /// + /// Dependencies on parameters that are bound by the partial_apply are + /// replaced with 'captures' dependencies. + /// + /// Example: binding 1 argument of a 2-argument function. + /// %b = ... : B + /// %f = function_ref @closure : (A, B) -> @lifetime(borrow 1) C + /// %c = partial_apply %f(%b) : (A) -> @lifetime(captures) C + static ArrayRef + partialApply(ASTContext &ctx, ArrayRef lifetimes, + unsigned numFormalParams, unsigned numBoundParams); + bool operator==(const LifetimeDependenceInfo &other) const { return this->hasImmortalSpecifier() == other.hasImmortalSpecifier() && this->hasCaptures() == other.hasCaptures() && diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index ae1c16c632f..e2b385d8921 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -16,6 +16,7 @@ #ifndef SWIFT_TYPE_CHECK_REQUESTS_H #define SWIFT_TYPE_CHECK_REQUESTS_H +#include "swift/ABI/InvertibleProtocols.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTTypeIDs.h" #include "swift/AST/ActorIsolation.h" @@ -66,6 +67,7 @@ struct PropertyWrapperMutability; class RequirementRepr; class ReturnStmt; class AbstractSpecializeAttr; +class PreInverseGenericsAttr; class TrailingWhereClause; class TypeAliasDecl; class TypeLoc; @@ -3847,6 +3849,26 @@ public: void cacheResult(Type value) const; }; +class ResolvePreInverseGenericsRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + Type evaluate(Evaluator &evaluator, + Decl *decl, + PreInverseGenericsAttr *attr) const; + +public: + bool isCached() const { return true; } + std::optional getCachedResult() const; + void cacheResult(Type value) const; +}; + class ResolveRawLayoutTypeRequest : public SimpleRequest>(Meta); + auto elementAddress = RemoteAddress( + fixedArray->Element, MetadataAddress.getAddressSpace()); + auto Element = + readTypeFromMetadata(elementAddress, false, recursion_limit); + if (!Element) return BuiltType(); + + auto count = static_cast(fixedArray->Count); + BuiltType Size = (count < 0) ? Builder.createNegativeIntegerType(count) + : Builder.createIntegerType(count); + if (!Size) return BuiltType(); + + auto BuiltFixedArray = Builder.createBuiltinFixedArrayType(Size, Element); + TypeCache[TypeCacheKey] = BuiltFixedArray; + return BuiltFixedArray; + } case MetadataKind::HeapLocalVariable: case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: @@ -3150,14 +3167,28 @@ private: auto numGenericArgs = generics->getGenericContextHeader().getNumArguments(); - + auto offsetToGenericArgs = readGenericArgsOffset(metadata, descriptor); if (!offsetToGenericArgs) return {}; - auto genericArgsAddr = getAddress(metadata) + auto genericArgsBaseAddr = getAddress(metadata) + sizeof(StoredPointer) * *offsetToGenericArgs; + // The generic argument layout begins with one StoredSize "shape class" + // entry per same-shape equivalence class. These hold pack lengths for + // pack-typed key arguments. Skip past them to reach the metadata pointers. + auto packShapeHeader = generics->getGenericPackShapeHeader(); + auto packShapeDescriptors = generics->getGenericPackShapeDescriptors(); + if (numGenericArgs < packShapeHeader.NumShapeClasses) + return {}; + numGenericArgs -= packShapeHeader.NumShapeClasses; + + auto genericArgsAddr = genericArgsBaseAddr + + sizeof(StoredPointer) * packShapeHeader.NumShapeClasses; + + unsigned packIndex = 0; + std::vector builtSubsts; for (auto param : generics->getGenericParams()) { switch (param.getKind()) { @@ -3187,10 +3218,65 @@ private: return {}; } break; - + case GenericParamKind::TypePack: - // assert(false && "Packs not supported here yet"); - return {}; + if (param.hasKeyArgument()) { + if (numGenericArgs == 0) + return {}; + --numGenericArgs; + + // Find the matching pack shape descriptor. By invariant the + // metadata pack descriptors come before any witness table pack + // descriptors, in the same order as the pack-typed parameters + // with key arguments, so the next descriptor is ours. + if (packIndex >= packShapeDescriptors.size()) + return {}; + const auto &packDescriptor = packShapeDescriptors[packIndex++]; + if (packDescriptor.Kind != GenericPackKind::Metadata) + return {}; + + // The pack length is stored in the shape class slot at the + // start of the generic argument layout. + auto packLengthAddr = genericArgsBaseAddr + + sizeof(StoredPointer) * packDescriptor.ShapeClass; + StoredSize packLength; + if (!Reader->readInteger(packLengthAddr, &packLength)) + return {}; + + // Read the pack pointer. Its low bit indicates heap vs. stack + // lifetime; the actual element array is at the address with the + // low bit cleared. + StoredPointer rawPackPtr; + if (!Reader->readInteger(genericArgsAddr, &rawPackPtr)) + return {}; + genericArgsAddr += sizeof(StoredPointer); + + auto packElementsAddr = RemoteAddress( + rawPackPtr & ~StoredPointer(1), + genericArgsAddr.getAddressSpace()); + + std::vector elements; + elements.reserve(packLength); + for (StoredSize i = 0; i < packLength; ++i) { + RemoteAddress elementMetadata; + if (!Reader->template readRemoteAddress( + packElementsAddr, elementMetadata)) { + return {}; + } + packElementsAddr += sizeof(StoredPointer); + + auto builtElement = readTypeFromMetadata( + elementMetadata, false, recursion_limit); + if (!builtElement) + return {}; + elements.push_back(builtElement); + } + + builtSubsts.push_back(Builder.createPackType(elements)); + } else { + return {}; + } + break; default: // We don't know about this kind of parameter. diff --git a/include/swift/RemoteInspection/TypeLowering.h b/include/swift/RemoteInspection/TypeLowering.h index 98f0ed33174..144111b198f 100644 --- a/include/swift/RemoteInspection/TypeLowering.h +++ b/include/swift/RemoteInspection/TypeLowering.h @@ -383,17 +383,22 @@ public: /// Array based layouts like Builtin.FixedArray class ArrayTypeInfo : public TypeInfo { + const TypeRef *ElementTR; const TypeInfo *ElementTI; + intptr_t ElementCount; public: - explicit ArrayTypeInfo(intptr_t size, const TypeInfo *elementTI); + explicit ArrayTypeInfo(intptr_t size, const TypeRef *elementTR, + const TypeInfo *elementTI); bool readExtraInhabitantIndex(remote::MemoryReader &reader, remote::RemoteAddress address, int *extraInhabitantIndex) const override; BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override; + const TypeRef *getElementTypeRef() const { return ElementTR; } const TypeInfo *getElementTypeInfo() const { return ElementTI; } + intptr_t getElementCount() const { return ElementCount; } static bool classof(const TypeInfo *TI) { return TI->getKind() == TypeInfoKind::Array; } diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h index ae52d80b5db..e83974458f1 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h @@ -145,6 +145,9 @@ typedef enum swift_layout_kind { SWIFT_CLOSURE_CONTEXT, // A contiguous list of N Ts, typically for Builtin.FixedArray. + // NumFields will report the array count. All children are the same type, so + // users can make a single call to swift_reflection_childOfTypeRef regardless + // of the count. SWIFT_ARRAY, } swift_layout_kind_t; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index c07f99f8603..172ee30ab34 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -5143,7 +5143,6 @@ public: TRIVIAL_ATTR_PRINTER(Override, override) TRIVIAL_ATTR_PRINTER(Owned, owned) TRIVIAL_ATTR_PRINTER(Postfix, postfix) - TRIVIAL_ATTR_PRINTER(PreInverseGenerics, pre_inverse_generics) TRIVIAL_ATTR_PRINTER(Preconcurrency, preconcurrency) TRIVIAL_ATTR_PRINTER(Prefix, prefix) TRIVIAL_ATTR_PRINTER(PropertyWrapper, property_wrapper) @@ -5503,6 +5502,12 @@ public: Label::always("comment_range")); printFoot(); } + void visitPreInverseGenericsAttr(PreInverseGenericsAttr *Attr, Label label) { + printCommon(Attr, "pre_inverse_generics_attr", label); + if (auto *tyR = Attr->getExceptTypeRepr()) + printRec(tyR, Label::optional("except_repr")); + printFoot(); + } void visitRawLayoutAttr(RawLayoutAttr *Attr, Label label) { printCommon(Attr, "raw_layout_attr", label); if (auto *tyR = Attr->getScalarLikeType()) { diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 47591eadd97..cb07e36139f 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -104,18 +104,21 @@ bool ASTMangler::tryMangleSubstitution(const Decl *decl) { return Mangler::tryMangleSubstitution(decl); } -bool ASTMangler::inversesAllowed(const Decl *decl) { +InvertibleProtocolSet ASTMangler::inversesAllowed(const Decl *decl) { if (!decl) - return true; + return InvertibleProtocolSet::allKnown(); if (auto accessor = dyn_cast(decl)) if (auto *storage = accessor->getStorage()) decl = storage; - return !decl->getAttrs().hasAttribute(); + if (auto *attr = decl->getAttrs().getAttribute()) + return attr->getAllowedInverses(decl); + + return InvertibleProtocolSet::allKnown(); } -bool ASTMangler::inversesAllowedIn(const DeclContext *ctx) { +InvertibleProtocolSet ASTMangler::inversesAllowedIn(const DeclContext *ctx) { assert(ctx); return inversesAllowed(ctx->getInnermostDeclarationDeclContext()); } @@ -159,7 +162,7 @@ static StringRef getCodeForAccessorKind(AccessorKind kind) { std::string ASTMangler::mangleClosureEntity(const AbstractClosureExpr *closure, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowedIn(closure)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(closure)); beginMangling(); appendClosureEntity(closure); appendSymbolKind(SKind); @@ -167,7 +170,7 @@ std::string ASTMangler::mangleClosureEntity(const AbstractClosureExpr *closure, } std::string ASTMangler::mangleEntity(const ValueDecl *decl, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(decl)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(decl)); beginMangling(); appendEntity(decl); appendSymbolKind(SKind); @@ -177,7 +180,7 @@ std::string ASTMangler::mangleEntity(const ValueDecl *decl, SymbolKind SKind) { std::string ASTMangler::mangleDestructorEntity(const DestructorDecl *decl, DestructorKind kind, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(decl)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(decl)); beginMangling(); appendDestructorEntity(decl, kind); appendSymbolKind(SKind); @@ -187,7 +190,7 @@ std::string ASTMangler::mangleDestructorEntity(const DestructorDecl *decl, std::string ASTMangler::mangleConstructorEntity(const ConstructorDecl *ctor, bool isAllocating, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(ctor)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(ctor)); beginMangling(); appendConstructorEntity(ctor, isAllocating); appendSymbolKind(SKind); @@ -197,7 +200,7 @@ std::string ASTMangler::mangleConstructorEntity(const ConstructorDecl *ctor, std::string ASTMangler::mangleIVarInitDestroyEntity(const ClassDecl *decl, bool isDestroyer, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(decl)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(decl)); beginMangling(); BaseEntitySignature base(decl); appendContext(decl, base, decl->getAlternateModuleName()); @@ -210,7 +213,7 @@ std::string ASTMangler::mangleAccessorEntity(AccessorKind kind, const AbstractStorageDecl *decl, bool isStatic, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(decl)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(decl)); beginMangling(); appendAccessorEntity(getCodeForAccessorKind(kind), decl, isStatic); appendSymbolKind(SKind); @@ -220,7 +223,7 @@ std::string ASTMangler::mangleAccessorEntity(AccessorKind kind, std::string ASTMangler::mangleDefaultArgumentEntity(const DeclContext *func, unsigned index, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowedIn(func)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(func)); beginMangling(); appendDefaultArgumentEntity(func, index); appendSymbolKind(SKind); @@ -229,7 +232,7 @@ std::string ASTMangler::mangleDefaultArgumentEntity(const DeclContext *func, std::string ASTMangler::mangleInitializerEntity(const VarDecl *var, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); beginMangling(); appendInitializerEntity(var); appendSymbolKind(SKind); @@ -238,7 +241,7 @@ std::string ASTMangler::mangleInitializerEntity(const VarDecl *var, std::string ASTMangler::mangleBackingInitializerEntity(const VarDecl *var, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); beginMangling(); appendBackingInitializerEntity(var); appendSymbolKind(SKind); @@ -248,7 +251,7 @@ std::string ASTMangler::mangleBackingInitializerEntity(const VarDecl *var, std::string ASTMangler::manglePropertyWrappedFieldInitAccessorEntity(const VarDecl *var, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); beginMangling(); appendPropertyWrappedFieldInitAccessorEntity(var); appendSymbolKind(SKind); @@ -257,7 +260,7 @@ ASTMangler::manglePropertyWrappedFieldInitAccessorEntity(const VarDecl *var, std::string ASTMangler::mangleInitFromProjectedValueEntity(const VarDecl *var, SymbolKind SKind) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); beginMangling(); appendInitFromProjectedValueEntity(var); appendSymbolKind(SKind); @@ -295,7 +298,7 @@ std::string ASTMangler::mangleConstructorVTableThunk( } std::string ASTMangler::mangleWitnessTable(const ProtocolConformance *C) { - llvm::SaveAndRestore X(AllowInverses, + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(C->getDeclContext())); beginMangling(); @@ -1634,7 +1637,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig, // ExtendedExistentialTypeShapes consider existential metatypes to // be part of the existential, so if we're symbolically referencing // shapes, we need to handle that at this level. - if (EMT->getExistentialLayout().needsExtendedShape(AllowInverses)) { + if (EMT->getExistentialLayout().needsExtendedShape(AllowedInverses)) { auto referent = SymbolicReferent::forExtendedExistentialTypeShape(EMT); if (canSymbolicReference(referent)) { appendSymbolicExtendedExistentialType(referent, EMT, sig, forDecl); @@ -1643,7 +1646,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig, } if (EMT->getInstanceType()->isExistentialType() && - EMT->getExistentialLayout().needsExtendedShape(AllowInverses)) + EMT->getExistentialLayout().needsExtendedShape(AllowedInverses)) appendConstrainedExistential(EMT->getInstanceType(), sig, forDecl); else appendType(EMT->getInstanceType(), sig, forDecl); @@ -1699,7 +1702,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig, return appendType(strippedTy, sig, forDecl); } - if (PCT->getExistentialLayout().needsExtendedShape(AllowInverses)) + if (PCT->getExistentialLayout().needsExtendedShape(AllowedInverses)) return appendConstrainedExistential(PCT, sig, forDecl); // We mangle ProtocolType and ProtocolCompositionType using the @@ -1714,7 +1717,7 @@ void ASTMangler::appendType(Type type, GenericSignature sig, case TypeKind::Existential: { auto *ET = cast(tybase); - if (ET->getExistentialLayout().needsExtendedShape(AllowInverses)) { + if (ET->getExistentialLayout().needsExtendedShape(AllowedInverses)) { auto referent = SymbolicReferent::forExtendedExistentialTypeShape(ET); if (canSymbolicReference(referent)) { appendSymbolicExtendedExistentialType(referent, ET, sig, forDecl); @@ -2964,14 +2967,23 @@ void ASTMangler::appendSymbolicReference(SymbolicReferent referent) { static void reconcileInverses( SmallVector &inverses, GenericSignature sig, + InvertibleProtocolSet allowedInverses, std::optional inversesAlreadyMangledDepth, std::optional suppressedInnermostDepth) { + if (inverses.empty()) + return; + CanGenericSignature baseSig; if (sig) baseSig = sig.getCanonicalSignature(); - if (baseSig || inversesAlreadyMangledDepth || suppressedInnermostDepth) - llvm::erase_if(inverses, [&](InverseRequirement const& inv) -> bool { + if (baseSig || inversesAlreadyMangledDepth || suppressedInnermostDepth || + allowedInverses != InvertibleProtocolSet::allKnown()) { + llvm::erase_if(inverses, [&](InverseRequirement const &inv) -> bool { + // Drop inverses that aren't to be mangled due to @_preInverseGenerics. + if (!allowedInverses.contains(inv.getKind())) + return true; + // Drop inverses that aren't applicable in the nested / child signature, // because of an added requirement. if (baseSig && baseSig->requiresProtocol(inv.subject, inv.protocol)) @@ -2991,6 +3003,7 @@ static void reconcileInverses( return false; }); + } // Sort inverse requirements for stability. llvm::array_pod_sort( @@ -3723,14 +3736,10 @@ void ASTMangler::gatherGenericSignatureParts(GenericSignature sig, auto &inverseReqs = parts.inverses; canSig->getRequirementsWithInverses(reqs, inverseReqs); - // Process inverses relative to the base entity's signature. - if (AllowInverses) { - // Simplify and canonicalize inverses. - reconcileInverses(inverseReqs, base.getSignature(), base.getDepth(), - base.getSuppressedInnermostInversesDepth()); - } else { - inverseReqs.clear(); - } + // Simplify and canonicalize the inverses. + reconcileInverses(inverseReqs, base.getSignature(), AllowedInverses, + base.getDepth(), + base.getSuppressedInnermostInversesDepth()); base.setDepth(canSig->getMaxDepth()); unsigned &initialParamDepth = parts.initialParamDepth; @@ -4147,14 +4156,14 @@ void ASTMangler::appendDefaultArgumentEntity(const DeclContext *func, } void ASTMangler::appendInitializerEntity(const VarDecl *var) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); BaseEntitySignature base(var); appendEntity(var, base, "vp", var->isStatic()); appendOperator("fi"); } void ASTMangler::appendBackingInitializerEntity(const VarDecl *var) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); BaseEntitySignature base(var); appendEntity(var, base, "vp", var->isStatic()); appendOperator("fP"); @@ -4162,14 +4171,14 @@ void ASTMangler::appendBackingInitializerEntity(const VarDecl *var) { void ASTMangler::appendPropertyWrappedFieldInitAccessorEntity( const VarDecl *var) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); BaseEntitySignature base(var); appendEntity(var, base, "vp", var->isStatic()); appendOperator("fF"); } void ASTMangler::appendInitFromProjectedValueEntity(const VarDecl *var) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(var)); BaseEntitySignature base(var); appendEntity(var, base, "vp", var->isStatic()); appendOperator("fW"); @@ -5351,6 +5360,9 @@ void ASTMangler::extractExistentialInverseRequirements( return; for (auto ip : PCT->getInverses()) { + if (!AllowedInverses.contains(ip)) + continue; + auto *proto = Context.getProtocol(getKnownProtocolKind(ip)); assert(proto); ASSERT(!getABIDecl(proto) && "can't use @abi on inverse protocols"); @@ -5370,8 +5382,7 @@ void ASTMangler::gatherExistentialRequirements( for (auto memberTy : compositionTy->getMembers()) gatherExistentialRequirements(reqs, inverses, memberTy); - if (AllowInverses) - extractExistentialInverseRequirements(inverses, compositionTy); + extractExistentialInverseRequirements(inverses, compositionTy); } } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 5c866e5ad7a..81b7952ef0e 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1710,6 +1710,20 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } + case DeclAttrKind::PreInverseGenerics: { + auto *attr = cast(this); + Printer.printAttrName("@_preInverseGenerics"); + Type exceptTy = attr->getResolvedExceptType(D); + // Avoid printing `@_preInverseGenerics(except: Any)` despite that being the + // meaning of the no-arg version. It's rejected as it can confuse people. + if (exceptTy->getCanonicalType() != D->getASTContext().TheAnyType) { + Printer << "(except: "; + exceptTy.print(Printer, Options); + Printer << ")"; + } + break; + } + case DeclAttrKind::RawLayout: { auto *attr = cast(this); Printer.printAttrName("@_rawLayout"); @@ -1729,6 +1743,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, } else { llvm_unreachable("unhandled @_rawLayout form"); } + if (attr->shouldMoveAsLikeType()) + Printer << ", movesAsLike"; Printer << ")"; break; } @@ -2050,6 +2066,8 @@ StringRef DeclAttribute::getAttrName() const { case MacroSyntax::Attached: return "attached"; } + case DeclAttrKind::PreInverseGenerics: + return "_preInverseGenerics"; case DeclAttrKind::RawLayout: return "_rawLayout"; case DeclAttrKind::Extern: @@ -2295,6 +2313,43 @@ bool TypeEraserAttr::isEquivalent(const TypeEraserAttr *other, return thisType->getCanonicalType() == otherType->getCanonicalType(); } +PreInverseGenericsAttr::PreInverseGenericsAttr(SourceLoc AtLoc, + SourceRange Range, + TypeRepr *exceptRepr, + Type exceptType) + : DeclAttribute(DeclAttrKind::PreInverseGenerics, AtLoc, Range, + /*Implicit=*/false), + ExceptTypeRepr(exceptRepr), ExceptType(exceptType) { + assert(!exceptType || exceptType->is()); +} + +bool PreInverseGenericsAttr::isEquivalent(const PreInverseGenericsAttr *other, + Decl *attachedTo) const { + return eqTypes(getResolvedExceptType(attachedTo), + other->getResolvedExceptType(attachedTo)); +} + +Type PreInverseGenericsAttr::getResolvedExceptType( + const Decl *attachedTo) const { + if (!ExceptType) { + auto &ctx = attachedTo->getASTContext(); + evaluateOrDefault(ctx.evaluator, + ResolvePreInverseGenericsRequest{ + const_cast(attachedTo), + const_cast(this)}, + ctx.TheAnyType); + } + assert(ExceptType && "resolution didn't save the except type?"); + return ExceptType; +} + +InvertibleProtocolSet +PreInverseGenericsAttr::getAllowedInverses(const Decl *attachedTo) const { + return getResolvedExceptType(attachedTo) + ->castTo() + ->getInverses(); +} + Type RawLayoutAttr::getResolvedLikeType(StructDecl *sd) const { auto &ctx = sd->getASTContext(); return evaluateOrDefault(ctx.evaluator, diff --git a/lib/AST/AvailabilityScopeBuilder.cpp b/lib/AST/AvailabilityScopeBuilder.cpp index 92ee6051c19..3d1195944bb 100644 --- a/lib/AST/AvailabilityScopeBuilder.cpp +++ b/lib/AST/AvailabilityScopeBuilder.cpp @@ -1001,6 +1001,7 @@ private: // Tracks if we're refining for availability or unavailability. std::optional isUnavailability = std::nullopt; + bool hasAnyNonAvailabilityCondition = false; for (StmtConditionElement element : cond) { auto *currentScope = getCurrentScope(); @@ -1008,10 +1009,7 @@ private: // If the element is not a condition, walk it in the current scope. if (element.getKind() != StmtConditionElement::CK_Availability) { - // Assume any condition element that is not a #available() can - // potentially be false, so conservatively make the false flow's - // refinement undefined since there is nothing we can prove about it. - falseFlowBuilder.setUndefined(); + hasAnyNonAvailabilityCondition = true; element.walk(*this); continue; } @@ -1137,44 +1135,53 @@ private: ++nestedCount; } + // Determine the availability context for the branch where the availability + // conditions hold. If there are any scopes on the stack, it will be the + // availability context for the scope at the top. Otherwise, no distinct + // context is introduced by the availability conditions. + std::optional trueRefinement = std::nullopt; + if (nestedCount > 0) + trueRefinement = getCurrentScope()->getAvailabilityContext(); + + // Pop the stack. + while (nestedCount-- > 0) + ContextStack.pop_back(); + + DEBUG_ASSERT(getCurrentScope() == startingScope); + + // Determine availability for the branch where the availability conditions + // do not hold. auto startingContext = startingScope->getAvailabilityContext(); auto falseFlowContext = falseFlowBuilder.constrainContext(startingContext); - // The version range for the false branch should never have any versions - // that weren't possible when the condition started evaluating. + // The availability context for the false flow should either be the same + // as the starting context or it should refine it. If not, there's a logic + // error. DEBUG_ASSERT(falseFlowContext.isContainedIn(startingContext)); // If the starting availability context is not completely contained in the // false flow context then it must be the case that false flow context - // is strictly smaller than the starting context (because the false flow - // context *is* contained in the starting context), so we should introduce a - // new availability scope for the false flow. + // is strictly contained in the starting context. Introduce a new + // availability scope for the false flow in that case. std::optional falseRefinement = std::nullopt; - if (!startingScope->getAvailabilityContext().isContainedIn( - falseFlowContext)) { + if (!startingContext.isContainedIn(falseFlowContext)) falseRefinement = falseFlowContext; - } - auto makeResult = - [isUnavailability](std::optional trueRefinement, - std::optional falseRefinement) { - if (isUnavailability.has_value() && *isUnavailability) { - // If this is an unavailability check, invert the result. - return std::make_pair(falseRefinement, trueRefinement); - } - return std::make_pair(trueRefinement, falseRefinement); - }; + // For #unavailable, the then/else semantics are inverted: the then branch + // executes when the availability condition is NOT met, and the else + // executes it IS met. So swap the refinements. + auto thenRefinement = trueRefinement; + auto elseRefinement = falseRefinement; + if (isUnavailability && *isUnavailability) + std::swap(thenRefinement, elseRefinement); - if (nestedCount == 0) - return makeResult(std::nullopt, falseRefinement); + // If there were any non-availability conditions in the if statement then + // the else branch cannot be refined at all because it can be reached + // regardless of any availability condition. + if (hasAnyNonAvailabilityCondition) + elseRefinement = std::nullopt; - AvailabilityScope *nestedScope = getCurrentScope(); - while (nestedCount-- > 0) - ContextStack.pop_back(); - - assert(getCurrentScope() == startingScope); - - return makeResult(nestedScope->getAvailabilityContext(), falseRefinement); + return {thenRefinement, elseRefinement}; } /// Return the best active spec for the target platform or nullptr if no diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index d7092e42d58..662b32257b8 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -184,6 +184,13 @@ BridgedAllowFeatureSuppressionAttr_createParsed(BridgedASTContext cContext, features); } +BridgedPreInverseGenericsAttr +BridgedPreInverseGenericsAttr_createParsed(BridgedASTContext cContext, + SourceLoc atLoc, + SourceRange range) { + return new (cContext.unbridged()) PreInverseGenericsAttr(atLoc, range); +} + BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed( BridgedASTContext cContext, SourceLoc atLoc, SourceRange range, swift::PlatformKind platform, BridgedVersionTuple cVersion) { diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index eb446842222..96a14fd0e6c 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -280,6 +280,21 @@ static bool usesFeatureLifetimes(Decl *decl) { return false; } +static PreInverseGenericsAttr *getPreInverseGenericsExcept(Decl *decl) { + if (auto pbd = dyn_cast(decl)) + for (auto i : range(pbd->getNumPatternEntries())) + if (auto anchorVar = pbd->getAnchoringVarDecl(i)) + return getPreInverseGenericsExcept(anchorVar); + + return decl->getAttrs().getAttribute(); +} + +static bool usesFeaturePreInverseGenericsExcept(Decl *decl) { + if (auto *attr = getPreInverseGenericsExcept(decl)) + return attr->hasExcept(decl); + return false; +} + static bool hasLifetimeDependencies(Type type) { if (auto *aft = type->getAs()) { return aft->hasExplicitLifetimeDependencies(); diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 88a3538e488..d69a98d2ac2 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -1125,6 +1125,11 @@ protected: switch (parsedLifetimeKind) { case ParsedLifetimeDependenceKind::Default: { + // Infer copy dependence on @noescape function types by default. + if (type->isNoEscape()) { + return LifetimeDependenceKind::Inherit; + } + if (type->isEscapable()) { if (loweredOwnership == ValueOwnership::Shared || loweredOwnership == ValueOwnership::InOut) { @@ -1823,13 +1828,19 @@ protected: // The usual diagnostic check is sufficient. return; } - // Do not infer non-escapable dependence kind -- it is ambiguous. + // Do not infer non-escapable dependence kind -- it is ambiguous, except for + // noescape function types, for which we should always infer a copy dependence. auto const ¶mInfo = parameterInfos[0]; Type paramTypeInContext = paramInfo.typeInContext; if (paramTypeInContext->hasError()) { return; } if (!paramTypeInContext->isEscapable()) { + if (paramTypeInContext->isNoEscape()) { + resultDeps->addIfNew(/*paramIndex*/ 0, LifetimeDependenceKind::Inherit); + return; + } + diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_kind, diagnosticQualifier(), paramInfo.name()); return; @@ -2064,6 +2075,90 @@ ArrayRef LifetimeDependenceInfo::uncurry( return ctx.AllocateCopy(uncurried); } +ArrayRef LifetimeDependenceInfo::partialApply( + ASTContext &ctx, ArrayRef lifetimes, + unsigned numFormalParams, unsigned numBoundParams) { + + if (numBoundParams == 0) + return lifetimes; + + ASSERT(numBoundParams <= numFormalParams && + "A partial application can only bind as many parameters as the " + "function has."); + + // How many parameters the resulting closure will have. + const unsigned numClosureParams = numFormalParams - numBoundParams; + + SmallVector curried; + + for (const auto &dep : lifetimes) { + // Determine the new target index. + unsigned targetIndex; + if (dep.getTargetIndex() == numFormalParams) { + // The target is the result. + // Its index is the number of parameters. + targetIndex = numClosureParams; + } else if (dep.getTargetIndex() >= numClosureParams) { + // The target is a captured parameter. + // The resulting closure does not need a lifetime dependence entry for it. + continue; + } else { + // The target is an uncaptured parameter. + // Its index remains the same. + targetIndex = dep.getTargetIndex(); + } + + auto flags = dep.flags; + + const auto captureBoundParams = [&](IndexSubset *indices) -> IndexSubset * { + if (!indices) + return nullptr; + + ASSERT(indices->getCapacity() <= numFormalParams && + "There should be at most 1 index per parameter. SIL functions " + "cannot have " + "an implicit self parameter."); + + auto bits = indices->getBitVector(); + + if (bits.find_last() >= int(numClosureParams)) { + // One of the lifetime source parameters is bound by the partial_apply. + // This becomes a captures dependence in the resulting closure. + flags.setCaptures(true); + } + + // Remove the indices of the captured parameters, leaving only those of + // the closure parameters. + + if (bits.find_first() >= int(numClosureParams)) { + // All lifetime sources are captured. The resulting empty list of + // indices should be represented with a nullptr. + return nullptr; + } + + if (bits.size() > numClosureParams) + bits.resize(numClosureParams); + + return IndexSubset::get(ctx, bits); + }; + + auto inherit = captureBoundParams(dep.getInheritIndices()); + auto scope = captureBoundParams(dep.getScopeIndices()); + auto addressable = captureBoundParams(dep.getAddressableIndices()); + auto conditionallyAddressable = + captureBoundParams(dep.getConditionallyAddressableIndices()); + + curried.push_back(LifetimeDependenceInfo(inherit, scope, targetIndex, + addressable, + conditionallyAddressable, flags)); + } + + // FIXME: Avoid allocating context memory for every partial apply. Instead, + // cache a single uniqueLifetimeDependenceInfo array for each combination + // of FunctionType + numBoundParams. + return ctx.AllocateCopy(curried); +} + void LifetimeDependenceInfo::dump() const { llvm::errs() << "target: " << getTargetIndex() << '\n'; if (hasImmortalSpecifier()) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 4f5f64645d8..9ddde061f62 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -440,14 +440,14 @@ Type ExistentialLayout::getSuperclass() const { return Type(); } -bool ExistentialLayout::needsExtendedShape(bool allowInverses) const { +bool ExistentialLayout::needsExtendedShape( + InvertibleProtocolSet allowedInverses) const { if (!getParameterizedProtocols().empty()) return true; - if (allowInverses && hasInverses()) - return true; - - return false; + // Would any inverses in this layout would be considered by the mangler? + allowedInverses.intersect(inverses); + return !allowedInverses.empty(); } bool TypeBase::isObjCExistentialType() { diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index fa35a2aa44d..a6a486429e7 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1633,6 +1633,25 @@ void ResolveTypeEraserTypeRequest::cacheResult(Type value) const { } } +//----------------------------------------------------------------------------// +// ResolvePreInverseGenericsRequest computation. +//----------------------------------------------------------------------------// + +std::optional ResolvePreInverseGenericsRequest::getCachedResult() const { + auto *attr = std::get<1>(getStorage()); + auto Ty = attr->ExceptType; + if (!Ty) + return std::nullopt; + + return Ty; +} + +void ResolvePreInverseGenericsRequest::cacheResult(Type Ty) const { + auto *attr = std::get<1>(getStorage()); + assert(Ty && Ty->is()); + attr->ExceptType = Ty; +} + //----------------------------------------------------------------------------// // ResolveRawLayoutTypeRequest computation. //----------------------------------------------------------------------------// diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 27cddcaf8e6..6ce346dc429 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -176,6 +176,8 @@ extension ASTGenVisitor { return handle(self.generateOptimizeAttr(attribute: node)?.asDeclAttribute) case .OriginallyDefinedIn: return self.generateOriginallyDefinedInAttr(attribute: node).forEach { handle($0.asDeclAttribute) } + case .PreInverseGenerics: + return handle(self.generatePreInverseGenericsAttr(attribute: node)?.asDeclAttribute) case .PrivateImport: return handle(self.generatePrivateImportAttr(attribute: node)?.asDeclAttribute) case .ProjectedValueProperty: @@ -296,7 +298,6 @@ extension ASTGenVisitor { .ObjCNonLazyRealization, .Owned, .Preconcurrency, - .PreInverseGenerics, .PropertyWrapper, .Reparentable, .RequiresStoredPropertyInits, @@ -1732,6 +1733,23 @@ extension ASTGenVisitor { ) } + func generatePreInverseGenericsAttr(attribute node: AttributeSyntax) -> BridgedPreInverseGenericsAttr? { + self.generateWithLabeledExprListArguments(attribute: node) { args in + switch args.first?.label?.rawText { + case "except": + fatalError("ASTGen does not yet support the except: argument") + default: + // TODO: Diagnose. + fatalError("invalid argument for @_preInverseGenerics attribute") + } + return .createParsed( + self.ctx, + atLoc: self.generateSourceLoc(node.atSign), + range: self.generateAttrSourceRange(node) + ) + } + } + func generateRawLayoutAttr(attribute node: AttributeSyntax) -> BridgedRawLayoutAttr? { self.generateWithLabeledExprListArguments(attribute: node) { args in switch args.first?.label?.rawText { diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 6e989bbbbcf..85be36d89b7 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1622,7 +1622,11 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t, // Otherwise, see if a layout has been emitted with these characteristics // already. - FixedLayoutKey key{size, numExtraInhabitants, align, pod, unsigned(bt)}; + bool addressableForDependencies = + getTypeProperties(silTy, TypeExpansionContext::minimal()) + .isAddressableForDependencies(); + FixedLayoutKey key{size, numExtraInhabitants, align, + pod, unsigned(bt), addressableForDependencies}; auto found = PrivateFixedLayouts.find(key); if (found != PrivateFixedLayouts.end()) @@ -1657,7 +1661,8 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t, "type_layout_" + llvm::Twine(size) + "_" + llvm::Twine(align) + "_" + llvm::Twine::utohexstr(numExtraInhabitants) - + pod_bt_string(pod, bt), + + pod_bt_string(pod, bt) + + (addressableForDependencies ? "_afd" : ""), getPointerAlignment(), /*constant*/ true, llvm::GlobalValue::PrivateLinkage); diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index e4815b850c1..f72726dd748 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -218,7 +218,7 @@ IRGenMangler::mangleTypeForFlatUniqueTypeRef(CanGenericSignature sig, std::string IRGenMangler::mangleProtocolConformanceDescriptor( const RootProtocolConformance *conformance) { - llvm::SaveAndRestore X(AllowInverses, + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(conformance->getDeclContext())); beginMangling(); @@ -235,7 +235,7 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptor( std::string IRGenMangler::mangleProtocolConformanceDescriptorRecord( const RootProtocolConformance *conformance) { - llvm::SaveAndRestore X(AllowInverses, + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(conformance->getDeclContext())); beginMangling(); @@ -246,7 +246,7 @@ std::string IRGenMangler::mangleProtocolConformanceDescriptorRecord( std::string IRGenMangler::mangleProtocolConformanceInstantiationCache( const RootProtocolConformance *conformance) { - llvm::SaveAndRestore X(AllowInverses, + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(conformance->getDeclContext())); beginMangling(); @@ -556,7 +556,7 @@ IRGenMangler::appendExtendedExistentialTypeShape(CanGenericSignature genSig, // Append the generalization signature. if (genSig) { // Generalization signature never mangles inverses. - llvm::SaveAndRestore X(AllowInverses, false); + llvm::SaveAndRestore X(AllowedInverses, InvertibleProtocolSet()); appendGenericSignature(genSig); } @@ -571,7 +571,7 @@ std::string IRGenMangler::mangleConformanceSymbol(Type type, const ProtocolConformance *Conformance, const char *Op) { - llvm::SaveAndRestore X(AllowInverses, + llvm::SaveAndRestore X(AllowedInverses, inversesAllowedIn(Conformance->getDeclContext())); beginMangling(); diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index d26ed89f431..7c7ffffc646 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -47,7 +47,7 @@ public: IRGenMangler(ASTContext &Ctx) : ASTMangler(Ctx) { } std::string mangleDispatchThunk(const FuncDecl *func) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(func)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(func)); beginMangling(); appendEntity(func); appendOperator("Tj"); @@ -59,7 +59,7 @@ public: std::string mangleDerivativeDispatchThunk( const AbstractFunctionDecl *func, AutoDiffDerivativeFunctionIdentifier *derivativeId) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(func)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(func)); beginManglingWithAutoDiffOriginalFunction(func); auto kind = Demangle::getAutoDiffFunctionKind(derivativeId->getKind()); auto *resultIndices = @@ -76,7 +76,7 @@ public: std::string mangleConstructorDispatchThunk(const ConstructorDecl *ctor, bool isAllocating) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(ctor)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(ctor)); beginMangling(); appendConstructorEntity(ctor, isAllocating); appendOperator("Tj"); @@ -84,7 +84,7 @@ public: } std::string mangleMethodDescriptor(const FuncDecl *func) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(func)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(func)); beginMangling(); appendEntity(func); appendOperator("Tq"); @@ -96,7 +96,7 @@ public: std::string mangleDerivativeMethodDescriptor( const AbstractFunctionDecl *func, AutoDiffDerivativeFunctionIdentifier *derivativeId) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(func)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(func)); beginManglingWithAutoDiffOriginalFunction(func); auto kind = Demangle::getAutoDiffFunctionKind(derivativeId->getKind()); auto *resultIndices = @@ -113,7 +113,7 @@ public: std::string mangleConstructorMethodDescriptor(const ConstructorDecl *ctor, bool isAllocating) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(ctor)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(ctor)); beginMangling(); appendConstructorEntity(ctor, isAllocating); appendOperator("Tq"); @@ -401,7 +401,7 @@ public: const RootProtocolConformance *conformance); std::string manglePropertyDescriptor(const AbstractStorageDecl *storage) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(storage)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(storage)); beginMangling(); appendEntity(storage); appendOperator("MV"); @@ -409,7 +409,7 @@ public: } std::string mangleFieldOffset(const ValueDecl *Decl) { - llvm::SaveAndRestore X(AllowInverses, inversesAllowed(Decl)); + llvm::SaveAndRestore X(AllowedInverses, inversesAllowed(Decl)); beginMangling(); appendEntity(Decl); appendOperator("Wvd"); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 26ac53aaddd..afe1a4d3265 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1454,6 +1454,7 @@ private: unsigned align: 16; unsigned pod: 1; unsigned bitwiseTakable: 2; + unsigned addressableForDependencies: 1; }; friend struct ::llvm::DenseMapInfo; llvm::DenseMap PrivateFixedLayouts; @@ -2162,23 +2163,25 @@ struct DenseMapInfo { using FixedLayoutKey = swift::irgen::IRGenModule::FixedLayoutKey; static inline FixedLayoutKey getEmptyKey() { - return {0, 0xFFFFFFFFu, 0, 0, 0}; + return {0, 0xFFFFFFFFu, 0, 0, 0, 0}; } static inline FixedLayoutKey getTombstoneKey() { - return {0, 0xFFFFFFFEu, 0, 0, 0}; + return {0, 0xFFFFFFFEu, 0, 0, 0, 0}; } static unsigned getHashValue(const FixedLayoutKey &key) { return hash_combine(key.size, key.numExtraInhabitants, key.align, - (bool)key.pod, (bool)key.bitwiseTakable); + (bool)key.pod, (bool)key.bitwiseTakable, + (bool)key.addressableForDependencies); } static bool isEqual(const FixedLayoutKey &a, const FixedLayoutKey &b) { return a.size == b.size && a.numExtraInhabitants == b.numExtraInhabitants && a.align == b.align && a.pod == b.pod - && a.bitwiseTakable == b.bitwiseTakable; + && a.bitwiseTakable == b.bitwiseTakable + && a.addressableForDependencies == b.addressableForDependencies; } }; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 4b858df6a1c..8243f166522 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2743,6 +2743,39 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, case DeclAttrKind::SetterAccess: llvm_unreachable("handled by DeclAttrKind::AccessControl"); + case DeclAttrKind::PreInverseGenerics: { + TypeRepr *exceptType = nullptr; + SourceLoc rParenLoc = Loc; + + if (consumeIfAttributeLParen()) { + if (Tok.getText() != "except" || peekToken().isNot(tok::colon)) { + diagnose(Tok, diag::attr_pre_inverse_generics_expected_except); + skipUntil(tok::r_paren); + consumeIf(tok::r_paren); + return makeParserSuccess(); + } + consumeToken(tok::identifier); + consumeToken(tok::colon); + + auto type = parseType(diag::expected_type); + if (type.isNull()) + return makeParserSuccess(); + exceptType = type.get(); + + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(Tok.getLoc(), diag::attr_expected_rparen, + AttrName, /*isModifier=*/false); + return makeParserSuccess(); + } + } + + if (!DiscardAttribute) + Attributes.add(new (Context) PreInverseGenericsAttr( + AtLoc, SourceRange(AtLoc.isValid() ? AtLoc : Loc, rParenLoc), + exceptType)); + break; + } + #define SIMPLE_DECL_ATTR(_, CLASS, ...) case DeclAttrKind::CLASS: #include "swift/AST/DeclAttr.def" if (!DiscardAttribute) diff --git a/lib/SIL/IR/SILBuilder.cpp b/lib/SIL/IR/SILBuilder.cpp index 1f119403299..69454716363 100644 --- a/lib/SIL/IR/SILBuilder.cpp +++ b/lib/SIL/IR/SILBuilder.cpp @@ -72,7 +72,11 @@ SILType SILBuilder::getPartialApplyResultType( .intoBuilder() .withRepresentation(SILFunctionType::Representation::Thick) .withIsolation(resultIsolation) - .withIsPseudogeneric(false); + .withIsPseudogeneric(false) + .withLifetimeDependencies(LifetimeDependenceInfo::partialApply( + context.getContext()->getASTContext(), + FTI->getLifetimeDependencies(), FTI->getNumParameters(), + argCount)); if (onStack) extInfoBuilder = extInfoBuilder.withNoEscape(); auto extInfo = extInfoBuilder.build(); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index b9326978851..edcb9425d52 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -222,7 +222,6 @@ public: IGNORED_ATTR(Documentation) IGNORED_ATTR(LexicalLifetimes) IGNORED_ATTR(AllowFeatureSuppression) - IGNORED_ATTR(PreInverseGenerics) IGNORED_ATTR(Safe) IGNORED_ATTR(Diagnose) #undef IGNORED_ATTR @@ -523,7 +522,8 @@ public: void visitSendableAttr(SendableAttr *attr); void visitMacroRoleAttr(MacroRoleAttr *attr); - + + void visitPreInverseGenericsAttr(PreInverseGenericsAttr *attr); void visitRawLayoutAttr(RawLayoutAttr *attr); void visitNonEscapableAttr(NonEscapableAttr *attr); @@ -8710,6 +8710,80 @@ void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) { {}); } +void AttributeChecker::visitPreInverseGenericsAttr( + PreInverseGenericsAttr *attr) { + if (isa(D)) { + diagnose(attr->getLocation(), + diag::attr_pre_inverse_generics_on_extension); + return; + } + + if (attr->hasExcept(D) && + !Ctx.LangOpts.hasFeature(Feature::PreInverseGenericsExcept)) { + Ctx.Diags + .diagnose(attr->getLocation(), + diag::attribute_requires_experimental_feature, attr, + "PreInverseGenericsExcept") + .warnInSwiftInterface(D->getDeclContext()); + } + + // Trigger the request to resolve and validate the optional 'except:' argument. + (void)attr->getAllowedInverses(D); +} + +Type +ResolvePreInverseGenericsRequest::evaluate(Evaluator &evaluator, + Decl *decl, + PreInverseGenericsAttr *attr) const { + // Declarations deserialized from a module file should have the resolved + // type cached already and never reach here. + if (auto fileUnit = + dyn_cast(decl->getDeclContext()->getModuleScopeContext())) + if (fileUnit->getKind() == FileUnitKind::SerializedAST) + llvm::report_fatal_error("cannot resolve serialized @_preInverseGenerics " + "as it is missing the TypeRepr!"); + + auto &ctx = decl->getASTContext(); + auto *typeRepr = attr->ExceptTypeRepr; + + // Mangle zero inverses by returning the composition that contains none. + // This is also the fall-back if they didn't provide a valid except: argument. + if (!typeRepr) + return ctx.TheAnyType; + + auto resolution = TypeResolution::forInterface( + decl->getDeclContext(), + TypeResolutionOptions(TypeResolverContext::GenericRequirement), + /*unboundTyOpener=*/nullptr, /*placeholderHandler=*/nullptr, + /*packElementOpener=*/nullptr); + Type resolvedTy = resolution.resolveType(typeRepr); + + if (!resolvedTy || resolvedTy->hasError()) { + ctx.Diags.diagnose(attr->getLocation(), + diag::attr_pre_inverse_generics_invalid_except); + return ctx.TheAnyType; + } + + // Don't permit compositions with non-inverse members. + // Don't permit `@_preInverseGenerics(except: Any)` as that's just confusing. + auto *pct = resolvedTy->getCanonicalType()->getAs(); + if (!pct || !pct->getMembers().empty() || pct->getCanonicalType() == ctx.TheAnyType) { + ctx.Diags.diagnose(attr->getLocation(), + diag::attr_pre_inverse_generics_invalid_except); + return ctx.TheAnyType; + } + + // TheUnconstrainedAnyType contains all inverses currently known. + // Just warn that `except: ` is the same as not writing the + // attribute, according to this version of the compiler. + if (pct->getCanonicalType() == ctx.TheUnconstrainedAnyType) { + ctx.Diags.diagnose(attr->getLocation(), + diag::attr_pre_inverse_generics_except_all); + } + + return pct; +} + void AttributeChecker::visitRawLayoutAttr(RawLayoutAttr *attr) { if (!Ctx.LangOpts.hasFeature(Feature::RawLayout)) { diagnoseAndRemoveAttr(attr, diag::attr_rawlayout_experimental); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 6dbe65cf215..4a3f4a80fdc 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3433,8 +3433,13 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( // Determine the access level for the property. property->overwriteAccess(var->getFormalAccess()); - // Determine setter access. - property->overwriteSetterAccess(var->getSetterFormalAccess()); + // Determine setter access. `projectedValue` setter could be less + // accessible than the variable itself and vice versa, we need to + // account for that and take the less permitting access of the two. + property->overwriteSetterAccess( + wrapperVar ? std::min(var->getSetterFormalAccess(), + wrapperVar->getSetterFormalAccess()) + : var->getSetterFormalAccess()); // Add the accessors we need. if (var->hasImplicitPropertyWrapper()) { diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 9da75f04000..374430b1fea 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6757,6 +6757,26 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { } #include "swift/AST/DeclAttr.def" + case decls_block::PreInverseGenerics_DECL_ATTR: { + bool isImplicit; + TypeID typeID; + serialization::decls_block::PreInverseGenericsDeclAttrLayout:: + readRecord(scratch, isImplicit, typeID); + assert(!isImplicit); + + if (typeID) { + auto type = MF.getTypeChecked(typeID); + if (!type) { + return type.takeError(); + } + Attr = new (ctx) PreInverseGenericsAttr( + SourceLoc(), SourceRange(), /*exceptRepr=*/nullptr, type.get()); + } else { + Attr = new (ctx) PreInverseGenericsAttr(SourceLoc(), SourceRange()); + } + break; + } + default: // We don't know how to deserialize this kind of attribute. MF.fatal(llvm::make_error(recordID)); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index c7ab9aa55aa..ef47101291f 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 1002; // Hidden type layout block +const uint16_t SWIFTMODULE_VERSION_MINOR = 1003; // Hidden type layout block /// A standard hash seed used for all string hashes in a serialized module. /// @@ -2596,6 +2596,12 @@ namespace decls_block { >; #include "swift/AST/DeclAttr.def" + using PreInverseGenericsDeclAttrLayout = BCRecordLayout< + PreInverseGenerics_DECL_ATTR, + BCFixed<1>, // implicit + TypeIDField // except type + >; + using DynamicReplacementDeclAttrLayout = BCRecordLayout< DynamicReplacement_DECL_ATTR, BCFixed<1>, // implicit flag diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index aaaac24f2b2..97a4bc1fdba 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3074,6 +3074,20 @@ class Serializer::DeclSerializer : public DeclVisitor { } #include "swift/AST/DeclAttr.def" + case DeclAttrKind::PreInverseGenerics: { + auto *attr = cast(DA); + auto abbrCode = + S.DeclTypeAbbrCodes[PreInverseGenericsDeclAttrLayout::Code]; + auto exceptType = attr->getResolvedExceptType(D); + if (S.skipTypeIfInvalid(exceptType, attr->getExceptTypeRepr())) + return; + + auto typeID = S.addTypeRef(exceptType); + PreInverseGenericsDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), typeID); + return; + } + case DeclAttrKind::ABI: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[ABIDeclAttrLayout::Code]; diff --git a/stdlib/public/RemoteInspection/TypeLowering.cpp b/stdlib/public/RemoteInspection/TypeLowering.cpp index 604a45f7694..95a619a6379 100644 --- a/stdlib/public/RemoteInspection/TypeLowering.cpp +++ b/stdlib/public/RemoteInspection/TypeLowering.cpp @@ -222,6 +222,9 @@ public: case TypeInfoKind::Array: { printHeader("array"); printBasic(TI); + auto &ArrayTI = cast(TI); + printField("count", std::to_string(ArrayTI.getElementCount())); + printRec(*ArrayTI.getElementTypeInfo()); stream << ")"; return; } @@ -493,7 +496,8 @@ BitMask RecordTypeInfo::getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const return mask; } -ArrayTypeInfo::ArrayTypeInfo(intptr_t size, const TypeInfo *elementTI) +ArrayTypeInfo::ArrayTypeInfo(intptr_t size, const TypeRef *elementTR, + const TypeInfo *elementTI) : TypeInfo(TypeInfoKind::Array, /* size */ elementTI->getStride() * size, /* alignment */ elementTI->getAlignment(), @@ -501,7 +505,7 @@ ArrayTypeInfo::ArrayTypeInfo(intptr_t size, const TypeInfo *elementTI) /* numExtraInhabitants */ elementTI->getNumExtraInhabitants(), /* borrowability */ elementTI->getBorrowability(), /* FixedArray is always afd */ true), - ElementTI(elementTI) {} + ElementTR(elementTR), ElementTI(elementTI), ElementCount(size) {} bool ArrayTypeInfo::readExtraInhabitantIndex( remote::MemoryReader &reader, remote::RemoteAddress address, @@ -2771,7 +2775,8 @@ public: return nullptr; } - return TC.makeTypeInfo(sizeInt->getValue(), elementTI); + return TC.makeTypeInfo(sizeInt->getValue(), + BA->getElementType(), elementTI); } const TypeInfo *visitBuiltinBorrowTypeRef(const BuiltinBorrowTypeRef *BA) { diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 94e4213c951..f1b27c5f7f9 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -586,6 +586,8 @@ static swift_typeinfo_t convertTypeInfo(const TypeInfo *TI) { NumFields = RecordTI->getNumCases(); } else if (auto *RecordTI = dyn_cast(TI)) { NumFields = RecordTI->getNumFields(); + } else if (auto *ArrayTI = dyn_cast(TI)) { + NumFields = ArrayTI->getElementCount(); } return { @@ -601,13 +603,23 @@ static swift_childinfo_t convertChild(const TypeInfo *TI, unsigned Index) { if (!TI) return {}; + if (auto *ArrayTI = dyn_cast(TI)) { + auto *ElementTI = ArrayTI->getElementTypeInfo(); + return { + "element", + Index * ElementTI->getStride(), + getTypeInfoKind(*ElementTI), + reinterpret_cast(ArrayTI->getElementTypeRef()), + }; + } + const FieldInfo *FieldInfo = nullptr; if (auto *EnumTI = dyn_cast(TI)) { FieldInfo = &(EnumTI->getCases()[Index]); } else if (auto *RecordTI = dyn_cast(TI)) { FieldInfo = &(RecordTI->getFields()[Index]); } else { - assert(false && "convertChild(TI): TI must be record or enum typeinfo"); + assert(false && "convertChild(TI): TI must be record, enum, or array typeinfo"); return { "unknown TypeInfo kind", 0, diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 319f3dab73e..5e639d816d4 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -267,12 +267,7 @@ swift::swift_getFunctionFullNameFromMangledName( return TypeNamePair{nullptr, 0}; } - // Read-only lookup failed, we may need to demangle and cache the entry. - // We have to copy the string to be able to refer to it "forever": - auto copy = (char *)malloc(mangledNameLength); - memcpy(copy, mangledNameStart, mangledNameLength); - mangledName = StringRef(copy, mangledNameLength); - + // Read-only lookup failed. Demangle and cache the entry. std::string demangled; StackAllocatedDemangler<1024> Dem; NodePointer node = Dem.demangleSymbol(mangledName); @@ -361,6 +356,11 @@ swift::swift_getFunctionFullNameFromMangledName( } demangled += ")"; + // We have to copy the string to be able to refer to it "forever": + auto copy = (char *)malloc(mangledNameLength); + memcpy(copy, mangledNameStart, mangledNameLength); + llvm::StringRef copiedMangledName(copy, mangledNameLength); + // We have to copy the string to be able to refer to it; auto size = demangled.size(); auto result = (char *)malloc(size + 1); @@ -370,7 +370,15 @@ swift::swift_getFunctionFullNameFromMangledName( { LazyMutex::ScopedLock guard(MangledToPrettyFunctionNameCacheLock); - cache.insert({mangledName, {result, size}}); + auto [it, inserted] = cache.insert({copiedMangledName, {result, size}}); + if (!inserted) { + // We raced with another thread and lost. Free our data and return theirs. + free(copy); + free(result); + return {it->second.first, it->second.second}; + } + + // Successfully inserted into the cache. Return our cached value. return TypeNamePair{result, size}; } } diff --git a/test/Availability/availability_custom_domains.swift b/test/Availability/availability_custom_domains.swift index 517c97494a1..2abe7ac259a 100644 --- a/test/Availability/availability_custom_domains.swift +++ b/test/Availability/availability_custom_domains.swift @@ -64,7 +64,7 @@ func testDeployment() { // expected-note 3 {{add '@available' attribute to enclo // FIXME: [availability] Test @inlinable functions. -func testIfAvailable(_ truthy: Bool) { // expected-note 9 {{add '@available' attribute to enclosing global function}} +func testIfAvailable(_ truthy: Bool) { // expected-note 11 {{add '@available' attribute to enclosing global function}} if #available(EnabledDomain) { // expected-note {{enclosing scope here}} availableInEnabledDomain() availableInAlwaysEnabledDomain() @@ -142,6 +142,18 @@ func testIfAvailable(_ truthy: Bool) { // expected-note 9 {{add '@available' att unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}} } + if #unavailable(EnabledDomain), truthy { + availableInEnabledDomain() // expected-error {{'availableInEnabledDomain()' is only available in EnabledDomain}} + // expected-note@-1 {{add 'if #available' version check}} + unavailableInEnabledDomain() + } else { + // In this branch, the state of EnabledDomain remains unknown since + // execution will reach here if "truthy" is false. + availableInEnabledDomain() // expected-error {{'availableInEnabledDomain()' is only available in EnabledDomain}} + // expected-note@-1 {{add 'if #available' version check}} + unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}} + } + // FIXME: [availability] Support mixed #available and #unavailable. if #unavailable(EnabledDomain), #available(DynamicDomain) { // expected-error@-1 {{#available and #unavailable cannot be in the same statement}} @@ -242,8 +254,8 @@ func testAlwaysEnabledDomainUnavailable() { @available(*, unavailable) func testUniversallyUnavailable() { - // expected-note@-1 {{add '@available' attribute to enclosing global function}}{{243:1-1=@available(EnabledDomain)\n}} - // expected-note@-2 {{add '@available' attribute to enclosing global function}}{{243:1-1=@available(DynamicDomain)\n}} + // expected-note@-1 {{add '@available' attribute to enclosing global function}}{{-1:1-1=@available(EnabledDomain)\n}} + // expected-note@-2 {{add '@available' attribute to enclosing global function}}{{-1:1-1=@available(DynamicDomain)\n}} alwaysAvailable() // FIXME: [availability] Diagnostic consistency: potentially unavailable declaration shouldn't be diagnosed // in contexts that are unavailable to broader domains diff --git a/test/Availability/availability_scopes.swift b/test/Availability/availability_scopes.swift index 84918b8fd7b..3634cec4478 100644 --- a/test/Availability/availability_scopes.swift +++ b/test/Availability/availability_scopes.swift @@ -1,477 +1,84 @@ -// RUN: %target-swift-frontend -typecheck -dump-availability-scopes %s -target %target-cpu-apple-macos50 -swift-version 5 > %t.dump 2>&1 +// RUN: %target-swift-frontend -typecheck -dump-availability-scopes %s -swift-version 5 > %t.dump 2>&1 // RUN: %FileCheck --strict-whitespace %s < %t.dump -// REQUIRES: OS=macosx - -// CHECK: {{^}}(root version=50 - -// CHECK-NEXT: {{^}} (decl version=51 decl=SomeClass -// CHECK-NEXT: {{^}} (decl version=52 decl=someMethod() -// CHECK-NEXT: {{^}} (decl version=53 decl=someInnerFunc() -// CHECK-NEXT: {{^}} (decl version=53 decl=InnerClass -// CHECK-NEXT: {{^}} (decl version=54 decl=innerClassMethod -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticProperty -// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticProperty -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticPropertyInferredType -// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticPropertyInferredType -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=multiPatternStaticPropertyA -// CHECK-NEXT: {{^}} (decl version=52 decl=multiPatternStaticPropertyA -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someComputedProperty -// CHECK-NEXT: {{^}} (decl version=52 decl=someComputedProperty -// CHECK-NEXT: {{^}} (decl version=52 decl=someOtherMethod() -@available(OSX 51, *) -class SomeClass { - @available(OSX 52, *) - func someMethod() { - - @available(OSX 53, *) - func someInnerFunc() { } - - @available(OSX 53, *) - class InnerClass { - @available(OSX 54, *) - func innerClassMethod() { } - } - } - - func someUnrefinedMethod() { } - - @available(OSX 52, *) - static var someStaticProperty: Int = 7 - - @available(OSX 52, *) - static var someStaticPropertyInferredType = 7 - - @available(OSX 52, *) - static var multiPatternStaticPropertyA = 7, - multiPatternStaticPropertyB = 8 - - @available(OSX 52, *) - var someComputedProperty: Int { - get { } - set(v) { } - } - - @available(OSX 52, *) - func someOtherMethod() { } -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=someFunction() -@available(OSX 51, *) -func someFunction() { } - -// CHECK-NEXT: {{^}} (decl version=51 decl=SomeProtocol -// CHECK-NEXT: {{^}} (decl version=52 decl=protoMethod() -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=protoProperty -// CHECK-NEXT: {{^}} (decl version=52 decl=protoProperty -@available(OSX 51, *) -protocol SomeProtocol { - @available(OSX 52, *) - func protoMethod() -> Int - - @available(OSX 52, *) - var protoProperty: Int { get } -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl version=51 decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl version=52 decl=someExtensionFunction() -@available(OSX 51, *) -extension SomeClass { - @available(OSX 52, *) - func someExtensionFunction() { } -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability version=52 -// CHECK-NEXT: {{^}} (condition_following_availability version=53 -// CHECK-NEXT: {{^}} (if_then version=53 -// CHECK-NEXT: {{^}} (condition_following_availability version=54 -// CHECK-NEXT: {{^}} (if_then version=54 -// CHECK-NEXT: {{^}} (condition_following_availability version=55 -// CHECK-NEXT: {{^}} (decl version=55 decl=funcInGuardElse() -// CHECK-NEXT: {{^}} (guard_fallthrough version=55 -// CHECK-NEXT: {{^}} (condition_following_availability version=56 -// CHECK-NEXT: {{^}} (guard_fallthrough version=56 -// CHECK-NEXT: {{^}} (decl version=57 decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (decl version=53 decl=funcInOuterIfElse() -@available(OSX 51, *) -func functionWithStmtCondition() { - if #available(OSX 52, *), - let x = (nil as Int?), - #available(OSX 53, *) { - if #available(OSX 54, *) { - guard #available(OSX 55, *) else { - @available(OSX 55, *) - func funcInGuardElse() { } - } - guard #available(OSX 56, *) else { } - } else { - @available(OSX 57, *) - func funcInInnerIfElse() { } - } - } else { - @available(OSX 53, *) - func funcInOuterIfElse() { } - } -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithUnnecessaryStmtCondition -// CHECK-NEXT: {{^}} (condition_following_availability version=53 -// CHECK-NEXT: {{^}} (if_then version=53 -// CHECK-NEXT: {{^}} (condition_following_availability version=54 -// CHECK-NEXT: {{^}} (if_then version=54 - -@available(OSX 51, *) -func functionWithUnnecessaryStmtCondition() { - // Shouldn't introduce availability scope for then branch when unnecessary - if #available(OSX 51, *) { - } - - if #available(OSX 10.9, *) { - } - - // Nested in conjunctive statement condition - if #available(OSX 53, *), - let x = (nil as Int?), - #available(OSX 52, *) { - } - - if #available(OSX 54, *), - #available(OSX 54, *) { - } - - // Wildcard is same as minimum deployment target - if #available(iOS 7.0, *) { - } -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithUnnecessaryStmtConditionsHavingElseBranch -// CHECK-NEXT: {{^}} (if_else version=none -// CHECK-NEXT: {{^}} (decl version=none decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (if_else version=none -// CHECK-NEXT: {{^}} (guard_else version=none -// CHECK-NEXT: {{^}} (guard_else version=none -// CHECK-NEXT: {{^}} (if_else version=none - -@available(OSX 51, *) -func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { - // Else branch context version is bottom when check is unnecessary - if #available(OSX 51, *) { - } else { - if #available(OSX 52, *) { - } - - @available(OSX 52, *) - func funcInInnerIfElse() { } - - if #available(iOS 7.0, *) { - } else { - } - } - - if #available(iOS 7.0, *) { - } else { - } - - guard #available(iOS 8.0, *) else { } - - // Else branch will execute if p is nil, so it is not dead. - if #available(iOS 7.0, *), - let x = p { - } else { - } - - if #available(iOS 7.0, *), - let x = p, - #available(iOS 7.0, *) { - } else { - } - - // Else branch is dead - guard #available(iOS 7.0, *), - #available(iOS 8.0, *) else { } - - if #available(OSX 51, *), - #available(OSX 51, *) { - } else { - } - -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithWhile() -// CHECK-NEXT: {{^}} (condition_following_availability version=52 -// CHECK-NEXT: {{^}} (while_body version=52 -// CHECK-NEXT: {{^}} (decl version=54 decl=funcInWhileBody() -@available(OSX 51, *) -func functionWithWhile() { - while #available(OSX 52, *), - let x = (nil as Int?) { - @available(OSX 54, *) - func funcInWhileBody() { } - } -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithDefer() -// CHECK-NEXT: {{^}} (condition_following_availability version=52 -// CHECK-NEXT: {{^}} (if_then version=52 -@available(OSX 51, *) -func functionWithDefer() { - defer { - if #available(OSX 52, *) {} - } -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl version=51 decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticPropertyWithClosureInit -// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticPropertyWithClosureInit -// CHECK-NEXT: {{^}} (condition_following_availability version=54 -// CHECK-NEXT: {{^}} (if_then version=54 -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticPropertyWithClosureInitInferred -// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticPropertyWithClosureInitInferred -// CHECK-NEXT: {{^}} (condition_following_availability version=54 -// CHECK-NEXT: {{^}} (if_then version=54 -@available(OSX 51, *) -extension SomeClass { - @available(OSX 52, *) - static var someStaticPropertyWithClosureInit: Int = { - if #available(OSX 54, *) { - return 54 - } - return 53 - }() - - @available(OSX 52, *) - static var someStaticPropertyWithClosureInitInferred = { - if #available(OSX 54, *) { - return 54 - } - return 53 - }() -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=functionWithStmtConditionsInUnavailableExt() -// CHECK-NEXT: {{^}} (condition_following_availability version=52 unavailable=macOS -// CHECK-NEXT: {{^}} (condition_following_availability version=53 unavailable=macOS -// CHECK-NEXT: {{^}} (if_then version=53 unavailable=macOS -// CHECK-NEXT: {{^}} (condition_following_availability version=54 unavailable=macOS -// CHECK-NEXT: {{^}} (if_then version=54 unavailable=macOS -// CHECK-NEXT: {{^}} (condition_following_availability version=55 unavailable=macOS -// CHECK-NEXT: {{^}} (decl version=54 unavailable=macOS decl=funcInGuardElse() -// CHECK-NEXT: {{^}} (guard_fallthrough version=55 unavailable=macOS -// CHECK-NEXT: {{^}} (condition_following_availability version=56 unavailable=macOS -// CHECK-NEXT: {{^}} (guard_fallthrough version=56 unavailable=macOS -// CHECK-NEXT: {{^}} (decl version=53 unavailable=macOS decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=funcInOuterIfElse() -@available(OSX, unavailable) -extension SomeClass { - @available(OSX 51, *) - func functionWithStmtConditionsInUnavailableExt() { - if #available(OSX 52, *), - let x = (nil as Int?), - #available(OSX 53, *) { - if #available(OSX 54, *) { - guard #available(OSX 55, *) else { - @available(OSX 55, *) - func funcInGuardElse() { } - } - guard #available(OSX 56, *) else { } - } else { - @available(OSX 57, *) - func funcInInnerIfElse() { } - } - } else { - @available(OSX 53, *) - func funcInOuterIfElse() { } - } - } -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=wrappedValue - -@propertyWrapper -struct Wrapper { - var wrappedValue: T -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=SomeStruct -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someLazyVar -// CHECK-NEXT: {{^}} (condition_following_availability version=52 -// CHECK-NEXT: {{^}} (guard_fallthrough version=52 -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someWrappedVar -// CHECK-NEXT: {{^}} (condition_following_availability version=52 -// CHECK-NEXT: {{^}} (guard_fallthrough version=52 -// CHECK-NEXT: {{^}} (decl version=52 decl=someMethodAvailable52() -@available(OSX 51, *) -struct SomeStruct { - lazy var someLazyVar = { - guard #available(OSX 52, *) else { - return someMethod() - } - return someMethodAvailable52() - }() - - @Wrapper var someWrappedVar = { - guard #available(OSX 52, *) else { - return 51 - } - return 52 - }() - - func someMethod() -> Int { return 51 } - - @available(OSX 52, *) - func someMethodAvailable52() -> Int { return 52 } -} - -// CHECK-NEXT: {{^}} (decl version=51 decl=SomeEnum -// CHECK-NEXT: {{^}} (decl version=52 decl=a -// CHECK-NEXT: {{^}} (decl version=53 decl=b - -@available(OSX 51, *) -enum SomeEnum { - @available(OSX 52, *) - case a - - @available(OSX 53, *) - case b, c -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=someComputedGlobalVar -// CHECK-NEXT: {{^}} (decl version=51 decl=_ -// CHECK-NEXT: {{^}} (decl version=52 decl=_ - -var someComputedGlobalVar: Int { - @available(OSX 51, *) - get { 1 } - - @available(OSX 52, *) - set { } -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=interpolated -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=string - -func testStringInterpolation() { - let interpolated = """ - \([""].map { - let string = $0 - return string - }) - """ -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=result -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=unusedA -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=unusedB - -func testSequenceExprs(b: Bool, x: Int?) { - let result = b - ? x.map { - let unusedA: Int - return $0 - } - : x.map { - let unusedB: Int - return $0 - } -} - -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOS() -// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=x - -@available(macOS, unavailable) -func unavailableOnMacOS() { - let x = 1 -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum -// CHECK-NEXT: {{^}} (decl version=51 decl=extension.SomeEnum -// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=unavailableOnMacOS -// CHECK-NEXT: {{^}} (decl version=51 unavailable=macOS decl=unavailableOnMacOS -@available(OSX 51, *) -extension SomeEnum { - @available(macOS, unavailable) - var unavailableOnMacOS: Int { 1 } -} - -// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeEnum -// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=availableMacOS_52 -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=availableMacOS_52 -// CHECK-NEXT: {{^}} (decl version=50 unavailable=*,macOS decl=neverAvailable() - -@available(macOS, unavailable) -extension SomeEnum { - @available(OSX 52, *) - var availableMacOS_52: Int { 1 } - - @available(macOSApplicationExtension, unavailable) - func unavailableInAppExtensions() {} - - @available(*, unavailable) - func neverAvailable() {} -} - -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroduced() - -@available(macOS, unavailable) -@available(macOS, introduced: 52) -func unavailableOnMacOSAndIntroduced() { -} - -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=introducedOnMacOSAndUnavailable() - -@available(macOS, introduced: 53) -@available(macOS, unavailable) -func introducedOnMacOSAndUnavailable() { -} - - -// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroducedSameAttr() - -@available(macOS, unavailable, introduced: 54) -func unavailableOnMacOSAndIntroducedSameAttr() { -} - -// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=NeverAvailable -// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=unavailableOnMacOS() +// CHECK: {{^}}(root +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=* decl=universallyUnavailable() @available(*, unavailable) -struct NeverAvailable { - @available(macOS, unavailable) - func unavailableOnMacOS() {} -} +func universallyUnavailable() { } -// CHECK-NEXT: {{^}} (decl version=50 deprecated decl=deprecatedOnMacOS() -// CHECK-NEXT: {{^}} (decl_implicit version=50 deprecated decl=x - -@available(macOS, deprecated) -func deprecatedOnMacOS() { - let x = 1 -} - -// Since availableOniOS() doesn't have any active @available attributes it -// shouldn't create a scope. -// CHECK-NOT: availableOniOS - -@available(iOS, introduced: 53) -func availableOniOS() { } - -// CHECK-NEXT: {{^}} (decl version=50 decl=availableInSwift5 +// CHECK-NEXT: {{^}} (decl version={{.*}} deprecated decl=universallyDeprecated() +@available(*, deprecated) +func universallyDeprecated() { } +// CHECK-NEXT: {{^}} (decl version={{.*}} decl=introducedInSwift5() @available(swift 5) -func availableInSwift5() { } - -// CHECK-NEXT: {{^}} (decl version=50 unavailable=swift decl=availableInSwift6 +func introducedInSwift5() { } +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=introducedInSwift6() @available(swift 6) -func availableInSwift6() { } +func introducedInSwift6() { } -// CHECK-NEXT: {{^}} (decl version=51 decl=FinalDecl +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=obsoletedInSwift5() +@available(swift, obsoleted: 5) +func obsoletedInSwift5() { } -@available(OSX 51, *) -typealias FinalDecl = Int +// CHECK-NEXT: {{^}} (decl version={{.*}} decl=obsoletedInSwift6() +@available(swift, obsoleted: 6) +func obsoletedInSwift6() { } + +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=* decl=UniversallyUnavailable +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=* decl=universallyUnavailable() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=* deprecated decl=universallyDeprecated() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=* decl=introducedInSwift5() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=*,swift decl=introducedInSwift6() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=*,swift decl=obsoletedInSwift5 +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=* decl=obsoletedInSwift6() +@available(*, unavailable) +struct UniversallyUnavailable { + @available(*, unavailable) + func universallyUnavailable() { } + + @available(*, deprecated) + func universallyDeprecated() { } + + @available(swift 5) + func introducedInSwift5() { } + + @available(swift 6) + func introducedInSwift6() { } + + @available(swift, obsoleted: 5) + func obsoletedInSwift5() { } + + @available(swift, obsoleted: 6) + func obsoletedInSwift6() { } +} + +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=IntroducedInSwift6 +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=*,swift decl=universallyUnavailable() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift deprecated decl=universallyDeprecated() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=introducedInSwift5() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=introducedInSwift6() +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=obsoletedInSwift5 +// CHECK-NEXT: {{^}} (decl version={{.*}} unavailable=swift decl=obsoletedInSwift6() +@available(swift 6) +struct IntroducedInSwift6 { + @available(*, unavailable) + func universallyUnavailable() { } + + @available(*, deprecated) + func universallyDeprecated() { } + + @available(swift 5) + func introducedInSwift5() { } + + @available(swift 6) + func introducedInSwift6() { } + + @available(swift, obsoleted: 5) + func obsoletedInSwift5() { } + + @available(swift, obsoleted: 6) + func obsoletedInSwift6() { } +} diff --git a/test/Availability/availability_scopes_macos.swift b/test/Availability/availability_scopes_macos.swift new file mode 100644 index 00000000000..e6c970b99c7 --- /dev/null +++ b/test/Availability/availability_scopes_macos.swift @@ -0,0 +1,467 @@ +// RUN: %target-swift-frontend -typecheck -dump-availability-scopes %s -target %target-cpu-apple-macos50 -swift-version 5 > %t.dump 2>&1 +// RUN: %FileCheck --strict-whitespace %s < %t.dump + +// REQUIRES: OS=macosx + +// CHECK: {{^}}(root version=50 + +// CHECK-NEXT: {{^}} (decl version=51 decl=SomeClass +// CHECK-NEXT: {{^}} (decl version=52 decl=someMethod() +// CHECK-NEXT: {{^}} (decl version=53 decl=someInnerFunc() +// CHECK-NEXT: {{^}} (decl version=53 decl=InnerClass +// CHECK-NEXT: {{^}} (decl version=54 decl=innerClassMethod +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticProperty +// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticProperty +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticPropertyInferredType +// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticPropertyInferredType +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=multiPatternStaticPropertyA +// CHECK-NEXT: {{^}} (decl version=52 decl=multiPatternStaticPropertyA +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someComputedProperty +// CHECK-NEXT: {{^}} (decl version=52 decl=someComputedProperty +// CHECK-NEXT: {{^}} (decl version=52 decl=someOtherMethod() +@available(OSX 51, *) +class SomeClass { + @available(OSX 52, *) + func someMethod() { + + @available(OSX 53, *) + func someInnerFunc() { } + + @available(OSX 53, *) + class InnerClass { + @available(OSX 54, *) + func innerClassMethod() { } + } + } + + func someUnrefinedMethod() { } + + @available(OSX 52, *) + static var someStaticProperty: Int = 7 + + @available(OSX 52, *) + static var someStaticPropertyInferredType = 7 + + @available(OSX 52, *) + static var multiPatternStaticPropertyA = 7, + multiPatternStaticPropertyB = 8 + + @available(OSX 52, *) + var someComputedProperty: Int { + get { } + set(v) { } + } + + @available(OSX 52, *) + func someOtherMethod() { } +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=someFunction() +@available(OSX 51, *) +func someFunction() { } + +// CHECK-NEXT: {{^}} (decl version=51 decl=SomeProtocol +// CHECK-NEXT: {{^}} (decl version=52 decl=protoMethod() +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=protoProperty +// CHECK-NEXT: {{^}} (decl version=52 decl=protoProperty +@available(OSX 51, *) +protocol SomeProtocol { + @available(OSX 52, *) + func protoMethod() -> Int + + @available(OSX 52, *) + var protoProperty: Int { get } +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl version=51 decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl version=52 decl=someExtensionFunction() +@available(OSX 51, *) +extension SomeClass { + @available(OSX 52, *) + func someExtensionFunction() { } +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability version=52 +// CHECK-NEXT: {{^}} (condition_following_availability version=53 +// CHECK-NEXT: {{^}} (if_then version=53 +// CHECK-NEXT: {{^}} (condition_following_availability version=54 +// CHECK-NEXT: {{^}} (if_then version=54 +// CHECK-NEXT: {{^}} (condition_following_availability version=55 +// CHECK-NEXT: {{^}} (decl version=55 decl=funcInGuardElse() +// CHECK-NEXT: {{^}} (guard_fallthrough version=55 +// CHECK-NEXT: {{^}} (condition_following_availability version=56 +// CHECK-NEXT: {{^}} (guard_fallthrough version=56 +// CHECK-NEXT: {{^}} (decl version=57 decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (decl version=53 decl=funcInOuterIfElse() +@available(OSX 51, *) +func functionWithStmtCondition() { + if #available(OSX 52, *), + let x = (nil as Int?), + #available(OSX 53, *) { + if #available(OSX 54, *) { + guard #available(OSX 55, *) else { + @available(OSX 55, *) + func funcInGuardElse() { } + } + guard #available(OSX 56, *) else { } + } else { + @available(OSX 57, *) + func funcInInnerIfElse() { } + } + } else { + @available(OSX 53, *) + func funcInOuterIfElse() { } + } +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithUnnecessaryStmtCondition +// CHECK-NEXT: {{^}} (condition_following_availability version=53 +// CHECK-NEXT: {{^}} (if_then version=53 +// CHECK-NEXT: {{^}} (condition_following_availability version=54 +// CHECK-NEXT: {{^}} (if_then version=54 + +@available(OSX 51, *) +func functionWithUnnecessaryStmtCondition() { + // Shouldn't introduce availability scope for then branch when unnecessary + if #available(OSX 51, *) { + } + + if #available(OSX 10.9, *) { + } + + // Nested in conjunctive statement condition + if #available(OSX 53, *), + let x = (nil as Int?), + #available(OSX 52, *) { + } + + if #available(OSX 54, *), + #available(OSX 54, *) { + } + + // Wildcard is same as minimum deployment target + if #available(iOS 7.0, *) { + } +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithUnnecessaryStmtConditionsHavingElseBranch +// CHECK-NEXT: {{^}} (if_else version=none +// CHECK-NEXT: {{^}} (decl version=none decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (if_else version=none +// CHECK-NEXT: {{^}} (guard_else version=none +// CHECK-NEXT: {{^}} (guard_else version=none +// CHECK-NEXT: {{^}} (if_else version=none + +@available(OSX 51, *) +func functionWithUnnecessaryStmtConditionsHavingElseBranch(p: Int?) { + // Else branch context version is bottom when check is unnecessary + if #available(OSX 51, *) { + } else { + if #available(OSX 52, *) { + } + + @available(OSX 52, *) + func funcInInnerIfElse() { } + + if #available(iOS 7.0, *) { + } else { + } + } + + if #available(iOS 7.0, *) { + } else { + } + + guard #available(iOS 8.0, *) else { } + + // Else branch will execute if p is nil, so it is not dead. + if #available(iOS 7.0, *), + let x = p { + } else { + } + + if #available(iOS 7.0, *), + let x = p, + #available(iOS 7.0, *) { + } else { + } + + // Else branch is dead + guard #available(iOS 7.0, *), + #available(iOS 8.0, *) else { } + + if #available(OSX 51, *), + #available(OSX 51, *) { + } else { + } + +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithWhile() +// CHECK-NEXT: {{^}} (condition_following_availability version=52 +// CHECK-NEXT: {{^}} (while_body version=52 +// CHECK-NEXT: {{^}} (decl version=54 decl=funcInWhileBody() +@available(OSX 51, *) +func functionWithWhile() { + while #available(OSX 52, *), + let x = (nil as Int?) { + @available(OSX 54, *) + func funcInWhileBody() { } + } +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=functionWithDefer() +// CHECK-NEXT: {{^}} (condition_following_availability version=52 +// CHECK-NEXT: {{^}} (if_then version=52 +@available(OSX 51, *) +func functionWithDefer() { + defer { + if #available(OSX 52, *) {} + } +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl version=51 decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticPropertyWithClosureInit +// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticPropertyWithClosureInit +// CHECK-NEXT: {{^}} (condition_following_availability version=54 +// CHECK-NEXT: {{^}} (if_then version=54 +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someStaticPropertyWithClosureInitInferred +// CHECK-NEXT: {{^}} (decl version=52 decl=someStaticPropertyWithClosureInitInferred +// CHECK-NEXT: {{^}} (condition_following_availability version=54 +// CHECK-NEXT: {{^}} (if_then version=54 +@available(OSX 51, *) +extension SomeClass { + @available(OSX 52, *) + static var someStaticPropertyWithClosureInit: Int = { + if #available(OSX 54, *) { + return 54 + } + return 53 + }() + + @available(OSX 52, *) + static var someStaticPropertyWithClosureInitInferred = { + if #available(OSX 54, *) { + return 54 + } + return 53 + }() +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeClass +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=functionWithStmtConditionsInUnavailableExt() +// CHECK-NEXT: {{^}} (condition_following_availability version=52 unavailable=macOS +// CHECK-NEXT: {{^}} (condition_following_availability version=53 unavailable=macOS +// CHECK-NEXT: {{^}} (if_then version=53 unavailable=macOS +// CHECK-NEXT: {{^}} (condition_following_availability version=54 unavailable=macOS +// CHECK-NEXT: {{^}} (if_then version=54 unavailable=macOS +// CHECK-NEXT: {{^}} (condition_following_availability version=55 unavailable=macOS +// CHECK-NEXT: {{^}} (decl version=54 unavailable=macOS decl=funcInGuardElse() +// CHECK-NEXT: {{^}} (guard_fallthrough version=55 unavailable=macOS +// CHECK-NEXT: {{^}} (condition_following_availability version=56 unavailable=macOS +// CHECK-NEXT: {{^}} (guard_fallthrough version=56 unavailable=macOS +// CHECK-NEXT: {{^}} (decl version=53 unavailable=macOS decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=funcInOuterIfElse() +@available(OSX, unavailable) +extension SomeClass { + @available(OSX 51, *) + func functionWithStmtConditionsInUnavailableExt() { + if #available(OSX 52, *), + let x = (nil as Int?), + #available(OSX 53, *) { + if #available(OSX 54, *) { + guard #available(OSX 55, *) else { + @available(OSX 55, *) + func funcInGuardElse() { } + } + guard #available(OSX 56, *) else { } + } else { + @available(OSX 57, *) + func funcInInnerIfElse() { } + } + } else { + @available(OSX 53, *) + func funcInOuterIfElse() { } + } + } +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=wrappedValue + +@propertyWrapper +struct Wrapper { + var wrappedValue: T +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=SomeStruct +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someLazyVar +// CHECK-NEXT: {{^}} (condition_following_availability version=52 +// CHECK-NEXT: {{^}} (guard_fallthrough version=52 +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=someWrappedVar +// CHECK-NEXT: {{^}} (condition_following_availability version=52 +// CHECK-NEXT: {{^}} (guard_fallthrough version=52 +// CHECK-NEXT: {{^}} (decl version=52 decl=someMethodAvailable52() +@available(OSX 51, *) +struct SomeStruct { + lazy var someLazyVar = { + guard #available(OSX 52, *) else { + return someMethod() + } + return someMethodAvailable52() + }() + + @Wrapper var someWrappedVar = { + guard #available(OSX 52, *) else { + return 51 + } + return 52 + }() + + func someMethod() -> Int { return 51 } + + @available(OSX 52, *) + func someMethodAvailable52() -> Int { return 52 } +} + +// CHECK-NEXT: {{^}} (decl version=51 decl=SomeEnum +// CHECK-NEXT: {{^}} (decl version=52 decl=a +// CHECK-NEXT: {{^}} (decl version=53 decl=b + +@available(OSX 51, *) +enum SomeEnum { + @available(OSX 52, *) + case a + + @available(OSX 53, *) + case b, c +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=someComputedGlobalVar +// CHECK-NEXT: {{^}} (decl version=51 decl=_ +// CHECK-NEXT: {{^}} (decl version=52 decl=_ + +var someComputedGlobalVar: Int { + @available(OSX 51, *) + get { 1 } + + @available(OSX 52, *) + set { } +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=interpolated +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=string + +func testStringInterpolation() { + let interpolated = """ + \([""].map { + let string = $0 + return string + }) + """ +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=result +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=unusedA +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=unusedB + +func testSequenceExprs(b: Bool, x: Int?) { + let result = b + ? x.map { + let unusedA: Int + return $0 + } + : x.map { + let unusedB: Int + return $0 + } +} + +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOS() +// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=x + +@available(macOS, unavailable) +func unavailableOnMacOS() { + let x = 1 +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum +// CHECK-NEXT: {{^}} (decl version=51 decl=extension.SomeEnum +// CHECK-NEXT: {{^}} (decl_implicit version=51 decl=unavailableOnMacOS +// CHECK-NEXT: {{^}} (decl version=51 unavailable=macOS decl=unavailableOnMacOS +@available(OSX 51, *) +extension SomeEnum { + @available(macOS, unavailable) + var unavailableOnMacOS: Int { 1 } +} + +// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeEnum +// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=availableMacOS_52 +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=availableMacOS_52 +// CHECK-NEXT: {{^}} (decl version=50 unavailable=*,macOS decl=neverAvailable() + +@available(macOS, unavailable) +extension SomeEnum { + @available(OSX 52, *) + var availableMacOS_52: Int { 1 } + + @available(macOSApplicationExtension, unavailable) + func unavailableInAppExtensions() {} + + @available(*, unavailable) + func neverAvailable() {} +} + +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroduced() + +@available(macOS, unavailable) +@available(macOS, introduced: 52) +func unavailableOnMacOSAndIntroduced() { +} + +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=introducedOnMacOSAndUnavailable() + +@available(macOS, introduced: 53) +@available(macOS, unavailable) +func introducedOnMacOSAndUnavailable() { +} + + +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroducedSameAttr() + +@available(macOS, unavailable, introduced: 54) +func unavailableOnMacOSAndIntroducedSameAttr() { +} + +// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=NeverAvailable +// CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=unavailableOnMacOS() + +@available(*, unavailable) +struct NeverAvailable { + @available(macOS, unavailable) + func unavailableOnMacOS() {} +} + +// CHECK-NEXT: {{^}} (decl version=50 deprecated decl=deprecatedOnMacOS() +// CHECK-NEXT: {{^}} (decl_implicit version=50 deprecated decl=x + +@available(macOS, deprecated) +func deprecatedOnMacOS() { + let x = 1 +} + +// Since availableOniOS() doesn't have any active @available attributes it +// shouldn't create a scope. +// CHECK-NOT: availableOniOS + +@available(iOS, introduced: 53) +func availableOniOS() { } + +// CHECK-NEXT: {{^}} (decl version=51 decl=FinalDecl + +@available(OSX 51, *) +typealias FinalDecl = Int diff --git a/test/Availability/result_builder_availability.swift b/test/Availability/result_builder_availability.swift index 0777d011a82..35015d1f012 100644 --- a/test/Availability/result_builder_availability.swift +++ b/test/Availability/result_builder_availability.swift @@ -100,9 +100,11 @@ tuplify(true) { cond in globalFuncAvailableOn52() // expected-error{{'globalFuncAvailableOn52()' is only available in macOS 52 or newer}} // expected-note@-1{{add 'if #available' version check}} } else if true { - globalFuncAvailableOn52() + globalFuncAvailableOn52() // expected-error{{'globalFuncAvailableOn52()' is only available in macOS 52 or newer}} + // expected-note@-1{{add 'if #available' version check}} } else if false { - globalFuncAvailableOn52() + globalFuncAvailableOn52() // expected-error{{'globalFuncAvailableOn52()' is only available in macOS 52 or newer}} + // expected-note@-1{{add 'if #available' version check}} } } } @@ -162,7 +164,8 @@ tuplifyWithAvailabilityErasure(true) { cond in if cond, #unavailable(OSX 52) { cond } else { - globalFuncAvailableOn52() + globalFuncAvailableOn52() // expected-error{{'globalFuncAvailableOn52()' is only available in macOS 52 or newer}} + // expected-note@-1{{add 'if #available' version check}} } // https://github.com/apple/swift/issues/63764 diff --git a/test/IRGen/rdar176795176.swift b/test/IRGen/rdar176795176.swift new file mode 100644 index 00000000000..04baa7d6385 --- /dev/null +++ b/test/IRGen/rdar176795176.swift @@ -0,0 +1,138 @@ +// RUN: %target-swift-frontend -emit-ir -O -enable-experimental-feature Lifetimes %s + +// REQUIRES: swift_feature_Lifetimes + +// Minimal reproducer for IRGen crash: rdar://176795176 + +// MARK: - NE + +public struct NE: ~Escapable { + @_lifetime(immortal) + public init() {} +} + +// MARK: - INES + +public protocol INES: ~Escapable { + @_lifetime(copy self) + consuming func f(at indices: Range) -> NE +} + +// MARK: - NES + +public protocol NES: ~Copyable { + associatedtype Bytes: INES & ~Escapable +} + +// MARK: - IRef + +public protocol IRef: ~Copyable { + associatedtype PR: ~Copyable + associatedtype P: BitwiseCopyable + + static func a(of p: P) -> UnsafePointer +} + +// MARK: - Ref + +@frozen +public struct Ref: ~Escapable & ~Copyable +where + L: ~Escapable, + C: ~Copyable & IRef, + PR: ~Copyable +{ + @usableFromInline + let p: C.P + + @_lifetime(immortal) + @usableFromInline + init(immortal p: C.P) { + self.p = p + } +} + +extension Ref: Copyable +where + L: ~Escapable, + C: Copyable +{} + +extension Ref: BitwiseCopyable +where + L: ~Escapable, + C: Copyable +{} + +// MARK: - The crashing extension + +extension Ref: INES +where + L: ~Escapable, + C: Copyable, + PR: NES & ~Copyable +{ + @_lifetime(copy self) + public consuming func f( + at indices: Range + ) -> NE { + let bytes = self.fromPR { + return _overrideL(immortal: $0[bytes: indices]) + } + return _overrideL(bytes, copy: self) + } +} + +extension Ref +where + L: ~Escapable, + C: ~Copyable, + PR: ~Copyable +{ + @_lifetime(copy self) + fileprivate func fromPR( + body: (borrowing PR) throws(Thrown) -> Output + ) throws(Thrown) -> Output + where + Thrown: Error, + Output: ~Copyable + { + return _overrideL( + try body(C.a(of: self.p).pointee), + copy: self + ) + } +} + +// Subscripts used by f +extension NES where Self: ~Copyable { + subscript(bytes indices: Range) -> NE { + @_lifetime(borrow self) + get { + NE() + } + } +} + +// _overrideL stubs — mimicking stdlib + +@_unsafeNonescapableResult +@_lifetime(immortal) +@_alwaysEmitIntoClient +@_transparent +public func _overrideL( + immortal value: consuming T +) -> T { + value +} + +@_unsafeNonescapableResult +@_lifetime(copy source) +@_alwaysEmitIntoClient +@_transparent +public func _overrideL( + _ value: consuming T, + copy source: borrowing U +) -> T { + value +} diff --git a/test/IRGen/type_layout_addressable_for_dependencies_dedup.swift b/test/IRGen/type_layout_addressable_for_dependencies_dedup.swift new file mode 100644 index 00000000000..04fc043faf2 --- /dev/null +++ b/test/IRGen/type_layout_addressable_for_dependencies_dedup.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend -enable-experimental-feature AddressableTypes -emit-ir %s | %FileCheck %s + +// REQUIRES: swift_feature_AddressableTypes + +// Two types with identical {size, EI, align, pod, bitwiseTakable} but +// differing IsAddressableForDependencies must NOT share a single private +// type_layout_* global. The Flags word inside the global encodes AFD, so +// sharing causes the second-emitted type's runtime VWT to silently inherit +// the first type's AFD bit (after the runtime-side patch that ORs each +// field's TypeLayout AFD into the aggregate VWT during instantiation). + +@_addressableForDependencies +struct AFD { var x: UInt16; var y: UInt16 } + +struct NotAFD { var x: UInt16; var y: UInt16 } + +struct G { + var t: T + var afd: AFD + var notAFD: NotAFD +} + +func use(_ g: G) { + _ = g +} + +// AFD field: alignment mask 1 (align=2) + IsAddressableForDependencies +// (0x02000000) = 0x02000001 = 33554433. +// +// CHECK-DAG: @type_layout_4_2_0_pod_afd = private constant {{.*}} {{i64|i32}} 4, {{i64|i32}} 4, i32 33554433, i32 0 } +// +// Non-AFD field: alignment mask 1 = 0x00000001 = 1. +// +// CHECK-DAG: @type_layout_4_2_0_pod = private constant {{.*}} {{i64|i32}} 4, {{i64|i32}} 4, i32 1, i32 0 } diff --git a/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift b/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift index 4f5416654b6..ebf1da809fa 100644 --- a/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift +++ b/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift @@ -159,3 +159,28 @@ public func takeReadBorrower(f: @_lifetime(borrow a) (_ a: AnotherView) -> Anoth @inlinable public func takeWriteBorrower(f: @_lifetime(&a) (_ a: inout AnotherView) -> AnotherView) {} + +// Infer @_lifetime(copy f) +@inlinable +public func takeClosureImplicitDependence(f: () -> AnotherView) -> AnotherView { + f() +} + +@_lifetime(f) // Infer @_lifetime(copy f) +@inlinable +public func takeClosureImplicitDependenceKind(f: () -> AnotherView) -> AnotherView { + f() +} + +@inlinable +public func takeTakeImplicitCopyClosure(g: @_lifetime(copy f) (_ f: () -> AnotherView) -> AnotherView) { +} + +// Verify that takeClosureImplicitDependenceKind and takeClosureImplicitDependence +// are both inferred as @_lifetime(copy f). It is illegal to pass a function that +// borrows its context into a function-type parameter that copies its context. +@inlinable +public func callTTICC() { + takeTakeImplicitCopyClosure(g: takeClosureImplicitDependenceKind) + takeTakeImplicitCopyClosure(g: takeClosureImplicitDependence) +} diff --git a/test/ModuleInterface/attr-rawlayout.swift b/test/ModuleInterface/attr-rawlayout.swift new file mode 100644 index 00000000000..5b032a388c0 --- /dev/null +++ b/test/ModuleInterface/attr-rawlayout.swift @@ -0,0 +1,25 @@ +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name attrs -enable-experimental-feature RawLayout +// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name attrs +// RUN: %FileCheck %s --input-file %t.swiftinterface + +// REQUIRES: swift_feature_RawLayout + +// CHECK: @_rawLayout(size: 5, alignment: 4) public struct A_ExplicitSizeAlign +@_rawLayout(size: 5, alignment: 4) +public struct A_ExplicitSizeAlign: ~Copyable {} + +// CHECK: @_rawLayout(like: T) public struct B_Cell +@_rawLayout(like: T) +public struct B_Cell: ~Copyable {} + +// CHECK: @_rawLayout(like: T, movesAsLike) public struct B2_CellMovesAsLike +@_rawLayout(like: T, movesAsLike) +public struct B2_CellMovesAsLike: ~Copyable {} + +// CHECK: @_rawLayout(likeArrayOf: T, count: 8) public struct C_SmallVector +@_rawLayout(likeArrayOf: T, count: 8) +public struct C_SmallVector: ~Copyable {} + +// CHECK: @_rawLayout(likeArrayOf: T, count: 8, movesAsLike) public struct C2_SmallVectorMovesAsLike +@_rawLayout(likeArrayOf: T, count: 8, movesAsLike) +public struct C2_SmallVectorMovesAsLike: ~Copyable {} diff --git a/test/ModuleInterface/lifetime_underscored_dependence_test.swift b/test/ModuleInterface/lifetime_underscored_dependence_test.swift index b21fc74a13d..8220dca74bf 100644 --- a/test/ModuleInterface/lifetime_underscored_dependence_test.swift +++ b/test/ModuleInterface/lifetime_underscored_dependence_test.swift @@ -235,3 +235,35 @@ import lifetime_underscored_dependence // CHECK-NEXT: #if compiler(>=5.3) && $ClosureLifetimes // CHECK-NEXT: @inlinable public func takeWriteBorrower(f: @_lifetime(&a) (_ a: inout lifetime_underscored_dependence::AnotherView) -> lifetime_underscored_dependence::AnotherView) {} // CHECK-NEXT: #endif + +// CHECK-NEXT: #if compiler(>=5.3) && $Lifetimes +// CHECK-NEXT: @inlinable public func takeClosureImplicitDependence(f: () -> lifetime_underscored_dependence::AnotherView) -> lifetime_underscored_dependence::AnotherView { +// CHECK-NEXT: f() +// CHECK-NEXT: } +// CHECK-NEXT: #else +// CHECK-NEXT: @inlinable public func takeClosureImplicitDependence(f: () -> lifetime_underscored_dependence::AnotherView) -> lifetime_underscored_dependence::AnotherView { +// CHECK-NEXT: f() +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +// CHECK-NEXT: #if compiler(>=5.3) && $Lifetimes +// CHECK-NEXT: @_lifetime(f) +// CHECK-NEXT: @inlinable public func takeClosureImplicitDependenceKind(f: () -> lifetime_underscored_dependence::AnotherView) -> lifetime_underscored_dependence::AnotherView { +// CHECK-NEXT: f() +// CHECK-NEXT: } +// CHECK-NEXT: #else +// CHECK-NEXT: @lifetime(f) +// CHECK-NEXT: @inlinable public func takeClosureImplicitDependenceKind(f: () -> lifetime_underscored_dependence::AnotherView) -> lifetime_underscored_dependence::AnotherView { +// CHECK-NEXT: f() +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +// CHECK: #if compiler(>=5.3) && $ClosureLifetimes +// CHECK-NEXT: @inlinable public func takeTakeImplicitCopyClosure(g: @_lifetime(copy f) (_ f: () -> lifetime_underscored_dependence::AnotherView) -> lifetime_underscored_dependence::AnotherView) { +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +// CHECK: @inlinable public func callTTICC() { +// CHECK-NEXT: takeTakeImplicitCopyClosure(g: takeClosureImplicitDependenceKind) +// CHECK-NEXT: takeTakeImplicitCopyClosure(g: takeClosureImplicitDependence) +// CHECK-NEXT: } diff --git a/test/ModuleInterface/pre_inverse_generics_except.swift b/test/ModuleInterface/pre_inverse_generics_except.swift new file mode 100644 index 00000000000..c13dc774755 --- /dev/null +++ b/test/ModuleInterface/pre_inverse_generics_except.swift @@ -0,0 +1,43 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-module-interface(%t/Test.swiftinterface) %s \ +// RUN: -module-name Test \ +// RUN: -enable-experimental-feature PreInverseGenericsExcept + +// RUN: %FileCheck --implicit-check-not '#if' %s < %t/Test.swiftinterface + +// REQUIRES: swift_feature_PreInverseGenericsExcept + +// The bare @_preInverseGenerics needs no feature guard. +// CHECK: @_preInverseGenerics public func bare(_ t: borrowing T) where T : ~Copyable +@_preInverseGenerics +public func bare(_ t: borrowing T) {} + +// The except: form requires a #if $PreInverseGenericsExcept guard. +// Older compilers that don't support the feature will not see the declaration. + +// CHECK: #if compiler(>=5.3) && $PreInverseGenericsExcept +// CHECK-NEXT: @_preInverseGenerics(except: ~Copyable) public func exceptCopyable(_ t: borrowing T) where T : ~Copyable, T : ~Escapable +// CHECK-NEXT: #endif +@_preInverseGenerics(except: ~Copyable) +public func exceptCopyable(_ t: borrowing T) {} + +// CHECK: #if compiler(>=5.3) && $PreInverseGenericsExcept +// CHECK-NEXT: @_preInverseGenerics(except: ~Escapable) public func exceptEscapable(_ t: borrowing T) where T : ~Copyable, T : ~Escapable +// CHECK-NEXT: #endif +@_preInverseGenerics(except: ~Escapable) +public func exceptEscapable(_ t: borrowing T) {} + +// CHECK: #if compiler(>=5.3) && $PreInverseGenericsExcept +// CHECK-NEXT: @_preInverseGenerics(except: ~Copyable & ~Escapable) public func exceptBoth(_ t: borrowing T) where T : ~Copyable, T : ~Escapable +@_preInverseGenerics(except: ~Copyable & ~Escapable) +public func exceptBoth(_ t: borrowing T) {} + + +@frozen +public struct MySpan: ~Copyable { +// CHECK: #if compiler(>=5.3) && $PreInverseGenericsExcept +// CHECK-NEXT: @_preInverseGenerics(except: ~Copyable) public var _count: Swift::Int +// CHECK-NEXT: #endif + @_preInverseGenerics(except: ~Copyable) + public var _count: Int +} diff --git a/test/ModuleInterface/property_wrapper_with_internal_set_projected_value.swift b/test/ModuleInterface/property_wrapper_with_internal_set_projected_value.swift new file mode 100644 index 00000000000..1e610a5539d --- /dev/null +++ b/test/ModuleInterface/property_wrapper_with_internal_set_projected_value.swift @@ -0,0 +1,113 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-emit-module-interface(%t/TestResilient.swiftinterface) %s -module-name TestResilient +// RUN: %target-swift-typecheck-module-from-interface(%t/TestResilient.swiftinterface) -module-name TestResilient +// RUN: %FileCheck %s < %t/TestResilient.swiftinterface + +// RUN: %target-swift-frontend -compile-module-from-interface %t/TestResilient.swiftinterface -o %t/TestResilient.swiftmodule + +// CHECK: @propertyWrapper public struct WrapperWithInternalSet { +// CHECK: public var wrappedValue: T +// CHECK: public var projectedValue: Swift::Bool { +// CHECK: get +// CHECK: } +// CHECK: public init(wrappedValue: T) +// CHECK: } +@propertyWrapper +public struct WrapperWithInternalSet { + public var wrappedValue: T + public internal(set) var projectedValue: Bool = true + public init(wrappedValue: T) { self.wrappedValue = wrappedValue } +} + +// CHECK: @propertyWrapper public struct WrapperWithPrivateSet { +// CHECK: public var wrappedValue: T +// CHECK: public var projectedValue: Swift::Bool { +// CHECK: get +// CHECK: } +// CHECK: public init(wrappedValue: T) +// CHECK: } +@propertyWrapper +public struct WrapperWithPrivateSet { + public var wrappedValue: T + public private(set) var projectedValue: Bool = true + public init(wrappedValue: T) { self.wrappedValue = wrappedValue } +} + +// CHECK: @propertyWrapper public struct WrapperWithImmutable { +// CHECK: public var wrappedValue: T +// CHECK: public let projectedValue: Swift::Bool +// CHECK: public init(wrappedValue: T) +// CHECK: } +@propertyWrapper +public struct WrapperWithImmutable { + public var wrappedValue: T + public let projectedValue: Bool = true + public init(wrappedValue: T) { self.wrappedValue = wrappedValue } +} + +// CHECK: @propertyWrapper public struct WrapperWithComputedPublic { +// CHECK: public var wrappedValue: T +// CHECK: public var projectedValue: Swift::Bool { +// CHECK: get +// CHECK: set +// CHECK: } +// CHECK: } +@propertyWrapper +public struct WrapperWithComputedPublic { + public var wrappedValue: T + public var projectedValue: Bool { + get { false } + set { } + } + public init(wrappedValue: T) { self.wrappedValue = wrappedValue } +} + +// CHECK: @propertyWrapper public struct WrapperWithComputedImmutable { +// CHECK: public var wrappedValue: T +// CHECK: public var projectedValue: Swift::Bool { +// CHECK: get +// CHECK: } +// CHECK: public init(wrappedValue: T) +// CHECK: } +@propertyWrapper +public struct WrapperWithComputedImmutable { + public var wrappedValue: T + public var projectedValue: Bool { + get { false } + } + public init(wrappedValue: T) { self.wrappedValue = wrappedValue } +} + +public struct Test { + // CHECK: public var $v1: Swift::Bool { + // CHECK: get + // CHECK: } + @WrapperWithInternalSet public var v1: Int + + // CHECK: public var $v2: Swift::Bool { + // CHECK: get + // CHECK: } + @WrapperWithPrivateSet public var v2: String + + // CHECK: public var $v3: Swift::Bool { + // CHECK: get + // CHECK: } + @WrapperWithImmutable public var v3: Bool + + // CHECK: public var $v4: Swift::Bool { + // CHECK: get + // CHECK: set + // CHECK: } + @WrapperWithComputedPublic public var v4: Double + + // CHECK: public var $v5: Swift::Bool { + // CHECK: get + // CHECK: } + @WrapperWithComputedImmutable public var v5: Int? + + // CHECK: public var $v6: Swift::Bool { + // CHECK: get + // CHECK: } + @WrapperWithComputedPublic public private(set) var v6: Bool? +} diff --git a/test/Reflection/typeref_lowering.swift b/test/Reflection/typeref_lowering.swift index 8f1c96a2397..31691dcf91a 100644 --- a/test/Reflection/typeref_lowering.swift +++ b/test/Reflection/typeref_lowering.swift @@ -1277,12 +1277,18 @@ $1_SiBV // CHECK-64: (builtin_fixed_array // CHECK-64-NEXT: (integer value=2) // CHECK-64-NEXT: (struct Swift.Int)) -// CHECK-64-NEXT: (array size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1) +// CHECK-64-NEXT: (array size=16 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1 count=2 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) // CHECK-32: (builtin_fixed_array // CHECK-32-NEXT: (integer value=2) // CHECK-32-NEXT: (struct Swift.Int)) -// CHECK-32-NEXT: (array size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1) +// CHECK-32-NEXT: (array size=8 alignment=4 stride=8 num_extra_inhabitants=0 bitwise_takable=1 count=2 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) SiBW // CHECK-64: (builtin_borrow diff --git a/test/SIL/closure_lifetime_dependence.swift b/test/SIL/closure_lifetime_dependence.swift index d86a39f52df..989fcec9654 100644 --- a/test/SIL/closure_lifetime_dependence.swift +++ b/test/SIL/closure_lifetime_dependence.swift @@ -113,3 +113,13 @@ func callContextAndArgDependentPicker() { func copyClosureNE(body: () -> NE) -> NE { body() } + +// CHECK-LABEL: sil hidden @$s27closure_lifetime_dependence23implicitDependClosureNE4bodyAA0G0VAEyXE_tF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> @lifetime(captures) @owned NE) -> @lifetime(copy 0) @owned NE { +// CHECK: bb0(%0 : $@noescape @callee_guaranteed () -> @lifetime(captures) @owned NE): +// CHECK: [[RESULT:%[0-9]+]] = apply %0() : $@noescape @callee_guaranteed () -> @lifetime(captures) @owned NE +// CHECK-NEXT: return [[RESULT]] +// CHECK-LABEL: } // end sil function '$s27closure_lifetime_dependence23implicitDependClosureNE4bodyAA0G0VAEyXE_tF' +@_lifetime(body) +func implicitDependClosureNE(body: () -> NE) -> NE { + body() +} diff --git a/test/SILGen/mangling_inverse_generics.swift b/test/SILGen/mangling_inverse_generics.swift index 91a7f7f3edd..015c8abc516 100644 --- a/test/SILGen/mangling_inverse_generics.swift +++ b/test/SILGen/mangling_inverse_generics.swift @@ -1,11 +1,18 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-emit-silgen %s -module-name test \ // RUN: -parse-as-library \ +// RUN: -enable-experimental-feature Lifetimes \ +// RUN: -enable-experimental-feature LifetimeDependence \ +// RUN: -enable-experimental-feature PreInverseGenericsExcept \ // RUN: > %t/test.silgen // RUN: %FileCheck %s < %t/test.silgen // RUN: %swift-demangle < %t/test.silgen | %FileCheck %s --check-prefix=DEMANGLED +// REQUIRES: swift_feature_Lifetimes +// REQUIRES: swift_feature_LifetimeDependence +// REQUIRES: swift_feature_PreInverseGenericsExcept + protocol NoncopyableProto: ~Copyable {} @@ -352,4 +359,81 @@ extension E where T: ~Copyable & NoncopyableProto { func dumb() {} } +//===----------------------------------------------------------------------===// +// @_preInverseGenerics(except:) +//===----------------------------------------------------------------------===// + +// DEMANGLED: test.keepCopyable(A) -> () +// CHECK: sil [ossa] @$s4test12keepCopyableyyxRi_zlF : $@convention(thin) (@in_guaranteed T) -> () { +@_preInverseGenerics(except: ~Copyable) +public func keepCopyable(_ t: borrowing T) {} + +// DEMANGLED: test.keepEscapable(A) -> () +// CHECK: sil [ossa] @$s4test13keepEscapableyyxRi0_zlF : $@convention(thin) (@in_guaranteed T) -> () { +@_preInverseGenerics(except: ~Escapable) +public func keepEscapable(_ t: borrowing T) {} + +// DEMANGLED: test.stripBoth(A) -> () +// CHECK: sil [ossa] @$s4test9stripBothyyxlF : $@convention(thin) (@in_guaranteed T) -> () { +@_preInverseGenerics +public func stripBoth(_ t: borrowing T) {} + + +public struct F: ~Copyable { + // DEMANGLED: (extension in test):test.F< where A: ~Swift.Copyable>.memberKeepCopyable() -> () + // CHECK: sil [ossa] @$s4test1FVAARi_zrlE18memberKeepCopyableyyF : $@convention(method) (@guaranteed F) -> () { + @_preInverseGenerics(except: ~Copyable) + public func memberKeepCopyable() {} + + // Strips ~Copyable from T but keeps ~Escapable from U + // DEMANGLED: test.F.withEscapable(A1) -> () + // CHECK: sil [ossa] @$s4test1FV13withEscapableyyqd__Ri0_d__lF : $@convention(method) (@in_guaranteed U, @guaranteed F) -> () { + @_preInverseGenerics(except: ~Escapable) + public func withEscapable(_ u: borrowing U) {} + + // Keeps ~Copyable from both T and U, strips ~Escapable from U + // DEMANGLED: (extension in test):test.F< where A: ~Swift.Copyable>.withEscapable2(A1) -> () + // CHECK: sil [ossa] @$s4test1FVAARi_zrlE14withEscapable2yyqd__Ri_d__lF : $@convention(method) (@in_guaranteed U, @guaranteed F) -> () { + @_preInverseGenerics(except: ~Copyable) + public func withEscapable2(_ u: borrowing U) {} +} + +// Simulates Span gaining ~Escapable. Only ~Copyable is mangled into its symbols. +@frozen +public struct MySpan: ~Copyable, ~Escapable { + // DEMANGLED: (extension in test):test.MySpan< where A: ~Swift.Copyable>._count.getter : Swift.Int + // CHECK: sil [transparent] [serialized] [ossa] @$s4test6MySpanVAARi_zrlE6_countSivg : $@convention(method) (@guaranteed MySpan) -> Int { + @_preInverseGenerics(except: ~Copyable) + public var _count: Int + + // DEMANGLED: (extension in test):test.MySpan< where A: ~Swift.Copyable>._pointer.getter : Swift.UnsafeRawPointer? + // CHECK: sil [transparent] [serialized] [ossa] @$s4test6MySpanVAARi_zrlE8_pointerSVSgvg : $@convention(method) (@guaranteed MySpan) -> Optional { + @_preInverseGenerics(except: ~Copyable) + public var _pointer: UnsafeRawPointer? + + // DEMANGLED: (extension in test):test.MySpan< where A: ~Swift.Copyable>.oldMethod() -> () + // CHECK: sil [ossa] @$s4test6MySpanVAARi_zrlE9oldMethodyyF : $@convention(method) (@guaranteed MySpan) -> () { + @_preInverseGenerics(except: ~Copyable) + public func oldMethod() {} + + // DEMANGLED: (extension in test):test.MySpan< where A: ~Swift.Copyable>.oldComputed.getter : Swift.Int + // CHECK: sil [ossa] @$s4test6MySpanVAARi_zrlE11oldComputedSivg : $@convention(method) (@guaranteed MySpan) -> Int { + @_preInverseGenerics(except: ~Copyable) + public var oldComputed: Int { return _count } + + @_lifetime(immortal) + public init() { + self._count = 0 + self._pointer = nil + } +} + +// @_preInverseGenerics on an extension was permitted but seems to have no effect on member mangling; warn about that. +@_preInverseGenerics +extension MySpan where T: ~Copyable & ~Escapable { + // Both inverses are still mangled. + // DEMANGLED: (extension in test):test.MySpan< where A: ~Swift.Copyable, A: ~Swift.Escapable>.extMethod() -> () + // CHECK: sil [ossa] @$s4test6MySpanVAARi_zRi0_zrlE9extMethodyyF : $@convention(method) (@guaranteed MySpan) -> () { + public func extMethod() {} +} diff --git a/test/SILGen/partial_apply_lifetime.swift b/test/SILGen/partial_apply_lifetime.swift new file mode 100644 index 00000000000..27244e7be33 --- /dev/null +++ b/test/SILGen/partial_apply_lifetime.swift @@ -0,0 +1,230 @@ +// RUN: %target-swift-emit-silgen -module-name partial_apply_lifetime -enable-experimental-feature Lifetimes %s | %FileCheck %s + +// REQUIRES: swift_feature_Lifetimes + +// These tests exercise the lifetime dependencies computed for partial_apply +// result types by LifetimeDependenceInfo::partialApply. Each case pins down +// exactly what partialApply must produce by declaring an explicit +// @_lifetime(...) on the callee's closure parameter and then checking the +// convert_escape_to_noescape target type (which is the partial_apply's result +// type, minus the @noescape attribute). A convert_function between the +// partial_apply and the consuming apply would mean partialApply disagreed with +// the callee's expected type, so `CHECK-NOT: convert_function` is added as an +// extra guard. + +struct NE: ~Escapable { + @_lifetime(immortal) + init() {} +} + +// Baseline: a single-capture closure with a borrow-on-capture result. + +@_lifetime(copy f) +func copyNE(f: () -> NE) -> NE { + f() +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime9callGetNE3ne1AA0F0VAE_tF : $@convention(thin) (@guaranteed NE) -> @lifetime(copy 0) @owned NE { +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime9callGetNE3ne1AA0F0VAE_tFAEyXEfU_ : $@convention(thin) (@guaranteed NE) -> @lifetime(borrow 0) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: [[NECLOSURE:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed () -> @lifetime(captures) @owned NE +// CHECK: [[GETNE:%[0-9]+]] = function_ref @$s22partial_apply_lifetime6copyNE1fAA0E0VAEyXE_tF +// CHECK: apply [[GETNE]]([[NECLOSURE]]) +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime9callGetNE3ne1AA0F0VAE_tF' +func callGetNE(ne1: NE) -> NE { + copyNE { ne1 } +} + +// Dependency on an unbound formal parameter: the source index is kept and no +// captures flag is added. + +@_lifetime(copy f) +func eatOneBorrow(f: @_lifetime(borrow ne) (_ ne: NE) -> NE) -> NE { + let local = NE() + return f(local) +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime19callBorrowOnUnbound4condAA2NEVSb_tF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime19callBorrowOnUnbound4condAA2NEVSb_tFA2EXEfU_ : $@convention(thin) (@guaranteed NE, Bool) -> @lifetime(borrow 0) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed (@guaranteed NE) -> @lifetime(borrow 0) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime19callBorrowOnUnbound4condAA2NEVSb_tF' +func callBorrowOnUnbound(cond: Bool) -> NE { + eatOneBorrow { n in if cond { return n } else { return n } } +} + +// Dependency on a borrowed source: replaced with captures. + +@_lifetime(copy f) +func eatOneCaptures(f: @_lifetime(captures) (NE) -> NE) -> NE { + let local = NE() + return f(local) +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime14callDepOnBound5boundAA2NEVAE_tF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime14callDepOnBound5boundAA2NEVAE_tFA2EXEfU_ : $@convention(thin) (@guaranteed NE, @guaranteed NE) -> @lifetime(borrow 1) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed (@guaranteed NE) -> @lifetime(captures) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime14callDepOnBound5boundAA2NEVAE_tF' +@_lifetime(copy bound) +func callDepOnBound(bound: NE) -> NE { + eatOneCaptures { _ in bound } +} + +// Dependency on a mix of captured and uncaptured sources: only the dependencies +// on captured parameters (those bound by the partial apply) are replaced with +// the captures dependency source. + +@_lifetime(copy f) +func eatOneCapturesAndBorrow(f: @_lifetime(captures, borrow ne) (_ ne: NE) -> NE) -> NE { + let local = NE() + return f(local) +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime9callMixed5bound4condAA2NEVAF_SbtF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime9callMixed5bound4condAA2NEVAF_SbtFA2FXEfU_ : $@convention(thin) (@guaranteed NE, Bool, @guaranteed NE) -> @lifetime(borrow 0, borrow 1, borrow 2) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed (@guaranteed NE) -> @lifetime(captures, borrow 0) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime9callMixed5bound4condAA2NEVAF_SbtF' +@_lifetime(copy bound) +func callMixed(bound: NE, cond: Bool) -> NE { + eatOneCapturesAndBorrow { n in cond ? n : bound } +} + +// Multiple parameters, all captured. + +@_lifetime(copy f) +func eatZeroCaptures(f: @_lifetime(captures) () -> NE) -> NE { + f() +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime18callBindMultiBound1a1b4condAA2NEVAG_AGSbtF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime18callBindMultiBound1a1b4condAA2NEVAG_AGSbtFAGyXEfU_ : $@convention(thin) (Bool, @guaranteed NE, @guaranteed NE) -> @lifetime(borrow 0, borrow 1, borrow 2) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed () -> @lifetime(captures) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime18callBindMultiBound1a1b4condAA2NEVAG_AGSbtF' +@_lifetime(copy a, copy b) +func callBindMultiBound(a: NE, b: NE, cond: Bool) -> NE { + eatZeroCaptures { cond ? a : b } +} + +// Multiple parameters: some captured, some not. + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime20callBindMultiUnbound4cond3tagAA2NEVSb_SitF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime20callBindMultiUnbound4cond3tagAA2NEVSb_SitFA2FXEfU_ : $@convention(thin) (@guaranteed NE, Bool, Int) -> @lifetime(borrow 0) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed (@guaranteed NE) -> @lifetime(borrow 0) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime20callBindMultiUnbound4cond3tagAA2NEVSb_SitF' +@_lifetime(immortal) +func callBindMultiUnbound(cond: Bool, tag: Int) -> NE { + eatOneBorrow { n in + if cond && tag > 0 { return n } else { return n } + } +} + +// No lifetime sources: unaffected. + +@_lifetime(copy f) +func eatImmortal(f: @_lifetime(immortal) () -> NE) -> NE { + f() +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime12callImmortal5extraAA2NEVSi_tF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime12callImmortal5extraAA2NEVSi_tFAEyXEfU_ : $@convention(thin) (Int) -> @lifetime(immortal) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed () -> @lifetime(immortal) @owned NE +// CHECK-NOT: captures +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime12callImmortal5extraAA2NEVSi_tF' +@_lifetime(immortal) +func callImmortal(extra: Int) -> NE { + eatImmortal { let _ = extra; return NE() } +} + +// Mixed dependency kinds (inherit + scope) within a single entry. + +@_lifetime(copy f) +func eatOneCapturesCopy(f: @_lifetime(captures, copy ne) (_ ne: NE) -> NE) -> NE { + let local = NE() + return f(local) +} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime14callMixedKinds5bound4condAA2NEVAF_SbtF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime14callMixedKinds5bound4condAA2NEVAF_SbtFA2FXEfU_ : $@convention(thin) (@guaranteed NE, Bool, @guaranteed NE) -> @lifetime(copy 0, borrow 1, borrow 2) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed (@guaranteed NE) -> @lifetime(captures, copy 0) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime14callMixedKinds5bound4condAA2NEVAF_SbtF' +@_lifetime(copy bound) +func callMixedKinds(bound: NE, cond: Bool) -> NE { + eatOneCapturesCopy { n in cond ? n : bound } +} + +// Converting a non-throwing function to typed-throws: this legitimately requires a +// convert_function. + +func takeTypedThrowingNEReturning(_ f: (NE) throws(E) -> NE) {} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime34reabstractToTypedThrowsNEReturningyyAA2NEVADXEF : +// CHECK: [[THUNK:%[0-9]+]] = function_ref @$s22partial_apply_lifetime2NEVACIggo_A2Cs5NeverOIeggozr_TR : $@convention(thin) (@guaranteed NE, @lifetime(captures, copy 0) @guaranteed @noescape @callee_guaranteed (@guaranteed NE) -> @lifetime(captures, copy 0) @owned NE) -> (@owned NE, @error_indirect Never) +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[THUNK]] +// CHECK: convert_function [[CLOSURE]] to $@callee_guaranteed @substituted <τ_0_0> (@guaranteed NE) -> @lifetime(captures, copy 0) (@owned NE, @error_indirect τ_0_0) for +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime34reabstractToTypedThrowsNEReturningyyAA2NEVADXEF' +func reabstractToTypedThrowsNEReturning(_ f: (NE) -> NE) { + takeTypedThrowingNEReturning(f) +} + +// Typed throws with inout parameter. + +func takeTypedThrowingInout(_ f: (inout NE) throws(E) -> Void) {} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime28reabstractToTypedThrowsInoutyyyAA2NEVzXEF : +// CHECK: [[THUNK:%[0-9]+]] = function_ref @$s22partial_apply_lifetime2NEVIgl_ACs5NeverOIeglzr_TR : $@convention(thin) (@lifetime(copy 0) @inout NE, @guaranteed @noescape @callee_guaranteed (@lifetime(copy 0) @inout NE) -> ()) -> @error_indirect Never +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[THUNK]] +// CHECK: convert_function [[CLOSURE]] to $@callee_guaranteed @substituted <τ_0_0> (@lifetime(copy 0) @inout NE) -> @error_indirect τ_0_0 for +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime28reabstractToTypedThrowsInoutyyyAA2NEVzXEF' +func reabstractToTypedThrowsInout(_ f: (inout NE) -> Void) { + takeTypedThrowingInout(f) +} + +// Inout parameter, without a legitimate need for a convert_function: +// No convert_function is emitted. + +struct Holder: ~Escapable { + @_lifetime(immortal) + init() {} + + @_lifetime(self: copy other) + mutating func mut(other: NE) {} +} + +func consumeInout(_ f: (inout Holder) -> ()) {} + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime13driveMutation5otheryAA2NEV_tF : +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime13driveMutation5otheryAA2NEV_tFyAA6HolderVzXEfU_ : $@convention(thin) (@lifetime(copy 0) @inout Holder, @guaranteed NE) -> () +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed (@lifetime(copy 0) @inout Holder) -> () +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime13driveMutation5otheryAA2NEV_tF' +func driveMutation(other: NE) { + consumeInout { (h: inout Holder) in h.mut(other: other) } +} + +// Captured inout parameter. + +// CHECK-LABEL: sil hidden [ossa] @$s22partial_apply_lifetime21callWithCapturedInoutyAA2NEVADzF : $@convention(thin) (@lifetime(copy 0) @inout NE) -> @lifetime(borrow 0) @owned NE { +// CHECK: [[FNREF:%[0-9]+]] = function_ref @$s22partial_apply_lifetime21callWithCapturedInoutyAA2NEVADzFADyXEfU_ : $@convention(thin) (@inout_aliasable NE) -> @lifetime(borrow 0) @owned NE +// CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FNREF]] +// CHECK-NOT: convert_function +// CHECK: convert_escape_to_noescape [not_guaranteed] [[CLOSURE]] to $@noescape @callee_guaranteed () -> @lifetime(captures) @owned NE +// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime21callWithCapturedInoutyAA2NEVADzF' +@_lifetime(&ne) +func callWithCapturedInout(_ ne: inout NE) -> NE { + copyNE { ne } +} diff --git a/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift b/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift index d99dd4f60f1..f6897bccdb7 100644 --- a/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift +++ b/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift @@ -450,18 +450,19 @@ func testMutableCapture(arg: consuming NCE, action: @escaping (inout NCE) -> ()) } } +// Explicit dependence on a nonescaping closure context. +@_lifetime(body) +func testBasicExplicitClosureDependency(body: () -> NE) -> NE { + return body() +} + // Implicit dependence on a nonescaping closure context. -// -// TODO: remove the _overrideLifetime when context dependencies are tracked and -// non-escaping function types can be used as (copy) dependence sources (rdar://172511809). -@_lifetime(borrow value) -func testBasicClosureDependency(value: AnyObject, body: () -> NE) -> NE { - return _overrideLifetime(body(), borrowing: value) +func testBasicClosureDependency(body: () -> NE) -> NE { + return body() } // Implicit dependence on a nonescaping closure context. The result is escaping in the current generic context, so // should not be diagnosed as an escape. -@_lifetime(copy f) func testIndirectClosureResult(f: () -> CNE) -> CNE { return f() } diff --git a/test/SILOptimizer/specialize_by_integer_parameter.swift b/test/SILOptimizer/specialize_by_integer_parameter.swift index 5a654d908bc..7aa73ebcbba 100644 --- a/test/SILOptimizer/specialize_by_integer_parameter.swift +++ b/test/SILOptimizer/specialize_by_integer_parameter.swift @@ -5,7 +5,7 @@ public struct Foo { // CHECK: [[PARAM_VAL:%.*]] = type_value $Int for count // CHECK: [[SPEC_INT:%.*]] = integer_literal ${{.*}}, 3 // CHECK: [[PARAM_INT:%.*]] = struct_extract [[PARAM_VAL]], #Int._value - // CHECK: builtin "cmp_eq_Int64"([[PARAM_INT]], [[SPEC_INT]]) + // CHECK: builtin "cmp_eq_Int{{.*}}"([[PARAM_INT]], [[SPEC_INT]]) @specialized(where count == 3) public func bar() -> Int { return count diff --git a/test/Sema/lifetime_dependence_functype.swift b/test/Sema/lifetime_dependence_functype.swift index b206bc8e764..d9215d36e5d 100644 --- a/test/Sema/lifetime_dependence_functype.swift +++ b/test/Sema/lifetime_dependence_functype.swift @@ -429,11 +429,26 @@ func callLifetimeFunctions() { } // ~Escapable function types -@_lifetime(body) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow body)' or '@_lifetime(copy body)'}} +@_lifetime(body) // OK: Infer copy +func implicitDependKindClosureNE(body: () -> NE) -> NE { + body() +} + +func callImplicitDependKindClosureNE(ne: NE) -> NE { + let neo = implicitDependClosureNE { ne } + return neo +} + func implicitDependClosureNE(body: () -> NE) -> NE { body() } +func callImplicitDependClosureNE(ne: NE) -> NE { + // OK: Infer copy + let neo = implicitDependClosureNE { ne } + return neo +} + @_lifetime(copy body) func explicitCopyClosureNE(body: () -> NE) -> NE { body() diff --git a/test/Sema/preInverseGenerics.swift b/test/Sema/preInverseGenerics.swift new file mode 100644 index 00000000000..0a21560b5da --- /dev/null +++ b/test/Sema/preInverseGenerics.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature PreInverseGenericsExcept + +// REQUIRES: swift_feature_PreInverseGenericsExcept + +@_preInverseGenerics +func bare(_ t: borrowing T) {} + +@_preInverseGenerics(except: ~Copyable) +func exceptCopyable(_ t: borrowing T) {} + +@_preInverseGenerics(except: ~Escapable) +func exceptEscapable(_ t: borrowing T) {} + +// excepting all inverses is equivalent to not using the attribute +@_preInverseGenerics(except: ~Copyable & ~Escapable) // expected-warning {{'@_preInverseGenerics' that excepts all inverse constraints is equivalent to not using the attribute}} +func exceptBoth(_ t: borrowing T) {} + +// `except: Any` is confusing; reject it in favor of the bare form. +@_preInverseGenerics(except: Any) // expected-error {{'except' argument to '@_preInverseGenerics' must consist only of inverse constraints such as '~Copyable' or '~Escapable'}} +func exceptAny(_ t: borrowing T) {} + +// wrong label +@_preInverseGenerics(foo: ~Copyable) // expected-error {{expected 'except:' argument in '@_preInverseGenerics'}} +func bad1() {} + +// non-inverse type +@_preInverseGenerics(except: Int) // expected-error {{'except' argument to '@_preInverseGenerics' must consist only of inverse constraints such as '~Copyable' or '~Escapable'}} +func bad2() {} + +// positive protocol (not inverted) +@_preInverseGenerics(except: Copyable) // expected-error {{'except' argument to '@_preInverseGenerics' must consist only of inverse constraints such as '~Copyable' or '~Escapable'}} +func bad3() {} + +// Warning: attribute on extension has no effect +struct S: ~Copyable {} + +@_preInverseGenerics // expected-warning {{'@_preInverseGenerics' has no effect on an extension; place it on individual members instead}} +extension S where T: ~Copyable { + func extMethod() {} +} diff --git a/test/Serialization/attr-rawlayout.swift b/test/Serialization/attr-rawlayout.swift index bf7b468e3c0..76937c9e2ba 100644 --- a/test/Serialization/attr-rawlayout.swift +++ b/test/Serialization/attr-rawlayout.swift @@ -5,16 +5,24 @@ // REQUIRES: swift_feature_RawLayout -// BC-CHECK: : ~Copyable {} + // MODULE-CHECK: @_rawLayout(like: T) struct B_Cell @_rawLayout(like: T) struct B_Cell: ~Copyable {} +// MODULE-CHECK: @_rawLayout(likeArrayOf: T, count: 8, movesAsLike) struct C2_SmallVectorMovesAsLike +@_rawLayout(likeArrayOf: T, count: 8, movesAsLike) +struct C2_SmallVectorMovesAsLike: ~Copyable {} + // MODULE-CHECK: @_rawLayout(likeArrayOf: T, count: 8) struct C_SmallVector @_rawLayout(likeArrayOf: T, count: 8) struct C_SmallVector: ~Copyable {} diff --git a/test/attr/attr_availability.swift b/test/attr/attr_availability.swift index 993e5a77876..a26ef15830a 100644 --- a/test/attr/attr_availability.swift +++ b/test/attr/attr_availability.swift @@ -1217,9 +1217,17 @@ struct BadRename { init(range: Range, step: Int) { } } +func log(message: String) { } + +@available(*, unavailable, renamed: "log(message:)") +func log(format: String, _ args: Any...) { fatalError() } // expected-note {{'log(format:_:)' has been explicitly marked unavailable here}} + func testBadRename() { _ = BadRename(from: 5, to: 17) // expected-warning{{'init(from:to:step:)' is deprecated: replaced by 'init(range:step:)'}}{{documentation-file=deprecated-declaration}} // expected-note@-1{{use 'init(range:step:)' instead}} + + // Regression test for https://github.com/apple/swift/issues/64694 + log(format: "") // expected-error{{'log(format:_:)' has been renamed to 'log(message:)'}} } struct AvailableGenericParam<@available(*, deprecated) T> {} diff --git a/test/attr/attr_availability_unavailable_query.swift b/test/attr/attr_availability_unavailable_query.swift index 3f4e3ac34e9..d6205680680 100644 --- a/test/attr/attr_availability_unavailable_query.swift +++ b/test/attr/attr_availability_unavailable_query.swift @@ -60,12 +60,38 @@ func testUnavailableExpandAllElsePaths() { } } -func log(message: String) {} +// Verify that secondary (non-availability) conditions prevent the else branch +// from being refined. The else branch can fire because the secondary condition +// failed, in which case the platform may still be unavailable. +// expected-note@+1 *{{add '@available' attribute to enclosing global function}} +func testUnavailableWithSecondaryConditions() { + let x: Int? = 0 -@available(*, unavailable, renamed: "log(message:)") -func log(format: String, _ args: Any...) { fatalError() } // expected-note {{'log(format:_:)' has been explicitly marked unavailable here}} + // With a secondary condition, the else branch is not refined because it + // could be reached when the secondary condition fails (not because the + // platform became available). + if #unavailable(macOS 998.0), let x { + _ = x + } else { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } -// Regression test for https://github.com/apple/swift/issues/64694 -func testUnavailableRenamedFromVariadicDoesntAssert() { - log(format: "") // expected-error{{'log(format:_:)' has been renamed to 'log(message:)'}} + // Same with a boolean secondary condition. + if #unavailable(macOS 998.0), x != nil { + } else { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } + + // Else-if chains are also not refined. + if #unavailable(macOS 998.0), let x { + _ = x + } else if x == nil { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } else { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } } diff --git a/test/attr/attr_preInverseGenerics.swift b/test/attr/attr_preInverseGenerics.swift new file mode 100644 index 00000000000..0203faaf6aa --- /dev/null +++ b/test/attr/attr_preInverseGenerics.swift @@ -0,0 +1,16 @@ +// RUN: %target-typecheck-verify-swift + +// The bare @_preInverseGenerics does NOT require the experimental feature. +@_preInverseGenerics +func bare(_ t: borrowing T) {} + +// The 'except:' form DOES require the experimental feature. +@_preInverseGenerics(except: ~Copyable) // expected-error {{'@_preInverseGenerics' is an experimental feature; use '-enable-experimental-feature PreInverseGenericsExcept'}} +func exceptCopyable(_ t: borrowing T) {} + +// An invalid attribute with 'except:' still DOES require the feature. +@_preInverseGenerics(except: Int) // expected-error {{'@_preInverseGenerics' is an experimental feature; use '-enable-experimental-feature PreInverseGenericsExcept'}} + // expected-error@-1 {{'except' argument to '@_preInverseGenerics' must consist only of inverse constraints such as '~Copyable' or '~Escapable'}} +func exceptInt(_ t: borrowing T) {} + + diff --git a/utils/build-script-impl b/utils/build-script-impl index fa57964cc69..8dca4d02f40 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -3187,7 +3187,7 @@ function build_and_test_installable_package() { call ${PLISTBUDDY_BIN} -c "Add Aliases array" "${DARWIN_TOOLCHAIN_INFO_PLIST}" # Marker used to populate in the final toolchain .pkg distribution - call ${PLISTBUDDY_BIN} -c "Add MinimumSystemVersion '${DARWIN_DEPLOYMENT_VERSION_OSX}'" "${DARWIN_TOOLCHAIN_INFO_PLIST}" + call ${PLISTBUDDY_BIN} -c "Add MinimumSystemVersion string '${DARWIN_DEPLOYMENT_VERSION_OSX}'" "${DARWIN_TOOLCHAIN_INFO_PLIST}" aliases_count=0 local IFS="," diff --git a/validation-test/Reflection/reflect_InlineArray.swift b/validation-test/Reflection/reflect_InlineArray.swift new file mode 100644 index 00000000000..cf6e93c3de3 --- /dev/null +++ b/validation-test/Reflection/reflect_InlineArray.swift @@ -0,0 +1,130 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -o %t/reflect_InlineArray -Xfrontend -disable-availability-checking +// RUN: %target-codesign %t/reflect_InlineArray + +// RUN: %target-run %target-swift-reflection-test %t/reflect_InlineArray | %FileCheck %s --check-prefix=CHECK-%target-ptrsize + +// REQUIRES: reflection_test_support +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: asan + +import SwiftReflectionTest + +class TestClass { + var t: InlineArray<42, Int> + var u: InlineArray<42, Int> + var v: InlineArray<42, Int> + var bytes: InlineArray<7, UInt8> + var nested: InlineArray<3, InlineArray<5, Int16>> + init(t: InlineArray<42, Int>) { + self.t = t + self.u = t + self.v = t + self.bytes = .init(repeating: 0) + self.nested = .init(repeating: .init(repeating: 0)) + } +} + +var obj = TestClass(t: .init(repeating: 42)) + +reflect(object: obj) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_InlineArray.TestClass) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size={{[0-9]+}} alignment=8 stride={{[0-9]+}} num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=t offset={{[0-9]+}} +// CHECK-64: (struct size=336 alignment=8 stride=336 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_storage offset=0 +// CHECK-64: (array size=336 alignment=8 stride=336 num_extra_inhabitants=0 bitwise_takable=1 count=42 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-64: (field name=u offset={{[0-9]+}} +// CHECK-64: (struct size=336 alignment=8 stride=336 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_storage offset=0 +// CHECK-64: (array size=336 alignment=8 stride=336 num_extra_inhabitants=0 bitwise_takable=1 count=42 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-64: (field name=v offset={{[0-9]+}} +// CHECK-64: (struct size=336 alignment=8 stride=336 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_storage offset=0 +// CHECK-64: (array size=336 alignment=8 stride=336 num_extra_inhabitants=0 bitwise_takable=1 count=42 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-64: (field name=bytes offset={{[0-9]+}} +// CHECK-64: (struct size=7 alignment=1 stride=7 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_storage offset=0 +// CHECK-64: (array size=7 alignment=1 stride=7 num_extra_inhabitants=0 bitwise_takable=1 count=7 +// CHECK-64: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-64: (field name=nested offset={{[0-9]+}} +// CHECK-64: (struct size=30 alignment=2 stride=30 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_storage offset=0 +// CHECK-64: (array size=30 alignment=2 stride=30 num_extra_inhabitants=0 bitwise_takable=1 count=3 +// CHECK-64: (struct size=10 alignment=2 stride=10 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_storage offset=0 +// CHECK-64: (array size=10 alignment=2 stride=10 num_extra_inhabitants=0 bitwise_takable=1 count=5 +// CHECK-64: (struct size=2 alignment=2 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=_value offset=0 +// CHECK-64: (builtin size=2 alignment=2 stride=2 num_extra_inhabitants=0 bitwise_takable=1))))))))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_InlineArray.TestClass) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size={{[0-9]+}} alignment={{[0-9]+}} stride={{[0-9]+}} num_extra_inhabitants={{[0-9]+}} bitwise_takable=1 +// CHECK-32: (field name=t offset={{[0-9]+}} +// CHECK-32: (struct size=168 alignment=4 stride=168 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_storage offset=0 +// CHECK-32: (array size=168 alignment=4 stride=168 num_extra_inhabitants=0 bitwise_takable=1 count=42 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-32: (field name=u offset={{[0-9]+}} +// CHECK-32: (struct size=168 alignment=4 stride=168 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_storage offset=0 +// CHECK-32: (array size=168 alignment=4 stride=168 num_extra_inhabitants=0 bitwise_takable=1 count=42 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-32: (field name=v offset={{[0-9]+}} +// CHECK-32: (struct size=168 alignment=4 stride=168 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_storage offset=0 +// CHECK-32: (array size=168 alignment=4 stride=168 num_extra_inhabitants=0 bitwise_takable=1 count=42 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-32: (field name=bytes offset={{[0-9]+}} +// CHECK-32: (struct size=7 alignment=1 stride=7 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_storage offset=0 +// CHECK-32: (array size=7 alignment=1 stride=7 num_extra_inhabitants=0 bitwise_takable=1 count=7 +// CHECK-32: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-32: (field name=nested offset={{[0-9]+}} +// CHECK-32: (struct size=30 alignment=2 stride=30 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_storage offset=0 +// CHECK-32: (array size=30 alignment=2 stride=30 num_extra_inhabitants=0 bitwise_takable=1 count=3 +// CHECK-32: (struct size=10 alignment=2 stride=10 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_storage offset=0 +// CHECK-32: (array size=10 alignment=2 stride=10 num_extra_inhabitants=0 bitwise_takable=1 count=5 +// CHECK-32: (struct size=2 alignment=2 stride=2 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=_value offset=0 +// CHECK-32: (builtin size=2 alignment=2 stride=2 num_extra_inhabitants=0 bitwise_takable=1))))))))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done. diff --git a/validation-test/Reflection/reflect_variadic_generic.swift b/validation-test/Reflection/reflect_variadic_generic.swift new file mode 100644 index 00000000000..6d1eb8fdf7c --- /dev/null +++ b/validation-test/Reflection/reflect_variadic_generic.swift @@ -0,0 +1,65 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -target %target-swift-5.9-abi-triple -lswiftSwiftReflectionTest %s -o %t/reflect_variadic_generic +// RUN: %target-codesign %t/reflect_variadic_generic + +// RUN: %target-run %target-swift-reflection-test %t/reflect_variadic_generic | tee /dev/stderr | %FileCheck %s --dump-input=fail + +// REQUIRES: reflection_test_support +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: asan + +import SwiftReflectionTest + +struct VariadicStruct { + var values: (repeat each T) +} + +class VariadicHolder { + var contents: (repeat each T) + init(_ values: repeat each T) { + contents = (repeat each values) + } +} + +// Empty pack. +reflect(any: VariadicStruct< >(values: ())) + +// CHECK: Reflecting an existential. +// CHECK: Type reference: +// CHECK: (bound_generic_struct reflect_variadic_generic.VariadicStruct +// CHECK-NEXT: (pack)) + +// One-element pack. +reflect(any: VariadicStruct(values: 5)) + +// CHECK: Reflecting an existential. +// CHECK: Type reference: +// CHECK: (bound_generic_struct reflect_variadic_generic.VariadicStruct +// CHECK-NEXT: (pack +// CHECK-NEXT: (struct Swift.Int))) + +// Multi-element pack with mixed element types. +reflect(any: VariadicStruct(values: (1, "two", true))) + +// CHECK: Reflecting an existential. +// CHECK: Type reference: +// CHECK: (bound_generic_struct reflect_variadic_generic.VariadicStruct +// CHECK-NEXT: (pack +// CHECK-NEXT: (struct Swift.Int) +// CHECK-NEXT: (struct Swift.String) +// CHECK-NEXT: (struct Swift.Bool))) + +// Class with parameter pack. +reflect(object: VariadicHolder(7, 3.14)) + +// CHECK: Reflecting an object. +// CHECK: Type reference: +// CHECK: (bound_generic_class reflect_variadic_generic.VariadicHolder +// CHECK-NEXT: (pack +// CHECK-NEXT: (struct Swift.Int) +// CHECK-NEXT: (struct Swift.Double))) + +doneReflecting() + +// CHECK: Done. diff --git a/validation-test/Sema/type_checker_perf/fast/rdar170160180.swift b/validation-test/Sema/type_checker_perf/fast/rdar170160180.swift new file mode 100644 index 00000000000..354b3f18217 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar170160180.swift @@ -0,0 +1,116 @@ +// RUN: %target-typecheck-verify-swift -solver-scope-threshold=3000 + +class RequestRecord: Record { + var urlNoQuery: String + var method: String? + var hash: String + var instance: Int64 + + required init(row: Row) { fatalError() } +} + +extension Database { + func record(tmp: RequestRecord) { + // The formerly slow expression + let _ = RequestRecord.filter( + Column("") == tmp.hash + && Column("") == tmp.instance + && Column("") == tmp.urlNoQuery + && Column("") == tmp.method + && Column("") == tmp.method + && Column("") == tmp.method + ) + .fetchOne(self) + } +} + +// The rest was reduced from https://github.com/groue/GRDB.swift + +struct Database {} + +class Record {} + +extension Record: TableRecord { } +extension Record: FetchableRecord { } + +protocol TableRecord {} + +protocol DatabaseValueConvertible {} + +protocol StatementColumnConvertible {} + +protocol FetchableRecord {} + +struct Row {} + +protocol FetchRequest { + associatedtype RowDecoder +} + +extension FetchRequest where Self.RowDecoder : DatabaseValueConvertible { + func fetchOne(_: Database) -> Self.RowDecoder? { fatalError() } +} + +extension FetchRequest where Self.RowDecoder : DatabaseValueConvertible & StatementColumnConvertible { + func fetchOne(_: Database) -> Self.RowDecoder? { fatalError() } +} + +extension FetchRequest where Self.RowDecoder : FetchableRecord { + func fetchOne(_: Database) -> Self.RowDecoder? { fatalError() } +} + +extension FetchRequest where Self.RowDecoder == Row { + func fetchOne(_: Database) -> Row? { fatalError() } +} + +struct QueryInterfaceRequest: FetchRequest { +} + +extension TableRecord { + static func filter(_ predicate: some SQLSpecificExpressible) -> QueryInterfaceRequest { + fatalError() + } +} + +struct Request {} + +struct Column: Sendable { + init(_ name: String) {} + init(_ codingKey: some CodingKey) {} +} + +protocol SQLExpressible {} + +extension Column: SQLSpecificExpressible {} + +protocol SQLSpecificExpressible: SQLExpressible {} + +struct SQLExpression: SQLSpecificExpressible {} + +extension SQLSpecificExpressible { + static func && (lhs: Self, rhs: some SQLExpressible) -> SQLExpression { fatalError() } + static func && (lhs: some SQLExpressible, rhs: Self) -> SQLExpression { fatalError() } + static func && (lhs: Self, rhs: some SQLSpecificExpressible) -> SQLExpression { fatalError() } + static func == (lhs: Self, rhs: (any SQLExpressible)?) -> SQLExpression { fatalError() } + static func == (lhs: Self, rhs: Bool) -> SQLExpression { fatalError() } + static func == (lhs: (any SQLExpressible)?, rhs: Self) -> SQLExpression { fatalError() } + static func == (lhs: Bool, rhs: Self) -> SQLExpression { fatalError() } + static func == (lhs: Self, rhs: some SQLSpecificExpressible) -> SQLExpression { fatalError() } +} + +struct AssociationAggregate { + static func && (lhs: Self, rhs: Self) -> Self { fatalError()} + static func && (lhs: Self, rhs: some SQLExpressible) -> Self { fatalError()} + static func && (lhs: some SQLExpressible, rhs: Self) -> Self { fatalError()} + static func == (lhs: Self, rhs: Self) -> Self { fatalError() } + static func == (lhs: Self, rhs: (any SQLExpressible)?) -> Self { fatalError()} + static func == (lhs: (any SQLExpressible)?, rhs: Self) -> Self { fatalError()} + static func == (lhs: Self, rhs: Bool) -> Self { fatalError()} + static func == (lhs: Bool, rhs: Self) -> Self { fatalError()} +} + +struct Data {} + +extension Data: SQLExpressible {} +extension Int64: SQLExpressible {} +extension String: SQLExpressible {} \ No newline at end of file diff --git a/validation-test/Sema/type_checker_perf/fast/rdar176813402.swift b/validation-test/Sema/type_checker_perf/fast/rdar176813402.swift new file mode 100644 index 00000000000..e9d28161769 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar176813402.swift @@ -0,0 +1,25 @@ +// RUN: %target-typecheck-verify-swift -solver-scope-threshold=150 + +// This took ~2000 scopes in 6.2 and ~240k scopes in 6.3. + +struct S1 { + var x: Int + var y: Int + var z: Int +} + +struct S2 { + var x: Int32 + var y: Int32 + var z: Int32 +} + +func test(u: [Int32], v: [Int32]) { + let _ = stride(from: 0, to: u.count, by: 3).map { + S2(x: u[$0 + 1], y: u[$0], z: u[$0 + 2]) + } + + let _ = stride(from: 0, to: v.count, by: 3).map { + S1(x: Int(v[$0]), y: Int(v[$0 + 1]), z: Int(v[$0 + 2])) + } +} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar176848577.swift b/validation-test/Sema/type_checker_perf/fast/rdar176848577.swift new file mode 100644 index 00000000000..8dbfe56ae42 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar176848577.swift @@ -0,0 +1,50 @@ +// RUN: %target-typecheck-verify-swift -solver-scope-threshold=100 + +protocol P1 {} + +extension P1 { + static func +(lhs: Self, rhs: Self) -> Self { fatalError() } + static func -(lhs: Self, rhs: Self) -> Self { fatalError() } + static func *(lhs: Self, rhs: Double) -> Self { fatalError() } + static func /(lhs: Self, rhs: Double) -> Self { fatalError() } +} + +struct S1: P1 {} + +protocol P2 { + associatedtype A + associatedtype B +} + +protocol P3 { + associatedtype A: P2 +} + +extension P3 { + var x: A.B { fatalError() } + var y: A.B { fatalError() } +} + +struct S3: P3 { + // Note that S3 has two overloads of 'x' and 'y' each, one that + // returns A.B and one that returns 'S1'. + var x: S1 { fatalError() } + var y: S1 { fatalError() } +} + +struct S2: P2 { + typealias A = S3 + typealias B = Double +} + +struct G { + init(first: A.A, second: A.A) {} + init(first: (A.B, A.B), second: (A.B, A.B)) {} +} + +func test(x: S3, y: Double, z: Double) { + // This used to be slow because the solver would struggle with dead ends + // involving the wrong overloads of 'x.x' and 'x.y'. + let _ = G(first: (x.x - y / 2, x.y - z / 2), + second: (x.x + y / 2, x.y + z / 2)) +}