Skip to content

Commit f5b4092

Browse files
authored
Merge pull request swiftlang#41172 from CodaFi/special-sauce
2 parents 3336e2d + 25ab410 commit f5b4092

File tree

9 files changed

+182
-5
lines changed

9 files changed

+182
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,8 @@ ERROR(extension_metatype,none,
19041904
ERROR(extension_specialization,none,
19051905
"constrained extension must be declared on the unspecialized generic "
19061906
"type %0 with constraints specified by a 'where' clause", (Identifier))
1907+
ERROR(extension_placeholder,none,
1908+
"cannot extend a type that contains placeholders", ())
19071909
ERROR(extension_stored_property,none,
19081910
"extensions must not contain stored properties", ())
19091911
NOTE(extension_stored_property_fixit,none,

include/swift/Basic/LangOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,13 @@ namespace swift {
477477
// FrontendOptions.
478478
bool AllowModuleWithCompilerErrors = false;
479479

480+
/// Enable extensions of (sugared) bound generic types
481+
///
482+
/// \code
483+
/// extension [Int] { /**/ }
484+
/// \endcode
485+
bool EnableExperimentalBoundGenericExtensions = false;
486+
480487
/// A helper enum to represent whether or not we customized the default
481488
/// ASTVerifier behavior via a frontend flag. By default, we do not
482489
/// customize.

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ def enable_resilience : Flag<["-"], "enable-resilience">,
309309
def enable_experimental_async_top_level :
310310
Flag<["-"], "enable-experimental-async-top-level">,
311311
HelpText<"Enable experimental concurrency in top-level code">;
312+
313+
def enable_experimental_bound_generic_extensions :
314+
Flag<["-"], "enable-experimental-bound-generic-extensions">,
315+
HelpText<"Enable experimental support for extensions of bound generic types">;
312316
}
313317

314318
// HIDDEN FLAGS

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
503503
Opts.EnableExperimentalStringProcessing |=
504504
Args.hasArg(OPT_enable_experimental_string_processing);
505505

506+
Opts.EnableExperimentalBoundGenericExtensions |=
507+
Args.hasArg(OPT_enable_experimental_bound_generic_extensions);
508+
506509
Opts.DisableAvailabilityChecking |=
507510
Args.hasArg(OPT_disable_availability_checking);
508511
Opts.CheckAPIAvailabilityOnly |=

lib/Sema/TypeCheckDecl.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,9 +2792,17 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const {
27922792
return error();
27932793
}
27942794

2795-
// Cannot extend a bound generic type, unless it's referenced via a
2796-
// non-generic typealias type.
2797-
if (extendedType->isSpecialized() &&
2795+
// Cannot extend types who contain placeholders.
2796+
if (extendedType->hasPlaceholder()) {
2797+
diags.diagnose(ext->getLoc(), diag::extension_placeholder)
2798+
.highlight(extendedRepr->getSourceRange());
2799+
return error();
2800+
}
2801+
2802+
// By default, the user cannot extend a bound generic type, unless it's
2803+
// referenced via a non-generic typealias type.
2804+
if (!ext->getASTContext().LangOpts.EnableExperimentalBoundGenericExtensions &&
2805+
extendedType->isSpecialized() &&
27982806
!isNonGenericTypeAliasType(extendedType)) {
27992807
diags.diagnose(ext->getLoc(), diag::extension_specialization,
28002808
extendedType->getAnyNominal()->getName())

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,8 @@ static Type formExtensionInterfaceType(
562562
auto nominal = dyn_cast<NominalTypeDecl>(genericDecl);
563563
auto typealias = dyn_cast<TypeAliasDecl>(genericDecl);
564564
if (!nominal) {
565-
Type underlyingType = typealias->getUnderlyingType();
566-
nominal = underlyingType->getNominalOrBoundGenericNominal();
565+
type = typealias->getUnderlyingType();
566+
nominal = type->getNominalOrBoundGenericNominal();
567567
}
568568

569569
// Form the result.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-swift-frontend -module-name Test -typecheck -emit-module-interface-path - -enable-experimental-bound-generic-extensions %s | %FileCheck %s
2+
3+
public struct Tree<T> {
4+
public struct Branch<B> {
5+
public struct Nest<N> {
6+
public struct Egg {}
7+
}
8+
}
9+
}
10+
11+
// CHECK: extension Test.Tree.Branch.Nest.Egg {
12+
// CHECK: public static func tweet()
13+
// CHECK: }
14+
extension Tree.Branch.Nest.Egg { public static func tweet() {} }
15+
16+
// CHECK: extension Test.Tree.Branch.Nest.Egg where T == Swift.Int {
17+
// CHECK: public static func twoot()
18+
// CHECK: }
19+
extension Tree<Int>.Branch.Nest.Egg { public static func twoot() {} }
20+
21+
// CHECK: extension Test.Tree.Branch.Nest.Egg where T == Swift.Int, B == Swift.String {
22+
// CHECK: public static func twote()
23+
// CHECK: }
24+
extension Tree<Int>.Branch<String>.Nest.Egg { public static func twote() {} }
25+
26+
// CHECK: extension Test.Tree.Branch.Nest.Egg where T == Swift.Int, B == Swift.String, N == Swift.Void {
27+
// CHECK: public static func twite()
28+
// CHECK: }
29+
extension Tree<Int>.Branch<String>.Nest<Void>.Egg { public static func twite() {} }
30+
31+
// CHECK: extension Swift.Array where Element == Swift.String {
32+
// CHECK: public func rejoinder() -> Swift.String
33+
// CHECK: }
34+
extension [String] { public func rejoinder() -> String { return self.joined() } }
35+
36+
// CHECK: public typealias StringDict<T> = [Swift.String : T]
37+
public typealias StringDict<T> = [String: T]
38+
39+
// CHECK: extension Swift.Dictionary where Key == Swift.String, Value == Swift.Int
40+
extension StringDict<Int> { public static func mark() {} }
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-emit-silgen -enable-experimental-bound-generic-extensions %s | %FileCheck %s
2+
3+
struct Tree<T> {
4+
struct Branch<B> {
5+
struct Nest<N> {
6+
struct Egg {}
7+
}
8+
}
9+
}
10+
11+
// CHECK: extension Tree.Branch.Nest.Egg {
12+
// CHECK: static func tweet()
13+
// CHECK: }
14+
15+
// CHECK: extension Tree.Branch.Nest.Egg where T == Int {
16+
// CHECK: static func twoot()
17+
// CHECK: }
18+
19+
// CHECK: extension Tree.Branch.Nest.Egg where T == Int, B == String {
20+
// CHECK: static func twote()
21+
// CHECK: }
22+
23+
// CHECK: extension Tree.Branch.Nest.Egg where T == Int, B == String, N == () {
24+
// CHECK: static func twite()
25+
// CHECK: }
26+
27+
// CHECK: @$s31mangling_specialized_extensions4TreeV6BranchV4NestV3EggV5tweetyyFZ : $@convention(method) <T><B><N> (@thin Tree<T>.Branch<B>.Nest<N>.Egg.Type) -> ()
28+
extension Tree.Branch.Nest.Egg { static func tweet() {} }
29+
30+
// CHECK: @$s31mangling_specialized_extensions4TreeV6BranchV4NestV3EggVAASiRszrlE5twootyyFZ : $@convention(method) <T where T == Int><B><N> (@thin Tree<Int>.Branch<B>.Nest<N>.Egg.Type) -> ()
31+
extension Tree<Int>.Branch.Nest.Egg { static func twoot() {} }
32+
33+
// CHECK: @$s31mangling_specialized_extensions4TreeV6BranchV4NestV3EggVAASiRszSSRsd__rlE5twoteyyFZ : $@convention(method) <T where T == Int><B where B == String><N> (@thin Tree<Int>.Branch<String>.Nest<N>.Egg.Type) -> ()
34+
extension Tree<Int>.Branch<String>.Nest.Egg { static func twote() {} }
35+
36+
// CHECK: @$s31mangling_specialized_extensions4TreeV6BranchV4NestV3EggVAASiRszSSRsd__ytRsd0__rlE5twiteyyFZ : $@convention(method) (@thin Tree<Int>.Branch<String>.Nest<()>.Egg.Type) -> ()
37+
extension Tree<Int>.Branch<String>.Nest<Void>.Egg { static func twite() {} }

test/decl/ext/specialize.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-bound-generic-extensions
2+
3+
extension Array<Int> {
4+
func someIntFuncOnArray() {}
5+
}
6+
7+
let _ = [0, 1, 2].someIntFuncOnArray()
8+
9+
extension [Character] {
10+
func makeString() -> String { fatalError() }
11+
}
12+
13+
let _ = ["a", "b", "c"].makeString()
14+
let _ = [1, 2, 3].makeString() // expected-error 3 {{cannot convert value of type 'Int' to expected element type 'Character'}}
15+
16+
extension Set<_> {} // expected-error {{cannot extend a type that contains placeholders}}
17+
18+
// https://bugs.swift.org/browse/SR-4875
19+
20+
struct Foo<T, U> {
21+
var x: T
22+
var y: U
23+
}
24+
25+
typealias IntFoo<U> = Foo<Int, U>
26+
27+
extension IntFoo where U == Int {
28+
func hello() {
29+
print("hello")
30+
}
31+
}
32+
33+
Foo(x: "test", y: 1).hello()
34+
35+
struct MyType<TyA, TyB> {
36+
var a : TyA, b : TyB
37+
}
38+
39+
typealias A<T1, T2> = MyType<T2, T1>
40+
41+
extension A {}
42+
43+
extension A<Float, Int> {}
44+
extension A<Void, Void> {}
45+
46+
struct Tree<T> {
47+
struct Branch<B> {
48+
struct Nest<N> {
49+
struct Egg {}
50+
}
51+
}
52+
}
53+
54+
extension Tree.Branch.Nest.Egg { static func tweet() {} }
55+
extension Tree<Int>.Branch.Nest.Egg { static func twoot() {} }
56+
extension Tree<Int>.Branch<String>.Nest.Egg { static func twote() {} }
57+
extension Tree<Int>.Branch<String>.Nest<Void>.Egg { static func twite() {} }
58+
59+
func testNestedExtensions() {
60+
do {
61+
Tree<Void>.Branch<Void>.Nest<Void>.Egg.tweet()
62+
}
63+
64+
do {
65+
Tree<Int>.Branch<Void>.Nest<Void>.Egg.twoot()
66+
Tree<Int>.Branch<Int>.Nest<Void>.Egg.twoot()
67+
Tree<Int>.Branch<Int>.Nest<Int>.Egg.twoot()
68+
}
69+
70+
do {
71+
Tree<Int>.Branch<String>.Nest<Void>.Egg.twote()
72+
Tree<Int>.Branch<String>.Nest<Float>.Egg.twote()
73+
}
74+
75+
Tree<Int>.Branch<String>.Nest<Void>.Egg.twite()
76+
}

0 commit comments

Comments
 (0)