Skip to content

Commit 2185f43

Browse files
committed
[TypeCheckAttr] Improve diagnostics and candidate notes for typeEraser in
the case where the type eraser has no viable initializers.
1 parent 3d18c07 commit 2185f43

File tree

3 files changed

+49
-41
lines changed

3 files changed

+49
-41
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4420,21 +4420,25 @@ ERROR(non_nominal_type_eraser,none,
44204420
"type eraser must be a class, struct, or enum", ())
44214421
ERROR(type_eraser_does_not_conform,none,
44224422
"type eraser %0 must conform to protocol %1", (Type, Type))
4423-
ERROR(type_eraser_missing_init,none,
4424-
"type eraser %0 must have an initializer of the form "
4425-
"'init<T: %1>(erasing: T)'", (Type, Type))
44264423
ERROR(type_eraser_not_accessible,none,
44274424
"%select{private|fileprivate|internal|public|open}0 type eraser %1 "
44284425
"cannot have more restrictive access than protocol %2 "
44294426
"(which is %select{private|fileprivate|internal|public|open}3)",
44304427
(AccessLevel, Identifier, Type, AccessLevel))
4428+
ERROR(type_eraser_missing_init,none,
4429+
"type eraser %0 must have an initializer of the form "
4430+
"'init<T: %1>(erasing: T)'", (Type, StringRef))
4431+
ERROR(type_eraser_unviable_init,none,
4432+
"type eraser %0 has no viable initializer of the form "
4433+
"'init<T: %1>(erasing: T)'", (Type, StringRef))
44314434

44324435
NOTE(type_eraser_declared_here,none,
44334436
"type eraser declared here",())
44344437
NOTE(type_eraser_failable_init,none,
44354438
"'init(erasing:)' cannot be failable",())
44364439
NOTE(type_eraser_init_unsatisfied_requirements,none,
4437-
"'init(erasing:)' has unsatisfied requirements",())
4440+
"'init(erasing:)' cannot have unsatisfied requirements "
4441+
"when %0 = 'some %1'", (Type, StringRef))
44384442
NOTE(type_eraser_init_not_accessible,none,
44394443
"%select{private|fileprivate|internal|public|open}0 'init(erasing:)' "
44404444
"cannot have more restrictive access than protocol %1 "

lib/Sema/TypeCheckAttr.cpp

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,7 +2434,7 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
24342434
UnsatisfiedRequirements,
24352435
Inaccessible,
24362436
};
2437-
SmallVector<std::pair<ConstructorDecl *, UnviableReason>, 2> unviable;
2437+
SmallVector<std::tuple<ConstructorDecl *, UnviableReason, Type>, 2> unviable;
24382438

24392439
bool foundMatch = llvm::any_of(lookupResult, [&](const LookupResultEntry &entry) {
24402440
auto *init = cast<ConstructorDecl>(entry.getValueDecl());
@@ -2482,41 +2482,50 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
24822482
None);
24832483

24842484
if (result != RequirementCheckResult::Success) {
2485-
unviable.push_back({init, UnviableReason::UnsatisfiedRequirements});
2485+
unviable.push_back(
2486+
std::make_tuple(init, UnviableReason::UnsatisfiedRequirements,
2487+
genericParamType));
24862488
return false;
24872489
}
24882490

24892491
if (init->isFailable()) {
2490-
unviable.push_back({init, UnviableReason::Failable});
2492+
unviable.push_back(
2493+
std::make_tuple(init, UnviableReason::Failable, genericParamType));
24912494
return false;
24922495
}
24932496

24942497
if (init->getFormalAccess() < protocol->getFormalAccess()) {
2495-
unviable.push_back({init, UnviableReason::Inaccessible});
2498+
unviable.push_back(
2499+
std::make_tuple(init, UnviableReason::Inaccessible, genericParamType));
24962500
return false;
24972501
}
24982502

24992503
return true;
25002504
});
25012505

