Skip to content

Commit 3d18c07

Browse files
committed
[TypeCheckAttr] Implement access control checking for the typeEraser
attribute.
1 parent f141b4d commit 3d18c07

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,13 +4423,23 @@ ERROR(type_eraser_does_not_conform,none,
44234423
ERROR(type_eraser_missing_init,none,
44244424
"type eraser %0 must have an initializer of the form "
44254425
"'init<T: %1>(erasing: T)'", (Type, Type))
4426+
ERROR(type_eraser_not_accessible,none,
4427+
"%select{private|fileprivate|internal|public|open}0 type eraser %1 "
4428+
"cannot have more restrictive access than protocol %2 "
4429+
"(which is %select{private|fileprivate|internal|public|open}3)",
4430+
(AccessLevel, Identifier, Type, AccessLevel))
44264431

44274432
NOTE(type_eraser_declared_here,none,
44284433
"type eraser declared here",())
44294434
NOTE(type_eraser_failable_init,none,
44304435
"'init(erasing:)' cannot be failable",())
44314436
NOTE(type_eraser_init_unsatisfied_requirements,none,
44324437
"'init(erasing:)' has unsatisfied requirements",())
4438+
NOTE(type_eraser_init_not_accessible,none,
4439+
"%select{private|fileprivate|internal|public|open}0 'init(erasing:)' "
4440+
"cannot have more restrictive access than protocol %1 "
4441+
"(which is %select{private|fileprivate|internal|public|open}2)",
4442+
(AccessLevel, Type, AccessLevel))
44334443

44344444
//------------------------------------------------------------------------------
44354445
// MARK: @available

lib/Sema/TypeCheckAttr.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,14 +2398,23 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
23982398
if (typeEraser->hasError())
23992399
return false;
24002400

2401-
// 1. The type eraser must be a concrete nominal type
2401+
// The type eraser must be a concrete nominal type
24022402
auto nominalTypeDecl = typeEraser->getAnyNominal();
24032403
if (!nominalTypeDecl || isa<ProtocolDecl>(nominalTypeDecl)) {
24042404
diags.diagnose(typeEraserLoc.getLoc(), diag::non_nominal_type_eraser);
24052405
return false;
24062406
}
24072407

2408-
// 2. The type eraser must conform to the annotated protocol
2408+
// The nominal type must be accessible wherever the protocol is accessible
2409+
if (nominalTypeDecl->getFormalAccess() < protocol->getFormalAccess()) {
2410+
diags.diagnose(typeEraserLoc.getLoc(), diag::type_eraser_not_accessible,
2411+
nominalTypeDecl->getFormalAccess(), nominalTypeDecl->getName(),
2412+
protocolType, protocol->getFormalAccess());
2413+
diags.diagnose(nominalTypeDecl->getLoc(), diag::type_eraser_declared_here);
2414+
return false;
2415+
}
2416+
2417+
// The type eraser must conform to the annotated protocol
24092418
SmallVector<ProtocolConformance *, 2> conformances;
24102419
if (!nominalTypeDecl->lookupConformance(dc->getParentModule(), protocol,
24112420
conformances)) {
@@ -2415,14 +2424,15 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
24152424
return false;
24162425
}
24172426

2418-
// 3. The type eraser must have an init of the form init<T: Protocol>(erasing: T)
2427+
// The type eraser must have an init of the form init<T: Protocol>(erasing: T)
24192428
auto lookupResult = TypeChecker::lookupMember(dc, typeEraser,
24202429
DeclNameRef::createConstructor());
24212430

24222431
// Keep track of unviable init candidates for diagnostics
24232432
enum class UnviableReason {
24242433
Failable,
24252434
UnsatisfiedRequirements,
2435+
Inaccessible,
24262436
};
24272437
SmallVector<std::pair<ConstructorDecl *, UnviableReason>, 2> unviable;
24282438

@@ -2481,6 +2491,11 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
24812491
return false;
24822492
}
24832493

2494+
if (init->getFormalAccess() < protocol->getFormalAccess()) {
2495+
unviable.push_back({init, UnviableReason::Inaccessible});
2496+
return false;
2497+
}
2498+
24842499
return true;
24852500
});
24862501

@@ -2503,6 +2518,11 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
25032518
diags.diagnose(init->getLoc(),
25042519
diag::type_eraser_init_unsatisfied_requirements);
25052520
break;
2521+
case UnviableReason::Inaccessible:
2522+
diags.diagnose(init->getLoc(), diag::type_eraser_init_not_accessible,
2523+
init->getFormalAccess(), protocolType,
2524+
protocol->getFormalAccess());
2525+
break;
25062526
}
25072527
}
25082528
return false;

test/attr/typeEraser.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,17 @@ class UnsatisfiedReq: D7 {
100100
}
101101
@_typeEraser(UnsatisfiedReq) // expected-error {{type eraser 'UnsatisfiedReq' must have an initializer of the form 'init<T: 'D7'>(erasing: T)'}}
102102
protocol D7 {}
103+
104+
// MARK: - Type eraser and initializer cannot be more restrictive than the annotated protocol
105+
106+
class MoreRestrictive: E1 { // expected-note {{type eraser declared here}}
107+
init<T: E1>(erasing t: T) {}
108+
}
109+
@_typeEraser(MoreRestrictive) // expected-error {{internal type eraser 'MoreRestrictive' cannot have more restrictive access than protocol 'E1' (which is public)}}
110+
public protocol E1 {}
111+
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)}}
114+
}
115+
@_typeEraser(MoreRestrictiveInit) // expected-error {{type eraser 'MoreRestrictiveInit' must have an initializer of the form 'init<T: 'E2'>(erasing: T)'}}
116+
public protocol E2 {}

0 commit comments

Comments
 (0)