Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libcmd/repl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
+ "\n\n";
}

markdown += stripIndentation(doc->doc);
markdown += stripIndentation(doc->doc.view());

logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else if (fallbackPos) {
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr-c/nix_api_value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ PrimOp * nix_alloc_primop(
.name = name,
.args = {},
.arity = (size_t) arity,
.doc = doc,
.doc = &nix::StringData::make(doc),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a reference to a local temporary StringData which promptly becomes a dangling pointer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is turning a reference into a pointer, and the reference is to a freshly heap-allocated thing, so actually it is fine.

(It errs on the side of leaking without GC, not on the side of the lifetime being too short and getting heap corruption.)

.fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)};
if (args)
for (size_t i = 0; args[i]; i++)
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr-tests/json.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"

namespace nix {
// Testing the conversion to JSON
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr-tests/value/print.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"

#include "nix/expr/value.hh"
#include "nix/expr/print.hh"
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr-tests/value/value.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"

#include "nix/store/tests/libstore.hh"
#include <gtest/gtest.h>
Expand Down
37 changes: 3 additions & 34 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,36 +51,6 @@ using json = nlohmann::json;

namespace nix {

/**
* Just for doc strings. Not for regular string values.
*/
static char * allocString(size_t size)
{
char * t;
t = (char *) GC_MALLOC_ATOMIC(size);
if (!t)
throw std::bad_alloc();
return t;
}

// When there's no need to write to the string, we can optimize away empty
// 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();
if (size == 0)
return "";
auto t = allocString(size + 1);
memcpy(t, s.data(), size);
t[size] = '\0';
return t;
}

StringData & StringData::alloc(size_t size)
{
void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1);
Expand Down Expand Up @@ -571,7 +541,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = v2->primOp()->name,
.arity = v2->primOp()->arity,
.args = v2->primOp()->args,
.doc = doc,
.doc = *doc,
};
}
if (v.isLambda()) {
Expand Down Expand Up @@ -613,9 +583,8 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = name,
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
.args = {},
/* 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()),
/* NOTE: memory leak when compiled without GC. */
.doc = StringData::make(s.view()),
};
}
if (isFunctor(v)) {
Expand Down
10 changes: 3 additions & 7 deletions src/libexpr/include/nix/expr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ struct PrimOp
/**
* Optional free-form documentation about the primop.
*/
const char * doc = nullptr;
const StringData * doc = nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrimOp is usually not traced, so these StringData would get collected and become a dangling reference.
It could be done by allocating the native primops on the heap or with traceable_allocator.

The C API does allocate them on the GC heap because I needed dynamically allocated ones for NixOps4, but the other, "static" primops will reference garbage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I made these all be statically allocated StringData with the _sds thing.


/**
* Add a trace item, while calling the `<name>` builtin.
Expand Down Expand Up @@ -156,7 +156,7 @@ struct Constant
/**
* Optional free-form documentation about the constant.
*/
const char * doc = nullptr;
const StringData * doc = nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same


/**
* Whether the constant is impure, and not available in pure mode.
Expand Down Expand Up @@ -837,11 +837,7 @@ public:
std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
/**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc;
const StringData & doc;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be similar to the RootValue solution so that Doc can be manipulated as an idiomatic C++ object.

};

/**
Expand Down
3 changes: 2 additions & 1 deletion src/libexpr/include/nix/expr/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ headers = [ config_pub_h ] + files(
'print.hh',
'repl-exit-status.hh',
'search-path.hh',
'static-string-data.hh',
'string-data.hh',
'string-data-static.hh',
'symbol-table.hh',
'value-to-json.hh',
'value-to-xml.hh',
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/include/nix/expr/nixexpr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +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/expr/string-data-static.hh"
#include "nix/util/pos-idx.hh"
#include "nix/expr/counter.hh"
#include "nix/util/pos-table.hh"
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/include/nix/expr/parser-state.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#include "nix/expr/eval.hh"
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"

namespace nix {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
///@file

#include "nix/expr/value.hh"
#include "nix/expr/string-data.hh"

namespace nix {

Expand Down
96 changes: 96 additions & 0 deletions src/libexpr/include/nix/expr/string-data.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#pragma once
///@file

#include <cstddef>
#include <cstring>
#include <memory_resource>
#include <string_view>

namespace nix {

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<size_t N>
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 nix
2 changes: 1 addition & 1 deletion src/libexpr/include/nix/expr/symbol-table.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include <memory_resource>
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/expr/string-data-static.hh"
#include "nix/util/chunked-vector.hh"
#include "nix/util/error.hh"

Expand Down
89 changes: 1 addition & 88 deletions src/libexpr/include/nix/expr/value.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@

#include <bit>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <memory>
#include <memory_resource>
#include <span>
#include <string_view>
#include <type_traits>
#include <concepts>

#include "nix/expr/eval-gc.hh"
#include "nix/expr/string-data.hh"
#include "nix/expr/value/context.hh"
#include "nix/util/source-path.hh"
#include "nix/expr/print-options.hh"
Expand Down Expand Up @@ -193,91 +191,6 @@ 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<size_t N>
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 {

/**
Expand Down
Loading
Loading