@@ -2842,6 +2842,22 @@ class ExprAvailabilityWalker : public ASTWalker {
28422842 E->getLoc (), Where);
28432843 }
28442844
2845+ if (AbstractClosureExpr *closure = dyn_cast<AbstractClosureExpr>(E)) {
2846+ // Multi-statement closures are collected by ExprWalker::rewriteFunction
2847+ // and checked by ExprWalker::processDelayed in CSApply.cpp.
2848+ // Single-statement closures only have the attributes checked
2849+ // by TypeChecker::checkClosureAttributes in that rewriteFunction.
2850+ // Multi-statement closures will be checked explicitly later (as the decl
2851+ // context in the Where). Single-expression closures will not be
2852+ // revisited, and are not automatically set as the context of the 'where'.
2853+ // Don't double-check multi-statement closures, but do check
2854+ // single-statement closures, setting the closure as the decl context.
2855+ if (closure->hasSingleExpressionBody ()) {
2856+ walkAbstractClosure (closure);
2857+ return skipChildren ();
2858+ }
2859+ }
2860+
28452861 return visitChildren ();
28462862 }
28472863
@@ -2853,13 +2869,13 @@ class ExprAvailabilityWalker : public ASTWalker {
28532869 }
28542870
28552871 std::pair<bool , Stmt *> walkToStmtPre (Stmt *S) override {
2872+
28562873 // We end up here when checking the output of the result builder transform,
28572874 // which includes closures that are not "separately typechecked" and yet
28582875 // contain statements and declarations. We need to walk them recursively,
28592876 // since these availability for these statements is not diagnosed from
28602877 // typeCheckStmt() as usual.
2861- diagnoseStmtAvailability (S, Where.getDeclContext (),
2862- /* walkRecursively=*/ true );
2878+ diagnoseStmtAvailability (S, Where.getDeclContext (), /* walkRecursively=*/ true );
28632879 return std::make_pair (false , S);
28642880 }
28652881
@@ -2978,6 +2994,21 @@ class ExprAvailabilityWalker : public ASTWalker {
29782994 walkInContext (E, E->getSubExpr (), MemberAccessContext::InOut);
29792995 }
29802996
2997+ // / Walk an abstract closure expression, checking for availability
2998+ void walkAbstractClosure (AbstractClosureExpr *closure) {
2999+ // Do the walk with the closure set as the decl context of the 'where'
3000+ auto where = ExportContext::forFunctionBody (closure, closure->getStartLoc ());
3001+ if (where.isImplicit ())
3002+ return ;
3003+ ExprAvailabilityWalker walker (where);
3004+
3005+ // Manually dive into the body
3006+ closure->getBody ()->walk (walker);
3007+
3008+ return ;
3009+ }
3010+
3011+
29813012 // / Walk the given expression in the member access context.
29823013 void walkInContext (Expr *baseExpr, Expr *E,
29833014 MemberAccessContext AccessContext) {
@@ -3038,8 +3069,8 @@ class ExprAvailabilityWalker : public ASTWalker {
30383069
30393070 Flags &= DeclAvailabilityFlag::ForInout;
30403071 Flags |= DeclAvailabilityFlag::ContinueOnPotentialUnavailability;
3041- if (diagnoseDeclAvailability (D, ReferenceRange, /* call*/ nullptr ,
3042- Where, Flags))
3072+ if (diagnoseDeclAvailability (D, ReferenceRange, /* call*/ nullptr , Where ,
3073+ Flags))
30433074 return ;
30443075 }
30453076};
@@ -3074,6 +3105,56 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30743105 return false ;
30753106}
30763107
3108+ // / Diagnose uses of API annotated '@unavailableFromAsync' when used from
3109+ // / asynchronous contexts.
3110+ // / Returns true if a diagnostic was emitted, false otherwise.
3111+ static bool
3112+ diagnoseDeclUnavailableFromAsync (const ValueDecl *D, SourceRange R,
3113+ const Expr *call, const ExportContext &Where) {
3114+ // FIXME: I don't think this is right, but I don't understand the issue well
3115+ // enough to fix it properly. If the decl context is an abstract
3116+ // closure, we need it to have a type assigned to it before we can
3117+ // determine whether it is an asynchronous context. It will crash
3118+ // when we go to check without one. In TypeChecker::typeCheckExpression
3119+ // (TypeCheckConstraints.cpp:403), we apply a solution before calling
3120+ // `performSyntacticDiagnosticsForTarget`, which eventually calls
3121+ // down to this function. Under most circumstances, the context that
3122+ // we're in is typechecked at that point and has a type assigned.
3123+ // When working with specific result builders, the solution applied
3124+ // results in an expression with an unset type. In these cases, the
3125+ // application makes its way into `ConstraintSystem::applySolution` for
3126+ // closures (CSClosure.cpp:1356). The type is computed, but is
3127+ // squirreled away in the constrain system to be applied once the
3128+ // checks (including this one) approve of the decls within the decl
3129+ // context before applying the type to the expression. It might be
3130+ // possible to drive the constraint solver through the availability
3131+ // checker and into us so that we can ask for it, but that feels wrong
3132+ // too.
3133+ // This behavior is demonstrated by the first use of the `tuplify`
3134+ // function in `testExistingPatternsInCaseStatements` in
3135+ // `test/Constraints/result_builder.swift`.
3136+ const AbstractClosureExpr *declCtxAsExpr =
3137+ dyn_cast<AbstractClosureExpr>(Where.getDeclContext ());
3138+ if (declCtxAsExpr && !declCtxAsExpr->getType ()) {
3139+ return false ;
3140+ }
3141+
3142+ // If we are in a synchronous context, don't check it
3143+ if (!Where.getDeclContext ()->isAsyncContext ())
3144+ return false ;
3145+ if (!D->getAttrs ().hasAttribute <UnavailableFromAsyncAttr>())
3146+ return false ;
3147+
3148+ ASTContext &ctx = Where.getDeclContext ()->getASTContext ();
3149+ SourceLoc diagLoc = call ? call->getLoc () : R.Start ;
3150+ ctx.Diags
3151+ .diagnose (diagLoc, diag::async_unavailable_decl, D->getDescriptiveKind (),
3152+ D->getBaseName ())
3153+ .warnUntilSwiftVersion (6 );
3154+ D->diagnose (diag::decl_declared_here, D->getName ());
3155+ return true ;
3156+ }
3157+
30773158// / Diagnose uses of unavailable declarations. Returns true if a diagnostic
30783159// / was emitted.
30793160bool swift::diagnoseDeclAvailability (const ValueDecl *D, SourceRange R,
@@ -3107,6 +3188,9 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
31073188 if (diagnoseExplicitUnavailability (D, R, Where, call, Flags))
31083189 return true ;
31093190
3191+ if (diagnoseDeclUnavailableFromAsync (D, R, call, Where))
3192+ return true ;
3193+
31103194 // Make sure not to diagnose an accessor's deprecation if we already
31113195 // complained about the property/subscript.
31123196 bool isAccessorWithDeprecatedStorage =
0 commit comments