25022506
if (!foundMatch) {
2503-
diags.diagnose(attr->getLocation(), diag::type_eraser_missing_init,
2504-
typeEraser, protocolType);
2505-
if (unviable.empty())
2506-
diags.diagnose(nominalTypeDecl->getLoc(),
2507-
diag::type_eraser_declared_here);
2507+
if (unviable.empty()) {
2508+
diags.diagnose(attr->getLocation(), diag::type_eraser_missing_init,
2509+
typeEraser, protocol->getName().str());
2510+
diags.diagnose(nominalTypeDecl->getLoc(), diag::type_eraser_declared_here);
2511+
return false;
2512+
}
25082513

2514+
diags.diagnose(attr->getLocation(), diag::type_eraser_unviable_init,
2515+
typeEraser, protocol->getName().str());
25092516
for (auto &candidate: unviable) {
2510-
auto init = candidate.first;
2511-
auto reason = candidate.second;
2517+
auto init = std::get<0>(candidate);
2518+
auto reason = std::get<1>(candidate);
2519+
auto genericParamType = std::get<2>(candidate);
25122520

25132521
switch (reason) {
25142522
case UnviableReason::Failable:
25152523
diags.diagnose(init->getLoc(), diag::type_eraser_failable_init);
25162524
break;
25172525
case UnviableReason::UnsatisfiedRequirements:
25182526
diags.diagnose(init->getLoc(),
2519-
diag::type_eraser_init_unsatisfied_requirements);
2527+
diag::type_eraser_init_unsatisfied_requirements,
2528+
genericParamType, protocol->getName().str());
25202529
break;
25212530
case UnviableReason::Inaccessible:
25222531
diags.diagnose(init->getLoc(), diag::type_eraser_init_not_accessible,

test/attr/typeEraser.swift

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ protocol B4 {}
5353
@_typeEraser(Generic<Int>) // bound generic is okay
5454
protocol B5 {}
5555

56+
class MoreRestrictive: B6 { // expected-note {{type eraser declared here}}
57+
init<T: B6>(erasing t: T) {}
58+
}
59+
@_typeEraser(MoreRestrictive) // expected-error {{internal type eraser 'MoreRestrictive' cannot have more restrictive access than protocol 'B6' (which is public)}}
60+
public protocol B6 {}
61+
5662
// MARK: - Type eraser must conform to the annotated protocol
5763

5864
class DoesNotConform {} // expected-note {{type eraser declared here}}
@@ -62,55 +68,44 @@ protocol C1 {}
6268
// MARK: - Type eraser must have an initializer in the form init<T>(erasing: T) with T constrained to annotated protocol
6369

6470
class NoArgInit: D1 {} // expected-note {{type eraser declared here}}
65-
@_typeEraser(NoArgInit) // expected-error {{type eraser 'NoArgInit' must have an initializer of the form 'init<T: 'D1'>(erasing: T)'}}
71+
@_typeEraser(NoArgInit) // expected-error {{type eraser 'NoArgInit' must have an initializer of the form 'init<T: D1>(erasing: T)'}}
6672
protocol D1 {}
6773

6874
class InvalidArgInit: D2 { // expected-note {{type eraser declared here}}
6975
init<T>(erasing t: T) {}
7076
}
71-
@_typeEraser(InvalidArgInit) // expected-error {{type eraser 'InvalidArgInit' must have an initializer of the form 'init<T: 'D2'>(erasing: T)'}}
77+
@_typeEraser(InvalidArgInit) // expected-error {{type eraser 'InvalidArgInit' must have an initializer of the form 'init<T: D2>(erasing: T)'}}
7278
protocol D2 {}
7379

7480
class ExtraArgInit: D3 { // expected-note {{type eraser declared here}}
7581
init<T: D3>(erasing t: T, extraArg: Int) {}
7682
}
77-
@_typeEraser(ExtraArgInit) // expected-error {{type eraser 'ExtraArgInit' must have an initializer of the form 'init<T: 'D3'>(erasing: T)'}}
83+
@_typeEraser(ExtraArgInit) // expected-error {{type eraser 'ExtraArgInit' must have an initializer of the form 'init<T: D3>(erasing: T)'}}
7884
protocol D3 {}
7985

8086
class WrongLabelInit: D4 { // expected-note {{type eraser declared here}}
8187
init<T: D4>(wrongLabel: T) {}
8288
}
83-
@_typeEraser(WrongLabelInit) // expected-error {{type eraser 'WrongLabelInit' must have an initializer of the form 'init<T: 'D4'>(erasing: T)'}}
89+
@_typeEraser(WrongLabelInit) // expected-error {{type eraser 'WrongLabelInit' must have an initializer of the form 'init<T: D4>(erasing: T)'}}
8490
protocol D4 {}
8591

8692
class NoLabel: D5 { // expected-note {{type eraser declared here}}
8793
init<T: D5>(_ t: T) {}
8894
}
89-
@_typeEraser(NoLabel) // expected-error {{type eraser 'NoLabel' must have an initializer of the form 'init<T: 'D5'>(erasing: T)'}}
95+
@_typeEraser(NoLabel) // expected-error {{type eraser 'NoLabel' must have an initializer of the form 'init<T: D5>(erasing: T)'}}
9096
protocol D5 {}
9197

92-
class FailableInit: D6 {
93-
init?<T: D6>(erasing t: T) {} // expected-note {{'init(erasing:)' cannot be failable}}
94-
}
95-
@_typeEraser(FailableInit) // expected-error {{type eraser 'FailableInit' must have an initializer of the form 'init<T: 'D6'>(erasing: T)'}}
96-
protocol D6 {}
97-
98-
class UnsatisfiedReq: D7 {
99-
init<T: D7>(erasing t: T) where T: Hashable {} // expected-note {{'init(erasing:)' has unsatisfied requirements}}
100-
}
101-
@_typeEraser(UnsatisfiedReq) // expected-error {{type eraser 'UnsatisfiedReq' must have an initializer of the form 'init<T: 'D7'>(erasing: T)'}}
102-
protocol D7 {}
103-
104-
// MARK: - Type eraser and initializer cannot be more restrictive than the annotated protocol
98+
// MARK: - Unviable initializers
10599

106-
class MoreRestrictive: E1 { // expected-note {{type eraser declared here}}
107-
init<T: E1>(erasing t: T) {}
100+
public class UnviableInits: E1 {
101+
public init<T: E1>(erasing t: T) where T: Hashable {} // expected-note {{'init(erasing:)' cannot have unsatisfied requirements when 'T' = 'some E1'}}
102+
init<T: E1>(erasing t: T) {} // expected-note {{internal 'init(erasing:)' cannot have more restrictive access than protocol 'E1' (which is public)}}
108103
}
109-
@_typeEraser(MoreRestrictive) // expected-error {{internal type eraser 'MoreRestrictive' cannot have more restrictive access than protocol 'E1' (which is public)}}
104+
@_typeEraser(UnviableInits) // expected-error {{type eraser 'UnviableInits' has no viable initializer of the form 'init<T: E1>(erasing: T)'}}
110105
public protocol E1 {}
111106

112-
public class MoreRestrictiveInit: E2 {
113-
init<T: E2>(erasing t: T) {} // expected-note {{internal 'init(erasing:)' cannot have more restrictive access than protocol 'E2' (which is public)}}
107+
class FailableInit: E2 {
108+
init?<T: E2>(erasing t: T) {} // expected-note {{'init(erasing:)' cannot be failable}}
114109
}
115-
@_typeEraser(MoreRestrictiveInit) // expected-error {{type eraser 'MoreRestrictiveInit' must have an initializer of the form 'init<T: 'E2'>(erasing: T)'}}
116-
public protocol E2 {}
110+
@_typeEraser(FailableInit) // expected-error {{type eraser 'FailableInit' has no viable initializer of the form 'init<T: E2>(erasing: T)'}}
111+
protocol E2 {}

0 commit comments

Comments
 (0)