This is an archive of the discontinued LLVM Phabricator instance.

[Sema][ObjC] Invalidate BlockDecl with invalid return expr & its parent BlockExpr
ClosedPublic

Authored by danix800 on Jul 16 2023, 2:24 AM.

Details

Summary

Invalidate BlockDecl with implicit return type, in case any of the return value exprs is invalid.
Propagating the error info up by replacing BlockExpr with a RecoveryExpr.

(The idea of this fix is given by @hokein(Haojian Wu))

Fix https://github.com/llvm/llvm-project/issues/63863.

Diff Detail

Event Timeline

danix800 created this revision.Jul 16 2023, 2:24 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 16 2023, 2:24 AM
danix800 requested review of this revision.Jul 16 2023, 2:24 AM
aaron.ballman added a subscriber: rjmccall.
aaron.ballman added inline comments.
clang/include/clang/AST/ComputeDependence.h
134 ↗(On Diff #540769)

This doesn't seem like the correct fix, to me -- I would have expected the expression itself to track whether it contains errors, so you wouldn't need to pass in extra information about that.

clang/lib/Sema/SemaExpr.cpp
16749–16753

Hmmm -- is the block *expression* what contains the errors in this case or is it the block declaration? I would have expected this to be an issue for the block declaration created for the block expression. CC @rjmccall

clang/lib/Sema/SemaExpr.cpp
16749–16753

I think a reasonable model is to follow how we handle FunctionDecl, as BlockDecl and FunctionDecl are similar function-decl concepts.

For the crash case like int (^a)() = ^() { return undefined; }, we should:

  • invalidate the BlockDecl as its returned type can not be deduced because of the error return stmt (similar to FunctionDecl, we invalidate it for auto func() { return undefined; })
  • for an invalid BlockDecl, we should not build a BlockExpr that refers to it (we don't build DeclRefExpr for invalid FunctionDecl). For error recovery, we should use RecoveryExpr.

So I think the reasonable fix is to invalidate the BlockDecl (calling Decl->setInvalidDecl()) if its body has any error stmt, and return ExprError() if the BlockDecl is invalid.

danix800 added inline comments.Jul 20 2023, 9:08 AM
clang/lib/Sema/SemaExpr.cpp
16749–16753

Thanks for sharing and pointing out the right direction.

danix800 updated this revision to Diff 542559.Jul 20 2023, 9:17 AM
danix800 edited the summary of this revision. (Show Details)

Invalidate BlockDecl with implicit return type, in case any of the return value exprs is invalid.
Propagating the error info up by replacing BlockExpr with a RecoveryExpr.

This looks pretty good to me. Can you also add a release note for the fix to clang/docs/ReleaseNotes.rst?

clang/lib/Sema/SemaStmt.cpp
3733–3734
if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap); CurBlock && CurCap->HasImplicitReturnType) {
danix800 updated this revision to Diff 542735.Jul 20 2023, 6:16 PM
danix800 edited the summary of this revision. (Show Details)

Update clang/docs/ReleaseNotes.rst to reflect this fix.

danix800 marked 2 inline comments as done.Jul 20 2023, 6:17 PM

thanks!

clang/lib/Sema/SemaStmt.cpp
3736

nit: the isInvalidDecl check is not need, we can inline the RetValExpr to the above if as well.

if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap); CurBlock && CurCap->HasImplicitReturnType && RetValExp && RetValExp->containsErrors()) 
  CurBlock->TheDecl->setInvalidDecl();
clang/test/SemaObjC/crash-on-val-dep-block-expr.m
2

can you move these tests to llvm-project/clang/test/AST/ast-dump-recovery.c?

danix800 added inline comments.Jul 21 2023, 8:16 AM
clang/lib/Sema/SemaStmt.cpp
3736

Nice, look more cleaner!

clang/test/SemaObjC/crash-on-val-dep-block-expr.m
2

No problem. But I noticed llvm-project/clang/test/AST/ast-dump-recovery.m also exists,
should I put them into this .m file instead?

danix800 added inline comments.Jul 21 2023, 8:27 AM
clang/test/SemaObjC/crash-on-val-dep-block-expr.m
2

I'd prefer ast-dump-recovery.m since this file is much shorter. Either file needs appending -fblocks.

danix800 updated this revision to Diff 542950.EditedJul 21 2023, 8:46 AM
  1. Remove unnecessary condition & cleanup code
  2. Move testcase into correct place. Extraneous parenthesized blockexpr testcase is removed since errors can propagate between exprs normally.
danix800 updated this revision to Diff 543010.Jul 21 2023, 11:30 AM
danix800 retitled this revision from [Sema][ObjC] Propagating value-dependent errors into BlockExpr to [Sema][ObjC] Invalidate BlockDecl with invalid return expr & its parent BlockExpr.

Fix testcase tag with CHECK-NEXT.

Dropping the entire block from the AST on error would be unfortunate for some kinds of tooling, but as long as we maintain it with a RecoveryExpr, that seems like a fine approach to me.

As a general rule, I think we should be tracking basically everything on the BlockDecl and let BlockExpr just have a reference to that on top of the baseline Expr storage.

hokein accepted this revision.Jul 24 2023, 2:37 AM

thanks, looks good to me.

clang/test/AST/ast-dump-recovery.m
24 ↗(On Diff #543010)

nit: it'd be nice to encode the github issue number to the testcase here, for example (renaming the a to gh63863 etc).

25 ↗(On Diff #543010)

nit: use a more descriptive name return undef;

This revision is now accepted and ready to land.Jul 24 2023, 2:37 AM
danix800 added inline comments.Jul 24 2023, 2:47 AM
clang/test/AST/ast-dump-recovery.m
24 ↗(On Diff #543010)

Will be fixed in the final commit!

This revision was landed with ongoing or failed builds.Jul 24 2023, 3:25 AM
This revision was automatically updated to reflect the committed changes.