Skip to content

Commit 042f41b

Browse files
committedJan 27, 2014
StringRef: Extend constexpr capabilities and introduce ConstStringRef
(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. llvm-svn: 200187
1 parent 054234f commit 042f41b

File tree

4 files changed

+58
-22
lines changed

4 files changed

+58
-22
lines changed
 

‎llvm/include/llvm/ADT/StringRef.h

+27-19
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef LLVM_ADT_STRINGREF_H
1111
#define LLVM_ADT_STRINGREF_H
1212

13+
#include "llvm/Support/Compiler.h"
14+
#include "llvm/Support/ErrorHandling.h"
1315
#include "llvm/Support/type_traits.h"
1416
#include <algorithm>
1517
#include <cassert>
@@ -70,7 +72,7 @@ namespace llvm {
7072
/// @{
7173

7274
/// Construct an empty string ref.
73-
/*implicit*/ StringRef() : Data(0), Length(0) {}
75+
/*implicit*/ LLVM_CONSTEXPR StringRef() : Data(0), Length(0) {}
7476

7577
/// Construct a string ref from a cstring.
7678
/*implicit*/ StringRef(const char *Str)
@@ -80,11 +82,8 @@ namespace llvm {
8082
}
8183

8284
/// Construct a string ref from a pointer and length.
83-
/*implicit*/ StringRef(const char *data, size_t length)
84-
: Data(data), Length(length) {
85-
assert((data || length == 0) &&
86-
"StringRef cannot be built from a NULL argument with non-null length");
87-
}
85+
/*implicit*/ LLVM_CONSTEXPR StringRef(const char *data, size_t length)
86+
: Data(data), Length((llvm_expect(data || length == 0), length)) {}
8887

8988
/// Construct a string ref from an std::string.
9089
/*implicit*/ StringRef(const std::string &Str)
@@ -104,24 +103,20 @@ namespace llvm {
104103

105104
/// data - Get a pointer to the start of the string (which may not be null
106105
/// terminated).
107-
const char *data() const { return Data; }
106+
LLVM_CONSTEXPR const char *data() const { return Data; }
108107

109108
/// empty - Check if the string is empty.
110-
bool empty() const { return Length == 0; }
109+
LLVM_CONSTEXPR bool empty() const { return Length == 0; }
111110

112111
/// size - Get the string size.
113-
size_t size() const { return Length; }
112+
LLVM_CONSTEXPR size_t size() const { return Length; }
114113

115114
/// front - Get the first character in the string.
116-
char front() const {
117-
assert(!empty());
118-
return Data[0];
119-
}
115+
LLVM_CONSTEXPR char front() const { return llvm_expect(!empty()), Data[0]; }
120116

121117
/// back - Get the last character in the string.
122-
char back() const {
123-
assert(!empty());
124-
return Data[Length-1];
118+
LLVM_CONSTEXPR char back() const {
119+
return llvm_expect(!empty()), Data[Length - 1];
125120
}
126121

127122
/// equals - Check for string equality, this is more efficient than
@@ -187,9 +182,8 @@ namespace llvm {
187182
/// @name Operator Overloads
188183
/// @{
189184

190-
char operator[](size_t Index) const {
191-
assert(Index < Length && "Invalid index!");
192-
return Data[Index];
185+
LLVM_CONSTEXPR char operator[](size_t Index) const {
186+
return llvm_expect(Index < Length), Data[Index];
193187
}
194188

195189
/// @}
@@ -547,6 +541,20 @@ namespace llvm {
547541

548542
/// @}
549543

544+
/// ConstStringRef - A \c StringRef carrying the additional stipulation that
545+
/// the referenced string is a compile-time constant.
546+
///
547+
/// Use this to specify function parameters that require fixed inputs such
548+
/// as debug and diagnostic messages or format strings.
549+
class ConstStringRef : public StringRef {
550+
public:
551+
/*implicit*/ LLVM_CONSTEXPR ConstStringRef() : StringRef() {}
552+
553+
template <size_t N>
554+
/*implicit*/ LLVM_CONSTEXPR ConstStringRef(const char (&data)[N])
555+
: StringRef(data, (llvm_expect(N > 0 && data[N - 1] == '\0'), N - 1)) {}
556+
};
557+
550558
/// \brief Compute a hash_code for a StringRef.
551559
hash_code hash_value(StringRef S);
552560

‎llvm/include/llvm/Support/ErrorHandling.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
#ifndef LLVM_SUPPORT_ERRORHANDLING_H
1616
#define LLVM_SUPPORT_ERRORHANDLING_H
1717

18-
#include "llvm/ADT/StringRef.h"
1918
#include "llvm/Support/Compiler.h"
2019
#include <string>
2120

2221
namespace llvm {
22+
class StringRef;
2323
class Twine;
2424

2525
/// An error handler callback.
@@ -78,7 +78,7 @@ namespace llvm {
7878
bool gen_crash_diag = true);
7979
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const std::string &reason,
8080
bool gen_crash_diag = true);
81-
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(StringRef reason,
81+
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const StringRef &reason,
8282
bool gen_crash_diag = true);
8383
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const Twine &reason,
8484
bool gen_crash_diag = true);
@@ -108,4 +108,14 @@ namespace llvm {
108108
#define llvm_unreachable(msg) ::llvm::llvm_unreachable_internal()
109109
#endif
110110

111+
/// An assert macro that's usable in constexprs and that becomes an optimizer
112+
/// hint in NDEBUG builds.
113+
///
114+
/// Unlike \c assert() the \param test expression may be evaluated in optimized
115+
/// builds and so should be simple, accurate and never have side effects.
116+
#define llvm_expect(test) (void)(!!(test) ? 0 : (llvm_unreachable(#test), 0))
117+
118+
// TODO: Update other headers to explicitly include StringRef.h and drop this.
119+
#include "llvm/ADT/StringRef.h"
120+
111121
#endif

‎llvm/lib/Support/ErrorHandling.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
5858
report_fatal_error(Twine(Reason), GenCrashDiag);
5959
}
6060

61-
void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
61+
void llvm::report_fatal_error(const StringRef &Reason, bool GenCrashDiag) {
6262
report_fatal_error(Twine(Reason), GenCrashDiag);
6363
}
6464

‎llvm/unittests/ADT/StringRefTest.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -531,4 +531,22 @@ TEST(StringRefTest, joinStrings) {
531531
EXPECT_TRUE(v2_join3);
532532
}
533533

534+
static void fn_stringref(StringRef str) {
535+
EXPECT_TRUE(str == "hello");
536+
}
537+
static void fn_conststringref(ConstStringRef str) {
538+
fn_stringref(str);
539+
}
540+
541+
TEST(StringRefTest, constStringRef) {
542+
LLVM_CONSTEXPR ConstStringRef csr("hello");
543+
#if __has_feature(cxx_constexpr) || defined(__GXX_EXPERIMENTAL_CXX0X__)
544+
LLVM_STATIC_ASSERT(csr[0] != csr[1], "");
545+
LLVM_STATIC_ASSERT(csr[2] == csr[3], "");
546+
LLVM_STATIC_ASSERT(csr.size() == 5, "");
547+
#endif
548+
llvm_expect(csr[2] == csr[3]);
549+
fn_conststringref(csr);
550+
}
551+
534552
} // end anonymous namespace

0 commit comments

Comments
 (0)
Please sign in to comment.