Skip to content

Commit 0556005

Browse files
authored
Don't use __derived_enum_equals for a resilient synthesized '==' (#18508)
(or __derived_struct_equals) We want to make sure that if someone replaces the synthesized implementation with a handwritten one, it doesn't change the ABI. The simplest way to do that is to not use this clever workaround. https://bugs.swift.org/browse/SR-8294
1 parent 634cf7e commit 0556005

File tree

3 files changed

+51
-30
lines changed

3 files changed

+51
-30
lines changed

lib/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ static void deriveBodyEquatable_struct_eq(AbstractFunctionDecl *eqDecl) {
550550

551551
/// Derive an '==' operator implementation for an enum or a struct.
552552
static ValueDecl *
553-
deriveEquatable_eq(DerivedConformance &derived, Identifier generatedIdentifier,
553+
deriveEquatable_eq(DerivedConformance &derived,
554554
void (*bodySynthesizer)(AbstractFunctionDecl *)) {
555555
// enum SomeEnum<T...> {
556556
// case A, B(Int), C(String, Int)
@@ -590,14 +590,14 @@ deriveEquatable_eq(DerivedConformance &derived, Identifier generatedIdentifier,
590590
ASTContext &C = derived.TC.Context;
591591

592592
auto parentDC = derived.getConformanceContext();
593-
auto enumTy = parentDC->getDeclaredTypeInContext();
594-
auto enumIfaceTy = parentDC->getDeclaredInterfaceType();
593+
auto selfTy = parentDC->getDeclaredTypeInContext();
594+
auto selfIfaceTy = parentDC->getDeclaredInterfaceType();
595595

596596
auto getParamDecl = [&](StringRef s) -> ParamDecl * {
597597
auto *param = new (C) ParamDecl(VarDecl::Specifier::Default, SourceLoc(),
598598
SourceLoc(), Identifier(), SourceLoc(),
599-
C.getIdentifier(s), enumTy, parentDC);
600-
param->setInterfaceType(enumIfaceTy);
599+
C.getIdentifier(s), selfTy, parentDC);
600+
param->setInterfaceType(selfIfaceTy);
601601
return param;
602602
};
603603

@@ -611,6 +611,17 @@ deriveEquatable_eq(DerivedConformance &derived, Identifier generatedIdentifier,
611611

612612
auto boolTy = C.getBoolDecl()->getDeclaredType();
613613

614+
Identifier generatedIdentifier;
615+
if (parentDC->getParentModule()->getResilienceStrategy() ==
616+
ResilienceStrategy::Resilient) {
617+
generatedIdentifier = C.Id_EqualsOperator;
618+
} else if (selfTy->getEnumOrBoundGenericEnum()) {
619+
generatedIdentifier = C.Id_derived_enum_equals;
620+
} else {
621+
assert(selfTy->getStructOrBoundGenericStruct());
622+
generatedIdentifier = C.Id_derived_struct_equals;
623+
}
624+
614625
DeclName name(C, generatedIdentifier, params);
615626
auto eqDecl =
616627
FuncDecl::create(C, /*StaticLoc=*/SourceLoc(),
@@ -626,17 +637,19 @@ deriveEquatable_eq(DerivedConformance &derived, Identifier generatedIdentifier,
626637
eqDecl->getAttrs().add(new (C) InfixAttr(/*implicit*/false));
627638

628639
// Add the @_implements(Equatable, ==(_:_:)) attribute
629-
auto equatableProto = C.getProtocol(KnownProtocolKind::Equatable);
630-
auto equatableTy = equatableProto->getDeclaredType();
631-
auto equatableTypeLoc = TypeLoc::withoutLoc(equatableTy);
632-
SmallVector<Identifier, 2> argumentLabels = { Identifier(), Identifier() };
633-
auto equalsDeclName = DeclName(C, DeclBaseName(C.Id_EqualsOperator),
634-
argumentLabels);
635-
eqDecl->getAttrs().add(new (C) ImplementsAttr(SourceLoc(),
636-
SourceRange(),
637-
equatableTypeLoc,
638-
equalsDeclName,
639-
DeclNameLoc()));
640+
if (generatedIdentifier != C.Id_EqualsOperator) {
641+
auto equatableProto = C.getProtocol(KnownProtocolKind::Equatable);
642+
auto equatableTy = equatableProto->getDeclaredType();
643+
auto equatableTypeLoc = TypeLoc::withoutLoc(equatableTy);
644+
SmallVector<Identifier, 2> argumentLabels = { Identifier(), Identifier() };
645+
auto equalsDeclName = DeclName(C, DeclBaseName(C.Id_EqualsOperator),
646+
argumentLabels);
647+
eqDecl->getAttrs().add(new (C) ImplementsAttr(SourceLoc(),
648+
SourceRange(),
649+
equatableTypeLoc,
650+
equalsDeclName,
651+
DeclNameLoc()));
652+
}
640653

641654
if (!C.getEqualIntDecl()) {
642655
derived.TC.diagnose(derived.ConformanceDecl->getLoc(),
@@ -683,11 +696,9 @@ ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) {
683696
: ed->hasOnlyCasesWithoutAssociatedValues()
684697
? &deriveBodyEquatable_enum_noAssociatedValues_eq
685698
: &deriveBodyEquatable_enum_hasAssociatedValues_eq;
686-
return deriveEquatable_eq(*this, TC.Context.Id_derived_enum_equals,
687-
bodySynthesizer);
699+
return deriveEquatable_eq(*this, bodySynthesizer);
688700
} else if (isa<StructDecl>(Nominal))
689-
return deriveEquatable_eq(*this, TC.Context.Id_derived_struct_equals,
690-
&deriveBodyEquatable_struct_eq);
701+
return deriveEquatable_eq(*this, &deriveBodyEquatable_struct_eq);
691702
else
692703
llvm_unreachable("todo");
693704
}

test/SILGen/synthesized_conformance_enum.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck %s
1+
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck -check-prefix CHECK -check-prefix CHECK-FRAGILE %s
2+
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 -enable-resilience | %FileCheck -check-prefix CHECK -check-prefix CHECK-RESILIENT %s
23

34
enum Enum<T> {
45
case a(T), b(T)
@@ -12,13 +13,15 @@ enum NoValues {
1213
}
1314
// CHECK-LABEL: enum NoValues {
1415
// CHECK: case a, b
15-
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: NoValues, _ b: NoValues) -> Bool
16+
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: NoValues, _ b: NoValues) -> Bool
17+
// CHECK-RESILIENT: static func == (a: NoValues, b: NoValues) -> Bool
1618
// CHECK: var hashValue: Int { get }
1719
// CHECK: func hash(into hasher: inout Hasher)
1820
// CHECK: }
1921

2022
// CHECK-LABEL: extension Enum : Equatable where T : Equatable {
21-
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: Enum<T>, _ b: Enum<T>) -> Bool
23+
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: Enum<T>, _ b: Enum<T>) -> Bool
24+
// CHECK-RESILIENT: static func == (a: Enum<T>, b: Enum<T>) -> Bool
2225
// CHECK: }
2326
// CHECK-LABEL: extension Enum : Hashable where T : Hashable {
2427
// CHECK: var hashValue: Int { get }
@@ -32,8 +35,10 @@ enum NoValues {
3235

3336

3437
extension Enum: Equatable where T: Equatable {}
35-
// CHECK-LABEL: // static Enum<A>.__derived_enum_equals(_:_:)
36-
// CHECK-NEXT: sil hidden @$S28synthesized_conformance_enum4EnumOAASQRzlE010__derived_C7_equalsySbACyxG_AEtFZ : $@convention(method) <T where T : Equatable> (@in_guaranteed Enum<T>, @in_guaranteed Enum<T>, @thin Enum<T>.Type) -> Bool {
38+
// CHECK-FRAGILE-LABEL: // static Enum<A>.__derived_enum_equals(_:_:)
39+
// CHECK-FRAGILE-NEXT: sil hidden @$S28synthesized_conformance_enum4EnumOAASQRzlE010__derived_C7_equalsySbACyxG_AEtFZ : $@convention(method) <T where T : Equatable> (@in_guaranteed Enum<T>, @in_guaranteed Enum<T>, @thin Enum<T>.Type) -> Bool {
40+
// CHECK-RESILIENT-LABEL: // static Enum<A>.== infix(_:_:)
41+
// CHECK-RESILIENT-NEXT: sil hidden @$S28synthesized_conformance_enum4EnumOAASQRzlE2eeoiySbACyxG_AEtFZ : $@convention(method) <T where T : Equatable> (@in_guaranteed Enum<T>, @in_guaranteed Enum<T>, @thin Enum<T>.Type) -> Bool {
3742

3843
extension Enum: Hashable where T: Hashable {}
3944
// CHECK-LABEL: // Enum<A>.hashValue.getter

test/SILGen/synthesized_conformance_struct.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck %s
1+
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck -check-prefix CHECK -check-prefix CHECK-FRAGILE %s
2+
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 -enable-resilience | %FileCheck -check-prefix CHECK -check-prefix CHECK-RESILIENT %s
23

34
struct Struct<T> {
45
var x: T
@@ -13,13 +14,15 @@ struct Struct<T> {
1314
// CHECK: init?(stringValue: String)
1415
// CHECK: var intValue: Int? { get }
1516
// CHECK: init?(intValue: Int)
16-
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: Struct<T>.CodingKeys, _ b: Struct<T>.CodingKeys) -> Bool
17+
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: Struct<T>.CodingKeys, _ b: Struct<T>.CodingKeys) -> Bool
18+
// CHECK-RESILIENT: static func == (a: Struct<T>.CodingKeys, b: Struct<T>.CodingKeys) -> Bool
1719
// CHECK: var hashValue: Int { get }
1820
// CHECK: func hash(into hasher: inout Hasher)
1921
// CHECK: }
2022
// CHECK: }
2123
// CHECK-LABEL: extension Struct : Equatable where T : Equatable {
22-
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_struct_equals(_ a: Struct<T>, _ b: Struct<T>) -> Bool
24+
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_struct_equals(_ a: Struct<T>, _ b: Struct<T>) -> Bool
25+
// CHECK-RESILIENT: static func == (a: Struct<T>, b: Struct<T>) -> Bool
2326
// CHECK: }
2427
// CHECK-LABEL: extension Struct : Hashable where T : Hashable {
2528
// CHECK: var hashValue: Int { get }
@@ -31,8 +34,10 @@ struct Struct<T> {
3134
// CHECK: }
3235

3336
extension Struct: Equatable where T: Equatable {}
34-
// CHECK-LABEL: // static Struct<A>.__derived_struct_equals(_:_:)
35-
// CHECK-NEXT: sil hidden @$S30synthesized_conformance_struct6StructVAASQRzlE010__derived_C7_equalsySbACyxG_AEtFZ : $@convention(method) <T where T : Equatable> (@in_guaranteed Struct<T>, @in_guaranteed Struct<T>, @thin Struct<T>.Type) -> Bool {
37+
// CHECK-FRAGILE-LABEL: // static Struct<A>.__derived_struct_equals(_:_:)
38+
// CHECK-FRAGILE-NEXT: sil hidden @$S30synthesized_conformance_struct6StructVAASQRzlE010__derived_C7_equalsySbACyxG_AEtFZ : $@convention(method) <T where T : Equatable> (@in_guaranteed Struct<T>, @in_guaranteed Struct<T>, @thin Struct<T>.Type) -> Bool {
39+
// CHECK-RESILIENT-LABEL: // static Struct<A>.== infix(_:_:)
40+
// CHECK-RESILIENT-NEXT: sil hidden @$S30synthesized_conformance_struct6StructVAASQRzlE2eeoiySbACyxG_AEtFZ : $@convention(method) <T where T : Equatable> (@in_guaranteed Struct<T>, @in_guaranteed Struct<T>, @thin Struct<T>.Type) -> Bool {
3641

3742
extension Struct: Hashable where T: Hashable {}
3843
// CHECK-LABEL: // Struct<A>.hashValue.getter

0 commit comments

Comments
 (0)