@@ -2767,6 +2767,7 @@ class ExprAvailabilityWalker : public ASTWalker {
27672767 ASTContext &Context;
27682768 MemberAccessContext AccessContext = MemberAccessContext::Getter;
27692769 SmallVector<const Expr *, 16 > ExprStack;
2770+ SmallVector<ClosureExpr *, 4 > singleExprClosureStack;
27702771 const ExportContext &Where;
27712772
27722773public:
@@ -2847,24 +2848,47 @@ class ExprAvailabilityWalker : public ASTWalker {
28472848 E->getLoc (), Where);
28482849 }
28492850
2851+ // multi-statement closures are collected by ExprWalker::rewriteFunction
2852+ // and checked by ExprWalker::processDelayed in CSApply.cpp.
2853+ // Single-statement closures only have the attributes checked
2854+ // by TypeChecker::checkClosureAttributes in that rewriteFunction.
2855+ // As a result, we never see single-statement closures as the decl context
2856+ // in out 'where' here. By keeping a stack of closures, we can see if
2857+ // we're inside of one of these closures and go from there.
2858+ if (ClosureExpr *closure = dyn_cast<ClosureExpr>(E)) {
2859+ if (closure->hasSingleExpressionBody ())
2860+ singleExprClosureStack.push_back (closure);
2861+ }
2862+
28502863 return visitChildren ();
28512864 }
28522865
28532866 Expr *walkToExprPost (Expr *E) override {
28542867 assert (ExprStack.back () == E);
28552868 ExprStack.pop_back ();
28562869
2870+ if (ClosureExpr *closure = dyn_cast<ClosureExpr>(E)) {
2871+ if (closure->hasSingleExpressionBody ()) {
2872+ assert (closure == singleExprClosureStack.back () &&
2873+ " Popping wrong closure" );
2874+ singleExprClosureStack.pop_back ();
2875+ }
2876+ }
2877+
28572878 return E;
28582879 }
28592880
28602881 std::pair<bool , Stmt *> walkToStmtPre (Stmt *S) override {
2882+
28612883 // We end up here when checking the output of the result builder transform,
28622884 // which includes closures that are not "separately typechecked" and yet
28632885 // contain statements and declarations. We need to walk them recursively,
28642886 // since these availability for these statements is not diagnosed from
28652887 // typeCheckStmt() as usual.
2866- diagnoseStmtAvailability (S, Where.getDeclContext (),
2867- /* walkRecursively=*/ true );
2888+ DeclContext *dc = singleExprClosureStack.empty ()
2889+ ? Where.getDeclContext ()
2890+ : singleExprClosureStack.back ();
2891+ diagnoseStmtAvailability (S, dc, /* walkRecursively=*/ true );
28682892 return std::make_pair (false , S);
28692893 }
28702894
@@ -3043,8 +3067,8 @@ class ExprAvailabilityWalker : public ASTWalker {
30433067
30443068 Flags &= DeclAvailabilityFlag::ForInout;
30453069 Flags |= DeclAvailabilityFlag::ContinueOnPotentialUnavailability;
3046- if (diagnoseDeclAvailability (D, ReferenceRange, /* call*/ nullptr ,
3047- Where, Flags ))
3070+ if (diagnoseDeclAvailability (D, ReferenceRange, /* call*/ nullptr , Where ,
3071+ Flags, nullptr ))
30483072 return ;
30493073 }
30503074};
@@ -3067,7 +3091,9 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30673091 return true ;
30683092 }
30693093
3070- diagnoseDeclAvailability (D, R, call, Where, Flags);
3094+ const ClosureExpr *closure =
3095+ singleExprClosureStack.empty () ? nullptr : singleExprClosureStack.back ();
3096+ diagnoseDeclAvailability (D, R, call, Where, Flags, closure);
30713097
30723098 if (R.isValid ()) {
30733099 if (diagnoseSubstitutionMapAvailability (R.Start , declRef.getSubstitutions (),
@@ -3079,12 +3105,71 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30793105 return false ;
30803106}
30813107
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+ const ClosureExpr *singleExprClosure) {
3115+ // FIXME: I don't think this is right, but I don't understand the issue well
3116+ // enough to fix it properly. If the decl context is an abstract
3117+ // closure, we need it to have a type assigned to it before we can
3118+ // determine whether it is an asynchronous context. It will crash
3119+ // when we go to check without one. In TypeChecker::typeCheckExpression
3120+ // (TypeCheckConstraints.cpp:403), we apply a solution before calling
3121+ // `performSyntacticDiagnosticsForTarget`, which eventually calls
3122+ // down to this function. Under most circumstances, the context that
3123+ // we're in is typechecked at that point and has a type assigned.
3124+ // When working with specific result builders, the solution applied
3125+ // results in an expression with an unset type. In these cases, the
3126+ // application makes its way into `ConstraintSystem::applySolution` for
3127+ // closures (CSClosure.cpp:1356). The type is computed, but is
3128+ // squirreled away in the constrain system to be applied once the
3129+ // checks (including this one) approve of the decls within the decl
3130+ // context before applying the type to the expression. It might be
3131+ // possible to drive the constraint solver through the availability
3132+ // checker and into us so that we can ask for it, but that feels wrong
3133+ // too.
3134+ // This behavior is demonstrated by the first use of the `tuplify`
3135+ // function in `testExistingPatternsInCaseStatements` in
3136+ // `test/Constraints/result_builder.swift`.
3137+ const AbstractClosureExpr *declCtxAsExpr =
3138+ dyn_cast<AbstractClosureExpr>(Where.getDeclContext ());
3139+ if (declCtxAsExpr && !declCtxAsExpr->getType ()) {
3140+ return false ;
3141+ }
3142+
3143+ // If we're directly in a sync closure, then we are not in an async context.
3144+ // If we're directly in a sync closure, or we're not in a closure and the
3145+ // outer context is not async, we are in a sync context.
3146+ const bool inSingleClosure = singleExprClosure;
3147+ const bool inSyncClosure =
3148+ inSingleClosure && !singleExprClosure->isAsyncContext ();
3149+ const bool inSyncContext =
3150+ !inSingleClosure && !Where.getDeclContext ()->isAsyncContext ();
3151+ if (inSyncClosure || inSyncContext)
3152+ return false ;
3153+ if (!D->getAttrs ().hasAttribute <UnavailableFromAsyncAttr>())
3154+ return false ;
3155+
3156+ ASTContext &ctx = Where.getDeclContext ()->getASTContext ();
3157+ SourceLoc diagLoc = call ? call->getLoc () : R.Start ;
3158+ ctx.Diags
3159+ .diagnose (diagLoc, diag::async_unavailable_decl, D->getDescriptiveKind (),
3160+ D->getBaseName ())
3161+ .warnUntilSwiftVersion (6 );
3162+ D->diagnose (diag::decl_declared_here, D->getName ());
3163+ return true ;
3164+ }
3165+
30823166// / Diagnose uses of unavailable declarations. Returns true if a diagnostic
30833167// / was emitted.
30843168bool swift::diagnoseDeclAvailability (const ValueDecl *D, SourceRange R,
30853169 const Expr *call,
30863170 const ExportContext &Where,
3087- DeclAvailabilityFlags Flags) {
3171+ DeclAvailabilityFlags Flags,
3172+ const ClosureExpr *singleExprClosure) {
30883173 assert (!Where.isImplicit ());
30893174
30903175 // Generic parameters are always available.
@@ -3112,6 +3197,9 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
31123197 if (diagnoseExplicitUnavailability (D, R, Where, call, Flags))
31133198 return true ;
31143199
3200+ if (diagnoseDeclUnavailableFromAsync (D, R, call, Where, singleExprClosure))
3201+ return true ;
3202+
31153203 // Make sure not to diagnose an accessor's deprecation if we already
31163204 // complained about the property/subscript.
31173205 bool isAccessorWithDeprecatedStorage =
@@ -3360,7 +3448,8 @@ class TypeReprAvailabilityWalker : public ASTWalker {
33603448 bool checkComponentIdentTypeRepr (ComponentIdentTypeRepr *ITR) {
33613449 if (auto *typeDecl = ITR->getBoundDecl ()) {
33623450 auto range = ITR->getNameLoc ().getSourceRange ();
3363- if (diagnoseDeclAvailability (typeDecl, range, nullptr , where, flags))
3451+ if (diagnoseDeclAvailability (typeDecl, range, nullptr , where, flags,
3452+ nullptr ))
33643453 return true ;
33653454 }
33663455
0 commit comments