mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
Merge remote-tracking branch 'origin/main' into serialize-hidden-type-layout
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)] = [
|
||||
// <a, b | aaa=1, abbbba=b> 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),
|
||||
|
||||
// <a, b | aaa=1, ababbba=b> 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),
|
||||
|
||||
// <a, b | aaaa=1, ababba=b> 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<Word> = []
|
||||
|
||||
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<Symbol>) -> 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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<uint8_t>(kind);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
explicit constexpr InvertibleProtocolSet(StorageType bits) : bits(bits) {}
|
||||
constexpr InvertibleProtocolSet() : bits(0) {}
|
||||
|
||||
InvertibleProtocolSet(
|
||||
constexpr InvertibleProtocolSet(
|
||||
std::initializer_list<InvertibleProtocolKind> 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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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", ())
|
||||
|
||||
@@ -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<ProtocolDecl *, 4> protocols;
|
||||
|
||||
@@ -418,6 +418,21 @@ public:
|
||||
uncurry(ASTContext &ctx, ArrayRef<LifetimeDependenceInfo> 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<LifetimeDependenceInfo>
|
||||
partialApply(ASTContext &ctx, ArrayRef<LifetimeDependenceInfo> lifetimes,
|
||||
unsigned numFormalParams, unsigned numBoundParams);
|
||||
|
||||
bool operator==(const LifetimeDependenceInfo &other) const {
|
||||
return this->hasImmortalSpecifier() == other.hasImmortalSpecifier() &&
|
||||
this->hasCaptures() == other.hasCaptures() &&
|
||||
|
||||
@@ -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<ResolvePreInverseGenericsRequest,
|
||||
Type(Decl *, PreInverseGenericsAttr *),
|
||||
RequestFlags::SeparatelyCached> {
|
||||
public:
|
||||
using SimpleRequest::SimpleRequest;
|
||||
|
||||
private:
|
||||
friend SimpleRequest;
|
||||
|
||||
Type evaluate(Evaluator &evaluator,
|
||||
Decl *decl,
|
||||
PreInverseGenericsAttr *attr) const;
|
||||
|
||||
public:
|
||||
bool isCached() const { return true; }
|
||||
std::optional<Type> getCachedResult() const;
|
||||
void cacheResult(Type value) const;
|
||||
};
|
||||
|
||||
class ResolveRawLayoutTypeRequest
|
||||
: public SimpleRequest<ResolveRawLayoutTypeRequest,
|
||||
Type (StructDecl*, RawLayoutAttr *, bool),
|
||||
|
||||
@@ -430,6 +430,9 @@ SWIFT_REQUEST(TypeChecker, ConformanceIsolationRequest,
|
||||
SWIFT_REQUEST(TypeChecker, ResolveTypeRequest,
|
||||
Type (const TypeResolution *, TypeRepr *, GenericParamList *),
|
||||
Uncached, NoLocationInfo)
|
||||
SWIFT_REQUEST(TypeChecker, ResolvePreInverseGenericsRequest,
|
||||
Type(Decl *, PreInverseGenericsAttr *),
|
||||
SeparatelyCached, NoLocationInfo)
|
||||
SWIFT_REQUEST(TypeChecker, ResolveRawLayoutTypeRequest,
|
||||
Type(StructDecl *, RawLayoutAttr *, bool),
|
||||
SeparatelyCached, NoLocationInfo)
|
||||
|
||||
@@ -563,6 +563,9 @@ EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false)
|
||||
/// Enable @_lifetime attribute
|
||||
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true)
|
||||
|
||||
/// Enable @_preInverseGenerics(except:) attribute
|
||||
EXPERIMENTAL_FEATURE(PreInverseGenericsExcept, true)
|
||||
|
||||
/// Deprecates the compatibility memberwise initializer introduced by SE-0502,
|
||||
/// and removes it in the next major language mode.
|
||||
EXPERIMENTAL_FEATURE(DeprecateCompatMemberwiseInit, false)
|
||||
|
||||
@@ -1243,6 +1243,23 @@ public:
|
||||
TypeCache[TypeCacheKey] = BuiltForeign;
|
||||
return BuiltForeign;
|
||||
}
|
||||
case MetadataKind::FixedArray: {
|
||||
auto fixedArray = cast<TargetFixedArrayTypeMetadata<Runtime>>(Meta);
|
||||
auto elementAddress = RemoteAddress(
|
||||
fixedArray->Element, MetadataAddress.getAddressSpace());
|
||||
auto Element =
|
||||
readTypeFromMetadata(elementAddress, false, recursion_limit);
|
||||
if (!Element) return BuiltType();
|
||||
|
||||
auto count = static_cast<intptr_t>(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<BuiltType> 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<BuiltType> elements;
|
||||
elements.reserve(packLength);
|
||||
for (StoredSize i = 0; i < packLength; ++i) {
|
||||
RemoteAddress elementMetadata;
|
||||
if (!Reader->template readRemoteAddress<StoredPointer>(
|
||||
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.
|
||||
|
||||
@@ -383,17 +383,22 @@ public:
|
||||
|
||||
/// Array based layouts like Builtin.FixedArray<N, T>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -145,6 +145,9 @@ typedef enum swift_layout_kind {
|
||||
SWIFT_CLOSURE_CONTEXT,
|
||||
|
||||
// A contiguous list of N Ts, typically for Builtin.FixedArray<N, T>.
|
||||
// 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;
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+47
-36
@@ -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<AccessorDecl>(decl))
|
||||
if (auto *storage = accessor->getStorage())
|
||||
decl = storage;
|
||||
|
||||
return !decl->getAttrs().hasAttribute<PreInverseGenericsAttr>();
|
||||
if (auto *attr = decl->getAttrs().getAttribute<PreInverseGenericsAttr>())
|
||||
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<ExistentialType>(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<InverseRequirement, 2> &inverses,
|
||||
GenericSignature sig,
|
||||
InvertibleProtocolSet allowedInverses,
|
||||
std::optional<unsigned> inversesAlreadyMangledDepth,
|
||||
std::optional<unsigned> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1710,6 +1710,20 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
|
||||
break;
|
||||
}
|
||||
|
||||
case DeclAttrKind::PreInverseGenerics: {
|
||||
auto *attr = cast<PreInverseGenericsAttr>(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<RawLayoutAttr>(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<ProtocolCompositionType>());
|
||||
}
|
||||
|
||||
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<Decl *>(attachedTo),
|
||||
const_cast<PreInverseGenericsAttr *>(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<ProtocolCompositionType>()
|
||||
->getInverses();
|
||||
}
|
||||
|
||||
Type RawLayoutAttr::getResolvedLikeType(StructDecl *sd) const {
|
||||
auto &ctx = sd->getASTContext();
|
||||
return evaluateOrDefault(ctx.evaluator,
|
||||
|
||||
@@ -1001,6 +1001,7 @@ private:
|
||||
|
||||
// Tracks if we're refining for availability or unavailability.
|
||||
std::optional<bool> 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<AvailabilityContext> 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<AvailabilityContext> falseRefinement = std::nullopt;
|
||||
if (!startingScope->getAvailabilityContext().isContainedIn(
|
||||
falseFlowContext)) {
|
||||
if (!startingContext.isContainedIn(falseFlowContext))
|
||||
falseRefinement = falseFlowContext;
|
||||
}
|
||||
|
||||
auto makeResult =
|
||||
[isUnavailability](std::optional<AvailabilityContext> trueRefinement,
|
||||
std::optional<AvailabilityContext> 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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -280,6 +280,21 @@ static bool usesFeatureLifetimes(Decl *decl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static PreInverseGenericsAttr *getPreInverseGenericsExcept(Decl *decl) {
|
||||
if (auto pbd = dyn_cast<PatternBindingDecl>(decl))
|
||||
for (auto i : range(pbd->getNumPatternEntries()))
|
||||
if (auto anchorVar = pbd->getAnchoringVarDecl(i))
|
||||
return getPreInverseGenericsExcept(anchorVar);
|
||||
|
||||
return decl->getAttrs().getAttribute<PreInverseGenericsAttr>();
|
||||
}
|
||||
|
||||
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<AnyFunctionType>()) {
|
||||
return aft->hasExplicitLifetimeDependencies();
|
||||
|
||||
@@ -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> LifetimeDependenceInfo::uncurry(
|
||||
return ctx.AllocateCopy(uncurried);
|
||||
}
|
||||
|
||||
ArrayRef<LifetimeDependenceInfo> LifetimeDependenceInfo::partialApply(
|
||||
ASTContext &ctx, ArrayRef<LifetimeDependenceInfo> 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<LifetimeDependenceInfo, 2> 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()) {
|
||||
|
||||
+5
-5
@@ -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() {
|
||||
|
||||
@@ -1633,6 +1633,25 @@ void ResolveTypeEraserTypeRequest::cacheResult(Type value) const {
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// ResolvePreInverseGenericsRequest computation.
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
std::optional<Type> 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<ProtocolCompositionType>());
|
||||
attr->ExceptType = Ty;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// ResolveRawLayoutTypeRequest computation.
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -1454,6 +1454,7 @@ private:
|
||||
unsigned align: 16;
|
||||
unsigned pod: 1;
|
||||
unsigned bitwiseTakable: 2;
|
||||
unsigned addressableForDependencies: 1;
|
||||
};
|
||||
friend struct ::llvm::DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey>;
|
||||
llvm::DenseMap<FixedLayoutKey, llvm::Constant *> PrivateFixedLayouts;
|
||||
@@ -2162,23 +2163,25 @@ struct DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey> {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<ExtensionDecl>(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<FileUnit>(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<ProtocolCompositionType>();
|
||||
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: <all inverses>` 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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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<InvalidRecordKindError>(recordID));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3074,6 +3074,20 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
|
||||
}
|
||||
#include "swift/AST/DeclAttr.def"
|
||||
|
||||
case DeclAttrKind::PreInverseGenerics: {
|
||||
auto *attr = cast<PreInverseGenericsAttr>(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<ABIAttr>(DA);
|
||||
auto abbrCode = S.DeclTypeAbbrCodes[ABIDeclAttrLayout::Code];
|
||||
|
||||
@@ -222,6 +222,9 @@ public:
|
||||
case TypeInfoKind::Array: {
|
||||
printHeader("array");
|
||||
printBasic(TI);
|
||||
auto &ArrayTI = cast<ArrayTypeInfo>(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<ArrayTypeInfo>(sizeInt->getValue(), elementTI);
|
||||
return TC.makeTypeInfo<ArrayTypeInfo>(sizeInt->getValue(),
|
||||
BA->getElementType(), elementTI);
|
||||
}
|
||||
|
||||
const TypeInfo *visitBuiltinBorrowTypeRef(const BuiltinBorrowTypeRef *BA) {
|
||||
|
||||
@@ -586,6 +586,8 @@ static swift_typeinfo_t convertTypeInfo(const TypeInfo *TI) {
|
||||
NumFields = RecordTI->getNumCases();
|
||||
} else if (auto *RecordTI = dyn_cast<RecordTypeInfo>(TI)) {
|
||||
NumFields = RecordTI->getNumFields();
|
||||
} else if (auto *ArrayTI = dyn_cast<ArrayTypeInfo>(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<ArrayTypeInfo>(TI)) {
|
||||
auto *ElementTI = ArrayTI->getElementTypeInfo();
|
||||
return {
|
||||
"element",
|
||||
Index * ElementTI->getStride(),
|
||||
getTypeInfoKind(*ElementTI),
|
||||
reinterpret_cast<swift_typeref_t>(ArrayTI->getElementTypeRef()),
|
||||
};
|
||||
}
|
||||
|
||||
const FieldInfo *FieldInfo = nullptr;
|
||||
if (auto *EnumTI = dyn_cast<EnumTypeInfo>(TI)) {
|
||||
FieldInfo = &(EnumTI->getCases()[Index]);
|
||||
} else if (auto *RecordTI = dyn_cast<RecordTypeInfo>(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,
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<T> {
|
||||
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() { }
|
||||
}
|
||||
|
||||
@@ -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<T> {
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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<Int>) -> NE
|
||||
}
|
||||
|
||||
// MARK: - NES
|
||||
|
||||
public protocol NES: ~Copyable {
|
||||
associatedtype Bytes: INES & ~Escapable
|
||||
}
|
||||
|
||||
// MARK: - IRef
|
||||
|
||||
public protocol IRef<PR>: ~Copyable {
|
||||
associatedtype PR: ~Copyable
|
||||
associatedtype P: BitwiseCopyable
|
||||
|
||||
static func a(of p: P) -> UnsafePointer<PR>
|
||||
}
|
||||
|
||||
// MARK: - Ref
|
||||
|
||||
@frozen
|
||||
public struct Ref<L, C, PR>: ~Escapable & ~Copyable
|
||||
where
|
||||
L: ~Escapable,
|
||||
C: ~Copyable & IRef<PR>,
|
||||
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<Int>
|
||||
) -> 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<Thrown, Output: ~Escapable>(
|
||||
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<Int>) -> NE {
|
||||
@_lifetime(borrow self)
|
||||
get {
|
||||
NE()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _overrideL stubs — mimicking stdlib
|
||||
|
||||
@_unsafeNonescapableResult
|
||||
@_lifetime(immortal)
|
||||
@_alwaysEmitIntoClient
|
||||
@_transparent
|
||||
public func _overrideL<T: ~Copyable & ~Escapable>(
|
||||
immortal value: consuming T
|
||||
) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
@_unsafeNonescapableResult
|
||||
@_lifetime(copy source)
|
||||
@_alwaysEmitIntoClient
|
||||
@_transparent
|
||||
public func _overrideL<T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable>(
|
||||
_ value: consuming T,
|
||||
copy source: borrowing U
|
||||
) -> T {
|
||||
value
|
||||
}
|
||||
@@ -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<T> {
|
||||
var t: T
|
||||
var afd: AFD
|
||||
var notAFD: NotAFD
|
||||
}
|
||||
|
||||
func use<T>(_ g: G<T>) {
|
||||
_ = 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 }
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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<T>: ~Copyable {}
|
||||
|
||||
// CHECK: @_rawLayout(like: T, movesAsLike) public struct B2_CellMovesAsLike
|
||||
@_rawLayout(like: T, movesAsLike)
|
||||
public struct B2_CellMovesAsLike<T>: ~Copyable {}
|
||||
|
||||
// CHECK: @_rawLayout(likeArrayOf: T, count: 8) public struct C_SmallVector
|
||||
@_rawLayout(likeArrayOf: T, count: 8)
|
||||
public struct C_SmallVector<T>: ~Copyable {}
|
||||
|
||||
// CHECK: @_rawLayout(likeArrayOf: T, count: 8, movesAsLike) public struct C2_SmallVectorMovesAsLike
|
||||
@_rawLayout(likeArrayOf: T, count: 8, movesAsLike)
|
||||
public struct C2_SmallVectorMovesAsLike<T>: ~Copyable {}
|
||||
@@ -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: }
|
||||
|
||||
@@ -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>(_ t: borrowing T) where T : ~Copyable
|
||||
@_preInverseGenerics
|
||||
public func bare<T: ~Copyable>(_ 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>(_ t: borrowing T) where T : ~Copyable, T : ~Escapable
|
||||
// CHECK-NEXT: #endif
|
||||
@_preInverseGenerics(except: ~Copyable)
|
||||
public func exceptCopyable<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
// CHECK: #if compiler(>=5.3) && $PreInverseGenericsExcept
|
||||
// CHECK-NEXT: @_preInverseGenerics(except: ~Escapable) public func exceptEscapable<T>(_ t: borrowing T) where T : ~Copyable, T : ~Escapable
|
||||
// CHECK-NEXT: #endif
|
||||
@_preInverseGenerics(except: ~Escapable)
|
||||
public func exceptEscapable<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
// CHECK: #if compiler(>=5.3) && $PreInverseGenericsExcept
|
||||
// CHECK-NEXT: @_preInverseGenerics(except: ~Copyable & ~Escapable) public func exceptBoth<T>(_ t: borrowing T) where T : ~Copyable, T : ~Escapable
|
||||
@_preInverseGenerics(except: ~Copyable & ~Escapable)
|
||||
public func exceptBoth<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
|
||||
@frozen
|
||||
public struct MySpan<T: ~Copyable & ~Escapable>: ~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
|
||||
}
|
||||
@@ -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<T> {
|
||||
// CHECK: public var wrappedValue: T
|
||||
// CHECK: public var projectedValue: Swift::Bool {
|
||||
// CHECK: get
|
||||
// CHECK: }
|
||||
// CHECK: public init(wrappedValue: T)
|
||||
// CHECK: }
|
||||
@propertyWrapper
|
||||
public struct WrapperWithInternalSet<T> {
|
||||
public var wrappedValue: T
|
||||
public internal(set) var projectedValue: Bool = true
|
||||
public init(wrappedValue: T) { self.wrappedValue = wrappedValue }
|
||||
}
|
||||
|
||||
// CHECK: @propertyWrapper public struct WrapperWithPrivateSet<T> {
|
||||
// CHECK: public var wrappedValue: T
|
||||
// CHECK: public var projectedValue: Swift::Bool {
|
||||
// CHECK: get
|
||||
// CHECK: }
|
||||
// CHECK: public init(wrappedValue: T)
|
||||
// CHECK: }
|
||||
@propertyWrapper
|
||||
public struct WrapperWithPrivateSet<T> {
|
||||
public var wrappedValue: T
|
||||
public private(set) var projectedValue: Bool = true
|
||||
public init(wrappedValue: T) { self.wrappedValue = wrappedValue }
|
||||
}
|
||||
|
||||
// CHECK: @propertyWrapper public struct WrapperWithImmutable<T> {
|
||||
// CHECK: public var wrappedValue: T
|
||||
// CHECK: public let projectedValue: Swift::Bool
|
||||
// CHECK: public init(wrappedValue: T)
|
||||
// CHECK: }
|
||||
@propertyWrapper
|
||||
public struct WrapperWithImmutable<T> {
|
||||
public var wrappedValue: T
|
||||
public let projectedValue: Bool = true
|
||||
public init(wrappedValue: T) { self.wrappedValue = wrappedValue }
|
||||
}
|
||||
|
||||
// CHECK: @propertyWrapper public struct WrapperWithComputedPublic<T> {
|
||||
// CHECK: public var wrappedValue: T
|
||||
// CHECK: public var projectedValue: Swift::Bool {
|
||||
// CHECK: get
|
||||
// CHECK: set
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
@propertyWrapper
|
||||
public struct WrapperWithComputedPublic<T> {
|
||||
public var wrappedValue: T
|
||||
public var projectedValue: Bool {
|
||||
get { false }
|
||||
set { }
|
||||
}
|
||||
public init(wrappedValue: T) { self.wrappedValue = wrappedValue }
|
||||
}
|
||||
|
||||
// CHECK: @propertyWrapper public struct WrapperWithComputedImmutable<T> {
|
||||
// CHECK: public var wrappedValue: T
|
||||
// CHECK: public var projectedValue: Swift::Bool {
|
||||
// CHECK: get
|
||||
// CHECK: }
|
||||
// CHECK: public init(wrappedValue: T)
|
||||
// CHECK: }
|
||||
@propertyWrapper
|
||||
public struct WrapperWithComputedImmutable<T> {
|
||||
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?
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 where A: ~Swift.Copyable>(A) -> ()
|
||||
// CHECK: sil [ossa] @$s4test12keepCopyableyyxRi_zlF : $@convention(thin) <T where T : ~Copyable, T : ~Escapable> (@in_guaranteed T) -> () {
|
||||
@_preInverseGenerics(except: ~Copyable)
|
||||
public func keepCopyable<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
// DEMANGLED: test.keepEscapable<A where A: ~Swift.Escapable>(A) -> ()
|
||||
// CHECK: sil [ossa] @$s4test13keepEscapableyyxRi0_zlF : $@convention(thin) <T where T : ~Copyable, T : ~Escapable> (@in_guaranteed T) -> () {
|
||||
@_preInverseGenerics(except: ~Escapable)
|
||||
public func keepEscapable<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
// DEMANGLED: test.stripBoth<A>(A) -> ()
|
||||
// CHECK: sil [ossa] @$s4test9stripBothyyxlF : $@convention(thin) <T where T : ~Copyable, T : ~Escapable> (@in_guaranteed T) -> () {
|
||||
@_preInverseGenerics
|
||||
public func stripBoth<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
|
||||
public struct F<T: ~Copyable>: ~Copyable {
|
||||
// DEMANGLED: (extension in test):test.F< where A: ~Swift.Copyable>.memberKeepCopyable() -> ()
|
||||
// CHECK: sil [ossa] @$s4test1FVAARi_zrlE18memberKeepCopyableyyF : $@convention(method) <T where T : ~Copyable> (@guaranteed F<T>) -> () {
|
||||
@_preInverseGenerics(except: ~Copyable)
|
||||
public func memberKeepCopyable() {}
|
||||
|
||||
// Strips ~Copyable from T but keeps ~Escapable from U
|
||||
// DEMANGLED: test.F.withEscapable<A where A1: ~Swift.Escapable>(A1) -> ()
|
||||
// CHECK: sil [ossa] @$s4test1FV13withEscapableyyqd__Ri0_d__lF : $@convention(method) <T where T : ~Copyable><U where U : ~Copyable, U : ~Escapable> (@in_guaranteed U, @guaranteed F<T>) -> () {
|
||||
@_preInverseGenerics(except: ~Escapable)
|
||||
public func withEscapable<U: ~Copyable & ~Escapable>(_ 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<A where A1: ~Swift.Copyable>(A1) -> ()
|
||||
// CHECK: sil [ossa] @$s4test1FVAARi_zrlE14withEscapable2yyqd__Ri_d__lF : $@convention(method) <T where T : ~Copyable><U where U : ~Copyable, U : ~Escapable> (@in_guaranteed U, @guaranteed F<T>) -> () {
|
||||
@_preInverseGenerics(except: ~Copyable)
|
||||
public func withEscapable2<U: ~Copyable & ~Escapable>(_ u: borrowing U) {}
|
||||
}
|
||||
|
||||
// Simulates Span gaining ~Escapable. Only ~Copyable is mangled into its symbols.
|
||||
@frozen
|
||||
public struct MySpan<T: ~Copyable & ~Escapable>: ~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) <T where T : ~Copyable, T : ~Escapable> (@guaranteed MySpan<T>) -> 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) <T where T : ~Copyable, T : ~Escapable> (@guaranteed MySpan<T>) -> Optional<UnsafeRawPointer> {
|
||||
@_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) <T where T : ~Copyable, T : ~Escapable> (@guaranteed MySpan<T>) -> () {
|
||||
@_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) <T where T : ~Copyable, T : ~Escapable> (@guaranteed MySpan<T>) -> 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) <T where T : ~Copyable, T : ~Escapable> (@guaranteed MySpan<T>) -> () {
|
||||
public func extMethod() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<E: Error>(_ 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 <Never>
|
||||
// CHECK-LABEL: } // end sil function '$s22partial_apply_lifetime34reabstractToTypedThrowsNEReturningyyAA2NEVADXEF'
|
||||
func reabstractToTypedThrowsNEReturning(_ f: (NE) -> NE) {
|
||||
takeTypedThrowingNEReturning(f)
|
||||
}
|
||||
|
||||
// Typed throws with inout parameter.
|
||||
|
||||
func takeTypedThrowingInout<E: Error>(_ 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 <Never>
|
||||
// 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 }
|
||||
}
|
||||
@@ -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<T>(f: () -> CNE<T>) -> CNE<T> {
|
||||
return f()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ public struct Foo<let count: Int> {
|
||||
// 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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// RUN: %target-typecheck-verify-swift -enable-experimental-feature PreInverseGenericsExcept
|
||||
|
||||
// REQUIRES: swift_feature_PreInverseGenericsExcept
|
||||
|
||||
@_preInverseGenerics
|
||||
func bare<T: ~Copyable>(_ t: borrowing T) {}
|
||||
|
||||
@_preInverseGenerics(except: ~Copyable)
|
||||
func exceptCopyable<T: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
@_preInverseGenerics(except: ~Escapable)
|
||||
func exceptEscapable<T: ~Copyable & ~Escapable>(_ 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: ~Copyable & ~Escapable>(_ 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: ~Copyable & ~Escapable>(_ 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<T: ~Copyable>: ~Copyable {}
|
||||
|
||||
@_preInverseGenerics // expected-warning {{'@_preInverseGenerics' has no effect on an extension; place it on individual members instead}}
|
||||
extension S where T: ~Copyable {
|
||||
func extMethod() {}
|
||||
}
|
||||
@@ -5,16 +5,24 @@
|
||||
|
||||
// REQUIRES: swift_feature_RawLayout
|
||||
|
||||
// BC-CHECK: <RawLayout_DECL_ATTR
|
||||
// BC-CHECK: <RawLayout_DECL_ATTR
|
||||
|
||||
// MODULE-CHECK: @_rawLayout(size: 5, alignment: 4) struct A_ExplicitSizeAlign
|
||||
@_rawLayout(size: 5, alignment: 4)
|
||||
struct A_ExplicitSizeAlign: ~Copyable {}
|
||||
|
||||
// MODULE-CHECK: @_rawLayout(like: T, movesAsLike) struct B2_CellMovesAsLike
|
||||
@_rawLayout(like: T, movesAsLike)
|
||||
struct B2_CellMovesAsLike<T>: ~Copyable {}
|
||||
|
||||
// MODULE-CHECK: @_rawLayout(like: T) struct B_Cell
|
||||
@_rawLayout(like: T)
|
||||
struct B_Cell<T>: ~Copyable {}
|
||||
|
||||
// MODULE-CHECK: @_rawLayout(likeArrayOf: T, count: 8, movesAsLike) struct C2_SmallVectorMovesAsLike
|
||||
@_rawLayout(likeArrayOf: T, count: 8, movesAsLike)
|
||||
struct C2_SmallVectorMovesAsLike<T>: ~Copyable {}
|
||||
|
||||
// MODULE-CHECK: @_rawLayout(likeArrayOf: T, count: 8) struct C_SmallVector
|
||||
@_rawLayout(likeArrayOf: T, count: 8)
|
||||
struct C_SmallVector<T>: ~Copyable {}
|
||||
|
||||
@@ -1217,9 +1217,17 @@ struct BadRename {
|
||||
init(range: Range<Int>, 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> {}
|
||||
|
||||
@@ -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}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// RUN: %target-typecheck-verify-swift
|
||||
|
||||
// The bare @_preInverseGenerics does NOT require the experimental feature.
|
||||
@_preInverseGenerics
|
||||
func bare<T: ~Copyable>(_ 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: ~Copyable & ~Escapable>(_ 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: ~Copyable & ~Escapable>(_ t: borrowing T) {}
|
||||
|
||||
|
||||
@@ -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 <allowed-os-versions> 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=","
|
||||
|
||||
@@ -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.
|
||||
@@ -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<each T> {
|
||||
var values: (repeat each T)
|
||||
}
|
||||
|
||||
class VariadicHolder<each T> {
|
||||
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<Int>(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<Int, String, Bool>(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<Int, Double>(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.
|
||||
@@ -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<RowDecoder>: FetchRequest {
|
||||
}
|
||||
|
||||
extension TableRecord {
|
||||
static func filter(_ predicate: some SQLSpecificExpressible) -> QueryInterfaceRequest<Self> {
|
||||
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<RowDecoder> {
|
||||
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 {}
|
||||
@@ -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]))
|
||||
}
|
||||
}
|
||||
@@ -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<A: P2>: 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<S2>
|
||||
typealias B = Double
|
||||
}
|
||||
|
||||
struct G<A: P2> {
|
||||
init(first: A.A, second: A.A) {}
|
||||
init(first: (A.B, A.B), second: (A.B, A.B)) {}
|
||||
}
|
||||
|
||||
func test(x: S3<S2>, 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<S2>(first: (x.x - y / 2, x.y - z / 2),
|
||||
second: (x.x + y / 2, x.y + z / 2))
|
||||
}
|
||||
Reference in New Issue
Block a user