Skip to content
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

Always desugar the base type of an extension when serializing. #6336

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2242,9 +2242,20 @@ void Serializer::writeDecl(const Decl *D) {
auto contextID = addDeclContextRef(extension->getDeclContext());
Type baseTy = extension->getExtendedType();

// FIXME: Use the canonical type here in order to minimize circularity
// issues at deserialization time. A known problematic case here is
// "extension of typealias Foo"; "typealias Foo = SomeKit.Bar"; and then
// trying to import Bar accidentally asking for all of its extensions
// (perhaps because we're searching for a conformance).
//
// We could limit this to only the problematic cases, but it seems like a
// simpler user model to just always desugar extension types.
baseTy = baseTy->getCanonicalType();

// Make sure the base type has registered itself as a provider of generic
// parameters.
(void)addDeclRef(baseTy->getAnyNominal());
auto baseNominal = baseTy->getAnyNominal();
(void)addDeclRef(baseNominal);

auto conformances = extension->getLocalConformances(
ConformanceLookupKind::All,
Expand All @@ -2265,7 +2276,7 @@ void Serializer::writeDecl(const Decl *D) {
inheritedTypes);

bool isClassExtension = false;
if (auto baseNominal = baseTy->getAnyNominal()) {
if (baseNominal) {
isClassExtension = isa<ClassDecl>(baseNominal) ||
isa<ProtocolDecl>(baseNominal);
}
Expand Down
47 changes: 47 additions & 0 deletions test/Serialization/extension-of-typealias.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: rm -rf %t && mkdir -p %t
// RUN: %target-swift-frontend -emit-module -module-name Library -o %t -D LIBRARY %s
// RUN: %target-swift-ide-test -print-module -module-to-print=Library -I %t -source-filename=%s | %FileCheck %s
// RUN: %target-swift-frontend -typecheck -I %t %s -verify

// Check that base types of extensions are desugared. This isn't necessarily
// the behavior we want long-term, but it's the behavior we need right now.

#if LIBRARY

public typealias Zahl = Int

// CHECK-LABEL: extension Int {
extension Zahl {
// CHECK-NEXT: addedMember()
public func addedMember() {}
} // CHECK-NEXT: {{^}$}}

public typealias List<T> = Array<T>

// CHECK-LABEL: extension Array {
extension List {
// CHECK-NEXT: addedMember()
public func addedMember() {}
} // CHECK-NEXT: {{^}$}}

// CHECK-LABEL: extension Array where Element == Int {
extension List where Element == Int {
// CHECK-NEXT: addedMemberInt()
public func addedMemberInt() {}
} // CHECK-NEXT: {{^}$}}

// CHECK: typealias List
// CHECK: typealias Zahl

#else

import Library

func test(x: Int) {
x.addedMember()
[x].addedMember()
[x].addedMemberInt()
([] as [Bool]).addedMemberInt() // expected-error {{'[Bool]' is not convertible to 'Array<Int>'}}
}

#endif
13 changes: 13 additions & 0 deletions validation-test/Serialization/Inputs/rdar29694978.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@import Foundation;

@protocol BaseProto
@end

@protocol SubProto <BaseProto>
@end

@interface NonGenericType: NSObject <SubProto>
@end

@interface GenericType<Element>: NSObject <SubProto>
@end
28 changes: 28 additions & 0 deletions validation-test/Serialization/rdar29694978.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: rm -rf %t && mkdir -p %t
// RUN: %target-build-swift %s -import-objc-header %S/Inputs/rdar29694978.h -emit-module -o %t/Library.swiftmodule
// RUN: %target-swift-ide-test -print-module -module-to-print=Library -source-filename=x -I %S/Inputs/ -I %t | %FileCheck %s

// REQUIRES: objc_interop

// This would be wonderful to fold into a larger test, but I'm worried that
// trying to do so would perturb the original conditions that cause it to
// crash: deserializing a typealias (1) to an imported Objective-C type (2)
// that has a protocol (3) that refines another protocol (4) which causes us
// to load all extensions, including one defined in this file (5) in terms of
// the original typealias (1).
//
// It's probably best to just leave this test about that.

import Foundation

// CHECK-DAG: typealias MyNonGenericType = NonGenericType
typealias MyNonGenericType = NonGenericType
// CHECK-DAG: extension NonGenericType
extension MyNonGenericType {}

// CHECK-DAG: typealias MyGenericType<T> = GenericType<T>
typealias MyGenericType<T: NSObject> = GenericType<T>
// CHECK-DAG: extension GenericType where Element : AnyObject
extension MyGenericType {}
// CHECK-DAG: extension GenericType where Element == NSObject
extension MyGenericType where Element == NSObject {}