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))
+}