Skip to content

Commit 189c1d3

Browse files
committed
Sema: Check availability of type witnesses.
The type satisfying a protocol requirement must be at least as available as the associated type for the requirement. Resolves rdar://134093006.
1 parent a25f29a commit 189c1d3

8 files changed

+336
-11
lines changed

include/swift/AST/DiagnosticsCommon.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ WARNING(error_in_future_swift_version,none,
5151
"%0; this is an error in the Swift %1 language mode",
5252
(DiagnosticInfo *, unsigned))
5353

54+
WARNING(error_in_a_future_swift_version,none,
55+
"%0; this will be an error in a future Swift language mode",
56+
(DiagnosticInfo *))
57+
5458
// Generic disambiguation
5559
NOTE(while_parsing_as_left_angle_bracket,none,
5660
"while parsing this '<' as a type parameter bracket", ())

include/swift/AST/DiagnosticsSema.def

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6835,6 +6835,35 @@ ERROR(conformance_availability_only_version_newer, none,
68356835
"conformance of %0 to %1 is only available in %2 %3 or newer",
68366836
(Type, Type, StringRef, llvm::VersionTuple))
68376837

6838+
// Conformance checking type witness availability diagnostics
6839+
6840+
ERROR(type_witness_availability_unavailable, none,
6841+
"%kindonly0 witnessing requirement %1 of %2 is unavailable"
6842+
"%select{ in %4|}3%select{|: %5}5",
6843+
(const ValueDecl *, const AssociatedTypeDecl *, const ProtocolDecl *,
6844+
bool, StringRef, StringRef))
6845+
6846+
NOTE(type_witness_availability_marked_unavailable, none,
6847+
"%kindonly0 witnessing requirement %1 of %2 has been explicitly marked "
6848+
"unavailable here",
6849+
(const ValueDecl *, const AssociatedTypeDecl *, const ProtocolDecl *))
6850+
6851+
NOTE(type_witness_availability_introduced_in_version, none,
6852+
"%kindonly0 witnessing requirement %1 of %2 was introduced in %3 %4",
6853+
(const ValueDecl *, const AssociatedTypeDecl *, const ProtocolDecl *,
6854+
StringRef, llvm::VersionTuple))
6855+
6856+
NOTE(type_witness_availability_obsoleted, none,
6857+
"%kindonly0 witnessing requirement %1 of %2 was obsoleted in %3 %4",
6858+
(const ValueDecl *, const AssociatedTypeDecl *, const ProtocolDecl *,
6859+
StringRef, llvm::VersionTuple))
6860+
6861+
ERROR(type_witness_availability_only_version_newer, none,
6862+
"%kindonly0 witnessing requirement %1 of %2 is "
6863+
"only available in %3 %4 or newer",
6864+
(const ValueDecl *, const AssociatedTypeDecl *, const ProtocolDecl *,
6865+
StringRef, llvm::VersionTuple))
6866+
68386867
//------------------------------------------------------------------------------
68396868
// MARK: @discardableResult
68406869
//------------------------------------------------------------------------------

lib/AST/DiagnosticEngine.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,13 @@ InFlightDiagnostic::limitBehaviorUntilSwiftVersion(
405405
// in a message that this will become an error in a later Swift
406406
// version. We do this before limiting the behavior, because
407407
// wrapIn will result in the behavior of the wrapping diagnostic.
408-
if (limit >= DiagnosticBehavior::Warning)
409-
wrapIn(diag::error_in_future_swift_version, majorVersion);
408+
if (limit >= DiagnosticBehavior::Warning) {
409+
if (majorVersion > 6) {
410+
wrapIn(diag::error_in_a_future_swift_version);
411+
} else {
412+
wrapIn(diag::error_in_future_swift_version, majorVersion);
413+
}
414+
}
410415

411416
limitBehavior(limit);
412417
}

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,20 @@ computeExportContextBits(ASTContext &Ctx, Decl *D, bool *spi, bool *implicit,
216216
}
217217
}
218218

