================
@@ -2126,3 +2125,287 @@ void indexing_with_static_operator() {
}
} // namespace static_call_operator
+
+namespace track_origins_for_lifetimebound_record_type {
+
+template <class T> void use(T);
+
+struct S {
+ S();
+ S(const std::string &s [[clang::lifetimebound]]);
+
+ S return_self_after_registration() const;
+ std::string_view getData() const [[clang::lifetimebound]];
+};
+
+S getS(const std::string &s [[clang::lifetimebound]]);
+
+void from_free_function() {
+ S s = getS(std::string("temp")); // expected-warning {{object whose
reference is captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ use(s); // expected-note {{later used here}}
+}
+
+void from_constructor() {
+ S s(std::string("temp")); // expected-warning {{object whose reference is
captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ use(s); // expected-note {{later used here}}
+}
+
+struct Factory {
+ S make(const std::string &s [[clang::lifetimebound]]);
+ static S create(const std::string &s [[clang::lifetimebound]]);
+ S makeThis() const [[clang::lifetimebound]];
+};
+
+void from_method() {
+ Factory f;
+ S s = f.make(std::string("temp")); // expected-warning {{object whose
reference is captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ use(s); // expected-note {{later used here}}
+}
+
+void from_static_method() {
+ S s = Factory::create(std::string("temp")); // expected-warning {{object
whose reference is captured does not live long enough}} \
+ // expected-note {{destroyed
here}}
+ use(s); // expected-note {{later used
here}}
+}
+
+void from_lifetimebound_this_method() {
+ S value;
+ {
+ Factory f;
+ value = f.makeThis(); // expected-warning {{object whose reference is
captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ use(value); // expected-note {{later used here}}
+}
+
+void across_scope() {
+ S s{};
+ {
+ std::string str{"abc"};
+ s = getS(str); // expected-warning {{object whose reference is captured
does not live long enough}}
+ } // expected-note {{destroyed here}}
+ use(s); // expected-note {{later used here}}
+}
+
+void same_scope() {
+ std::string str{"abc"};
+ S s = getS(str);
+ use(s);
+}
+
+S copy_propagation() {
+ std::string str{"abc"};
+ S a = getS(str); // expected-warning {{address of stack memory is returned
later}}
+ S b = a;
+ return b; // expected-note {{returned here}}
+}
+
+void assignment_propagation() {
+ S a, b;
+ {
+ std::string str{"abc"};
+ a = getS(str); // expected-warning {{object whose reference is captured
does not live long enough}}
+ b = a;
+ } // expected-note {{destroyed here}}
+ use(b); // expected-note {{later used here}}
+}
+
+S getSNoAnnotation(const std::string &s);
+
+void no_annotation() {
+ S s = getSNoAnnotation(std::string("temp"));
+ use(s);
+}
+
+void mix_annotated_and_not() {
+ S s1 = getS(std::string("temp")); // expected-warning {{object whose
reference is captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ S s2 = getSNoAnnotation(std::string("temp"));
+ use(s1); // expected-note {{later used here}}
+ use(s2);
+}
+
+S getS2(const std::string &a [[clang::lifetimebound]], const std::string &b
[[clang::lifetimebound]]);
+
+S multiple_lifetimebound_params() {
+ std::string str{"abc"};
+ S s = getS2(str, std::string("temp")); // expected-warning {{address of
stack memory is returned later}} \
+ // expected-warning {{object whose
reference is captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ return s; // expected-note {{returned here}} \
+ // expected-note {{later used here}}
+}
+
+// TODO: Diagnose [[clang::lifetimebound]] on functions whose return value
+// cannot refer to any object (e.g., returning int or enum).
+int getInt(const std::string &s [[clang::lifetimebound]]);
+
+void primitive_return() {
+ int i = getInt(std::string("temp"));
+ use(i);
+}
+
+template <class T>
+T make(const std::string &s [[clang::lifetimebound]]);
+
+void from_template_instantiation() {
+ S s = make<S>(std::string("temp")); // expected-warning {{object whose
reference is captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ use(s); // expected-note {{later used here}}
+}
+
+struct FieldInitFromLifetimebound {
+ S value; // function-note {{this field dangles}}
+ FieldInitFromLifetimebound() : value(getS(std::string("temp"))) {} //
function-warning {{address of stack memory escapes to a field}}
+};
+
+S S::return_self_after_registration() const {
+ std::string s{"abc"};
+ getS(s);
+ return *this;
+}
+
+struct SWithUserDefinedCopyLikeOps {
+ SWithUserDefinedCopyLikeOps();
+ SWithUserDefinedCopyLikeOps(const std::string &s [[clang::lifetimebound]]) :
owned(s), data(s) {}
+
+ SWithUserDefinedCopyLikeOps(const SWithUserDefinedCopyLikeOps &other) :
owned("copy"), data(owned) {}
+
+ SWithUserDefinedCopyLikeOps &operator=(const SWithUserDefinedCopyLikeOps &) {
+ owned = "copy";
+ data = owned;
+ return *this;
+ }
+
+ std::string owned;
+ std::string_view data;
+};
+
+SWithUserDefinedCopyLikeOps getSWithUserDefinedCopyLikeOps(const std::string
&s [[clang::lifetimebound]]);
+
+SWithUserDefinedCopyLikeOps
user_defined_copy_ctor_should_not_assume_origin_propagation() {
+ std::string str{"abc"};
+ SWithUserDefinedCopyLikeOps s = getSWithUserDefinedCopyLikeOps(str);
+ SWithUserDefinedCopyLikeOps copy = s; // Copy is rescued by user-defined
copy constructor, so should not warn.
+ return copy;
+}
+
+void user_defined_assignment_should_not_assume_origin_propagation() {
+ SWithUserDefinedCopyLikeOps dst;
+ {
+ std::string str{"abc"};
+ SWithUserDefinedCopyLikeOps src = getSWithUserDefinedCopyLikeOps(str);
+ dst = src;
+ }
+ use(dst);
+}
+
+const S &getRef(const std::string &s [[clang::lifetimebound]]);
+
+S from_ref() {
+ std::string str{"abc"};
+ S s = getRef(str);
+ return s;
+}
+
+using SAlias = S;
+SAlias getSAlias(const std::string &s [[clang::lifetimebound]]);
+
+void from_typedef_return() {
+ SAlias s = getSAlias(std::string("temp")); // expected-warning {{object
whose reference is captured does not live long enough}} \
+ // expected-note {{destroyed
here}}
+ use(s); // expected-note {{later used
here}}
+}
+
+struct SWithOriginPropagatingCopy {
+ SWithOriginPropagatingCopy();
+ SWithOriginPropagatingCopy(const std::string &s [[clang::lifetimebound]]) :
data(s) {}
+ SWithOriginPropagatingCopy(const SWithOriginPropagatingCopy &other) :
data(other.data) {}
+ std::string_view data;
+};
+
+SWithOriginPropagatingCopy getSWithOriginPropagatingCopy(const std::string &s
[[clang::lifetimebound]]);
+
+// FIXME: False negative. User-defined copy ctor may propagate origins.
+SWithOriginPropagatingCopy user_defined_copy_with_origin_propagation() {
+ std::string str{"abc"};
+ SWithOriginPropagatingCopy s = getSWithOriginPropagatingCopy(str);
+ SWithOriginPropagatingCopy copy = s;
+ return copy; // Should warn.
+}
+
+struct DefaultedOuter {
+ DefaultedOuter();
+ DefaultedOuter(const std::string &s [[clang::lifetimebound]]) : inner(s) {}
+ SWithUserDefinedCopyLikeOps inner;
+};
+
+DefaultedOuter getDefaultedOuter(const std::string &s
[[clang::lifetimebound]]);
+
+// FIXME: False positive. The defaulted outer copy ctor invokes
----------------
Xazax-hun wrote:
Why is this a false positive? I totally expect the warning to trigger here. If
the code is incorrectly annotated that is not on the analysis.
https://github.com/llvm/llvm-project/pull/187917
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits