@@ -2767,6 +2767,7 @@ class ExprAvailabilityWalker : public ASTWalker {
2767
2767
ASTContext &Context;
2768
2768
MemberAccessContext AccessContext = MemberAccessContext::Getter;
2769
2769
SmallVector<const Expr *, 16 > ExprStack;
2770
+ SmallVector<ClosureExpr *, 4 > singleExprClosureStack;
2770
2771
const ExportContext &Where;
2771
2772
2772
2773
public:
@@ -2847,24 +2848,47 @@ class ExprAvailabilityWalker : public ASTWalker {
2847
2848
E->getLoc (), Where);
2848
2849
}
2849
2850
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
+
2850
2863
return visitChildren ();
2851
2864
}
2852
2865
2853
2866
Expr *walkToExprPost (Expr *E) override {
2854
2867
assert (ExprStack.back () == E);
2855
2868
ExprStack.pop_back ();
2856
2869
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
+
2857
2878
return E;
2858
2879
}
2859
2880
2860
2881
std::pair<bool , Stmt *> walkToStmtPre (Stmt *S) override {
2882
+
2861
2883
// We end up here when checking the output of the result builder transform,
2862
2884
// which includes closures that are not "separately typechecked" and yet
2863
2885
// contain statements and declarations. We need to walk them recursively,
2864
2886
// since these availability for these statements is not diagnosed from
2865
2887
// 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 );
2868
2892
return std::make_pair (false , S);
2869
2893
}
2870
2894
@@ -3043,8 +3067,8 @@ class ExprAvailabilityWalker : public ASTWalker {
3043
3067
3044
3068
Flags &= DeclAvailabilityFlag::ForInout;
3045
3069
Flags |= DeclAvailabilityFlag::ContinueOnPotentialUnavailability;
3046
- if (diagnoseDeclAvailability (D, ReferenceRange, /* call*/ nullptr ,
3047
- Where, Flags ))
3070
+ if (diagnoseDeclAvailability (D, ReferenceRange, /* call*/ nullptr , Where ,
3071
+ Flags, nullptr ))
3048
3072
return ;
3049
3073
}
3050
3074
};
@@ -3067,7 +3091,9 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
3067
3091
return true ;
3068
3092
}
3069
3093
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);
3071
3097
3072
3098
if (R.isValid ()) {
3073
3099
if (diagnoseSubstitutionMapAvailability (R.Start , declRef.getSubstitutions (),
@@ -3079,12 +3105,71 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
3079
3105
return false ;
3080
3106
}
3081
3107
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
+
3082
3166
// / Diagnose uses of unavailable declarations. Returns true if a diagnostic
3083
3167
// / was emitted.
3084
3168
bool swift::diagnoseDeclAvailability (const ValueDecl *D, SourceRange R,
3085
3169
const Expr *call,
3086
3170
const ExportContext &Where,
3087
- DeclAvailabilityFlags Flags) {
3171
+ DeclAvailabilityFlags Flags,
3172
+ const ClosureExpr *singleExprClosure) {
3088
3173
assert (!Where.isImplicit ());
3089
3174
3090
3175
// Generic parameters are always available.
@@ -3112,6 +3197,9 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
3112
3197
if (diagnoseExplicitUnavailability (D, R, Where, call, Flags))
3113
3198
return true ;
3114
3199
3200
+ if (diagnoseDeclUnavailableFromAsync (D, R, call, Where, singleExprClosure))
3201
+ return true ;
3202
+
3115
3203
// Make sure not to diagnose an accessor's deprecation if we already
3116
3204
// complained about the property/subscript.
3117
3205
bool isAccessorWithDeprecatedStorage =
@@ -3360,7 +3448,8 @@ class TypeReprAvailabilityWalker : public ASTWalker {
3360
3448
bool checkComponentIdentTypeRepr (ComponentIdentTypeRepr *ITR) {
3361
3449
if (auto *typeDecl = ITR->getBoundDecl ()) {
3362
3450
auto range = ITR->getNameLoc ().getSourceRange ();
3363
- if (diagnoseDeclAvailability (typeDecl, range, nullptr , where, flags))
3451
+ if (diagnoseDeclAvailability (typeDecl, range, nullptr , where, flags,
3452
+ nullptr ))
3364
3453
return true ;
3365
3454
}
3366
3455
0 commit comments