219+
static AvailabilityContext availabilityAtLocation(SourceLoc loc,
220+
const DeclContext *dc) {
221+
auto &ctx = dc->getASTContext();
222+
return ctx.LangOpts.DisableAvailabilityChecking
223+
? AvailabilityContext::alwaysAvailable()
224+
: TypeChecker::overApproximateAvailabilityAtLocation(loc, dc);
225+
}
226+
219227
ExportContext ExportContext::forDeclSignature(Decl *D) {
220228
auto &Ctx = D->getASTContext();
221229

222230
auto *DC = D->getInnermostDeclContext();
223231
auto fragileKind = DC->getFragileFunctionKind();
224-
auto runningOSVersion =
225-
(Ctx.LangOpts.DisableAvailabilityChecking
226-
? AvailabilityContext::alwaysAvailable()
227-
: TypeChecker::overApproximateAvailabilityAtLocation(D->getLoc(), DC));
232+
auto runningOSVersion = availabilityAtLocation(D->getLoc(), DC);
228233
bool spi = Ctx.LangOpts.LibraryLevel == LibraryLevel::SPI;
229234
bool implicit = false;
230235
bool deprecated = false;
@@ -249,11 +254,7 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC, SourceLoc loc) {
249254
auto &Ctx = DC->getASTContext();
250255

251256
auto fragileKind = DC->getFragileFunctionKind();
252-
auto runningOSVersion =
253-
(Ctx.LangOpts.DisableAvailabilityChecking
254-
? AvailabilityContext::alwaysAvailable()
255-
: TypeChecker::overApproximateAvailabilityAtLocation(loc, DC));
256-
257+
auto runningOSVersion = availabilityAtLocation(loc, DC);
257258
bool spi = Ctx.LangOpts.LibraryLevel == LibraryLevel::SPI;
258259
bool implicit = false;
259260
bool deprecated = false;
@@ -295,6 +296,13 @@ ExportContext ExportContext::withExported(bool exported) const {
295296
return copy;
296297
}
297298

299+
ExportContext
300+
ExportContext::withRefinedAvailability(AvailabilityContext availability) const {
301+
auto copy = *this;
302+
copy.RunningOSVersion.intersectWith(availability);
303+
return copy;
304+
}
305+
298306
std::optional<PlatformKind> ExportContext::getUnavailablePlatformKind() const {
299307
if (Unavailable)
300308
return PlatformKind(Platform);
@@ -4582,6 +4590,104 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc,
45824590
return hadAnyIssues;
45834591
}
45844592

4593+
static bool diagnoseExplicitUnavailabilityForTypeWitness(
4594+
const TypeDecl *witness, const AssociatedTypeDecl *assocType,
4595+
const ExportContext &where) {
4596+
auto diagnosticInfo = getExplicitUnavailabilityDiagnosticInfo(witness, where);
4597+
if (!diagnosticInfo)
4598+
return false;
4599+
4600+
ASTContext &ctx = witness->getASTContext();
4601+
auto &diags = ctx.Diags;
4602+
auto proto = assocType->getProtocol();
4603+
StringRef platform = diagnosticInfo->getPlatform();
4604+
const AvailableAttr *attr = diagnosticInfo->getAttr();
4605+
4606+
EncodedDiagnosticMessage EncodedMessage(attr->Message);
4607+
diags.diagnose(witness, diag::type_witness_availability_unavailable, witness,
4608+
assocType, proto, platform.empty(), platform,
4609+
EncodedMessage.Message);
4610+
4611+
switch (diagnosticInfo->getStatus()) {
4612+
case UnavailabilityDiagnosticInfo::Status::AlwaysUnavailable:
4613+
diags
4614+
.diagnose(witness, diag::type_witness_availability_marked_unavailable,
4615+
witness, assocType, proto)
4616+
.highlight(attr->getRange());
4617+
break;
4618+
case UnavailabilityDiagnosticInfo::Status::IntroducedInVersion:
4619+
diags.diagnose(witness,
4620+
diag::type_witness_availability_introduced_in_version,
4621+
witness, assocType, proto,
4622+
diagnosticInfo->getVersionedPlatform(), *attr->Introduced);
4623+
break;
4624+
case UnavailabilityDiagnosticInfo::Status::Obsoleted:
4625+
diags
4626+
.diagnose(witness, diag::type_witness_availability_obsoleted, witness,
4627+
assocType, proto, diagnosticInfo->getVersionedPlatform(),
4628+
*attr->Obsoleted)
4629+
.highlight(attr->getRange());
4630+
break;
4631+
}
4632+
return true;
4633+
}
4634+
4635+
static void diagnosePotentialUnavailabilityForTypeWitness(
4636+
const TypeDecl *witness, const AssociatedTypeDecl *assocType,
4637+
const DeclContext *dc, const UnavailabilityReason &reason) {
4638+
auto &ctx = dc->getASTContext();
4639+
auto proto = assocType->getProtocol();
4640+
auto requiredRange = reason.getRequiredOSVersionRange();
4641+
auto loc = witness->getLoc();
4642+
4643+
{
4644+
auto err = ctx.Diags.diagnose(
4645+
loc, diag::type_witness_availability_only_version_newer, witness,
4646+
assocType, proto, prettyPlatformString(targetPlatform(ctx.LangOpts)),
4647+
requiredRange.getLowerEndpoint());
4648+
err.warnUntilSwiftVersion(7);
4649+
4650+
// Direct a fixit to the error if an existing guard is nearly-correct
4651+
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc, requiredRange,
4652+
ctx, err))
4653+
return;
4654+
}
4655+
4656+
fixAvailability(loc, dc, requiredRange, ctx);
4657+
}
4658+
4659+
bool swift::diagnoseTypeWitnessAvailability(
4660+
const TypeDecl *witness, const AssociatedTypeDecl *assocType,
4661+
const NormalProtocolConformance *conformance, const ExportContext &where) {
4662+
if (where.isImplicit() || witness->isImplicit())
4663+
return false;
4664+
4665+
if (witness->isInvalid())
4666+
return false;
4667+
4668+
if (diagnoseExplicitUnavailabilityForTypeWitness(witness, assocType, where))
4669+
return true;
4670+
4671+
auto dc = conformance->getDeclContext();
4672+
auto &ctx = dc->getASTContext();
4673+
4674+
// The witness must be as available as the associated type and the conformance
4675+
// itself.
4676+
auto availability = AvailabilityInference::availableRange(assocType, ctx);
4677+
availability.intersectWith(
4678+
AvailabilityInference::availableRange(dc->getAsDecl(), ctx));
4679+
4680+
auto maybeUnavail = TypeChecker::checkDeclarationAvailability(
4681+
witness, where.withRefinedAvailability(availability));
4682+
if (maybeUnavail.has_value()) {
4683+
diagnosePotentialUnavailabilityForTypeWitness(witness, assocType, dc,
4684+
*maybeUnavail);
4685+
return true;
4686+
}
4687+
4688+
return false;
4689+
}
4690+
45854691
/// Should we warn that \p decl needs an explicit availability annotation
45864692
/// in -require-explicit-availability mode?
45874693
static bool declNeedsExplicitAvailability(const Decl *decl) {

lib/Sema/TypeCheckAvailability.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ class ExportContext {
153153
/// That is, this will perform a 'bitwise and' on the 'exported' bit.
154154
ExportContext withExported(bool exported) const;
155155

156+
/// Produce a new context with the same properties as this one, except the
157+
/// availability of the resulting context is constrained by \p availability
158+
/// if it is less available.
159+
ExportContext withRefinedAvailability(AvailabilityContext availability) const;
160+
156161
DeclContext *getDeclContext() const { return DC; }
157162

158163
AvailabilityContext getAvailabilityContext() const {
@@ -239,6 +244,12 @@ bool diagnoseSubstitutionMapAvailability(
239244
bool warnIfConformanceUnavailablePreSwift6 = false,
240245
bool suppressParameterizationCheckForOptional = false);
241246

247+
/// Diagnoses use of unavailable types as witnesses for associated type
248+
/// requirements. Returns true if a diagnostic was emitted.
249+
bool diagnoseTypeWitnessAvailability(
250+
const TypeDecl *witness, const AssociatedTypeDecl *assocType,
251+
const NormalProtocolConformance *conformance, const ExportContext &context);
252+
242253
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
243254
/// was emitted.
244255
bool diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4908,6 +4908,12 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
49084908
}
49094909
}
49104910

4911+
// The type witness must be as available as the associated type.
4912+
if (auto witnessTypeDecl = type->getAnyNominal()) {
4913+
diagnoseTypeWitnessAvailability(witnessTypeDecl, assocType, conformance,
4914+
where);
4915+
}
4916+
49114917
// Make sure any associated type witnesses don't make reference to a
49124918
// type we can't emit metadata for, or we're going to have trouble at
49134919
// runtime.

0 commit comments

Comments
 (0)