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 cf5c351449168..c0a32d3851b58 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)) @@ -60,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/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/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 ee0d94ef54b5f..969b53fd440db 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2984,36 +2984,171 @@ 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, - ReferenceDC->getASTContext().getParameterizedExistentialRuntimeAvailability(), + ReferenceDC->getASTContext().getParameterizedExistentialAvailability(), diag::availability_parameterized_protocol_only_version_newer, 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