=?utf-8?q?Félix?= Cloutier <fclout...@apple.com>,
=?utf-8?q?Félix?= Cloutier <fclout...@apple.com>,
=?utf-8?q?Félix?= Cloutier <fclout...@apple.com>,
=?utf-8?q?Félix?= Cloutier <fclout...@apple.com>,
=?utf-8?q?Félix?= Cloutier <fclout...@apple.com>,
=?utf-8?q?Félix?= Cloutier <fclout...@apple.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/135...@github.com>


================
@@ -238,3 +246,69 @@ void f(Scoped1 S1, Scoped2 S2) {
 }
 
 #endif
+
+#if __cplusplus >= 202000L
+class my_string {
+  char *data;
+  unsigned size;
+
+public:
+  template<unsigned N>
+  constexpr my_string(const char (&literal)[N]) {
+    data = new char[N+1];
+    for (size = 0; size < N; ++size) {
+      data[size] = literal[size];
+      if (data[size] == 0)
+        break;
+    }
+    data[size] = 0;
+  }
+
+  my_string(const my_string &) = delete;
+
+  constexpr my_string(my_string &&that) {
+    data = that.data;
+    size = that.size;
+    that.data = nullptr;
+    that.size = 0;
+  }
+
+  constexpr ~my_string() {
+    delete[] data;
+  }
+
+  template<unsigned N>
+  constexpr void append(const char (&literal)[N]) {
+    char *cat = new char[size + N + 1];
+    char *tmp = cat;
+    for (unsigned i = 0; i < size; ++i) {
+      *tmp++ = data[i];
+    }
+    for (unsigned i = 0; i < N; ++i) {
+      *tmp = literal[i];
+      if (*tmp == 0)
+        break;
+      ++tmp;
+    }
+    *tmp = 0;
+    delete[] data;
+    size = tmp - cat;
+    data = cat;
+  }
+
+  constexpr const char *c_str() const {
+    return data;
+  }
+};
+
+constexpr my_string const_string() {
+  my_string str("hello %s");
+  str.append(", %d");
+  return str;
+}
+
+void test_constexpr_string() {
+  printf(const_string().c_str(), "hello", 123); // no-warning
+  printf(const_string().c_str(), 123, 456); // expected-warning {{format 
specifies type 'char *' but the argument has type 'int'}}
+}
+#endif
----------------
apple-fcloutier wrote:

I played with this a bit yesterday and the last push improves the situation. 
When the format string checker follows a DeclRefExpr to a VarDecl that is found 
to have constant initialization (per `VarDecl::hasConstantInitialization`), we 
now honor that in the evaluation of the format string. This addresses the 
reduction that I made of your issue.

This does not address the issue as you reported it (where the function with 
`[[gnu::format]]` is constexpr/consteval itself) because at the point 
SemaChecking gets in play, we don't know yet that we are in a constant 
evaluation context. While parsing this definition:

```c++
constexpr auto myvprintf = getvprintf(const_string());
```

Format checking happens inside `Sema::ActOnCallExpr`, but we only know that 
`myvprintf` has constant initialization after its initializer has been parsed, 
and `Sema::isConstantEvaluatedContext()` is `false` at that time, so we don't 
have the information that we need.

I'm nervous to make changes around this because I don't understand constant 
evaluation well enough. I think that if we had a way to bail out of the 
constant evaluation machinery when running into an `if constexpr`, I could take 
that, but I don't think we have it either.

https://github.com/llvm/llvm-project/pull/135864
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to