Skip to content

Commit

Permalink
Merge pull request swiftlang#59192 from apple/rdar94175237
Browse files Browse the repository at this point in the history
[Sema] Check `@_typeEraser` SPI visibility
  • Loading branch information
rxwei authored Jun 2, 2022
2 parents 644a720 + c6ef5d2 commit 7c4b32f
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5488,6 +5488,9 @@ NOTE(type_eraser_init_not_accessible,none,
"cannot have more restrictive access than protocol %1 "
"(which is %select{private|fileprivate|internal|public|open}2)",
(AccessLevel, Type, AccessLevel))
NOTE(type_eraser_init_spi,none,
"'init(erasing:)' is SPI, but protocol %0 is not"
"%select{| in the same SPI groups as 'init(erasing:)'}1", (Type, bool))

//------------------------------------------------------------------------------
// MARK: @available
Expand Down
31 changes: 29 additions & 2 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3022,6 +3022,7 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
Failable,
UnsatisfiedRequirements,
Inaccessible,
SPI,
};
SmallVector<std::tuple<ConstructorDecl *, UnviableReason, Type>, 2> unviable;

Expand Down Expand Up @@ -3084,6 +3085,28 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
return false;
}

if (init->isSPI()) {
if (!protocol->isSPI()) {
unviable.push_back(
std::make_tuple(init, UnviableReason::SPI, genericParamType));
return false;
}
auto protocolSPIGroups = protocol->getSPIGroups();
auto initSPIGroups = init->getSPIGroups();
// If both are SPI, `init(erasing:)` must be available in all of the
// protocol's SPI groups.
// TODO: Do this more efficiently?
for (auto protocolGroup : protocolSPIGroups) {
auto foundIt = std::find(
initSPIGroups.begin(), initSPIGroups.end(), protocolGroup);
if (foundIt == initSPIGroups.end()) {
unviable.push_back(
std::make_tuple(init, UnviableReason::SPI, genericParamType));
return false;
}
}
}

return true;
});

Expand Down Expand Up @@ -3113,8 +3136,12 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
break;
case UnviableReason::Inaccessible:
diags.diagnose(init->getLoc(), diag::type_eraser_init_not_accessible,
init->getFormalAccess(), protocolType,
protocol->getFormalAccess());
init->getEffectiveAccess(), protocolType,
protocol->getEffectiveAccess());
break;
case UnviableReason::SPI:
diags.diagnose(init->getLoc(), diag::type_eraser_init_spi,
protocolType, protocol->isSPI());
break;
}
}
Expand Down
26 changes: 26 additions & 0 deletions test/attr/typeEraser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,29 @@ class FailableInit: E2 {
}
@_typeEraser(FailableInit) // expected-error {{type eraser 'FailableInit' has no viable initializer of the form 'init<T: E2>(erasing: T)'}}
protocol E2 {}

// SPI type eraser and non-SPI protocol
@_spi(SPI)
public struct AnyE3_SPI: E3 {
public init<T: E3>(erasing: T) {} // expected-note {{'init(erasing:)' is SPI, but protocol 'E3' is not}}
}
@_typeEraser(AnyE3_SPI) // expected-error {{type eraser 'AnyE3_SPI' has no viable initializer of the form 'init<T: E3>(erasing: T)'}}
public protocol E3 {}

// SPI type eraser and SPI protocol of different groups
@_spi(SPI2)
public struct AnyE4_SPI: E4 {
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:)'}}
}
@_spi(SPI1) @_spi(SPI2)
@_typeEraser(AnyE4_SPI) // expected-error {{type eraser 'AnyE4_SPI' has no viable initializer of the form 'init<T: E4>(erasing: T)'}}
public protocol E4 {}

// Same-group SPI type eraser and protocol
@_spi(SPI1) @_spi(SPI2)
public struct AnyE5_SPI: E5 {
public init<T: E5>(erasing: T) {}
}
@_spi(SPI2) @_spi(SPI1)
@_typeEraser(AnyE5_SPI) // same SPI groups, okay
public protocol E5 {}

0 comments on commit 7c4b32f

Please sign in to comment.