StringRef: Extend constexpr capabilities and introduce ConstStringRef
authorAlp Toker <alp@nuanti.com>
Mon, 27 Jan 2014 04:07:17 +0000 (04:07 +0000)
committerAlp Toker <alp@nuanti.com>
Mon, 27 Jan 2014 04:07:17 +0000 (04:07 +0000)
(1) Add llvm_expect(), an asserting macro that can be evaluated as a constexpr
    expression as well as a runtime assert or compiler hint in release builds. This
    technique can be used to construct functions that are both unevaluated and
    compiled depending on usage.

(2) Update StringRef using llvm_expect() to preserve runtime assertions while
    extending the same checks to static asserts in C++11 builds that support the
    feature.

(3) Introduce ConstStringRef, a strong subclass of StringRef that references
    compile-time constant strings. It's convertible to, but not from, ordinary
    StringRef and thus can be used to add compile-time safety to various interfaces
    in LLVM and clang that only accept fixed inputs such as diagnostic format
    strings that tend to get misused.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@200187 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/StringRef.h
include/llvm/Support/ErrorHandling.h
lib/Support/ErrorHandling.cpp
unittests/ADT/StringRefTest.cpp

index ec0c2849f37ef9c90c163e9dccefdd1cabe7368f..a35dfbfebac9b49fc63ca0a492cd345de2802a31 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef LLVM_ADT_STRINGREF_H
 #define LLVM_ADT_STRINGREF_H
 
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/type_traits.h"
 #include <algorithm>
 #include <cassert>
