From 69c8f53e763db9953f854217e902feda241999db Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 29 Feb 2024 01:06:02 -0500 Subject: [PATCH 1/2] [NFC] Remove "Runtime" from this feature name to avoid confusion --- include/swift/AST/FeatureAvailability.def | 10 +++++++++- lib/IRGen/IRGenModule.cpp | 2 +- lib/Sema/TypeCheckAvailability.cpp | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/FeatureAvailability.def b/include/swift/AST/FeatureAvailability.def index cf5c351449168..222c385017614 100644 --- a/include/swift/AST/FeatureAvailability.def +++ b/include/swift/AST/FeatureAvailability.def @@ -18,6 +18,13 @@ /// FEATURE(N, V) /// N - The name of the feature (in UpperCamelCase). /// V - The Swift version number, as a tuple, or FUTURE. +/// +/// The feature Foo turns into two methods on ASTContext: +/// getFooRuntimeAvailability(), which returns the exact version provided +/// here, and getFooAvailability(), which maps that version into a version +/// of the target OS. Because both of these methods exist, it is a bad idea +/// to use a feature name that ends in "Runtime", or people might get very +/// confused. #ifndef FEATURE #define FEATURE(N, V) #endif @@ -48,7 +55,8 @@ FEATURE(Concurrency, (5, 5)) FEATURE(MultiPayloadEnumTagSinglePayload, (5, 6)) FEATURE(ObjCIsUniquelyReferenced, (5, 6)) -FEATURE(ParameterizedExistentialRuntime, (5, 7)) +// Metadata and casting support for parameterized existential types +FEATURE(ParameterizedExistential, (5, 7)) FEATURE(VariadicGenericType, (5, 9)) FEATURE(SignedConformsToProtocol, (5, 9)) diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index b20eea94fbfe2..ab1e7cd3cffa4 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -960,7 +960,7 @@ namespace RuntimeConstants { } RuntimeAvailability ParameterizedExistentialAvailability(ASTContext &Context) { - auto featureAvailability = Context.getParameterizedExistentialRuntimeAvailability(); + auto featureAvailability = Context.getParameterizedExistentialAvailability(); if (!isDeploymentAvailabilityContainedIn(Context, featureAvailability)) { return RuntimeAvailability::ConditionallyAvailable; } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index ee0d94ef54b5f..0a1190c74b6b4 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2988,7 +2988,7 @@ bool swift::diagnoseParameterizedProtocolAvailability( SourceRange ReferenceRange, const DeclContext *ReferenceDC) { return TypeChecker::checkAvailability( ReferenceRange, - ReferenceDC->getASTContext().getParameterizedExistentialRuntimeAvailability(), + ReferenceDC->getASTContext().getParameterizedExistentialAvailability(), diag::availability_parameterized_protocol_only_version_newer, ReferenceDC); } From 0f076c070267e91616c1d6b0fc043211e9716852 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 29 Feb 2024 01:06:54 -0500 Subject: [PATCH 2/2] Fix metadata availability testing for parameterized existentials and implement it for @isolated(any) function types. The existing testing was pretty broken: we were diagnosing all sorts of things that don't require type metadata (like using a tuple with an extended existential in a value position in an API signature) and not diagnosing several things that do (like covariant function conversions that erase types). There's therefore some risk to this patch, but I'm not too worried because needing metadata like this is pretty uncommon, and it's likely that programs won't build correctly anyway --- it'll just get caught by the linker instead of the compiler. --- include/swift/AST/DiagnosticsSema.def | 5 + include/swift/AST/FeatureAvailability.def | 2 + lib/IRGen/GenReflection.cpp | 91 +++---- lib/IRGen/MetadataRequest.cpp | 10 +- lib/Sema/TypeCheckAvailability.cpp | 238 ++++++++++++++---- lib/Sema/TypeCheckAvailability.h | 10 +- lib/Sema/TypeCheckProtocol.cpp | 8 +- test/IRGen/isolated_any.sil | 2 +- test/IRGen/isolated_any_metadata.sil | 24 ++ test/Parse/isolated_any.swift | 2 +- test/Sema/availability_isolated_any.swift | 43 ++++ ...ailability_parameterized_existential.swift | 72 +++++- validation-test/IRGen/98995607.swift.gyb | 2 +- 13 files changed, 395 insertions(+), 114 deletions(-) create mode 100644 test/IRGen/isolated_any_metadata.sil create mode 100644 test/Sema/availability_isolated_any.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1c64bfb7be4fa..f31180f7d92b1 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6609,6 +6609,11 @@ ERROR(availability_parameterized_protocol_only_version_newer, none, "%0 %1 or newer", (StringRef, llvm::VersionTuple)) +ERROR(availability_isolated_any_only_version_newer, none, + "runtime support for @isolated(any) function types is only available in " + "%0 %1 or newer", + (StringRef, llvm::VersionTuple)) + ERROR(availability_variadic_type_only_version_newer, none, "parameter packs in generic types are only available in %0 %1 or newer", (StringRef, llvm::VersionTuple)) diff --git a/include/swift/AST/FeatureAvailability.def b/include/swift/AST/FeatureAvailability.def index 222c385017614..c0a32d3851b58 100644 --- a/include/swift/AST/FeatureAvailability.def +++ b/include/swift/AST/FeatureAvailability.def @@ -68,6 +68,8 @@ FEATURE(ObjCSymbolicReferences, (5, 11)) FEATURE(TypedThrows, (5, 11)) FEATURE(StaticReadOnlyArrays, (5, 11)) FEATURE(SwiftExceptionPersonality, (5, 11)) +// Metadata support for @isolated(any) function types +FEATURE(IsolatedAny, (5, 11)) FEATURE(TaskExecutor, FUTURE) FEATURE(Differentiation, FUTURE) diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index f26cf4e61abb2..b7539f9f88c1b 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -176,60 +176,65 @@ class PrintMetadataSource std::optional getRuntimeVersionThatSupportsDemanglingType(CanType type) { - // The Swift 6.0 runtime is the first version able to demangle types - // that involve typed throws. - bool usesTypedThrows = type.findIf([](CanType t) -> bool { - if (auto fn = dyn_cast(t)) { - if (!fn.getThrownError().isNull()) - return true; - } + enum VersionRequirement { + None, + Swift_5_2, + Swift_5_5, + Swift_6_0, + + // Short-circuit if we find this requirement. + Latest = Swift_6_0 + }; + VersionRequirement latestRequirement = None; + auto addRequirement = [&](VersionRequirement req) -> bool { + if (req > latestRequirement) { + latestRequirement = req; + return req == Latest; + } return false; - }); - if (usesTypedThrows) { - return llvm::VersionTuple(6, 0); - } + }; - // The Swift 5.5 runtime is the first version able to demangle types - // related to concurrency. - bool needsConcurrency = type.findIf([](CanType t) -> bool { + (void) type.findIf([&](CanType t) -> bool { if (auto fn = dyn_cast(t)) { - if (fn->isAsync() || fn->isSendable() || fn->hasGlobalActor()) - return true; - - for (const auto ¶m : fn->getParams()) { - if (param.isIsolated()) - return true; - } + // The Swift 6.0 runtime is the first version able to demangle types + // that involve typed throws or @isolated(any), or for that matter + // represent them at all at runtime. + if (!fn.getThrownError().isNull() || fn->getIsolation().isErased()) + return addRequirement(Swift_6_0); + + // The Swift 5.5 runtime is the first version able to demangle types + // related to concurrency. + if (fn->isAsync() || fn->isSendable() || + !fn->getIsolation().isNonIsolated()) + return addRequirement(Swift_5_5); return false; } + + if (auto opaqueArchetype = dyn_cast(t)) { + // Associated types of opaque types weren't mangled in a usable + // form by the Swift 5.1 runtime, so we needed to add a new + // mangling in 5.2. + if (opaqueArchetype->getInterfaceType()->is()) + return addRequirement(Swift_5_2); + + // Although opaque types in general were only added in Swift 5.1, + // declarations that use them are already covered by availability + // guards, so we don't need to limit availability of mangled names + // involving them. + } + return false; }); - if (needsConcurrency) { - return llvm::VersionTuple(5, 5); - } - // Associated types of opaque types weren't mangled in a usable form by the - // Swift 5.1 runtime, so we needed to add a new mangling in 5.2. - if (type->hasOpaqueArchetype()) { - auto hasOpaqueAssocType = type.findIf([](CanType t) -> bool { - if (auto a = dyn_cast(t)) { - return isa(a) && - a->getInterfaceType()->is(); - } - return false; - }); - - if (hasOpaqueAssocType) - return llvm::VersionTuple(5, 2); - // Although opaque types in general were only added in Swift 5.1, - // declarations that use them are already covered by availability - // guards, so we don't need to limit availability of mangled names - // involving them. + switch (latestRequirement) { + case Swift_6_0: return llvm::VersionTuple(6, 0); + case Swift_5_5: return llvm::VersionTuple(5, 5); + case Swift_5_2: return llvm::VersionTuple(5, 2); + case None: return std::nullopt; } - - return std::nullopt; + llvm_unreachable("bad kind"); } // Produce a fallback mangled type name that uses an open-coded callback diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index fb33c5e1cec54..5d9b308b243cc 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1416,10 +1416,15 @@ getFunctionTypeFlags(CanFunctionType type) { break; } + auto isolation = type->getIsolation(); + auto extFlags = ExtendedFunctionTypeFlags() .withTypedThrows(!type->getThrownError().isNull()) .withTransferringResult(type->hasTransferringResult()); + if (isolation.isErased()) + extFlags = extFlags.withIsolatedAny(); + auto flags = FunctionTypeFlags() .withConvention(metadataConvention) .withAsync(type->isAsync()) @@ -1428,7 +1433,7 @@ getFunctionTypeFlags(CanFunctionType type) { .withParameterFlags(hasParameterFlags) .withEscaping(isEscaping) .withDifferentiable(type->isDifferentiable()) - .withGlobalActor(!type->getGlobalActor().isNull()) + .withGlobalActor(isolation.isGlobalActor()) .withExtendedFlags(extFlags.getIntValue() != 0); return std::make_pair(flags, extFlags); @@ -1632,7 +1637,8 @@ static MetadataResponse emitFunctionTypeMetadataRef(IRGenFunction &IGF, default: assert((!params.empty() || type->isDifferentiable() || - type->getGlobalActor() || type->getThrownError()) && + !type->getIsolation().isNonIsolated() || + type->getThrownError()) && "0 parameter case should be specialized unless it is a " "differentiable function or has a global actor"); diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 0a1190c74b6b4..969b53fd440db 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2984,7 +2984,7 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) { return resultTy->isString(); } -bool swift::diagnoseParameterizedProtocolAvailability( +static bool diagnoseParameterizedProtocolAvailability( SourceRange ReferenceRange, const DeclContext *ReferenceDC) { return TypeChecker::checkAvailability( ReferenceRange, @@ -2993,27 +2993,162 @@ bool swift::diagnoseParameterizedProtocolAvailability( ReferenceDC); } -static void -maybeDiagParameterizedExistentialErasure(ErasureExpr *EE, - const ExportContext &Where) { - if (auto *OE = dyn_cast(EE->getSubExpr())) { - auto *OAT = OE->getType()->getAs(); - if (!OAT) - return; +static bool diagnoseIsolatedAnyAvailability( + SourceRange ReferenceRange, const DeclContext *ReferenceDC) { + return TypeChecker::checkAvailability( + ReferenceRange, + ReferenceDC->getASTContext().getIsolatedAnyAvailability(), + diag::availability_isolated_any_only_version_newer, + ReferenceDC); +} - auto opened = OAT->getGenericEnvironment()->getOpenedExistentialType(); - if (!opened || !opened->hasParameterizedExistential()) - return; +static bool checkTypeMetadataAvailabilityInternal(CanType type, + SourceRange refLoc, + const DeclContext *refDC) { + return type.findIf([&](CanType type) { + if (isa(type)) { + return diagnoseParameterizedProtocolAvailability(refLoc, refDC); + } else if (auto fnType = dyn_cast(type)) { + auto isolation = fnType->getIsolation(); + if (isolation.isErased()) + return diagnoseIsolatedAnyAvailability(refLoc, refDC); + } + return false; + }); +} + +/// Check whether type metadata is available for the given type (and its +/// component types). +bool swift::checkTypeMetadataAvailability(Type type, + SourceRange refLoc, + const DeclContext *refDC) { + if (!type) return false; + return checkTypeMetadataAvailabilityInternal(type->getCanonicalType(), + refLoc, refDC); +} + +/// Check whether type metadata is available for the given type, given that +/// it is the operand of a dynamic cast or existential conversion. +static bool checkTypeMetadataAvailabilityForConverted(Type refType, + SourceRange refLoc, + const DeclContext *refDC) { + if (!refType) return false; + + auto type = refType->getCanonicalType(); + + // SILGen emits these conversions by opening the outermost level of + // existential, so we never need to emit type metadata for an + // existential in such a position. We necessarily have type metadata + // for the dynamic type of the existential, so there's nothing to check + // there. + if (type.isAnyExistentialType()) return false; + + return checkTypeMetadataAvailabilityInternal(type, refLoc, refDC); +} + +namespace { + +class CheckConversionAvailability { + SourceRange refLoc; + const DeclContext *refDC; + +public: + CheckConversionAvailability(SourceRange refLoc, const DeclContext *refDC) + : refLoc(refLoc), refDC(refDC) {} + + void check(CanType srcType, CanType destType); + void checkFunction(CanAnyFunctionType srcType, CanAnyFunctionType destType); + +private: + void checkTuple(CanTupleType srcType, CanTupleType destType); +}; + +} // end anonymous namespace + +void CheckConversionAvailability::check(CanType srcType, CanType destType) { + if (srcType == destType) + return; + + // We care about specific optionality structure here: converting + // `(any P)?` to `Any?` doesn't require metadata for `any P`, + // but converting `(any P)?` to non-optional `Any` does. + if (auto destObjectType = destType.getOptionalObjectType()) { + // optional -> optional conversion + if (auto srcObjectType = srcType.getOptionalObjectType()) { + check(srcObjectType, destObjectType); + // optional injection + } else { + check(srcType, destObjectType); + } + + // Conversions to existential types require type metadata for the + // source type, except that we look into existentials. + } else if (destType.isAnyExistentialType()) { + checkTypeMetadataAvailabilityForConverted(srcType, refLoc, refDC); + + // Conversions between function types perform a bunch of recursive + // conversions. + } else if (auto destFnType = dyn_cast(destType)) { + if (auto srcFnType = dyn_cast(srcType)) { + checkFunction(srcFnType, destFnType); + } + + // Conversions between tuple types perform a bunch of recursive + // conversions. + } else if (auto destTupleType = dyn_cast(destType)) { + if (auto srcTupleType = dyn_cast(srcType)) { + checkTuple(srcTupleType, destTupleType); + } - (void)diagnoseParameterizedProtocolAvailability(EE->getLoc(), - Where.getDeclContext()); + // Conversions of things containing pack expansions convert the + // expansion patterns. We won't print the types we get here, so + // we can ignore them. + } else if (auto destExpType = dyn_cast(destType)) { + if (auto srcExpType = dyn_cast(srcType)) { + check(srcExpType.getPatternType(), destExpType.getPatternType()); + } + } +} + +void CheckConversionAvailability::checkFunction(CanAnyFunctionType srcType, + CanAnyFunctionType destType) { + // Results are covariantly converted. + check(srcType.getResult(), destType.getResult()); + + // Defensively ignored invalid conversion structure. + if (srcType->getNumParams() != destType->getNumParams()) + return; + + // Parameters are contravariantly converted. + for (auto i : range(srcType->getNumParams())) { + const auto &srcParam = srcType.getParams()[i]; + const auto &destParam = destType.getParams()[i]; + + // Note the reversal for contravariance. + check(destParam.getParameterType(), srcParam.getParameterType()); + } +} + +void CheckConversionAvailability::checkTuple(CanTupleType srcType, + CanTupleType destType) { + // Handle invalid structure appropriately. + if (srcType->getNumElements() != destType->getNumElements()) + return; + + for (auto i : range(srcType->getNumElements())) { + check(srcType.getElementType(i), destType.getElementType(i)); } +} + +static void checkFunctionConversionAvailability(Type srcType, Type destType, + SourceRange refLoc, + const DeclContext *refDC) { + if (srcType && destType) { + auto srcFnType = cast(srcType->getCanonicalType()); + auto destFnType = cast(destType->getCanonicalType()); - if (EE->getType() && - EE->getType()->isAny() && - EE->getSubExpr()->getType()->hasParameterizedExistential()) { - (void)diagnoseParameterizedProtocolAvailability(EE->getLoc(), - Where.getDeclContext()); + CheckConversionAvailability(refLoc, refDC) + .checkFunction(srcFnType, destFnType); } } @@ -3236,16 +3371,11 @@ class ExprAvailabilityWalker : public ASTWalker { diagnoseDeclRefAvailability(CE->getInitializer(), CE->getSourceRange()); } - if (auto *EE = dyn_cast(E)) { - maybeDiagParameterizedExistentialErasure(EE, Where); - } - if (auto *CC = dyn_cast(E)) { - if (!isa(CC) && CC->getCastType() && - CC->getCastType()->hasParameterizedExistential()) { - SourceLoc loc = CC->getCastTypeRepr() ? CC->getCastTypeRepr()->getLoc() - : E->getLoc(); - diagnoseParameterizedProtocolAvailability(loc, Where.getDeclContext()); - } + if (auto *FCE = dyn_cast(E)) { + checkFunctionConversionAvailability(FCE->getSubExpr()->getType(), + FCE->getType(), + FCE->getLoc(), + Where.getDeclContext()); } if (auto KP = dyn_cast(E)) { maybeDiagKeyPath(KP); @@ -3274,19 +3404,31 @@ class ExprAvailabilityWalker : public ASTWalker { : nullptr, CE->getResultType(), E->getLoc(), Where); } - if (auto CE = dyn_cast(E)) { - diagnoseTypeAvailability(CE->getCastTypeRepr(), CE->getCastType(), - E->getLoc(), Where); - } - if (AbstractClosureExpr *closure = dyn_cast(E)) { if (shouldWalkIntoClosure(closure)) { walkAbstractClosure(closure); return Action::SkipChildren(E); } } + + if (auto CE = dyn_cast(E)) { + if (!isa(CE)) { + SourceLoc loc = CE->getCastTypeRepr() ? CE->getCastTypeRepr()->getLoc() + : E->getLoc(); + checkTypeMetadataAvailability(CE->getCastType(), loc, + Where.getDeclContext()); + checkTypeMetadataAvailabilityForConverted(CE->getSubExpr()->getType(), + loc, Where.getDeclContext()); + } + + diagnoseTypeAvailability(CE->getCastTypeRepr(), CE->getCastType(), + E->getLoc(), Where); + } if (auto EE = dyn_cast(E)) { + checkTypeMetadataAvailability(EE->getSubExpr()->getType(), + EE->getLoc(), Where.getDeclContext()); + for (ProtocolConformanceRef C : EE->getConformances()) { diagnoseConformanceAvailability(E->getLoc(), C, Where, Type(), Type(), /*useConformanceAvailabilityErrorsOpt=*/true); @@ -3531,6 +3673,17 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability( return false; const ValueDecl *D = declRef.getDecl(); + // Suppress availability diagnostics for uses of builtins. We don't + // synthesize availability for builtin functions anyway, so this really + // means to not check availability for the substitution maps. This is + // abstractly reasonable, since calls to generic builtins usually do not + // require metadata for generic arguments the same way that calls to + // generic functions might. More importantly, the stdlib has to get the + // availability right anyway, and diagnostics from builtin usage are not + // likely to be of significant assistance in that. + if (D->getModuleContext()->isBuiltinModule()) + return false; + if (auto *attr = AvailableAttr::isUnavailable(D)) { if (diagnoseIncDecRemoval(D, R, attr)) return true; @@ -4045,19 +4198,6 @@ class ProblematicTypeFinder : public TypeDeclFinder { } } - if (auto *TT = T->getAs()) { - for (auto component : TT->getElementTypes()) { - // Let the walker find inner tuple types, we only want to diagnose - // non-compound components. - if (component->is()) - continue; - - if (component->hasParameterizedExistential()) - (void)diagnoseParameterizedProtocolAvailability( - Loc, Where.getDeclContext()); - } - } - return TypeDeclFinder::walkToTypePost(T); } }; @@ -4198,10 +4338,8 @@ swift::diagnoseSubstitutionMapAvailability(SourceLoc loc, return hadAnyIssues; for (auto replacement : subs.getReplacementTypes()) { - if (replacement->hasParameterizedExistential()) - if (diagnoseParameterizedProtocolAvailability(loc, - where.getDeclContext())) - hadAnyIssues = true; + if (checkTypeMetadataAvailability(replacement, loc, where.getDeclContext())) + hadAnyIssues = true; } return hadAnyIssues; } diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 0037e60e10aa3..c4e6fddfd57d2 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -275,10 +275,12 @@ bool diagnoseExplicitUnavailability( const ExportContext &where, bool warnIfConformanceUnavailablePreSwift6 = false); -/// Diagnose uses of the runtime features of parameterized protools. Returns -/// \c true if a diagnostic was emitted. -bool diagnoseParameterizedProtocolAvailability(SourceRange loc, - const DeclContext *DC); +/// Diagnose uses of the runtime support of the given type, such as +/// type metadata and dynamic casting. +/// +/// Returns \c true if a diagnostic was emitted. +bool checkTypeMetadataAvailability(Type type, SourceRange loc, + const DeclContext *DC); /// Check if \p decl has a introduction version required by -require-explicit-availability void checkExplicitAvailability(Decl *decl); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index dbec422c49eba..9e3de2aee5e8a 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4853,11 +4853,11 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx, } // Make sure any associated type witnesses don't make reference to a - // parameterized existential type, or we're going to have trouble at + // type we can't emit metadata for, or we're going to have trouble at // runtime. - if (type->hasParameterizedExistential()) - (void)diagnoseParameterizedProtocolAvailability(typeDecl->getLoc(), - where.getDeclContext()); + checkTypeMetadataAvailability(type, typeDecl->getLoc(), + where.getDeclContext()); + return false; }); diff --git a/test/IRGen/isolated_any.sil b/test/IRGen/isolated_any.sil index 5c688a2ab94cf..ee215de1a2837 100644 --- a/test/IRGen/isolated_any.sil +++ b/test/IRGen/isolated_any.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-ir %s | %IRGenFileCheck %s +// RUN: %target-swift-frontend -emit-ir %s -enable-experimental-feature IsolatedAny | %IRGenFileCheck %s // REQUIRES: concurrency diff --git a/test/IRGen/isolated_any_metadata.sil b/test/IRGen/isolated_any_metadata.sil new file mode 100644 index 0000000000000..25a23c61beef5 --- /dev/null +++ b/test/IRGen/isolated_any_metadata.sil @@ -0,0 +1,24 @@ +// RUN: %swift -emit-ir %s -enable-experimental-feature IsolatedAny -target x86_64-apple-macosx10.10 -disable-legacy-type-info -parse-stdlib | %IRGenFileCheck %s -check-prefix=CHECK-ACCESSOR +// RUN: %swift -emit-ir %s -enable-experimental-feature IsolatedAny -target x86_64-unknown-linux-gnu -disable-legacy-type-info -parse-stdlib | %IRGenFileCheck %s -check-prefix=CHECK-DEMANGLE + +// REQUIRES: concurrency + +sil_stage canonical + +// CHECK-LABEL: define{{.*}} swiftcc ptr @get_metadata +// CHECK: entry: +// CHECK-ACCESSOR-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$syyYAcMa"([[INT]] 0) +// CHECK-ACCESSOR-NEXT: [[METADATA:%.]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-DEMANGLE: [[METADATA:%.*]] = call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$syyYAcMD") +// CHECK-NEXT: ret ptr [[METADATA]] +sil @get_metadata : $() -> @thick Any.Type { +entry: + %type = metatype $@thick (@isolated(any) () -> ()).Type + %result = init_existential_metatype %type : $@thick (@isolated(any) () -> ()).Type, $@thick Any.Type + return %result : $@thick Any.Type +} + +// CHECK-ACCESSOR-LABEL: define{{.*}} swiftcc %swift.metadata_response @"$syyYAcMa" +// 2214592512 == 0x84000000 == (ExtendedFlags | Escaping) +// 2 == IsolatedAny +// CHECK-ACCESSOR: call ptr @swift_getExtendedFunctionTypeMetadata([[INT]] 2214592512, [[INT]] 0, ptr null, ptr null, ptr getelementptr inbounds {{.*}} @"$sytN"{{.*}}), ptr null, i32 2, ptr null) diff --git a/test/Parse/isolated_any.swift b/test/Parse/isolated_any.swift index 418e1f295beca..fbe713266949a 100644 --- a/test/Parse/isolated_any.swift +++ b/test/Parse/isolated_any.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-feature IsolatedAny +// RUN: %target-typecheck-verify-swift -enable-experimental-feature IsolatedAny -disable-availability-checking // REQUIRES: asserts diff --git a/test/Sema/availability_isolated_any.swift b/test/Sema/availability_isolated_any.swift new file mode 100644 index 0000000000000..48d489a9592bf --- /dev/null +++ b/test/Sema/availability_isolated_any.swift @@ -0,0 +1,43 @@ +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.50 -enable-experimental-feature IsolatedAny + +// REQUIRES: OS=macosx + +// Concrete uses of @isolated(any) are fine. +func expectIsolatedAny(fn: @isolated(any) () -> ()) {} + +func testConcrete1(fn: @isolated(any) () -> ()) { + expectIsolatedAny(fn: fn) +} +func testConcrete2(fn: @isolated(any) () -> ()) { + expectIsolatedAny {} +} +func testConcrete3(fn: @MainActor () -> ()) { + expectIsolatedAny {} +} + +func testErasure(fn: @escaping @isolated(any) () -> ()) -> Any { + return fn // expected-error {{runtime support for @isolated(any) function types is only available in}} + // expected-note @-2 {{add @available attribute to enclosing global function}} + // expected-note @-2 {{add 'if #available' version check}} +} + +func testCovariantErasure(fn: @escaping () -> @isolated(any) () -> Void) -> (() -> Any) { + return fn // expected-error {{runtime support for @isolated(any) function types is only available in}} + // expected-note @-2 {{add @available attribute to enclosing global function}} + // expected-note @-2 {{add 'if #available' version check}} +} + +func testContravariantErasure(fn: @escaping (Any) -> Void) -> ((@escaping @isolated(any) () -> Void) -> Void) { + return fn // expected-error {{runtime support for @isolated(any) function types is only available in}} + // expected-note @-2 {{add @available attribute to enclosing global function}} + // expected-note @-2 {{add 'if #available' version check}} +} + +protocol P { + associatedtype A +} + +struct S: P { // expected-note {{add @available attribute to enclosing struct}} + typealias A = @isolated(any) () -> () // expected-error {{runtime support for @isolated(any) function types is only available in}} + // expected-note @-1 {{add @available attribute to enclosing type alias}} +} diff --git a/test/Sema/availability_parameterized_existential.swift b/test/Sema/availability_parameterized_existential.swift index e329c52d32fa2..88db809cd3c6e 100644 --- a/test/Sema/availability_parameterized_existential.swift +++ b/test/Sema/availability_parameterized_existential.swift @@ -26,10 +26,35 @@ struct Wrapper {} func identity(_ x: any P) -> any P { return x } // OK func unwrapUnwrap(_ x: (any P)???) -> (any P)? { return x!! } // OK -func erase(_ x: any P) -> Any { return x } // expected-error {{runtime support for parameterized protocol types is only available in}} +func erase(_ x: any P) -> Any { return x } + +func nonerasingFunction(_ f: @escaping (any P) -> ()) -> Any { return 0 } + +func eraseFunction(_ f: @escaping (any P) -> ()) -> Any { return f } // expected-error {{runtime support for parameterized protocol types is only available in}} // expected-note@-1 {{add @available attribute to enclosing global function}} // expected-note@-2 {{add 'if #available' version check}} +// These are okay because we can convert between existentials without metadata. +func eraseFunctionCovariant(_ f: @escaping () -> any P) -> (() -> Any) { + return f +} +func eraseFunctionContravariant(_ f: @escaping (Any) -> ()) -> ((any P) -> Any) { + return f +} + +// We cannot convert from an optional existential to an existential without +// metadata. +func eraseFunctionCovariantOptional(_ f: @escaping () -> (any P)?) -> (() -> Any) { + return f // expected-error {{runtime support for parameterized protocol types is only available in}} + // expected-note@-2 {{add @available attribute to enclosing global function}} + // expected-note@-2 {{add 'if #available' version check}} +} +func eraseFunctionContravariantOptional(_ f: @escaping (Any) -> ()) -> (((any P)?) -> Any) { + return f // expected-error {{runtime support for parameterized protocol types is only available in}} + // expected-note@-2 {{add @available attribute to enclosing global function}} + // expected-note@-2 {{add 'if #available' version check}} +} + func eraseOptional(_ x: (any P)?) -> Any { return x } // expected-note@-1 {{add @available attribute to enclosing global function}} // expected-error@-2 {{runtime support for parameterized protocol types is only available in}} @@ -44,10 +69,8 @@ func eraseOptional2(_ x: (any P)?) -> Any { return x as Any } // expected-error@-2 {{runtime support for parameterized protocol types is only available in}} // expected-note@-3 {{add 'if #available' version check}} -func tupleOut() -> (any P, Int) { return tupleOut() } // expected-error {{runtime support for parameterized protocol types is only available in}} -// expected-note@-1 {{add @available attribute to enclosing global function}} -func tupleIn(_ xs: (any P, Int)) -> Int { return tupleIn(xs) } // expected-error {{runtime support for parameterized protocol types is only available in}} -// expected-note@-1 {{add @available attribute to enclosing global function}} +func tupleOut() -> (any P, Int) { return tupleOut() } +func tupleIn(_ xs: (any P, Int)) -> Int { return tupleIn(xs) } func wrap(_ x: any P) -> Wrapper> { return wrap(x) } // expected-error {{runtime support for parameterized protocol types is only available in}} // expected-note@-1 {{add @available attribute to enclosing global function}} func optionalWrap(_ x: any P) -> Wrapper<(any P)?> { return optionalWrap(x) } // expected-error {{runtime support for parameterized protocol types is only available in}} @@ -75,7 +98,7 @@ struct ParameterizedMembers { // expected-note {{add @available attribute to enc var broken: Wrapper<(any P)?> // expected-error {{runtime support for parameterized protocol types is only available in}} } -func casts() { // expected-note 5 {{add @available attribute to enclosing global function}} +func casts() { // expected-note 4 {{add @available attribute to enclosing global function}} struct Value: P { typealias T = String } let _ = Value() as any P // OK @@ -96,8 +119,8 @@ func casts() { // expected-note 5 {{add @available attribute to enclosing global let _ = Value() as! (any P, Int) // expected-warning@-1 {{cast from 'Value' to unrelated type '(any P, Int)' always fails}} - // expected-error@-2 2 {{runtime support for parameterized protocol types is only available in}} - // expected-note@-3 2 {{add 'if #available' version check}} + // expected-error@-2 1 {{runtime support for parameterized protocol types is only available in}} + // expected-note@-3 1 {{add 'if #available' version check}} } // FIXME: It's a little aggressive to also ban metatypes. @@ -110,3 +133,36 @@ func metatypes(_ x: T.Type) { // expected-note 2 {{add @available attribute // expected-error@-1 {{runtime support for parameterized protocol types is only available in}} // expected-note@-2 {{add 'if #available' version check}} } + +func tupleConversion1(_ tuple: (any P, Int)) { + let converted: (any P, Int?) = tuple + _ = converted +} +func tupleConversion2(_ tuple: (any P, Int)) { + let converted: (Any, Int?) = tuple + _ = converted +} +func tupleConversion3(_ tuple: ((any P)?, Int)) { + // expected-note @-1 {{add @available attribute to enclosing global function}} + + let converted: (Any, Int?) = tuple // expected-error {{runtime support for parameterized protocol types is only available in}} + // expected-note @-1 {{add 'if #available' version check}} + + // expected-warning @-3 {{expression implicitly coerced from '(any P)?' to 'Any'}} + // expected-note @-4 {{explicitly cast to 'Any'}} + // expected-note @-5 {{force-unwrap the value}} + // expected-note @-6 {{provide a default value}} + + _ = converted +} + +func tupleCovariantConversion1(fn: @escaping () -> (any P, Int)) -> (() -> (Any, Int)) { + return fn +} +func tupleCovariantConversion2(fn: @escaping () -> ((any P)?, Int)) -> (() -> (Any, Int)) { + // expected-note @-1 {{add @available attribute to enclosing global function}} + + return fn // expected-error {{runtime support for parameterized protocol types is only available in}} + // expected-note @-1 {{add 'if #available' version check}} + +} \ No newline at end of file diff --git a/validation-test/IRGen/98995607.swift.gyb b/validation-test/IRGen/98995607.swift.gyb index 6dcaa12ee5fd1..a6223d05a0775 100644 --- a/validation-test/IRGen/98995607.swift.gyb +++ b/validation-test/IRGen/98995607.swift.gyb @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swiftgyb +// RUN: %target-run-simple-swiftgyb(-Xfrontend -disable-availability-checking) // REQUIRES: executable_test