Skip to content

Don't use __derived_enum_equals for a resilient synthesized '==' #18508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions lib/Sema/DerivedConformanceEquatableHashable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ static void deriveBodyEquatable_struct_eq(AbstractFunctionDecl *eqDecl) {

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

auto parentDC = derived.getConformanceContext();
auto enumTy = parentDC->getDeclaredTypeInContext();
auto enumIfaceTy = parentDC->getDeclaredInterfaceType();
auto selfTy = parentDC->getDeclaredTypeInContext();
auto selfIfaceTy = parentDC->getDeclaredInterfaceType();

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

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

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

Identifier generatedIdentifier;
if (parentDC->getParentModule()->getResilienceStrategy() ==
ResilienceStrategy::Resilient) {
generatedIdentifier = C.Id_EqualsOperator;
} else if (selfTy->getEnumOrBoundGenericEnum()) {
generatedIdentifier = C.Id_derived_enum_equals;
} else {
assert(selfTy->getStructOrBoundGenericStruct());
generatedIdentifier = C.Id_derived_struct_equals;
}

DeclName name(C, generatedIdentifier, params);
auto eqDecl =
FuncDecl::create(C, /*StaticLoc=*/SourceLoc(),
Expand All @@ -626,17 +637,19 @@ deriveEquatable_eq(DerivedConformance &derived, Identifier generatedIdentifier,
eqDecl->getAttrs().add(new (C) InfixAttr(/*implicit*/false));

// Add the @_implements(Equatable, ==(_:_:)) attribute
auto equatableProto = C.getProtocol(KnownProtocolKind::Equatable);
auto equatableTy = equatableProto->getDeclaredType();
auto equatableTypeLoc = TypeLoc::withoutLoc(equatableTy);
SmallVector<Identifier, 2> argumentLabels = { Identifier(), Identifier() };
auto equalsDeclName = DeclName(C, DeclBaseName(C.Id_EqualsOperator),
argumentLabels);
eqDecl->getAttrs().add(new (C) ImplementsAttr(SourceLoc(),
SourceRange(),
equatableTypeLoc,
equalsDeclName,
DeclNameLoc()));
if (generatedIdentifier != C.Id_EqualsOperator) {
auto equatableProto = C.getProtocol(KnownProtocolKind::Equatable);
auto equatableTy = equatableProto->getDeclaredType();
auto equatableTypeLoc = TypeLoc::withoutLoc(equatableTy);
SmallVector<Identifier, 2> argumentLabels = { Identifier(), Identifier() };
auto equalsDeclName = DeclName(C, DeclBaseName(C.Id_EqualsOperator),
argumentLabels);
eqDecl->getAttrs().add(new (C) ImplementsAttr(SourceLoc(),
SourceRange(),
equatableTypeLoc,
equalsDeclName,
DeclNameLoc()));
}

if (!C.getEqualIntDecl()) {
derived.TC.diagnose(derived.ConformanceDecl->getLoc(),
Expand Down Expand Up @@ -683,11 +696,9 @@ ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) {
: ed->hasOnlyCasesWithoutAssociatedValues()
? &deriveBodyEquatable_enum_noAssociatedValues_eq
: &deriveBodyEquatable_enum_hasAssociatedValues_eq;
return deriveEquatable_eq(*this, TC.Context.Id_derived_enum_equals,
bodySynthesizer);
return deriveEquatable_eq(*this, bodySynthesizer);
} else if (isa<StructDecl>(Nominal))
return deriveEquatable_eq(*this, TC.Context.Id_derived_struct_equals,
&deriveBodyEquatable_struct_eq);
return deriveEquatable_eq(*this, &deriveBodyEquatable_struct_eq);
else
llvm_unreachable("todo");
}
Expand Down
15 changes: 10 additions & 5 deletions test/SILGen/synthesized_conformance_enum.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck %s
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck -check-prefix CHECK -check-prefix CHECK-FRAGILE %s
// 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

enum Enum<T> {
case a(T), b(T)
Expand All @@ -12,13 +13,15 @@ enum NoValues {
}
// CHECK-LABEL: enum NoValues {
// CHECK: case a, b
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: NoValues, _ b: NoValues) -> Bool
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: NoValues, _ b: NoValues) -> Bool
// CHECK-RESILIENT: static func == (a: NoValues, b: NoValues) -> Bool
// CHECK: var hashValue: Int { get }
// CHECK: func hash(into hasher: inout Hasher)
// CHECK: }

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


extension Enum: Equatable where T: Equatable {}
// CHECK-LABEL: // static Enum<A>.__derived_enum_equals(_:_:)
// 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 {
// CHECK-FRAGILE-LABEL: // static Enum<A>.__derived_enum_equals(_:_:)
// 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 {
// CHECK-RESILIENT-LABEL: // static Enum<A>.== infix(_:_:)
// 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 {

extension Enum: Hashable where T: Hashable {}
// CHECK-LABEL: // Enum<A>.hashValue.getter
Expand Down
15 changes: 10 additions & 5 deletions test/SILGen/synthesized_conformance_struct.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck %s
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-silgen %s -swift-version 4 | %FileCheck -check-prefix CHECK -check-prefix CHECK-FRAGILE %s
// 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

struct Struct<T> {
var x: T
Expand All @@ -13,13 +14,15 @@ struct Struct<T> {
// CHECK: init?(stringValue: String)
// CHECK: var intValue: Int? { get }
// CHECK: init?(intValue: Int)
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: Struct<T>.CodingKeys, _ b: Struct<T>.CodingKeys) -> Bool
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: Struct<T>.CodingKeys, _ b: Struct<T>.CodingKeys) -> Bool
// CHECK-RESILIENT: static func == (a: Struct<T>.CodingKeys, b: Struct<T>.CodingKeys) -> Bool
// CHECK: var hashValue: Int { get }
// CHECK: func hash(into hasher: inout Hasher)
// CHECK: }
// CHECK: }
// CHECK-LABEL: extension Struct : Equatable where T : Equatable {
// CHECK: @_implements(Equatable, ==(_:_:)) static func __derived_struct_equals(_ a: Struct<T>, _ b: Struct<T>) -> Bool
// CHECK-FRAGILE: @_implements(Equatable, ==(_:_:)) static func __derived_struct_equals(_ a: Struct<T>, _ b: Struct<T>) -> Bool
// CHECK-RESILIENT: static func == (a: Struct<T>, b: Struct<T>) -> Bool
// CHECK: }
// CHECK-LABEL: extension Struct : Hashable where T : Hashable {
// CHECK: var hashValue: Int { get }
Expand All @@ -31,8 +34,10 @@ struct Struct<T> {
// CHECK: }

extension Struct: Equatable where T: Equatable {}
// CHECK-LABEL: // static Struct<A>.__derived_struct_equals(_:_:)
// 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 {
// CHECK-FRAGILE-LABEL: // static Struct<A>.__derived_struct_equals(_:_:)
// 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 {
// CHECK-RESILIENT-LABEL: // static Struct<A>.== infix(_:_:)
// 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 {

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