Skip to content

Effects: Treat #selector as covering throws/async effects in its operand #81853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions lib/Sema/TypeCheckEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ class EffectsHandlingWalker : public ASTWalker {
}
} else if (auto TE = dyn_cast<MakeTemporarilyEscapableExpr>(E)) {
recurse = asImpl().checkTemporarilyEscapable(TE);
} else if (auto OSE = dyn_cast<ObjCSelectorExpr>(E)) {
recurse = asImpl().checkObjCSelector(OSE);
}

// Error handling validation (via checkTopLevelEffects) happens after
Expand Down Expand Up @@ -2269,6 +2271,10 @@ class ApplyClassifier {
return ShouldRecurse;
}

ShouldRecurse_t checkObjCSelector(ObjCSelectorExpr *E) {
return ShouldNotRecurse;
}

ConditionalEffectKind checkExhaustiveDoBody(DoCatchStmt *S) {
// All errors thrown by the do body are caught, but any errors thrown
// by the catch bodies are bounded by the throwing kind of the do body.
Expand Down Expand Up @@ -2418,6 +2424,10 @@ class ApplyClassifier {
return ShouldRecurse;
}

ShouldRecurse_t checkObjCSelector(ObjCSelectorExpr *E) {
return ShouldNotRecurse;
}

void visitExprPre(Expr *expr) { return; }
};

Expand Down Expand Up @@ -2535,6 +2545,10 @@ class ApplyClassifier {
return ShouldRecurse;
}

ShouldRecurse_t checkObjCSelector(ObjCSelectorExpr *E) {
return ShouldNotRecurse;
}

void visitExprPre(Expr *expr) { return; }
};

Expand Down Expand Up @@ -3716,6 +3730,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
Self.Flags.set(ContextFlags::InAsyncLet);
}

/// Enter a subexpression that's never exected.
void enterNonExecuting() {
Self.Flags.set(ContextFlags::IsTryCovered);
Self.Flags.set(ContextFlags::IsAsyncCovered);
}

void refineLocalContext(Context newContext) {
Self.CurContext = newContext;
}
Expand Down Expand Up @@ -3825,6 +3845,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
OldMaxThrowingKind = std::max(OldMaxThrowingKind, Self.MaxThrowingKind);
}

void preserveCoverageFromNonExecutingOperand() {
OldFlags.mergeFrom(ContextFlags::asyncAwaitFlags(), Self.Flags);
OldFlags.mergeFrom(ContextFlags::throwFlags(), Self.Flags);
OldFlags.mergeFrom(ContextFlags::unsafeFlags(), Self.Flags);
OldMaxThrowingKind = std::max(OldMaxThrowingKind, Self.MaxThrowingKind);
}

void preserveCoverageFromOptionalOrForcedTryOperand() {
OldFlags.mergeFrom(ContextFlags::asyncAwaitFlags(), Self.Flags);
OldFlags.mergeFrom(ContextFlags::unsafeFlags(), Self.Flags);
Expand Down Expand Up @@ -4026,6 +4053,17 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
return ShouldRecurse;
}

ShouldRecurse_t checkObjCSelector(ObjCSelectorExpr *E) {
// Walk the operand.
ContextScope scope(*this, std::nullopt);
scope.enterNonExecuting();

E->getSubExpr()->walk(*this);

scope.preserveCoverageFromNonExecutingOperand();
return ShouldNotRecurse;
}

ConditionalEffectKind checkExhaustiveDoBody(DoCatchStmt *S) {
// This is a context where errors are handled.
ContextScope scope(*this, CurContext.withHandlesErrors());
Expand Down
8 changes: 8 additions & 0 deletions test/expr/primary/selector/selector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,11 @@ extension SomeProtocol {
func test() -> Selector {
#selector(OverloadedFuncAndProperty.f)
}

@objc protocol HasThrows {
@objc optional func doSomething(to object: AnyObject) throws -> Void
}

func testWithThrowing(obj: AnyObject) {
_ = #selector(HasThrows.doSomething(to:))
}