Skip to content

Commit 857c578

Browse files
authored
Merge pull request #58655 from CodaFi/proto-molecule
Diagnose Availability for Parameterized Existential Types
2 parents a7ed41d + 5bcea41 commit 857c578

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
@@ -5596,6 +5596,11 @@ ERROR(availability_concurrency_only_version_newer, none,
55965596
"concurrency is only available in %0 %1 or newer",
55975597
(StringRef, llvm::VersionTuple))
55985598

5599+
ERROR(availability_parameterized_protocol_only_version_newer, none,
5600+
"runtime support for parameterized protocol types is only available in "
5601+
"%0 %1 or newer",
5602+
(StringRef, llvm::VersionTuple))
5603+
55995604
NOTE(availability_guard_with_version_check, none,
56005605
"add 'if #available' version check", ())
56015606

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
@@ -3441,6 +3441,8 @@ ExistentialMetatypeType::get(Type T, Optional<MetatypeRepresentation> repr,
34413441
T = existential->getConstraintType();
34423442

34433443
auto properties = T->getRecursiveProperties();
3444+
if (T->is<ParameterizedProtocolType>())
3445+
properties |= RecursiveTypeProperties::HasParameterizedExistential;
34443446
auto arena = getArena(properties);
34453447

34463448
unsigned reprKey;
@@ -3536,7 +3538,7 @@ isAnyFunctionTypeCanonical(ArrayRef<AnyFunctionType::Param> params,
35363538
static RecursiveTypeProperties
35373539
getGenericFunctionRecursiveProperties(ArrayRef<AnyFunctionType::Param> params,
35383540
Type result) {
3539-
static_assert(RecursiveTypeProperties::BitWidth == 13,
3541+
static_assert(RecursiveTypeProperties::BitWidth == 14,
35403542
"revisit this if you add new recursive type properties");
35413543
RecursiveTypeProperties properties;
35423544

@@ -4139,7 +4141,7 @@ CanSILFunctionType SILFunctionType::get(
41394141
void *mem = ctx.Allocate(bytes, alignof(SILFunctionType));
41404142

41414143
RecursiveTypeProperties properties;
4142-
static_assert(RecursiveTypeProperties::BitWidth == 13,
4144+
static_assert(RecursiveTypeProperties::BitWidth == 14,
41434145
"revisit this if you add new recursive type properties");
41444146
for (auto &param : params)
41454147
properties |= param.getInterfaceType()->getRecursiveProperties();
@@ -4257,6 +4259,8 @@ Type ExistentialType::get(Type constraint, bool forceExistential) {
42574259
assert(constraint->isConstraintType());
42584260

42594261
auto properties = constraint->getRecursiveProperties();
4262+
if (constraint->is<ParameterizedProtocolType>())
4263+
properties |= RecursiveTypeProperties::HasParameterizedExistential;
42604264
auto arena = getArena(properties);
42614265

42624266
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"
@@ -2781,6 +2782,74 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
27812782
return resultTy->isString();
27822783
}
27832784

2785+
static bool diagnosePotentialParameterizedProtocolUnavailability(
2786+
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
2787+
const UnavailabilityReason &Reason) {
2788+
ASTContext &Context = ReferenceDC->getASTContext();
2789+
2790+
auto RequiredRange = Reason.getRequiredOSVersionRange();
2791+
{
2792+
auto Err = Context.Diags.diagnose(
2793+
ReferenceRange.Start,
2794+
diag::availability_parameterized_protocol_only_version_newer,
2795+
prettyPlatformString(targetPlatform(Context.LangOpts)),
2796+
Reason.getRequiredOSVersionRange().getLowerEndpoint());
2797+
2798+
// Direct a fixit to the error if an existing guard is nearly-correct
2799+
if (fixAvailabilityByNarrowingNearbyVersionCheck(
2800+
ReferenceRange, ReferenceDC, RequiredRange, Context, Err))
2801+
return true;
2802+
}
2803+
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
2804+
return true;
2805+
}
2806+
2807+
bool swift::diagnoseParameterizedProtocolAvailability(
2808+
SourceRange ReferenceRange, const DeclContext *ReferenceDC) {
2809+
// Check the availability of parameterized existential runtime support.
2810+
ASTContext &ctx = ReferenceDC->getASTContext();
2811+
if (ctx.LangOpts.DisableAvailabilityChecking)
2812+
return false;
2813+
2814+
if (!shouldCheckAvailability(ReferenceDC->getAsDecl()))
2815+
return false;
2816+
2817+
auto runningOS = TypeChecker::overApproximateAvailabilityAtLocation(
2818+
ReferenceRange.Start, ReferenceDC);
2819+
auto availability = ctx.getParameterizedExistentialRuntimeAvailability();
2820+
if (!runningOS.isContainedIn(availability)) {
2821+
return diagnosePotentialParameterizedProtocolUnavailability(
2822+
ReferenceRange, ReferenceDC,
2823+
UnavailabilityReason::requiresVersionRange(
2824+
availability.getOSVersion()));
2825+
}
2826+
return false;
2827+
}
2828+
2829+
static void
2830+
maybeDiagParameterizedExistentialErasure(ErasureExpr *EE,
2831+
const ExportContext &Where) {
2832+
if (auto *OE = dyn_cast<OpaqueValueExpr>(EE->getSubExpr())) {
2833+
auto *OAT = OE->getType()->getAs<OpenedArchetypeType>();
2834+
if (!OAT)
2835+
return;
2836+
2837+
auto opened = OAT->getGenericEnvironment()->getOpenedExistentialType();
2838+
if (!opened || !opened->hasParameterizedExistential())
2839+
return;
2840+
2841+
(void)diagnoseParameterizedProtocolAvailability(EE->getLoc(),
2842+
Where.getDeclContext());
2843+
}
2844+
2845+
if (EE->getType() &&
2846+
EE->getType()->isAny() &&
2847+
EE->getSubExpr()->getType()->hasParameterizedExistential()) {
2848+
(void)diagnoseParameterizedProtocolAvailability(EE->getLoc(),
2849+
Where.getDeclContext());
2850+
}
2851+
}
2852+
27842853
bool swift::diagnoseExplicitUnavailability(
27852854
const ValueDecl *D,
27862855
SourceRange R,
@@ -2997,6 +3066,17 @@ class ExprAvailabilityWalker : public ASTWalker {
29973066
diagnoseDeclRefAvailability(Context.getRegexDecl(), Range);
29983067
diagnoseDeclRefAvailability(RLE->getInitializer(), Range);
29993068
}
3069+
if (auto *EE = dyn_cast<ErasureExpr>(E)) {
3070+
maybeDiagParameterizedExistentialErasure(EE, Where);
3071+
}
3072+
if (auto *CC = dyn_cast<ExplicitCastExpr>(E)) {
3073+
if (!isa<CoerceExpr>(CC) &&
3074+
CC->getCastType()->hasParameterizedExistential()) {
3075+
SourceLoc loc = CC->getCastTypeRepr() ? CC->getCastTypeRepr()->getLoc()
3076+
: E->getLoc();
3077+
diagnoseParameterizedProtocolAvailability(loc, Where.getDeclContext());
3078+
}
3079+
}
30003080
if (auto KP = dyn_cast<KeyPathExpr>(E)) {
30013081
maybeDiagKeyPath(KP);
30023082
}
@@ -3762,7 +3842,12 @@ class ProblematicTypeFinder : public TypeDeclFinder {
37623842

37633843
ModuleDecl *useModule = Where.getDeclContext()->getParentModule();
37643844
auto subs = ty->getContextSubstitutionMap(useModule, ty->getDecl());
3765-
(void) diagnoseSubstitutionMapAvailability(Loc, subs, Where);
3845+
(void)diagnoseSubstitutionMapAvailability(
3846+
Loc, subs, Where,
3847+
/*depTy=*/Type(),
3848+
/*replacementTy=*/Type(),
3849+
/*useConformanceAvailabilityErrorsOption=*/false,
3850+
/*suppressParameterizationCheckForOptional=*/ty->isOptional());
37663851
return Action::Continue;
37673852
}
37683853

@@ -3794,6 +3879,19 @@ class ProblematicTypeFinder : public TypeDeclFinder {
37943879
}
37953880
}
37963881

3882+
if (auto *TT = T->getAs<TupleType>()) {
3883+
for (auto component : TT->getElementTypes()) {
3884+
// Let the walker find inner tuple types, we only want to diagnose
3885+
// non-compound components.
3886+
if (component->is<TupleType>())
3887+
continue;
3888+
3889+
if (component->hasParameterizedExistential())
3890+
(void)diagnoseParameterizedProtocolAvailability(
3891+
Loc, Where.getDeclContext());
3892+
}
3893+
}
3894+
37973895
return TypeDeclFinder::walkToTypePost(T);
37983896
}
37993897
};
@@ -3908,14 +4006,27 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc,
39084006
SubstitutionMap subs,
39094007
const ExportContext &where,
39104008
Type depTy, Type replacementTy,
3911-
bool useConformanceAvailabilityErrorsOption) {
4009+
bool useConformanceAvailabilityErrorsOption,
4010+
bool suppressParameterizationCheckForOptional) {
39124011
bool hadAnyIssues = false;
39134012
for (ProtocolConformanceRef conformance : subs.getConformances()) {
39144013
if (diagnoseConformanceAvailability(loc, conformance, where,
39154014
depTy, replacementTy,
39164015
useConformanceAvailabilityErrorsOption))
39174016
hadAnyIssues = true;
39184017
}
4018+
4019+
// If we're looking at \c (any P)? (or any other depth of optional) then
4020+
// there's no availability problem.
4021+
if (suppressParameterizationCheckForOptional)
4022+
return hadAnyIssues;
4023+
4024+
for (auto replacement : subs.getReplacementTypes()) {
4025+
if (replacement->hasParameterizedExistential())
4026+
if (diagnoseParameterizedProtocolAvailability(loc,
4027+
where.getDeclContext()))
4028+
hadAnyIssues = true;
4029+
}
39194030
return hadAnyIssues;
39204031
}
39214032

lib/Sema/TypeCheckAvailability.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,14 @@ diagnoseConformanceAvailability(SourceLoc loc,
232232
Type replacementTy=Type(),
233233
bool useConformanceAvailabilityErrorsOption = false);
234234

235-
bool
236-
diagnoseSubstitutionMapAvailability(SourceLoc loc,
237-
SubstitutionMap subs,
238-
const ExportContext &context,
239-
Type depTy=Type(),
240-
Type replacementTy=Type(),
241-
bool useConformanceAvailabilityErrorsOption = false);
235+
bool diagnoseSubstitutionMapAvailability(
236+
SourceLoc loc,
237+
SubstitutionMap subs,
238+
const ExportContext &context,
239+
Type depTy = Type(),
240+
Type replacementTy = Type(),
241+
bool useConformanceAvailabilityErrorsOption = false,
242+
bool suppressParameterizationCheckForOptional = false);
242243

243244
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
244245
/// was emitted.
@@ -275,6 +276,11 @@ bool diagnoseExplicitUnavailability(
275276
const ExportContext &where,
276277
bool useConformanceAvailabilityErrorsOption = false);
277278

279+
/// Diagnose uses of the runtime features of parameterized protools. Returns
280+
/// \c true if a diagnostic was emitted.
281+
bool diagnoseParameterizedProtocolAvailability(SourceRange loc,
282+
const DeclContext *DC);
283+
278284
/// Check if \p decl has a introduction version required by -require-explicit-availability
279285
void checkExplicitAvailability(Decl *decl);
280286

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4919,6 +4919,17 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
49194919
if (where.isImplicit())
49204920
return;
49214921

4922+
Conformance->forEachTypeWitness([&](const AssociatedTypeDecl *assoc,
4923+
Type type, TypeDecl *typeDecl) -> bool {
4924+
// Make sure any associated type witnesses don't make reference to a
4925+
// parameterized existential type, or we're going to have trouble at
4926+
// runtime.
4927+
if (type->hasParameterizedExistential())
4928+
(void)diagnoseParameterizedProtocolAvailability(typeDecl->getLoc(),
4929+
where.getDeclContext());
4930+
return false;
4931+
});
4932+
49224933
for (auto req : proto->getRequirementSignature().getRequirements()) {
49234934
if (req.getKind() == RequirementKind::Conformance) {
49244935
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
// This test requires the new existential shape metadata accessors which are

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)