Replace the implementation of __builtin_constant_p (which was based on the GCC
documentation) with one based on what GCC's __builtin_constant_p is actually
intended to do (discovered by asking a friendly GCC developer).
In particular, an expression which folds to a pointer is now only considered to
be a "constant" by this builtin if it refers to the first character in a string
literal.
This fixes a rather subtle wrong-code issue when building with glibc. Given:
const char cs[4] = "abcd";
int f(const char *p) { return strncmp(p, cs, 4); }
... the macro magic for strncmp produces a (potentially crashing) call to
strlen(cs), because it expands to an expression starting with:
__builtin_constant_p(cs) && strlen(cs) < 4 ? /* ... */
Under the secret true meaning of __builtin_constant_p, this is guaranteed to be
safe!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146236 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/Sema/builtins.c b/test/Sema/builtins.c
index 0850cc4..8b3b965 100644
--- a/test/Sema/builtins.c
+++ b/test/Sema/builtins.c
@@ -102,3 +102,63 @@
return __builtin_constant_p() + // expected-error{{too few arguments}}
__builtin_constant_p(1, 2); // expected-error {{too many arguments}}
}
+
+const int test17_n = 0;
+const char test17_c[] = {1, 2, 3, 0};
+const char test17_d[] = {1, 2, 3, 4};
+typedef int __attribute__((vector_size(16))) IntVector;
+struct Aggregate { int n; char c; };
+enum Enum { EnumValue1, EnumValue2 };
+
+typedef __typeof(sizeof(int)) size_t;
+size_t strlen(const char *);
+
+void test17() {
+#define ASSERT(...) { int arr[(__VA_ARGS__) ? 1 : -1]; }
+#define T(...) ASSERT(__builtin_constant_p(__VA_ARGS__))
+#define F(...) ASSERT(!__builtin_constant_p(__VA_ARGS__))
+
+ // __builtin_constant_p returns 1 if the argument folds to:
+ // - an arithmetic constant with value which is known at compile time
+ T(test17_n);
+ T(&test17_c[3] - test17_c);
+ T(3i + 5); // expected-warning {{imaginary constant}}
+ T(4.2 * 7.6);
+ T(EnumValue1);
+ T((enum Enum)(int)EnumValue2);
+
+ // - the address of the first character of a string literal, losslessly cast
+ // to any type
+ T("string literal");
+ T((double*)"string literal");
+ T("string literal" + 0);
+ T((long)"string literal");
+
+ // ... and otherwise returns 0.
+ F("string literal" + 1);
+ F(&test17_n);
+ F(test17_c);
+ F(&test17_c);
+ F(&test17_d);
+ F((struct Aggregate){0, 1});
+ F((IntVector){0, 1, 2, 3});
+
+ // Ensure that a technique used in glibc is handled correctly.
+#define OPT(...) (__builtin_constant_p(__VA_ARGS__) && strlen(__VA_ARGS__) < 4)
+ // FIXME: These are incorrectly treated as ICEs because strlen is treated as
+ // a builtin.
+ ASSERT(OPT("abc"));
+ ASSERT(!OPT("abcd"));
+ // In these cases, the strlen is non-constant, but the __builtin_constant_p
+ // is 0: the array size is not an ICE but is foldable.
+ ASSERT(!OPT(test17_c)); // expected-warning {{folded}}
+ ASSERT(!OPT(&test17_c[0])); // expected-warning {{folded}}
+ ASSERT(!OPT((char*)test17_c)); // expected-warning {{folded}}
+ ASSERT(!OPT(test17_d)); // expected-warning {{folded}}
+ ASSERT(!OPT(&test17_d[0])); // expected-warning {{folded}}
+ ASSERT(!OPT((char*)test17_d)); // expected-warning {{folded}}
+
+#undef OPT
+#undef T
+#undef F
+}