Skip to content

Commit 7c4b32f

Browse files
authored
Merge pull request #59192 from apple/rdar94175237
[Sema] Check `@_typeEraser` SPI visibility
2 parents 644a720 + c6ef5d2 commit 7c4b32f

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5488,6 +5488,9 @@ NOTE(type_eraser_init_not_accessible,none,
54885488
"cannot have more restrictive access than protocol %1 "
54895489
"(which is %select{private|fileprivate|internal|public|open}2)",
54905490
(AccessLevel, Type, AccessLevel))
5491+
NOTE(type_eraser_init_spi,none,
5492+
"'init(erasing:)' is SPI, but protocol %0 is not"
5493+
"%select{| in the same SPI groups as 'init(erasing:)'}1", (Type, bool))
54915494

54925495
//------------------------------------------------------------------------------
54935496
// MARK: @available

lib/Sema/TypeCheckAttr.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,6 +3022,7 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
30223022
Failable,
30233023
UnsatisfiedRequirements,
30243024
Inaccessible,
3025+
SPI,
30253026
};
30263027
SmallVector<std::tuple<ConstructorDecl *, UnviableReason, Type>, 2> unviable;
30273028

@@ -3084,6 +3085,28 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
30843085
return false;
30853086
}
30863087

3088+
if (init->isSPI()) {
3089+
if (!protocol->isSPI()) {
3090+
unviable.push_back(
3091+
std::make_tuple(init, UnviableReason::SPI, genericParamType));
3092+
return false;
3093+
}
3094+
auto protocolSPIGroups = protocol->getSPIGroups();
3095+
auto initSPIGroups = init->getSPIGroups();
3096+
// If both are SPI, `init(erasing:)` must be available in all of the
3097+
// protocol's SPI groups.
3098+
// TODO: Do this more efficiently?
3099+
for (auto protocolGroup : protocolSPIGroups) {
3100+
auto foundIt = std::find(
3101+
initSPIGroups.begin(), initSPIGroups.end(), protocolGroup);
3102+
if (foundIt == initSPIGroups.end()) {
3103+
unviable.push_back(
3104+
std::make_tuple(init, UnviableReason::SPI, genericParamType));
3105+
return false;
3106+
}
3107+
}
3108+
}
3109+
30873110
return true;
30883111
});
30893112

@@ -3113,8 +3136,12 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
31133136
break;
31143137
case UnviableReason::Inaccessible:
31153138
diags.diagnose(init->getLoc(), diag::type_eraser_init_not_accessible,
3116-
init->getFormalAccess(), protocolType,
3117-
protocol->getFormalAccess());
3139+
init->getEffectiveAccess(), protocolType,
3140+
protocol->getEffectiveAccess());
3141+
break;
3142+
case UnviableReason::SPI:
3143+
diags.diagnose(init->getLoc(), diag::type_eraser_init_spi,
3144+
protocolType, protocol->isSPI());
31183145
break;
31193146
}
31203147
}

test/attr/typeEraser.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,29 @@ class FailableInit: E2 {
124124
}
125125
@_typeEraser(FailableInit) // expected-error {{type eraser 'FailableInit' has no viable initializer of the form 'init<T: E2>(erasing: T)'}}
126126
protocol E2 {}
127+
128+
// SPI type eraser and non-SPI protocol
129+
@_spi(SPI)
130+
public struct AnyE3_SPI: E3 {
131+
public init<T: E3>(erasing: T) {} // expected-note {{'init(erasing:)' is SPI, but protocol 'E3' is not}}
132+
}
133+
@_typeEraser(AnyE3_SPI) // expected-error {{type eraser 'AnyE3_SPI' has no viable initializer of the form 'init<T: E3>(erasing: T)'}}
134+
public protocol E3 {}
135+
136+
// SPI type eraser and SPI protocol of different groups
137+
@_spi(SPI2)
138+
public struct AnyE4_SPI: E4 {
139+
public init<T: E4>(erasing: T) {} // expected-note {{'init(erasing:)' is SPI, but protocol 'E4' is not in the same SPI groups as 'init(erasing:)'}}
140+
}
141+
@_spi(SPI1) @_spi(SPI2)
142+
@_typeEraser(AnyE4_SPI) // expected-error {{type eraser 'AnyE4_SPI' has no viable initializer of the form 'init<T: E4>(erasing: T)'}}
143+
public protocol E4 {}
144+
145+
// Same-group SPI type eraser and protocol
146+
@_spi(SPI1) @_spi(SPI2)
147+
public struct AnyE5_SPI: E5 {
148+
public init<T: E5>(erasing: T) {}
149+
}
150+
@_spi(SPI2) @_spi(SPI1)
151+
@_typeEraser(AnyE5_SPI) // same SPI groups, okay
152+
public protocol E5 {}

0 commit comments

Comments
 (0)