}
break;
}
- case ConstructionContext::SimpleReturnedValueKind: {
+ case ConstructionContext::SimpleReturnedValueKind:
+ case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
// The temporary is to be managed by the parent stack frame.
// So build it in the parent stack frame if we're not in the
// top frame of the analysis.
// call in the parent stack frame. This is equivalent to not being
// able to find construction context at all.
break;
- } else if (!isa<TemporaryObjectConstructionContext>(
- RTC->getConstructionContext())) {
- // FIXME: The return value is constructed directly into a
- // non-temporary due to C++17 mandatory copy elision. This is not
- // implemented yet.
- assert(getContext().getLangOpts().CPlusPlus17);
- break;
}
- CC = RTC->getConstructionContext();
- LCtx = CallerLCtx;
+ return prepareForObjectConstruction(
+ cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ RTC->getConstructionContext(), CallOpts);
} else {
// We are on the top frame of the analysis.
// TODO: What exactly happens when we are? Does the temporary object
SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
return std::make_pair(State, V);
}
-
- // Continue as if we have a temporary with a different location context.
- // FALLTHROUGH.
+ llvm_unreachable("Unhandled return value construction context!");
}
case ConstructionContext::TemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
CallOpts.IsTemporaryCtorOrDtor = true;
return std::make_pair(State, V);
}
- case ConstructionContext::CXX17ElidedCopyReturnedValueKind:
- // Not implemented yet.
- break;
}
}
// If we couldn't find an existing region to construct into, assume we're
ClassWithoutDestructor c = make3(v);
#if __cplusplus >= 201703L
- // FIXME: Both should be TRUE.
clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
- clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{FALSE}}
+ clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}
#else
clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}
AddressVector<ClassWithDestructor> v;
{
ClassWithDestructor c = ClassWithDestructor(v);
+ // Check if the last destructor is an automatic destructor.
+ // A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+ clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+ clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
+#endif
}
#if __cplusplus >= 201703L
// 0. Construct the variable.
AddressVector<ClassWithDestructor> v;
{
TestCtorInitializer t(v);
+ // Check if the last destructor is an automatic destructor.
+ // A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+ clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+ clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
+#endif
}
#if __cplusplus >= 201703L
// 0. Construct the member variable.
#endif
}
+
+ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
+ return ClassWithDestructor(v);
+}
+ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
+ return make1(v);
+}
+ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
+ return make2(v);
+}
+
+void testMultipleReturnsWithDestructors() {
+ AddressVector<ClassWithDestructor> v;
+ {
+ ClassWithDestructor c = make3(v);
+ // Check if the last destructor is an automatic destructor.
+ // A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+ clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+ clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}
+#endif
+ }
+
+#if __cplusplus >= 201703L
+ // 0. Construct the variable. Yes, constructor in make1() constructs
+ // the variable 'c'.
+ // 1. Destroy the variable.
+ clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
+#else
+ // 0. Construct the temporary in make1().
+ // 1. Construct the temporary in make2().
+ // 2. Destroy the temporary in make1().
+ // 3. Construct the temporary in make3().
+ // 4. Destroy the temporary in make2().
+ // 5. Construct the temporary here.
+ // 6. Destroy the temporary in make3().
+ // 7. Construct the variable.
+ // 8. Destroy the temporary here.
+ // 9. Destroy the variable.
+ clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}}
+#endif
+}
} // namespace address_vector_tests