diff --git a/src/libexpr-tests/json.cc b/src/libexpr-tests/json.cc index 8b1bd7d96..aa71c4d86 100644 --- a/src/libexpr-tests/json.cc +++ b/src/libexpr-tests/json.cc @@ -1,5 +1,6 @@ #include "nix/expr/tests/libexpr.hh" #include "nix/expr/value-to-json.hh" +#include "nix/expr/static-string-data.hh" namespace nix { // Testing the conversion to JSON @@ -54,7 +55,7 @@ TEST_F(JSONValueTest, IntNegative) TEST_F(JSONValueTest, String) { Value v; - v.mkStringNoCopy("test"); + v.mkStringNoCopy("test"_sds); ASSERT_EQ(getJSONValue(v), "\"test\""); } @@ -62,7 +63,7 @@ TEST_F(JSONValueTest, StringQuotes) { Value v; - v.mkStringNoCopy("test\""); + v.mkStringNoCopy("test\""_sds); ASSERT_EQ(getJSONValue(v), "\"test\\\"\""); } diff --git a/src/libexpr-tests/value/print.cc b/src/libexpr-tests/value/print.cc index 0006da2ff..0456835b4 100644 --- a/src/libexpr-tests/value/print.cc +++ b/src/libexpr-tests/value/print.cc @@ -1,4 +1,5 @@ #include "nix/expr/tests/libexpr.hh" +#include "nix/expr/static-string-data.hh" #include "nix/expr/value.hh" #include "nix/expr/print.hh" @@ -35,14 +36,14 @@ TEST_F(ValuePrintingTests, tBool) TEST_F(ValuePrintingTests, tString) { Value vString; - vString.mkStringNoCopy("some-string"); + vString.mkStringNoCopy("some-string"_sds); test(vString, "\"some-string\""); } TEST_F(ValuePrintingTests, tPath) { Value vPath; - vPath.mkStringNoCopy("/foo"); + vPath.mkStringNoCopy("/foo"_sds); test(vPath, "\"/foo\""); } @@ -289,10 +290,10 @@ TEST_F(StringPrintingTests, maxLengthTruncation) TEST_F(ValuePrintingTests, attrsTypeFirst) { Value vType; - vType.mkStringNoCopy("puppy"); + vType.mkStringNoCopy("puppy"_sds); Value vApple; - vApple.mkStringNoCopy("apple"); + vApple.mkStringNoCopy("apple"_sds); BindingsBuilder builder = state.buildBindings(10); builder.insert(state.symbols.create("type"), &vType); @@ -333,7 +334,7 @@ TEST_F(ValuePrintingTests, ansiColorsBool) TEST_F(ValuePrintingTests, ansiColorsString) { Value v; - v.mkStringNoCopy("puppy"); + v.mkStringNoCopy("puppy"_sds); test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true}); } @@ -341,7 +342,7 @@ TEST_F(ValuePrintingTests, ansiColorsString) TEST_F(ValuePrintingTests, ansiColorsStringElided) { Value v; - v.mkStringNoCopy("puppy"); + v.mkStringNoCopy("puppy"_sds); test( v, @@ -389,7 +390,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs) TEST_F(ValuePrintingTests, ansiColorsDerivation) { Value vDerivation; - vDerivation.mkStringNoCopy("derivation"); + vDerivation.mkStringNoCopy("derivation"_sds); BindingsBuilder builder = state.buildBindings(10); builder.insert(state.s.type, &vDerivation); @@ -412,7 +413,7 @@ TEST_F(ValuePrintingTests, ansiColorsError) { Value throw_ = state.getBuiltin("throw"); Value message; - message.mkStringNoCopy("uh oh!"); + message.mkStringNoCopy("uh oh!"_sds); Value vError; vError.mkApp(&throw_, &message); @@ -429,12 +430,12 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError) { Value throw_ = state.getBuiltin("throw"); Value message; - message.mkStringNoCopy("uh oh!"); + message.mkStringNoCopy("uh oh!"_sds); Value vError; vError.mkApp(&throw_, &message); Value vDerivation; - vDerivation.mkStringNoCopy("derivation"); + vDerivation.mkStringNoCopy("derivation"_sds); BindingsBuilder builder = state.buildBindings(10); builder.insert(state.s.type, &vDerivation); diff --git a/src/libexpr-tests/value/value.cc b/src/libexpr-tests/value/value.cc index 229e449db..420db0f31 100644 --- a/src/libexpr-tests/value/value.cc +++ b/src/libexpr-tests/value/value.cc @@ -1,4 +1,5 @@ #include "nix/expr/value.hh" +#include "nix/expr/static-string-data.hh" #include "nix/store/tests/libstore.hh" #include @@ -27,17 +28,17 @@ TEST_F(ValueTest, staticString) { Value vStr1; Value vStr2; - vStr1.mkStringNoCopy("foo"); - vStr2.mkStringNoCopy("foo"); + vStr1.mkStringNoCopy("foo"_sds); + vStr2.mkStringNoCopy("foo"_sds); - auto sd1 = vStr1.string_view(); - auto sd2 = vStr2.string_view(); + auto & sd1 = vStr1.string_data(); + auto & sd2 = vStr2.string_data(); // The strings should be the same - ASSERT_EQ(sd1, sd2); + ASSERT_EQ(sd1.view(), sd2.view()); // The strings should also be backed by the same (static) allocation - ASSERT_EQ(sd1.data(), sd2.data()); + ASSERT_EQ(&sd1, &sd2); } } // namespace nix diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 0372d6cc1..4cfa3dabb 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -147,7 +147,7 @@ struct AttrDb for (auto * elem : *context) { if (!first) ctx.push_back(' '); - ctx.append(elem); + ctx.append(elem->view()); first = false; } state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 807f7544c..71619a9be 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -3,6 +3,7 @@ #include "nix/expr/primops.hh" #include "nix/expr/print-options.hh" #include "nix/expr/symbol-table.hh" +#include "nix/expr/value.hh" #include "nix/util/exit.hh" #include "nix/util/types.hh" #include "nix/util/util.hh" @@ -28,6 +29,8 @@ #include "parser-tab.hh" #include +#include +#include #include #include #include @@ -48,6 +51,9 @@ using json = nlohmann::json; namespace nix { +/** + * Just for doc strings. Not for regular string values. + */ static char * allocString(size_t size) { char * t; @@ -61,6 +67,9 @@ static char * allocString(size_t size) // string allocations. // This function handles makeImmutableString(std::string_view()) by returning // the empty string. +/** + * Just for doc strings. Not for regular string values. + */ static const char * makeImmutableString(std::string_view s) { const size_t size = s.size(); @@ -72,6 +81,25 @@ static const char * makeImmutableString(std::string_view s) return t; } +StringData & StringData::alloc(size_t size) +{ + void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1); + if (!t) + throw std::bad_alloc(); + auto res = new (t) StringData(size); + return *res; +} + +const StringData & StringData::make(std::string_view s) +{ + if (s.empty()) + return ""_sds; + auto & res = alloc(s.size()); + std::memcpy(&res.data_, s.data(), s.size()); + res.data_[s.size()] = '\0'; + return res; +} + RootValue allocRootValue(Value * v) { return std::allocate_shared(traceable_allocator(), v); @@ -585,7 +613,9 @@ std::optional EvalState::getDoc(Value & v) .name = name, .arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though... .args = {}, - .doc = makeImmutableString(s.view()), // NOTE: memory leak when compiled without GC + /* N.B. Can't use StringData here, because that would lead to an interior pointer. + NOTE: memory leak when compiled without GC. */ + .doc = makeImmutableString(s.view()), }; } if (isFunctor(v)) { @@ -819,7 +849,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t) void Value::mkString(std::string_view s) { - mkStringNoCopy(makeImmutableString(s)); + mkStringNoCopy(StringData::make(s)); } Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context) @@ -829,23 +859,23 @@ Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuild auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size()); std::ranges::transform( - context, ctx->elems, [](const NixStringContextElem & elt) { return makeImmutableString(elt.to_string()); }); + context, ctx->elems, [](const NixStringContextElem & elt) { return &StringData::make(elt.to_string()); }); return ctx; } void Value::mkString(std::string_view s, const NixStringContext & context) { - mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context)); + mkStringNoCopy(StringData::make(s), Value::StringWithContext::Context::fromBuilder(context)); } -void Value::mkStringMove(const char * s, const NixStringContext & context) +void Value::mkStringMove(const StringData & s, const NixStringContext & context) { mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context)); } void Value::mkPath(const SourcePath & path) { - mkPath(&*path.accessor, makeImmutableString(path.path.abs())); + mkPath(&*path.accessor, StringData::make(path.path.abs())); } inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) @@ -2099,21 +2129,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) .atPos(pos) .withFrame(env, *this) .debugThrow(); - std::string result_str; - result_str.reserve(sSize); + std::string resultStr; + resultStr.reserve(sSize); for (const auto & part : strings) { - result_str += *part; + resultStr += *part; } - v.mkPath(state.rootPath(CanonPath(result_str))); + v.mkPath(state.rootPath(CanonPath(resultStr))); } else { - char * result_str = allocString(sSize + 1); - char * tmp = result_str; + auto & resultStr = StringData::alloc(sSize); + auto * tmp = resultStr.data(); for (const auto & part : strings) { - memcpy(tmp, part->data(), part->size()); + std::memcpy(tmp, part->data(), part->size()); tmp += part->size(); } - *tmp = 0; - v.mkStringMove(result_str, context); + *tmp = '\0'; + v.mkStringMove(resultStr, context); } } @@ -2288,7 +2318,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental { if (auto * ctx = v.context()) for (auto * elem : *ctx) - context.insert(NixStringContextElem::parse(elem, xpSettings)); + context.insert(NixStringContextElem::parse(elem->view(), xpSettings)); } std::string_view EvalState::forceString( diff --git a/src/libexpr/include/nix/expr/meson.build b/src/libexpr/include/nix/expr/meson.build index 44ff171c2..2b0fbc406 100644 --- a/src/libexpr/include/nix/expr/meson.build +++ b/src/libexpr/include/nix/expr/meson.build @@ -31,6 +31,7 @@ headers = [ config_pub_h ] + files( 'print.hh', 'repl-exit-status.hh', 'search-path.hh', + 'static-string-data.hh', 'symbol-table.hh', 'value-to-json.hh', 'value-to-xml.hh', diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index 08d39cd87..3f7c883f3 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include "nix/expr/value.hh" #include "nix/expr/symbol-table.hh" #include "nix/expr/eval-error.hh" +#include "nix/expr/static-string-data.hh" #include "nix/util/pos-idx.hh" #include "nix/expr/counter.hh" #include "nix/util/pos-table.hh" @@ -186,22 +188,18 @@ struct ExprString : Expr * This is only for strings already allocated in our polymorphic allocator, * or that live at least that long (e.g. c++ string literals) */ - ExprString(const char * s) + ExprString(const StringData & s) { v.mkStringNoCopy(s); }; ExprString(std::pmr::polymorphic_allocator & alloc, std::string_view sv) { - auto len = sv.length(); - if (len == 0) { - v.mkStringNoCopy(""); + if (sv.size() == 0) { + v.mkStringNoCopy(""_sds); return; } - char * s = alloc.allocate(len + 1); - sv.copy(s, len); - s[len] = '\0'; - v.mkStringNoCopy(s); + v.mkStringNoCopy(StringData::make(*alloc.resource(), sv)); }; Value * maybeThunk(EvalState & state, Env & env) override; @@ -216,11 +214,7 @@ struct ExprPath : Expr ExprPath(std::pmr::polymorphic_allocator & alloc, ref accessor, std::string_view sv) : accessor(accessor) { - auto len = sv.length(); - char * s = alloc.allocate(len + 1); - sv.copy(s, len); - s[len] = '\0'; - v.mkPath(&*accessor, s); + v.mkPath(&*accessor, StringData::make(*alloc.resource(), sv)); } Value * maybeThunk(EvalState & state, Env & env) override; diff --git a/src/libexpr/include/nix/expr/parser-state.hh b/src/libexpr/include/nix/expr/parser-state.hh index 89c424f82..661584ea0 100644 --- a/src/libexpr/include/nix/expr/parser-state.hh +++ b/src/libexpr/include/nix/expr/parser-state.hh @@ -4,6 +4,8 @@ #include #include "nix/expr/eval.hh" +#include "nix/expr/value.hh" +#include "nix/expr/static-string-data.hh" namespace nix { @@ -240,7 +242,7 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, std::vector>> && es) { if (es.empty()) - return exprs.add(""); + return exprs.add(""_sds); /* Figure out the minimum indentation. Note that by design whitespace-only final lines are not taken into account. (So @@ -332,7 +334,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector(""); + auto * const result = exprs.add(""_sds); return result; } diff --git a/src/libexpr/include/nix/expr/static-string-data.hh b/src/libexpr/include/nix/expr/static-string-data.hh new file mode 100644 index 000000000..93b5d46a0 --- /dev/null +++ b/src/libexpr/include/nix/expr/static-string-data.hh @@ -0,0 +1,44 @@ +#pragma once +///@file + +#include "nix/expr/value.hh" + +namespace nix { + +template +struct StringData::Static +{ + /** + * @note Must be first to make layout compatible with StringData. + */ + const size_t size = N - 1; + char data[N]; + + consteval Static(const char (&str)[N]) + { + static_assert(N > 0); + if (str[size] != '\0') + throw; + std::copy_n(str, N, data); + } + + operator const StringData &() const & + { + static_assert(sizeof(decltype(*this)) >= sizeof(StringData)); + static_assert(alignof(decltype(*this)) == alignof(StringData)); + /* NOTE: This cast is somewhat on the fence of what's legal in C++. + The question boils down to whether flexible array members are + layout compatible with fixed-size arrays. This is a gray area, since + FAMs are not standard anyway. + */ + return *reinterpret_cast(this); + } +}; + +template +const StringData & operator""_sds() +{ + return S; +} + +} // namespace nix diff --git a/src/libexpr/include/nix/expr/symbol-table.hh b/src/libexpr/include/nix/expr/symbol-table.hh index 5f2b47dd6..ccee8bee0 100644 --- a/src/libexpr/include/nix/expr/symbol-table.hh +++ b/src/libexpr/include/nix/expr/symbol-table.hh @@ -3,6 +3,7 @@ #include #include "nix/expr/value.hh" +#include "nix/expr/static-string-data.hh" #include "nix/util/chunked-vector.hh" #include "nix/util/error.hh" @@ -16,7 +17,6 @@ class SymbolValue : protected Value friend class SymbolStr; friend class SymbolTable; - uint32_t size_; uint32_t idx; SymbolValue() = default; @@ -24,7 +24,7 @@ class SymbolValue : protected Value public: operator std::string_view() const noexcept { - return {c_str(), size_}; + return string_view(); } }; @@ -96,13 +96,13 @@ class SymbolStr SymbolValueStore & store; std::string_view s; std::size_t hash; - std::pmr::polymorphic_allocator & alloc; + std::pmr::memory_resource & resource; - Key(SymbolValueStore & store, std::string_view s, std::pmr::polymorphic_allocator & stringAlloc) + Key(SymbolValueStore & store, std::string_view s, std::pmr::memory_resource & stringMemory) : store(store) , s(s) , hash(HashType{}(s)) - , alloc(stringAlloc) + , resource(stringMemory) { } }; @@ -122,14 +122,10 @@ public: // for multi-threaded implementations: lock store and allocator here const auto & [v, idx] = key.store.add(SymbolValue{}); if (size == 0) { - v.mkStringNoCopy("", nullptr); + v.mkStringNoCopy(""_sds, nullptr); } else { - auto s = key.alloc.allocate(size + 1); - memcpy(s, key.s.data(), size); - s[size] = '\0'; - v.mkStringNoCopy(s, nullptr); + v.mkStringNoCopy(StringData::make(key.resource, key.s)); } - v.size_ = size; v.idx = idx; this->s = &v; } @@ -139,6 +135,12 @@ public: return *s == s2; } + [[gnu::always_inline]] + const StringData & string_data() const noexcept + { + return s->string_data(); + } + [[gnu::always_inline]] const char * c_str() const noexcept { @@ -155,13 +157,17 @@ public: [[gnu::always_inline]] bool empty() const noexcept { - return s->size_ == 0; + auto * p = &s->string_data(); + // Save a dereference in the sentinel value case + if (p == &""_sds) + return true; + return p->size() == 0; } [[gnu::always_inline]] size_t size() const noexcept { - return s->size_; + return s->string_data().size(); } [[gnu::always_inline]] @@ -259,7 +265,6 @@ private: * During its lifetime the monotonic buffer holds all strings and nodes, if the symbol set is node based. */ std::pmr::monotonic_buffer_resource buffer; - std::pmr::polymorphic_allocator stringAlloc{&buffer}; SymbolStr::SymbolValueStore store{16}; /** @@ -282,7 +287,7 @@ public: // Most symbols are looked up more than once, so we trade off insertion performance // for lookup performance. // FIXME: make this thread-safe. - return Symbol(*symbols.insert(SymbolStr::Key{store, s, stringAlloc}).first); + return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first); } std::vector resolve(const std::vector & symbols) const diff --git a/src/libexpr/include/nix/expr/value.hh b/src/libexpr/include/nix/expr/value.hh index 0fb36e1b2..54a735fbd 100644 --- a/src/libexpr/include/nix/expr/value.hh +++ b/src/libexpr/include/nix/expr/value.hh @@ -1,8 +1,14 @@ #pragma once ///@file +#include #include +#include +#include +#include +#include #include +#include #include #include @@ -186,6 +192,91 @@ public: friend struct Value; }; +class StringData +{ +public: + using size_type = std::size_t; + + size_type size_; + char data_[]; + + /* + * This in particular ensures that we cannot have a `StringData` + * that we use by value, which is just what we want! + * + * Dynamically sized types aren't a thing in C++ and even flexible array + * members are a language extension and beyond the realm of standard C++. + * Technically, sizeof data_ member is 0 and the intended way to use flexible + * array members is to allocate sizeof(StrindData) + count * sizeof(char) bytes + * and the compiler will consider alignment restrictions for the FAM. + * + */ + + StringData(StringData &&) = delete; + StringData & operator=(StringData &&) = delete; + StringData(const StringData &) = delete; + StringData & operator=(const StringData &) = delete; + ~StringData() = default; + +private: + StringData() = delete; + + explicit StringData(size_type size) + : size_(size) + { + } + +public: + /** + * Allocate StringData on the (possibly) GC-managed heap and copy + * the contents of s to it. + */ + static const StringData & make(std::string_view s); + + /** + * Allocate StringData on the (possibly) GC-managed heap. + * @param size Length of the string (without the NUL terminator). + */ + static StringData & alloc(size_t size); + + size_t size() const + { + return size_; + } + + char * data() noexcept + { + return data_; + } + + const char * data() const noexcept + { + return data_; + } + + const char * c_str() const noexcept + { + return data_; + } + + constexpr std::string_view view() const noexcept + { + return std::string_view(data_, size_); + } + + template + struct Static; + + static StringData & make(std::pmr::memory_resource & resource, std::string_view s) + { + auto & res = + *new (resource.allocate(sizeof(StringData) + s.size() + 1, alignof(StringData))) StringData(s.size()); + std::memcpy(res.data_, s.data(), s.size()); + res.data_[s.size()] = '\0'; + return res; + } +}; + namespace detail { /** @@ -219,7 +310,7 @@ struct ValueBase */ struct StringWithContext { - const char * c_str; + const StringData * str; /** * The type of the context itself. @@ -234,7 +325,7 @@ struct ValueBase */ struct Context { - using value_type = const char *; + using value_type = const StringData *; using size_type = std::size_t; using iterator = const value_type *; @@ -285,7 +376,7 @@ struct ValueBase struct Path { SourceAccessor * accessor; - const char * path; + const StringData * path; }; struct Null @@ -646,13 +737,13 @@ protected: void getStorage(StringWithContext & string) const noexcept { string.context = untagPointer(payload[0]); - string.c_str = std::bit_cast(payload[1]); + string.str = std::bit_cast(payload[1]); } void getStorage(Path & path) const noexcept { path.accessor = untagPointer(payload[0]); - path.path = std::bit_cast(payload[1]); + path.path = std::bit_cast(payload[1]); } void setStorage(NixInt integer) noexcept @@ -697,7 +788,7 @@ protected: void setStorage(StringWithContext string) noexcept { - setUntaggablePayload(string.context, string.c_str); + setUntaggablePayload(string.context, string.str); } void setStorage(Path path) noexcept @@ -1050,22 +1141,22 @@ public: setStorage(b); } - void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept + void mkStringNoCopy(const StringData & s, const Value::StringWithContext::Context * context = nullptr) noexcept { - setStorage(StringWithContext{.c_str = s, .context = context}); + setStorage(StringWithContext{.str = &s, .context = context}); } void mkString(std::string_view s); void mkString(std::string_view s, const NixStringContext & context); - void mkStringMove(const char * s, const NixStringContext & context); + void mkStringMove(const StringData & s, const NixStringContext & context); void mkPath(const SourcePath & path); - inline void mkPath(SourceAccessor * accessor, const char * path) noexcept + inline void mkPath(SourceAccessor * accessor, const StringData & path) noexcept { - setStorage(Path{.accessor = accessor, .path = path}); + setStorage(Path{.accessor = accessor, .path = &path}); } inline void mkNull() noexcept @@ -1163,17 +1254,23 @@ public: SourcePath path() const { - return SourcePath(ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), pathStr())); + return SourcePath( + ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), std::string(pathStrView()))); } - std::string_view string_view() const noexcept + const StringData & string_data() const noexcept { - return std::string_view{getStorage().c_str}; + return *getStorage().str; } const char * c_str() const noexcept { - return getStorage().c_str; + return getStorage().str->data(); + } + + std::string_view string_view() const noexcept + { + return string_data().view(); } const Value::StringWithContext::Context * context() const noexcept @@ -1233,12 +1330,12 @@ public: const char * pathStr() const noexcept { - return getStorage().path; + return getStorage().path->c_str(); } std::string_view pathStrView() const noexcept { - return std::string_view{getStorage().path}; + return getStorage().path->view(); } SourceAccessor * pathAccessor() const noexcept diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0daeea77c..3b39b7f20 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -5,6 +5,7 @@ #include "nix/expr/eval-settings.hh" #include "nix/expr/gc-small-vector.hh" #include "nix/expr/json-to-value.hh" +#include "nix/expr/static-string-data.hh" #include "nix/store/globals.hh" #include "nix/store/names.hh" #include "nix/store/path-references.hh" @@ -487,34 +488,34 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, Valu state.forceValue(*args[0], pos); switch (args[0]->type()) { case nInt: - v.mkStringNoCopy("int"); + v.mkStringNoCopy("int"_sds); break; case nBool: - v.mkStringNoCopy("bool"); + v.mkStringNoCopy("bool"_sds); break; case nString: - v.mkStringNoCopy("string"); + v.mkStringNoCopy("string"_sds); break; case nPath: - v.mkStringNoCopy("path"); + v.mkStringNoCopy("path"_sds); break; case nNull: - v.mkStringNoCopy("null"); + v.mkStringNoCopy("null"_sds); break; case nAttrs: - v.mkStringNoCopy("set"); + v.mkStringNoCopy("set"_sds); break; case nList: - v.mkStringNoCopy("list"); + v.mkStringNoCopy("list"_sds); break; case nFunction: - v.mkStringNoCopy("lambda"); + v.mkStringNoCopy("lambda"_sds); break; case nExternal: v.mkString(args[0]->external()->typeOf()); break; case nFloat: - v.mkStringNoCopy("float"); + v.mkStringNoCopy("float"_sds); break; case nThunk: unreachable(); @@ -2024,9 +2025,9 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false); auto pos = path->rfind('/'); if (pos == path->npos) - v.mkStringMove(".", context); + v.mkStringMove("."_sds, context); else if (pos == 0) - v.mkStringMove("/", context); + v.mkStringMove("/"_sds, context); else v.mkString(path->substr(0, pos), context); } @@ -2309,10 +2310,10 @@ static const Value & fileTypeToString(EvalState & state, SourceAccessor::Type ty static const Constants stringValues = []() { Constants res; - res.regular.mkStringNoCopy("regular"); - res.directory.mkStringNoCopy("directory"); - res.symlink.mkStringNoCopy("symlink"); - res.unknown.mkStringNoCopy("unknown"); + res.regular.mkStringNoCopy("regular"_sds); + res.directory.mkStringNoCopy("directory"_sds); + res.symlink.mkStringNoCopy("symlink"_sds); + res.unknown.mkStringNoCopy("unknown"_sds); return res; }(); @@ -4463,7 +4464,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, V if (len == 0) { state.forceValue(*args[2], pos); if (args[2]->type() == nString) { - v.mkStringNoCopy("", args[2]->context()); + v.mkStringNoCopy(""_sds, args[2]->context()); return; } } diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 0d165f5c3..a06224fee 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -1,5 +1,6 @@ #include "nix/expr/primops.hh" #include "nix/expr/eval-inline.hh" +#include "nix/expr/static-string-data.hh" #include "expr-config-private.hh" @@ -136,7 +137,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va normalizeDatetimeFormat(t); #endif auto attrs = state.buildBindings(2); - attrs.alloc("_type").mkStringNoCopy("timestamp"); + attrs.alloc("_type").mkStringNoCopy("timestamp"_sds); std::ostringstream s; s << t; auto str = s.view(); diff --git a/src/nix/nix-env/user-env.cc b/src/nix/nix-env/user-env.cc index 81e2c4f80..21fdf25bc 100644 --- a/src/nix/nix-env/user-env.cc +++ b/src/nix/nix-env/user-env.cc @@ -9,6 +9,7 @@ #include "nix/expr/eval-inline.hh" #include "nix/store/profiles.hh" #include "nix/expr/print-ambiguous.hh" +#include "nix/expr/static-string-data.hh" #include #include @@ -56,7 +57,7 @@ bool createUserEnv( auto attrs = state.buildBindings(7 + outputs.size()); - attrs.alloc(state.s.type).mkStringNoCopy("derivation"); + attrs.alloc(state.s.type).mkStringNoCopy("derivation"_sds); attrs.alloc(state.s.name).mkString(i.queryName()); auto system = i.querySystem(); if (!system.empty())