@@ -70,7 +72,7 @@ namespace llvm {
     /// @{
 
     /// Construct an empty string ref.
-    /*implicit*/ StringRef() : Data(0), Length(0) {}
+    /*implicit*/ LLVM_CONSTEXPR StringRef() : Data(0), Length(0) {}
 
     /// Construct a string ref from a cstring.
     /*implicit*/ StringRef(const char *Str)
@@ -80,11 +82,8 @@ namespace llvm {
       }
 
     /// Construct a string ref from a pointer and length.
-    /*implicit*/ StringRef(const char *data, size_t length)
-      : Data(data), Length(length) {
-        assert((data || length == 0) &&
-        "StringRef cannot be built from a NULL argument with non-null length");
-      }
+      /*implicit*/ LLVM_CONSTEXPR StringRef(const char *data, size_t length)
+          : Data(data), Length((llvm_expect(data || length == 0), length)) {}
 
     /// Construct a string ref from an std::string.
     /*implicit*/ StringRef(const std::string &Str)
@@ -104,24 +103,20 @@ namespace llvm {
 
     /// data - Get a pointer to the start of the string (which may not be null
     /// terminated).
-    const char *data() const { return Data; }
+    LLVM_CONSTEXPR const char *data() const { return Data; }
 
     /// empty - Check if the string is empty.
-    bool empty() const { return Length == 0; }
+    LLVM_CONSTEXPR bool empty() const { return Length == 0; }
 
     /// size - Get the string size.
-    size_t size() const { return Length; }
+    LLVM_CONSTEXPR size_t size() const { return Length; }
 
     /// front - Get the first character in the string.
-    char front() const {
-      assert(!empty());
-      return Data[0];
-    }
+    LLVM_CONSTEXPR char front() const { return llvm_expect(!empty()), Data[0]; }
 
     /// back - Get the last character in the string.
-    char back() const {
-      assert(!empty());
-      return Data[Length-1];
+    LLVM_CONSTEXPR char back() const {
+      return llvm_expect(!empty()), Data[Length - 1];
     }
 
     /// equals - Check for string equality, this is more efficient than
@@ -187,9 +182,8 @@ namespace llvm {
     /// @name Operator Overloads
     /// @{
 
-    char operator[](size_t Index) const {
-      assert(Index < Length && "Invalid index!");
-      return Data[Index];
+    LLVM_CONSTEXPR char operator[](size_t Index) const {
+      return llvm_expect(Index < Length), Data[Index];
     }
 
     /// @}
@@ -547,6 +541,20 @@ namespace llvm {
 
   /// @}
 
+  /// ConstStringRef - A \c StringRef carrying the additional stipulation that
+  /// the referenced string is a compile-time constant.
+  ///
+  /// Use this to specify function parameters that require fixed inputs such
+  /// as debug and diagnostic messages or format strings.
+  class ConstStringRef : public StringRef {
+  public:
+    /*implicit*/ LLVM_CONSTEXPR ConstStringRef() : StringRef() {}
+
+    template <size_t N>
+    /*implicit*/ LLVM_CONSTEXPR ConstStringRef(const char (&data)[N])
+        : StringRef(data, (llvm_expect(N > 0 && data[N - 1] == '\0'), N - 1)) {}
+  };
+
   /// \brief Compute a hash_code for a StringRef.
   hash_code hash_value(StringRef S);
 
index b948d97bff929a54a0fb276559883e73a62d2cc7..d2f16821bff6db5ae722616816304a40ee095ee2 100644 (file)
 #ifndef LLVM_SUPPORT_ERRORHANDLING_H
 #define LLVM_SUPPORT_ERRORHANDLING_H
 
-#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
 #include <string>
 
 namespace llvm {
+  class StringRef;
   class Twine;
 
   /// An error handler callback.
@@ -78,7 +78,7 @@ namespace llvm {
                                                   bool gen_crash_diag = true);
   LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const std::string &reason,
                                                   bool gen_crash_diag = true);
-  LLVM_ATTRIBUTE_NORETURN void report_fatal_error(StringRef reason,
+  LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const StringRef &reason,
                                                   bool gen_crash_diag = true);
   LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const Twine &reason,
                                                   bool gen_crash_diag = true);
@@ -108,4 +108,14 @@ namespace llvm {
 #define llvm_unreachable(msg) ::llvm::llvm_unreachable_internal()
 #endif
 
+/// An assert macro that's usable in constexprs and that becomes an optimizer
+/// hint in NDEBUG builds.
+///
+/// Unlike \c assert() the \param test expression may be evaluated in optimized
+/// builds and so should be simple, accurate and never have side effects.
+#define llvm_expect(test) (void)(!!(test) ? 0 : (llvm_unreachable(#test), 0))
+
+// TODO: Update other headers to explicitly include StringRef.h and drop this.
+#include "llvm/ADT/StringRef.h"
+
 #endif
index 1aa8303b9e240d779b69df15aa95d224f6a6665c..141319ca4eaeb725fed3bf1212487a5014a5cb15 100644 (file)
@@ -58,7 +58,7 @@ void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
   report_fatal_error(Twine(Reason), GenCrashDiag);
 }
 
-void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
+void llvm::report_fatal_error(const StringRef &Reason, bool GenCrashDiag) {
   report_fatal_error(Twine(Reason), GenCrashDiag);
 }
 
index 0ab8fcf6f002ec935a055007b43c133bd1bc17cc..b240a87ef7544a483dc7d2340f76be45c63e9cda 100644 (file)
@@ -531,4 +531,22 @@ TEST(StringRefTest, joinStrings) {
   EXPECT_TRUE(v2_join3);
 }
 
+static void fn_stringref(StringRef str) {
+  EXPECT_TRUE(str == "hello");
+}
+static void fn_conststringref(ConstStringRef str) {
+  fn_stringref(str);
+}
+
+TEST(StringRefTest, constStringRef) {
+  LLVM_CONSTEXPR ConstStringRef csr("hello");
+#if __has_feature(cxx_constexpr) || defined(__GXX_EXPERIMENTAL_CXX0X__)
+  LLVM_STATIC_ASSERT(csr[0] != csr[1], "");
+  LLVM_STATIC_ASSERT(csr[2] == csr[3], "");
+  LLVM_STATIC_ASSERT(csr.size() == 5, "");
+#endif
+  llvm_expect(csr[2] == csr[3]);
+  fn_conststringref(csr);
+}
+
 } // end anonymous namespace