Skip to content

Commit e5a3e70

Browse files
authored
Merge pull request #58766 from CodaFi/of-no-avail
[5.7] Diagnose Availability for Parameterized Existential Types
2 parents cf9cba3 + 0dee946 commit e5a3e70

14 files changed

+289
-19
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,10 @@ class ASTContext final {
868868
/// swift_isUniquelyReferenced functions.
869869
AvailabilityContext getObjCIsUniquelyReferencedAvailability();
870870

871+
/// Get the runtime availability of metadata manipulation runtime functions
872+
/// for extended existential types.
873+
AvailabilityContext getParameterizedExistentialRuntimeAvailability();
874+
871875
/// Get the runtime availability of features introduced in the Swift 5.2
872876
/// compiler for the target platform.
873877
AvailabilityContext getSwift52Availability();

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5609,6 +5609,11 @@ ERROR(availability_concurrency_only_version_newer, none,
56095609
"concurrency is only available in %0 %1 or newer",
56105610
(StringRef, llvm::VersionTuple))
56115611

5612+
ERROR(availability_parameterized_protocol_only_version_newer, none,
5613+
"runtime support for parameterized protocol types is only available in "
5614+
"%0 %1 or newer",
5615+
(StringRef, llvm::VersionTuple))
5616+
56125617
NOTE(availability_guard_with_version_check, none,
56135618
"add 'if #available' version check", ())
56145619

include/swift/AST/Types.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,10 @@ class RecursiveTypeProperties {
163163
/// type sequence
164164
HasTypeSequence = 0x1000,
165165

166-
Last_Property = HasTypeSequence
166+
/// This type contains a parameterized existential type \c any P<T>.
167+
HasParameterizedExistential = 0x2000,
168+
169+
Last_Property = HasParameterizedExistential
167170
};
168171
enum { BitWidth = countBitsUsed(Property::Last_Property) };
169172

@@ -223,6 +226,12 @@ class RecursiveTypeProperties {
223226

224227
bool hasTypeSequence() const { return Bits & HasTypeSequence; }
225228

229+
/// Does a type with these properties structurally contain a
230+
/// parameterized existential type?
231+
bool hasParameterizedExistential() const {
232+
return Bits & HasParameterizedExistential;
233+
}
234+
226235
/// Returns the set of properties present in either set.
227236
friend RecursiveTypeProperties operator|(Property lhs, Property rhs) {
228237
return RecursiveTypeProperties(unsigned(lhs) | unsigned(rhs));
@@ -626,6 +635,11 @@ class alignas(1 << TypeAlignInBits) TypeBase
626635
return getRecursiveProperties().hasTypeSequence();
627636
}
628637

638+
/// Determine whether the type involves a parameterized existential type.
639+
bool hasParameterizedExistential() const {
640+
return getRecursiveProperties().hasParameterizedExistential();
641+
}
642+
629643
/// Determine whether the type involves the given opened existential
630644
/// archetype.
631645
bool hasOpenedExistentialWithRoot(const OpenedArchetypeType *root) const;

lib/AST/ASTContext.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3440,6 +3440,8 @@ ExistentialMetatypeType::get(Type T, Optional<MetatypeRepresentation> repr,
34403440
T = existential->getConstraintType();
34413441

34423442
auto properties = T->getRecursiveProperties();
3443+
if (T->is<ParameterizedProtocolType>())
3444+
properties |= RecursiveTypeProperties::HasParameterizedExistential;
34433445
auto arena = getArena(properties);
34443446

34453447
unsigned reprKey;
@@ -3535,7 +3537,7 @@ isAnyFunctionTypeCanonical(ArrayRef<AnyFunctionType::Param> params,
35353537
static RecursiveTypeProperties
35363538
getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
35373539
Type result) {
3538-
static_assert(RecursiveTypeProperties::BitWidth == 13,
3540+
static_assert(RecursiveTypeProperties::BitWidth == 14,
35393541
"revisit this if you add new recursive type properties");
35403542
RecursiveTypeProperties properties;
35413543

@@ -4124,7 +4126,7 @@ CanSILFunctionType SILFunctionType::get(
41244126
void *mem = ctx.Allocate(bytes, alignof(SILFunctionType));
41254127

41264128
RecursiveTypeProperties properties;
4127-
static_assert(RecursiveTypeProperties::BitWidth == 13,
4129+
static_assert(RecursiveTypeProperties::BitWidth == 14,
41284130
"revisit this if you add new recursive type properties");
41294131
for (auto &param : params)
41304132
properties |= param.getInterfaceType()->getRecursiveProperties();
@@ -4242,6 +4244,8 @@ Type ExistentialType::get(Type constraint, bool forceExistential) {
42424244
assert(constraint->isConstraintType());
42434245

42444246
auto properties = constraint->getRecursiveProperties();
4247+
if (constraint->is<ParameterizedProtocolType>())
4248+
properties |= RecursiveTypeProperties::HasParameterizedExistential;
42454249
auto arena = getArena(properties);
42464250

42474251
auto &entry = C.getImpl().getArena(arena).ExistentialTypes[constraint];

lib/AST/Availability.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ AvailabilityContext ASTContext::getObjCIsUniquelyReferencedAvailability() {
394394
return getSwift56Availability();
395395
}
396396

397+
AvailabilityContext
398+
ASTContext::getParameterizedExistentialRuntimeAvailability() {
399+
return getSwift57Availability();
400+
}
401+
397402
AvailabilityContext ASTContext::getSwift52Availability() {
398403
auto target = LangOpts.Target;
399404

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "TypeCheckAvailability.h"
18+
#include "MiscDiagnostics.h"
1819
#include "TypeCheckConcurrency.h"
19-
#include "TypeChecker.h"
2020
#include "TypeCheckObjC.h"
21-
#include "MiscDiagnostics.h"
21+
#include "TypeChecker.h"
2222
#include "swift/AST/ASTWalker.h"
23+
#include "swift/AST/GenericEnvironment.h"
2324
#include "swift/AST/Initializer.h"
2425
#include "swift/AST/NameLookup.h"
2526
#include "swift/AST/Pattern.h"
@@ -2749,6 +2750,74 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
27492750
return resultTy->isString();
27502751
}
27512752

2753+
static bool diagnosePotentialParameterizedProtocolUnavailability(
2754+
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
2755+
const UnavailabilityReason &Reason) {
2756+
ASTContext &Context = ReferenceDC->getASTContext();
2757+
2758+
auto RequiredRange = Reason.getRequiredOSVersionRange();
2759+
{
2760+
auto Err = Context.Diags.diagnose(
2761+
ReferenceRange.Start,
2762+
diag::availability_parameterized_protocol_only_version_newer,
2763+
prettyPlatformString(targetPlatform(Context.LangOpts)),
2764+
Reason.getRequiredOSVersionRange().getLowerEndpoint());
2765+
2766+
// Direct a fixit to the error if an existing guard is nearly-correct
2767+
if (fixAvailabilityByNarrowingNearbyVersionCheck(
2768+
ReferenceRange, ReferenceDC, RequiredRange, Context, Err))
2769+
return true;
2770+
}
2771+
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
2772+
return true;
2773+
}
2774+
2775+
bool swift::diagnoseParameterizedProtocolAvailability(
2776+
SourceRange ReferenceRange, const DeclContext *ReferenceDC) {
2777+
// Check the availability of parameterized existential runtime support.
2778+
ASTContext &ctx = ReferenceDC->getASTContext();
2779+
if (ctx.LangOpts.DisableAvailabilityChecking)
2780+
return false;
2781+
2782+
if (!shouldCheckAvailability(ReferenceDC->getAsDecl()))
2783+
return false;
2784+
2785+
auto runningOS = TypeChecker::overApproximateAvailabilityAtLocation(
2786+
ReferenceRange.Start, ReferenceDC);
2787+
auto availability = ctx.getParameterizedExistentialRuntimeAvailability();
2788+
if (!runningOS.isContainedIn(availability)) {
2789+
return diagnosePotentialParameterizedProtocolUnavailability(
2790+
ReferenceRange, ReferenceDC,
2791+
UnavailabilityReason::requiresVersionRange(
2792+
availability.getOSVersion()));
2793+
}
2794+
return false;
2795+
}
2796+
2797+
static void
2798+
maybeDiagParameterizedExistentialErasure(ErasureExpr *EE,
2799+
const ExportContext &Where) {
2800+
if (auto *OE = dyn_cast<OpaqueValueExpr>(EE->getSubExpr())) {
2801+
auto *OAT = OE->getType()->getAs<OpenedArchetypeType>();
2802+
if (!OAT)
2803+
return;
2804+
2805+
auto opened = OAT->getGenericEnvironment()->getOpenedExistentialType();
2806+
if (!opened || !opened->hasParameterizedExistential())
2807+
return;
2808+
2809+
(void)diagnoseParameterizedProtocolAvailability(EE->getLoc(),
2810+
Where.getDeclContext());
2811+
}
2812+
2813+
if (EE->getType() &&
2814+
EE->getType()->isAny() &&
2815+
EE->getSubExpr()->getType()->hasParameterizedExistential()) {
2816+
(void)diagnoseParameterizedProtocolAvailability(EE->getLoc(),
2817+
Where.getDeclContext());
2818+
}
2819+
}
2820+
27522821
bool swift::diagnoseExplicitUnavailability(
27532822
const ValueDecl *D,
27542823
SourceRange R,
@@ -2965,6 +3034,17 @@ class ExprAvailabilityWalker : public ASTWalker {
29653034
diagnoseDeclRefAvailability(Context.getRegexDecl(), Range);
29663035
diagnoseDeclRefAvailability(RLE->getInitializer(), Range);
29673036
}
3037+
if (auto *EE = dyn_cast<ErasureExpr>(E)) {
3038+
maybeDiagParameterizedExistentialErasure(EE, Where);
3039+
}
3040+
if (auto *CC = dyn_cast<ExplicitCastExpr>(E)) {
3041+
if (!isa<CoerceExpr>(CC) &&
3042+
CC->getCastType()->hasParameterizedExistential()) {
3043+
SourceLoc loc = CC->getCastTypeRepr() ? CC->getCastTypeRepr()->getLoc()
3044+
: E->getLoc();
3045+
diagnoseParameterizedProtocolAvailability(loc, Where.getDeclContext());
3046+
}
3047+
}
29683048
if (auto KP = dyn_cast<KeyPathExpr>(E)) {
29693049
maybeDiagKeyPath(KP);
29703050
}
@@ -3739,7 +3819,12 @@ class ProblematicTypeFinder : public TypeDeclFinder {
37393819

37403820
ModuleDecl *useModule = Where.getDeclContext()->getParentModule();
37413821
auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl());
3742-
(void) diagnoseSubstitutionMapAvailability(Loc, subs, Where);
3822+
(void)diagnoseSubstitutionMapAvailability(
3823+
Loc, subs, Where,
3824+
/*depTy=*/Type(),
3825+
/*replacementTy=*/Type(),
3826+
/*useConformanceAvailabilityErrorsOption=*/false,
3827+
/*suppressParameterizationCheckForOptional=*/ty->isOptional());
37433828
return Action::Continue;
37443829
}
37453830

@@ -3771,6 +3856,19 @@ class ProblematicTypeFinder : public TypeDeclFinder {
37713856
}
37723857
}
37733858

3859+
if (auto *TT = T->getAs<TupleType>()) {
3860+
for (auto component : TT->getElementTypes()) {
3861+
// Let the walker find inner tuple types, we only want to diagnose
3862+
// non-compound components.
3863+
if (component->is<TupleType>())
3864+
continue;
3865+
3866+
if (component->hasParameterizedExistential())
3867+
(void)diagnoseParameterizedProtocolAvailability(
3868+
Loc, Where.getDeclContext());
3869+
}
3870+
}
3871+
37743872
return TypeDeclFinder::walkToTypePost(T);
37753873
}
37763874
};
@@ -3885,14 +3983,27 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc,
38853983
SubstitutionMap subs,
38863984
const ExportContext &where,
38873985
Type depTy, Type replacementTy,
3888-
bool useConformanceAvailabilityErrorsOption) {
3986+
bool useConformanceAvailabilityErrorsOption,
3987+
bool suppressParameterizationCheckForOptional) {
38893988
bool hadAnyIssues = false;
38903989
for (ProtocolConformanceRef conformance : subs.getConformances()) {
38913990
if (diagnoseConformanceAvailability(loc, conformance, where,
38923991
depTy, replacementTy,
38933992
useConformanceAvailabilityErrorsOption))
38943993
hadAnyIssues = true;
38953994
}
3995+
3996+
// If we're looking at \c (any P)? (or any other depth of optional) then
3997+
// there's no availability problem.
3998+
if (suppressParameterizationCheckForOptional)
3999+
return hadAnyIssues;
4000+
4001+
for (auto replacement : subs.getReplacementTypes()) {
4002+
if (replacement->hasParameterizedExistential())
4003+
if (diagnoseParameterizedProtocolAvailability(loc,
4004+
where.getDeclContext()))
4005+
hadAnyIssues = true;
4006+
}
38964007
return hadAnyIssues;
38974008
}
38984009

lib/Sema/TypeCheckAvailability.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,14 @@ diagnoseConformanceAvailability(SourceLoc loc,
223223
Type replacementTy=Type(),
224224
bool useConformanceAvailabilityErrorsOption = false);
225225

226-
bool
227-
diagnoseSubstitutionMapAvailability(SourceLoc loc,
228-
SubstitutionMap subs,
229-
const ExportContext &context,
230-
Type depTy=Type(),
231-
Type replacementTy=Type(),
232-
bool useConformanceAvailabilityErrorsOption = false);
226+
bool diagnoseSubstitutionMapAvailability(
227+
SourceLoc loc,
228+
SubstitutionMap subs,
229+
const ExportContext &context,
230+
Type depTy = Type(),
231+
Type replacementTy = Type(),
232+
bool useConformanceAvailabilityErrorsOption = false,
233+
bool suppressParameterizationCheckForOptional = false);
233234

234235
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
235236
/// was emitted.
@@ -266,6 +267,11 @@ bool diagnoseExplicitUnavailability(
266267
const ExportContext &where,
267268
bool useConformanceAvailabilityErrorsOption = false);
268269

270+
/// Diagnose uses of the runtime features of parameterized protools. Returns
271+
/// \c true if a diagnostic was emitted.
272+
bool diagnoseParameterizedProtocolAvailability(SourceRange loc,
273+
const DeclContext *DC);
274+
269275
/// Check if \p decl has a introduction version required by -require-explicit-availability
270276
void checkExplicitAvailability(Decl *decl);
271277

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5053,6 +5053,17 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
50535053
if (where.isImplicit())
50545054
return;
50555055

5056+
Conformance->forEachTypeWitness([&](const AssociatedTypeDecl *assoc,
5057+
Type type, TypeDecl *typeDecl) -> bool {
5058+
// Make sure any associated type witnesses don't make reference to a
5059+
// parameterized existential type, or we're going to have trouble at
5060+
// runtime.
5061+
if (type->hasParameterizedExistential())
5062+
(void)diagnoseParameterizedProtocolAvailability(typeDecl->getLoc(),
5063+
where.getDeclContext());
5064+
return false;
5065+
});
5066+
50565067
for (auto req : proto->getRequirementSignature().getRequirements()) {
50575068
if (req.getKind() == RequirementKind::Conformance) {
50585069
auto depTy = req.getFirstType();

test/Constraints/parameterized_existential_metatypes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-parameterized-existential-types
1+
// RUN: %target-typecheck-verify-swift -enable-parameterized-existential-types -disable-availability-checking
22
//
33
// FIXME: Merge this file with existential_metatypes.swift once -enable-parameterized-existential-types becomes the default
44

test/Interpreter/parameterized_existentials.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-Xfrontend -enable-parameterized-existential-types)
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-parameterized-existential-types -Xfrontend -disable-availability-checking)
22
// REQUIRES: executable_test
33

44
import StdlibUnittest

test/RemoteAST/parameterized_existentials.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-remoteast-test -enable-parameterized-existential-types %s | %FileCheck %s
1+
// RUN: %target-swift-remoteast-test -enable-parameterized-existential-types -disable-availability-checking %s | %FileCheck %s
22

33
// REQUIRES: swift-remoteast-test
44

test/SILGen/parameterized_existentials.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-emit-silgen -module-name parameterized -enable-parameterized-existential-types %s | %FileCheck %s
1+
// RUN: %target-swift-emit-silgen -module-name parameterized -enable-parameterized-existential-types -disable-availability-checking %s | %FileCheck %s
22

33
protocol P<T, U, V> {
44
associatedtype T

test/SILOptimizer/cast_folding_parameterized_protocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend %s -emit-sil -enable-parameterized-existential-types -O -o - | %FileCheck %s
1+
// RUN: %target-swift-frontend %s -emit-sil -enable-parameterized-existential-types -disable-availability-checking -O -o - | %FileCheck %s
22

33
public protocol P<T> {
44
associatedtype T

0 commit comments

Comments
 (0)