================
@@ -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:
Oh, I see your point. So we have dangling in the original object but not in the
copy.
I am not actually sure if we ever want to support something like this. This is
just a pattern that does not fit into the ownership model that this check can
reasonably support.
So maybe the action here is to just not allude to the fact that we would ever
want to fix this.
https://github.com/llvm/llvm-project/pull/187917
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits