Merge remote-tracking branch 'origin/main' into serialize-hidden-type-layout

This commit is contained in:
Xi Ge
2026-05-15 10:12:20 -07:00
71 changed files with 3177 additions and 635 deletions
+1
View File
@@ -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
+580
View File
@@ -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 ToddCoxeter 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
}
}
+2
View File
@@ -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)
+19 -19
View File
@@ -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;
}
+6
View File
@@ -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(
+5 -4
View File
@@ -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,
+51
View File
@@ -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;
+1 -1
View File
@@ -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)
+3
View File
@@ -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))
+10
View File
@@ -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", ())
+5 -4
View File
@@ -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;
+15
View File
@@ -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() &&
+22
View File
@@ -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)
+3
View File
@@ -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)
+91 -5
View File
@@ -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;
+6 -1
View File
@@ -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
View File
@@ -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);
}
}
+55
View File
@@ -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,
+37 -30
View File
@@ -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) {
+15
View File
@@ -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();
+96 -1
View File
@@ -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 &paramInfo = 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
View File
@@ -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() {
+19
View File
@@ -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.
//----------------------------------------------------------------------------//
+19 -1
View File
@@ -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 {
+7 -2
View File
@@ -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);
+5 -5
View File
@@ -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();
+8 -8
View File
@@ -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");
+7 -4
View File
@@ -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;
}
};
+33
View File
@@ -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)
+5 -1
View File
@@ -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();
+76 -2
View File
@@ -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);
+7 -2
View File
@@ -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()) {
+20
View File
@@ -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));
+7 -1
View File
@@ -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
+14
View File
@@ -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,
+15 -7
View File
@@ -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
+73 -466
View File
@@ -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
+138
View File
@@ -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)
}
+25
View File
@@ -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?
}
+8 -2
View File
@@ -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() {}
}
+230
View File
@@ -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
+16 -1
View File
@@ -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()
+40
View File
@@ -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() {}
}
+9 -1
View File
@@ -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 {}
+8
View File
@@ -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}}
}
}
+16
View File
@@ -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) {}
+1 -1
View File
@@ -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))
}