This is an archive of the discontinued LLVM Phabricator instance.

[CodeGen] Emit destructor calls to destruct compound literals
ClosedPublic

Authored by ahatanak on Jul 9 2019, 6:40 PM.

Details

Summary

This patch fixes a bug in IRGen where it wasn't calling the destructors to destruct compound literals.

I've split out the changes needed to emit destructor calls for non-trivial C structs returned by function calls and loaded from volatile objects (https://reviews.llvm.org/D66094).

rdar://problem/51867864

Diff Detail

Repository
rC Clang

Event Timeline

ahatanak created this revision.Jul 9 2019, 6:40 PM
ahatanak marked an inline comment as done.Jul 9 2019, 6:42 PM
ahatanak added inline comments.
lib/CodeGen/CGExprAgg.cpp
248

This change wasn't needed to fix the bugs, but I think isExternallyDestructed instead of isIgnored should be called to determine whether the returned value requires destruction.

rjmccall added inline comments.Jul 9 2019, 10:48 PM
lib/CodeGen/CGExpr.cpp
4100

Unfortunately, the lifetime of compound literals in C is not this simple; they're like blocks in that they're destroyed at the end of the enclosing scope rather than at the end of the current statement. (The cleanup here will be popped at the end of the full-expression if we've entered an ExprWithCleanups.) And the l-value case is exactly the case where this matters.

I think you need to do something like what we do with blocks, where we record all the blocks in the full-expression on the ExprWithCleanups so that we can push an inactive cleanup for them and then activate it when we emit the block.

lib/CodeGen/CGExprAgg.cpp
248

Agreed.

668

The cleanup shouldn't be pushed until after you've finished evaluating the initializer.

ahatanak updated this revision to Diff 212942.Aug 1 2019, 5:54 PM
ahatanak marked 2 inline comments as done.
  • Emit member access, compound literal, and call expressions as subexpressions of ExprWithCleanups if the expressions are of C struct types that require non-trivial destruction. This fixes the bug in IRGen where it was destructing the function return at the end of the enclosing scope rather than at the end of the full expression (see the changes made in test/CodeGenObjC/arc.m).
  • Add compound literal expressions with automatic storage duration to the list of cleanup objects of ExprWithCleanups if the expressions have C struct types requiring non-trivial destruction. This enables IRGen to destruct the compound literals at the end of their enclosing scopes.
rjmccall added inline comments.Aug 2 2019, 8:45 AM
include/clang/AST/ExprCXX.h
3220 ↗(On Diff #212942)

Might be worth spelling out and block-scoped compound literals here.

include/clang/Basic/DiagnosticSemaKinds.td
5171 ↗(On Diff #212942)

Ah, good catch.

include/clang/Sema/Sema.h
587 ↗(On Diff #212942)

I think the second sentence in this comment is no longer useful; you can just strike it.

lib/CodeGen/CGBlocks.cpp
863 ↗(On Diff #212942)

This comment is no longer accurate. If you want to move the function, please do so in a separate commit, though.

lib/CodeGen/CGCleanup.cpp
1283 ↗(On Diff #212942)

Please include in the comment here that this doesn't apply in C++, where the compound literal has temporary lifetime.

Can we clarify that we're talking about block-scope literals in all the new method names?

lib/CodeGen/CGDecl.cpp
555 ↗(On Diff #212942)

It'd be nice to not shadow the enclosing variable.

lib/CodeGen/CGExpr.cpp
4100

Can we make the check here something like (1) this is a block-scope compound literal and (2) it has a non-trivially-destructed type (of any kind)? That way we're not conflating two potentially unrelated elements, the lifetime of the object and the kinds of types that can be constructed by the literal.

Oh, actually, there's a concrete reason to do this: C99 compound literals are not required to have struct type; they can have any object type, including arrays but also scalars. So we could, even without non-trivial C structs, have a block-scope compound of type __strong id[]; I guess we've always just gotten this wrong. Please add tests for this case. :)

4585

Does EmitCallExpr not enter a cleanup when it returns an aggregate that's not into an externally-destructed slot? That seems wrong and dangerous.

ahatanak marked an inline comment as done.Aug 12 2019, 10:00 AM
ahatanak added inline comments.
lib/CodeGen/CGExpr.cpp
4585

I'm going to split this patch into two parts, one for compound literals and the other for everything else. The patch is getting too large and I also found another bug: a cleanup isn't pushed for ObjC message send.

martong added inline comments.Aug 16 2019, 4:58 AM
test/Import/objc-arc/Inputs/cleanup-objects.m
6 ↗(On Diff #212942)

Perhaps then this patch depends on another patch which implements the import of a BlockExpr?
Or maybe the branch if (auto *BD = From.dyn_cast<BlockDecl *>()) should be left out from the ASTImporter code, and this way BlockExpr and BlockDecl would be implemented later in another patch.

ahatanak updated this revision to Diff 247919.Mar 3 2020, 9:01 AM
ahatanak marked 5 inline comments as done.
ahatanak retitled this revision from [CodeGen] Emit destructor calls for non-trivial C structs to [CodeGen] Emit destructor calls to destruct compound literals.
ahatanak edited the summary of this revision. (Show Details)

Address review comments:

  • Destruct compound literals of ObjC array types too.
  • Use pushLifetimeExtendedDestroy to extend the lifetime of compound literals to the end of the surrounding block. This simplifies the changes made in IRGen, but I was wondering whether we should do what we do to extend lifetime of block captures instead.
ahatanak added inline comments.Mar 3 2020, 9:02 AM
test/Import/objc-arc/Inputs/cleanup-objects.m
6 ↗(On Diff #212942)

Yes, I removed it from ASTImporter::Import and left a FIXME comment there.

ahatanak marked an inline comment as done.Mar 3 2020, 9:06 AM
ahatanak added inline comments.
lib/CodeGen/CGExpr.cpp
4100

There is a check E->isFileScope() above this. Is that sufficient to check for block-scoped compound literals?

martong added inline comments.Mar 5 2020, 8:25 AM
clang/lib/AST/ASTImporter.cpp
7918 ↗(On Diff #247919)

I think

return make_error<ImportError(ImportError::UnsupportedConstruct);

would be more appropriate here as this expresses that we do not support every aspect yet.

On the other hand a nullptr could be dereferenced later and thus could cause an unwanted crash.

ahatanak updated this revision to Diff 248586.Mar 5 2020, 1:11 PM
ahatanak marked an inline comment as done.

Return make_error<ImportError> instead of a nullptr.

rjmccall added inline comments.Mar 5 2020, 3:59 PM
lib/CodeGen/CGExpr.cpp
4100

That plus the C/C++ difference; compound literals in C++ are just temporaries.

This patch looks good except for that C/C++ semantic difference. A compound literal temporary can be lifetime-extended in C++, but only in the standard way of binding a reference to it.

ahatanak marked an inline comment as done.Mar 5 2020, 5:04 PM
ahatanak added inline comments.
lib/CodeGen/CGExpr.cpp
4100

I haven't been able to come up with a piece of C++ code that executes EmitCompoundLiteralLValue. The following code gets rejected because you can't take the address of a temporary object in C++:

StrongSmall *p = &(StrongSmall){ 1, 0 };

If a bind a reference to it, AggExprEmitter::VisitCompoundLiteralExpr is called.

rjmccall added inline comments.Mar 5 2020, 5:44 PM
lib/CodeGen/CGExpr.cpp
4100

That makes sense; they're not gl-values in C++. It would be reasonable to assert that. But the C++ point does apply elsewhere.

ahatanak updated this revision to Diff 248797.Mar 6 2020, 11:21 AM

Don't try to push a cleanup in C++.

ahatanak marked an inline comment as done.Mar 6 2020, 11:23 AM
ahatanak added inline comments.
lib/CodeGen/CGExpr.cpp
4100

It turns out this function is called in C++ when the compound literal is a vector type, so I've just added a check for C++ instead of an assert.

rjmccall added inline comments.Mar 7 2020, 1:02 PM
clang/lib/CodeGen/CGBlocks.cpp
869 ↗(On Diff #248797)

I wonder if we could just switch blocks to the same thing.

clang/lib/Sema/SemaExpr.cpp
6271 ↗(On Diff #248797)

This should all be conditional on C++ (but you should leave a comment explaining why).

clang/lib/Serialization/ASTWriterStmt.cpp
1729 ↗(On Diff #248797)

Will this just serialize a second copy of the compound literal expression?

lib/CodeGen/CGExpr.cpp
4100

Really? Is the expression actually an l-value in this case somehow?

martong resigned from this revision.Mar 9 2020, 3:28 AM

Looks good from the ASTImporter's point of view. I don't have the competence to review the rest, so I am resigning.

ahatanak updated this revision to Diff 249173.Mar 9 2020, 11:21 AM
ahatanak marked 4 inline comments as done.

Address review comments

ahatanak marked an inline comment as done.Mar 9 2020, 11:24 AM
ahatanak added inline comments.
clang/lib/CodeGen/CGBlocks.cpp
869 ↗(On Diff #248797)

I think we can, but I haven't tried. We can fix it in a separate patch.

clang/lib/CodeGen/CGExprAgg.cpp
664 ↗(On Diff #249173)

I fixed the way IsExternallyDestructed is used based on the comment in the other review.

clang/lib/Serialization/ASTWriterStmt.cpp
1729 ↗(On Diff #248797)

This should emit a serialization::STMT_REF_PTR record, which is a reference to the previously serialized compound literal (see ASTWriter::WriteSubStmt).

lib/CodeGen/CGExpr.cpp
4100

I see this function being called when ScalarExprEmitter::VisitCompoundLiteralExpr calls EmitLoadOfLValue.

rjmccall accepted this revision.Mar 9 2020, 12:55 PM

Minor comments; otherwise LGTM.

clang/lib/Sema/SemaExpr.cpp
6256 ↗(On Diff #249173)

Probably add "in C; in C++, they're just temporaries".

lib/CodeGen/CGExpr.cpp
4100

Okay. As a general rule, nothing should be calling EmitLValue on an expression that isn't actually an l-value. It's a little more reasonable here because it's more like a helper routine. If this is the cleanest way to handle this in C++, it's okay, but please leave a comment explaining that here.

This revision is now accepted and ready to land.Mar 9 2020, 12:55 PM
ahatanak marked 3 inline comments as done.Mar 10 2020, 2:10 PM
ahatanak added inline comments.
lib/CodeGen/CGExpr.cpp
4100

I added a comment in ScalarExprEmitter::VisitCompoundLiteralExpr.

This revision was automatically updated to reflect the committed changes.
ahatanak marked an inline comment as done.