[stdlib] Allow metatypes of noncopyable/nonescapable types to get compared

This commit is contained in:
Karoy Lorentey
2025-02-26 18:05:04 -08:00
parent eba00922c3
commit 26176cc929
5 changed files with 77 additions and 14 deletions

View File

@@ -158,6 +158,69 @@ internal func != (lhs: Builtin.RawPointer, rhs: Builtin.RawPointer) -> Bool {
return !(lhs == rhs)
}
#if !$Embedded
/// Returns a Boolean value indicating whether two types are identical.
///
/// - Parameters:
/// - t0: A type to compare.
/// - t1: Another type to compare.
/// - Returns: `true` if both `t0` and `t1` are `nil` or if they represent the
/// same type; otherwise, `false`.
@_alwaysEmitIntoClient
@_transparent
public func == (
t0: (any (~Copyable & ~Escapable).Type)?,
t1: (any (~Copyable & ~Escapable).Type)?
) -> Bool {
switch (t0, t1) {
case (.none, .none):
return true
case let (.some(ty0), .some(ty1)):
// FIXME: this should read `Bool(Builtin.is_same_metatype(ty0, ty1))`,
// but that currently requires copyability/escapability (rdar://145707064)
let p1 = unsafeBitCast(ty0, to: UnsafeRawPointer.self)
let p2 = unsafeBitCast(ty1, to: UnsafeRawPointer.self)
return p1 == p2
default:
return false
}
}
/// Returns a Boolean value indicating whether two types are not identical.
///
/// - Parameters:
/// - t0: A type to compare.
/// - t1: Another type to compare.
/// - Returns: `true` if one, but not both, of `t0` and `t1` are `nil`, or if
/// they represent different types; otherwise, `false`.
@_alwaysEmitIntoClient
@_transparent
public func != (
t0: (any (~Copyable & ~Escapable).Type)?,
t1: (any (~Copyable & ~Escapable).Type)?
) -> Bool {
!(t0 == t1)
}
@usableFromInline
@_spi(SwiftStdlibLegacyABI) @available(swift, obsoleted: 1)
internal func == (t0: Any.Type?, t1: Any.Type?) -> Bool {
switch (t0, t1) {
case (.none, .none): return true
case let (.some(ty0), .some(ty1)):
return Bool(Builtin.is_same_metatype(ty0, ty1))
default: return false
}
}
@usableFromInline
@_spi(SwiftStdlibLegacyABI) @available(swift, obsoleted: 1)
internal func != (t0: Any.Type?, t1: Any.Type?) -> Bool {
!(t0 == t1)
}
#else
// FIXME: $Embedded doesn't grok `any (~Copyable & Escapable).Type` yet (rdar://145706221)
/// Returns a Boolean value indicating whether two types are identical.
///
/// - Parameters:
@@ -186,7 +249,7 @@ public func == (t0: Any.Type?, t1: Any.Type?) -> Bool {
public func != (t0: Any.Type?, t1: Any.Type?) -> Bool {
return !(t0 == t1)
}
#endif
/// Tell the optimizer that this code is unreachable if condition is
/// known at compile-time to be true. If condition is false, or true

View File

@@ -277,7 +277,7 @@ func rdar_60185506() {
func rdar60727310() {
func myAssertion<T>(_ a: T, _ op: ((T,T)->Bool), _ b: T) {}
var e: Error? = nil
myAssertion(e, ==, nil) // expected-error {{cannot convert value of type '(any Error)?' to expected argument type '(any Any.Type)?'}}
myAssertion(e, ==, nil) // expected-error {{cannot convert value of type '(any Error)?' to expected argument type '(any (~Copyable & ~Escapable).Type)?'}}
}
// https://github.com/apple/swift/issues/54877

View File

@@ -44,8 +44,8 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) {
// METATYPE_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .callAsFunction({#(self): Adder#})[#(x: Int, y: Int) -> Int#];
// METATYPE_NO_DOT-DAG: Decl[Constructor]/CurrNominal: .init({#base: Int#})[#Adder#];
// METATYPE_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#Adder.Type#];
// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(any Any.Type)?#}[#Bool#];
// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(any Any.Type)?#}[#Bool#];
// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(any (~Copyable & ~Escapable).Type)?#}[#Bool#];
// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(any (~Copyable & ~Escapable).Type)?#}[#Bool#];
let _ = addTy.#^METATYPE_DOT^#;
// METATYPE_DOT: Begin completions, 3 items

View File

@@ -212,8 +212,8 @@ func testInfix15<T: P where T.T == S2>() {
// INFIX_15-DAG: Decl[InstanceMethod]/CurrNominal: .foo({#(self): P#})[#() -> S2#]; name=foo(:)
// INFIX_15-DAG: Keyword[self]/CurrNominal: .self[#T.Type#]; name=self
// INFIX_15-DAG: Keyword/CurrNominal: .Type[#T.Type#]; name=Type
// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(any Any.Type)?#}[#Bool#];
// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(any Any.Type)?#}[#Bool#];
// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(any (~Copyable & ~Escapable).Type)?#}[#Bool#];
// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(any (~Copyable & ~Escapable).Type)?#}[#Bool#];
func testInfix16<T: P where T.T == S2>() {
T.foo#^INFIX_16^#

View File

@@ -758,15 +758,15 @@ func invalidDictionaryLiteral() {
//===----------------------------------------------------------------------===//
// nil/metatype comparisons
//===----------------------------------------------------------------------===//
_ = Int.self == nil // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'nil' always returns false}}
_ = nil == Int.self // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'nil' always returns false}}
_ = Int.self != nil // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'nil' always returns true}}
_ = nil != Int.self // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'nil' always returns true}}
_ = Int.self == nil // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'nil' always returns false}}
_ = nil == Int.self // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'nil' always returns false}}
_ = Int.self != nil // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'nil' always returns true}}
_ = nil != Int.self // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'nil' always returns true}}
_ = Int.self == .none // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'Optional.none' always returns false}}
_ = .none == Int.self // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'Optional.none' always returns false}}
_ = Int.self != .none // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'Optional.none' always returns true}}
_ = .none != Int.self // expected-warning {{comparing non-optional value of type 'any Any.Type' to 'Optional.none' always returns true}}
_ = Int.self == .none // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'Optional.none' always returns false}}
_ = .none == Int.self // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'Optional.none' always returns false}}
_ = Int.self != .none // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'Optional.none' always returns true}}
_ = .none != Int.self // expected-warning {{comparing non-optional value of type 'any (~Copyable & ~Escapable).Type' to 'Optional.none' always returns true}}
// <rdar://problem/19032294> Disallow postfix ? when not chaining
func testOptionalChaining(_ a : Int?, b : Int!, c : Int??) {