From 6b2fbb79b9dfcf592a43bc71fb2e8efcba14f777 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 24 Jul 2025 14:49:40 -0400 Subject: [PATCH 1/2] Sema: Fix crash in MissingConformanceFailure::diagnoseAsError() It's difficult to trigger this because the code path is only reached for the standard operators. The issue in question was in embedded Swift due to an unsupported usage of the === operator, but we expect an upcoming standard library change to actually make us accept that code. However, the fix should be pretty safe, even without a test case. Fixes the crash in rdar://156095800. --- lib/Sema/CSDiagnostics.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 6d6feba3bf80e..8b376a29bbccc 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -671,15 +671,17 @@ bool MissingConformanceFailure::diagnoseAsError() { }; // Limit this to `Equatable` and `Comparable` protocols for now. - auto *protocol = getRHS()->castTo()->getDecl(); - if (isEnumWithAssociatedValues(getLHS()) && - (protocol->isSpecificProtocol(KnownProtocolKind::Equatable) || - protocol->isSpecificProtocol(KnownProtocolKind::Comparable))) { - if (RequirementFailure::diagnoseAsError()) { - auto opName = getOperatorName(expr); - emitDiagnostic(diag::no_binary_op_overload_for_enum_with_payload, - opName->str()); - return true; + if (auto *protocolTy = getRHS()->getAs()) { + auto *protocol = protocolTy->getDecl(); + if (isEnumWithAssociatedValues(getLHS()) && + (protocol->isSpecificProtocol(KnownProtocolKind::Equatable) || + protocol->isSpecificProtocol(KnownProtocolKind::Comparable))) { + if (RequirementFailure::diagnoseAsError()) { + auto opName = getOperatorName(expr); + emitDiagnostic(diag::no_binary_op_overload_for_enum_with_payload, + opName->str()); + return true; + } } } } From b7766b6361e2eb668f1bfcf3eb2c3a66f63f4e9b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 24 Jul 2025 14:51:30 -0400 Subject: [PATCH 2/2] stdlib: Use the original === and !== operators on embedded It seems that the restriction preventing these from working was lifted. The behavioral difference is that in Swift 5 mode, we don't actually open AnyObject like this, so the old operator could not be used with class-bound existentials. I added a trivial test case just to ensure that calls to === type check correctly in both language modes. Fixes rdar://156095800. --- stdlib/public/core/Equatable.swift | 9 +------- test/embedded/anyobject.swift | 36 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 test/embedded/anyobject.swift diff --git a/stdlib/public/core/Equatable.swift b/stdlib/public/core/Equatable.swift index ef2779f5b34fb..ae634bc4e3cf0 100644 --- a/stdlib/public/core/Equatable.swift +++ b/stdlib/public/core/Equatable.swift @@ -271,7 +271,7 @@ public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool { #else @inlinable // trivial-implementation @safe -public func ===(lhs: T?, rhs: U?) -> Bool { +public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool { switch (lhs, rhs) { case let (l?, r?): return Builtin.bridgeToRawPointer(l) == Builtin.bridgeToRawPointer(r) @@ -293,14 +293,7 @@ public func ===(lhs: T?, rhs: U?) -> Bool { /// - Parameters: /// - lhs: A reference to compare. /// - rhs: Another reference to compare. -#if !$Embedded @inlinable // trivial-implementation public func !== (lhs: AnyObject?, rhs: AnyObject?) -> Bool { return !(lhs === rhs) } -#else -@inlinable // trivial-implementation -public func !==(lhs: T, rhs: U) -> Bool { - return !(lhs === rhs) -} -#endif diff --git a/test/embedded/anyobject.swift b/test/embedded/anyobject.swift new file mode 100644 index 0000000000000..fb6849543424b --- /dev/null +++ b/test/embedded/anyobject.swift @@ -0,0 +1,36 @@ +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -swift-version 5) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -O -swift-version 5) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -Osize -swift-version 5) | %FileCheck %s + +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -swift-version 6) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -O -swift-version 6) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -Osize -swift-version 6) | %FileCheck %s + +// REQUIRES: swift_in_compiler +// REQUIRES: executable_test +// REQUIRES: optimized_stdlib +// REQUIRES: swift_feature_Embedded + +protocol P: AnyObject {} + +class C: P {} + +@main +struct Main { + static func main() { + let p1: any P = C() + let p2: any P = C() + let p3 = p1 + + // CHECK: false + print(p1 === p2) + print(p2 === p1) + // CHECK: true + print(p1 === p3) + print(p3 === p1) + // CHECK: false + print(p2 === p3) + print(p3 === p2) + } +} +