@@ -1018,6 +1018,16 @@ class Classification {
10181018
10191019 return result;
10201020 }
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+
10211031 // / Return a classification that promotes a typed throws effect to an
10221032 // / untyped throws effect.
10231033 Classification promoteToUntypedThrows () const {
@@ -3149,6 +3159,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
31493159 llvm::DenseMap<Expr *, std::vector<DiagnosticInfo>> uncoveredAsync;
31503160 llvm::DenseMap<Expr *, Expr *> parentMap;
31513161
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+
31523166 // / Tracks all of the uncovered uses of unsafe constructs based on their
31533167 // / anchor expression, so we can emit diagnostics at the end.
31543168 llvm::MapVector<Expr *, std::vector<UnsafeUse>> uncoveredUnsafeUses;
@@ -3526,6 +3540,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
35263540 ShouldRecurse_t checkWithSubstitutionMap (Expr *E, SubstitutionMap subs) {
35273541 auto classification = Classification::forSubstitutionMap (
35283542 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+
35293549 checkEffectSite (E, /* requiresTry=*/ false , classification);
35303550 return ShouldRecurse;
35313551 }
@@ -3535,13 +3555,25 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
35353555 ArrayRef<ProtocolConformanceRef> conformances) {
35363556 auto classification = Classification::forConformances (
35373557 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+
35383564 checkEffectSite (E, /* requiresTry=*/ false , classification);
35393565 return ShouldRecurse;
35403566 }
35413567
35423568 ShouldRecurse_t checkType (Expr *E, TypeRepr *typeRepr, Type type) {
35433569 SourceLoc loc = typeRepr ? typeRepr->getLoc () : E->getLoc ();
35443570 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+
35453577 checkEffectSite (E, /* requiresTry=*/ false , classification);
35463578 return ShouldRecurse;
35473579 }
@@ -3646,6 +3678,36 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
36463678 CurContext = savedContext;
36473679 }
36483680
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+
36493711 ShouldRecurse_t checkApply (ApplyExpr *E) {
36503712 // An apply expression is a potential throw site if the function throws.
36513713 // But if the expression didn't type-check, suppress diagnostics.
@@ -3673,6 +3735,14 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
36733735 E->setNoAsync (true );
36743736 }
36753737
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+
36763746 // If current apply expression did not type-check, don't attempt
36773747 // walking inside of it. This accounts for the fact that we don't
36783748 // erase types without type variables to enable better code complication,
@@ -3698,6 +3768,22 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
36983768 E->setThrows (throwDest);
36993769 }
37003770
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+
37013787 return ShouldRecurse;
37023788 }
37033789
@@ -3712,6 +3798,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
37123798 if (!isEvaluated)
37133799 classification = classification.onlyUnsafe ();
37143800
3801+ // If we're treating this expression as a safe argument, drop 'unsafe'.
3802+ if (assumedSafeArguments.contains (expr))
3803+ classification = classification.withoutUnsafe ();
3804+
37153805 auto throwDest = checkEffectSite (
37163806 expr, classification.hasThrows (), classification);
37173807
0 commit comments