@@ -1018,6 +1018,16 @@ class Classification {
1018
1018
1019
1019
return result;
1020
1020
}
1021
+
1022
+ // / Return a classification that is the same as this one, but drops the
1023
+ // / 'unsafe' effect.
1024
+ Classification withoutUnsafe () const {
1025
+ Classification result (*this );
1026
+ result.UnsafeKind = ConditionalEffectKind::None;
1027
+ result.UnsafeUses .clear ();
1028
+ return result;
1029
+ }
1030
+
1021
1031
// / Return a classification that promotes a typed throws effect to an
1022
1032
// / untyped throws effect.
1023
1033
Classification promoteToUntypedThrows () const {
@@ -3149,6 +3159,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3149
3159
llvm::DenseMap<Expr *, std::vector<DiagnosticInfo>> uncoveredAsync;
3150
3160
llvm::DenseMap<Expr *, Expr *> parentMap;
3151
3161
3162
+ // / Expressions that are assumed to be safe because they are being
3163
+ // / passed directly into an explicitly `@safe` function.
3164
+ llvm::DenseSet<const Expr *> assumedSafeArguments;
3165
+
3152
3166
// / Tracks all of the uncovered uses of unsafe constructs based on their
3153
3167
// / anchor expression, so we can emit diagnostics at the end.
3154
3168
llvm::MapVector<Expr *, std::vector<UnsafeUse>> uncoveredUnsafeUses;
@@ -3526,6 +3540,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3526
3540
ShouldRecurse_t checkWithSubstitutionMap (Expr *E, SubstitutionMap subs) {
3527
3541
auto classification = Classification::forSubstitutionMap (
3528
3542
subs, E->getLoc ());
3543
+
3544
+ // If this expression is covered as a safe argument, drop the unsafe
3545
+ // classification.
3546
+ if (assumedSafeArguments.contains (E))
3547
+ classification = classification.withoutUnsafe ();
3548
+
3529
3549
checkEffectSite (E, /* requiresTry=*/ false , classification);
3530
3550
return ShouldRecurse;
3531
3551
}
@@ -3535,13 +3555,25 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3535
3555
ArrayRef<ProtocolConformanceRef> conformances) {
3536
3556
auto classification = Classification::forConformances (
3537
3557
conformances, E->getLoc ());
3558
+
3559
+ // If this expression is covered as a safe argument, drop the unsafe
3560
+ // classification.
3561
+ if (assumedSafeArguments.contains (E))
3562
+ classification = classification.withoutUnsafe ();
3563
+
3538
3564
checkEffectSite (E, /* requiresTry=*/ false , classification);
3539
3565
return ShouldRecurse;
3540
3566
}
3541
3567
3542
3568
ShouldRecurse_t checkType (Expr *E, TypeRepr *typeRepr, Type type) {
3543
3569
SourceLoc loc = typeRepr ? typeRepr->getLoc () : E->getLoc ();
3544
3570
auto classification = Classification::forType (type, loc);
3571
+
3572
+ // If this expression is covered as a safe argument, drop the unsafe
3573
+ // classification.
3574
+ if (assumedSafeArguments.contains (E))
3575
+ classification = classification.withoutUnsafe ();
3576
+
3545
3577
checkEffectSite (E, /* requiresTry=*/ false , classification);
3546
3578
return ShouldRecurse;
3547
3579
}
@@ -3646,6 +3678,36 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3646
3678
CurContext = savedContext;
3647
3679
}
3648
3680
3681
+ // / Mark an argument as safe if it is of a form where a @safe declaration
3682
+ // / covers it.
3683
+ void markArgumentSafe (Expr *arg) {
3684
+ auto argValue = arg->findOriginalValue ();
3685
+
3686
+ // Reference to a local variable or parameter.
3687
+ if (auto argDRE = dyn_cast<DeclRefExpr>(argValue)) {
3688
+ if (auto argDecl = argDRE->getDecl ())
3689
+ if (argDecl->getDeclContext ()->isLocalContext ())
3690
+ assumedSafeArguments.insert (argDRE);
3691
+ }
3692
+
3693
+ // Metatype reference.
3694
+ if (isa<TypeExpr>(argValue))
3695
+ assumedSafeArguments.insert (argValue);
3696
+ }
3697
+
3698
+ // / Mark any of the local variable references within the given argument
3699
+ // / list as safe, so we don't diagnose unsafe uses of them.
3700
+ void markArgumentsSafe (ArgumentList *argumentList) {
3701
+ if (!argumentList)
3702
+ return ;
3703
+
3704
+ for (const auto &arg: *argumentList) {
3705
+ if (auto argExpr = arg.getExpr ()) {
3706
+ markArgumentSafe (argExpr);
3707
+ }
3708
+ }
3709
+ }
3710
+
3649
3711
ShouldRecurse_t checkApply (ApplyExpr *E) {
3650
3712
// An apply expression is a potential throw site if the function throws.
3651
3713
// But if the expression didn't type-check, suppress diagnostics.
@@ -3673,6 +3735,14 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3673
3735
E->setNoAsync (true );
3674
3736
}
3675
3737
3738
+ // If this is calling a @safe function, treat local variables as being
3739
+ // safe.
3740
+ if (auto calleeDecl = E->getCalledValue ()) {
3741
+ if (calleeDecl->getExplicitSafety () == ExplicitSafety::Safe) {
3742
+ markArgumentsSafe (E->getArgs ());
3743
+ }
3744
+ }
3745
+
3676
3746
// If current apply expression did not type-check, don't attempt
3677
3747
// walking inside of it. This accounts for the fact that we don't
3678
3748
// erase types without type variables to enable better code complication,
@@ -3698,6 +3768,22 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3698
3768
E->setThrows (throwDest);
3699
3769
}
3700
3770
3771
+ // If the declaration we're referencing is explicitly @safe, mark arguments
3772
+ // as potentially being safe.
3773
+ if (auto decl = E->getMember ().getDecl ()) {
3774
+ if (decl->getExplicitSafety () == ExplicitSafety::Safe) {
3775
+ // The base can be considered safe.
3776
+ markArgumentSafe (E->getBase ());
3777
+
3778
+ // Mark subscript arguments safe.
3779
+ if (auto subscript = dyn_cast<SubscriptExpr>(E)) {
3780
+ markArgumentsSafe (subscript->getArgs ());
3781
+ } else if (auto dynSubscript = dyn_cast<DynamicSubscriptExpr>(E)) {
3782
+ markArgumentsSafe (dynSubscript->getArgs ());
3783
+ }
3784
+ }
3785
+ }
3786
+
3701
3787
return ShouldRecurse;
3702
3788
}
3703
3789
@@ -3712,6 +3798,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
3712
3798
if (!isEvaluated)
3713
3799
classification = classification.onlyUnsafe ();
3714
3800
3801
+ // If we're treating this expression as a safe argument, drop 'unsafe'.
3802
+ if (assumedSafeArguments.contains (expr))
3803
+ classification = classification.withoutUnsafe ();
3804
+
3715
3805
auto throwDest = checkEffectSite (
3716
3806
expr, classification.hasThrows (), classification);
3717
3807
0 commit comments