/// 'const int &x = C().x;'.
bool IsTemporaryLifetimeExtendedViaSubobject = false;
+ /// This call is a constructor for a temporary that is lifetime-extended
+ /// by binding it to a reference-type field within an aggregate,
+ /// for example 'A { const C &c; }; A a = { C() };'
+ bool IsTemporaryLifetimeExtendedViaAggregate = false;
+
EvalCallOptions() {}
};
}
case ConstructionContext::TemporaryObjectKind: {
const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
- // See if we're lifetime-extended via our field. If so, take a note.
- // Because automatic destructors aren't quite working in this case.
if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
if (const ValueDecl *VD = MTE->getExtendingDecl()) {
- assert(VD->getType()->isReferenceType());
- if (VD->getType()->getPointeeType().getCanonicalType() !=
- MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
- CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
+ // Pattern-match various forms of lifetime extension that aren't
+ // currently supported by the CFG.
+ // FIXME: Is there a better way to retrieve this information from
+ // the MaterializeTemporaryExpr?
+ assert(MTE->getStorageDuration() != SD_FullExpression);
+ if (VD->getType()->isReferenceType()) {
+ assert(VD->getType()->isReferenceType());
+ if (VD->getType()->getPointeeType().getCanonicalType() !=
+ MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
+ // We're lifetime-extended via our field. Automatic destructors
+ // aren't quite working in this case.
+ CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
+ }
+ } else {
+ // We're lifetime-extended by a surrounding aggregate.
+ // Automatic destructors aren't quite working in this case.
+ CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
}
}
}
// within it to a reference, automatic destructors don't work properly.
if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject)
return CIP_DisallowedOnce;
+
+ // If the temporary is lifetime-extended by binding it to a reference-typ
+ // field within an aggregate, automatic destructors don't work properly.
+ if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate)
+ return CIP_DisallowedOnce;
}
break;
// FIXME: Should be TRUE. Should not warn about garbage value.
clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
}
+
+struct A { // A is an aggregate.
+ const C &c;
+};
+
+void f6() {
+ C *after, *before;
+ {
+ A a{C(true, &after, &before)};
+ }
+ // FIXME: Should be TRUE. Should not warn about garbage value.
+ clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
+}
+
+void f7() {
+ C *after, *before;
+ {
+ A a = {C(true, &after, &before)};
+ }
+ // FIXME: Should be TRUE. Should not warn about garbage value.
+ clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
+}
+
+void f8() {
+ C *after, *before;
+ {
+ A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)};
+ }
+ // FIXME: Should be TRUE. Should not warn about garbage value.
+ clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
+}
} // end namespace maintain_original_object_address_on_lifetime_extension
namespace maintain_original_object_address_on_move {