mirror of
https://github.com/NixOS/nix.git
synced 2025-11-27 20:51:00 +01:00
Merge remote-tracking branch 'origin/2.29-maintenance' into detsys-main
This commit is contained in:
commit
c20642ac7b
354 changed files with 6768 additions and 3808 deletions
|
|
@ -74,7 +74,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
|
||||
auto a = v->attrs()->get(state.symbols.create(attr));
|
||||
if (!a) {
|
||||
std::set<std::string> attrNames;
|
||||
StringSet attrNames;
|
||||
for (auto & attr : *v->attrs())
|
||||
attrNames.insert(std::string(state.symbols[attr.name]));
|
||||
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ Value & AttrCursor::forceValue()
|
|||
Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
|
||||
{
|
||||
auto attrNames = getAttrs();
|
||||
std::set<std::string> strAttrNames;
|
||||
StringSet strAttrNames;
|
||||
for (auto & name : attrNames)
|
||||
strAttrNames.insert(std::string(root->state.symbols[name]));
|
||||
|
||||
|
|
|
|||
|
|
@ -110,5 +110,6 @@ template class EvalErrorBuilder<UndefinedVarError>;
|
|||
template class EvalErrorBuilder<MissingArgumentError>;
|
||||
template class EvalErrorBuilder<InfiniteRecursionError>;
|
||||
template class EvalErrorBuilder<InvalidPathError>;
|
||||
template class EvalErrorBuilder<IFDError>;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,7 +252,25 @@ EvalState::EvalState(
|
|||
makeMountedSourceAccessor(
|
||||
{
|
||||
{CanonPath::root, makeEmptySourceAccessor()},
|
||||
{CanonPath(store->storeDir), makeFSSourceAccessor(dirOf(store->toRealPath(StorePath::dummy)))}
|
||||
/* In the pure eval case, we can simply require
|
||||
valid paths. However, in the *impure* eval
|
||||
case this gets in the way of the union
|
||||
mechanism, because an invalid access in the
|
||||
upper layer will *not* be caught by the union
|
||||
source accessor, but instead abort the entire
|
||||
lookup.
|
||||
|
||||
This happens when the store dir in the
|
||||
ambient file system has a path (e.g. because
|
||||
another Nix store there), but the relocated
|
||||
store does not.
|
||||
|
||||
TODO make the various source accessors doing
|
||||
access control all throw the same type of
|
||||
exception, and make union source accessor
|
||||
catch it, so we don't need to do this hack.
|
||||
*/
|
||||
{CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)},
|
||||
}))
|
||||
, rootFS(
|
||||
({
|
||||
|
|
@ -1435,7 +1453,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
} else {
|
||||
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
||||
if (!(j = vAttrs->attrs()->get(name))) {
|
||||
std::set<std::string> allAttrNames;
|
||||
StringSet allAttrNames;
|
||||
for (auto & attr : *vAttrs->attrs())
|
||||
allAttrNames.insert(std::string(state.symbols[attr.name]));
|
||||
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||
|
|
@ -1592,7 +1610,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
|||
user. */
|
||||
for (auto & i : *args[0]->attrs())
|
||||
if (!lambda.formals->has(i.name)) {
|
||||
std::set<std::string> formalNames;
|
||||
StringSet formalNames;
|
||||
for (auto & formal : lambda.formals->formals)
|
||||
formalNames.insert(std::string(symbols[formal.name]));
|
||||
auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ MakeError(TypeError, EvalError);
|
|||
MakeError(UndefinedVarError, EvalError);
|
||||
MakeError(MissingArgumentError, EvalError);
|
||||
MakeError(InfiniteRecursionError, EvalError);
|
||||
MakeError(IFDError, EvalBaseError);
|
||||
|
||||
struct InvalidPathError : public EvalError
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,12 @@ constexpr size_t conservativeStackReservation = 16;
|
|||
struct RegisterPrimOp
|
||||
{
|
||||
typedef std::vector<PrimOp> PrimOps;
|
||||
static PrimOps * primOps;
|
||||
|
||||
static PrimOps & primOps()
|
||||
{
|
||||
static PrimOps primOps;
|
||||
return primOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* You can register a constant by passing an arity of 0. fun
|
||||
|
|
|
|||
|
|
@ -81,26 +81,29 @@ public:
|
|||
class SymbolTable
|
||||
{
|
||||
private:
|
||||
std::unordered_map<std::string_view, std::pair<const std::string *, uint32_t>> symbols;
|
||||
/**
|
||||
* Map from string view (backed by ChunkedVector) -> offset into the store.
|
||||
* ChunkedVector references are never invalidated.
|
||||
*/
|
||||
std::unordered_map<std::string_view, uint32_t> symbols;
|
||||
ChunkedVector<std::string, 8192> store{16};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* converts a string into a symbol.
|
||||
* Converts a string into a symbol.
|
||||
*/
|
||||
Symbol create(std::string_view s)
|
||||
{
|
||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||
// for lookup performance.
|
||||
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
||||
// on the original implementation using unordered_set
|
||||
// FIXME: make this thread-safe.
|
||||
auto it = symbols.find(s);
|
||||
if (it != symbols.end()) return Symbol(it->second.second + 1);
|
||||
if (it != symbols.end())
|
||||
return Symbol(it->second + 1);
|
||||
|
||||
const auto & [rawSym, idx] = store.add(std::string(s));
|
||||
symbols.emplace(rawSym, std::make_pair(&rawSym, idx));
|
||||
const auto & [rawSym, idx] = store.add(s);
|
||||
symbols.emplace(rawSym, idx);
|
||||
return Symbol(idx + 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
%option 8bit
|
||||
%option reentrant bison-bridge bison-locations
|
||||
%option align
|
||||
%option noyywrap
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ lexer_tab = custom_target(
|
|||
],
|
||||
command : [
|
||||
'flex',
|
||||
'-Cf', # Use full scanner tables
|
||||
'--outfile',
|
||||
'@OUTPUT0@',
|
||||
'--header-file=' + '@OUTPUT1@',
|
||||
|
|
|
|||
|
|
@ -179,7 +179,12 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) {
|
|||
|
||||
%%
|
||||
|
||||
start: expr { state->result = $1; };
|
||||
start: expr {
|
||||
state->result = $1;
|
||||
|
||||
// This parser does not use yynerrs; suppress the warning.
|
||||
(void) yynerrs;
|
||||
};
|
||||
|
||||
expr: expr_function;
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
|
|||
if (drvs.empty()) return {};
|
||||
|
||||
if (isIFD && !settings.enableImportFromDerivation)
|
||||
error<EvalBaseError>(
|
||||
error<IFDError>(
|
||||
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
|
||||
drvs.begin()->to_string(*store)
|
||||
).debugThrow();
|
||||
|
|
@ -895,18 +895,40 @@ static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
{
|
||||
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos),
|
||||
"while evaluating the first argument passed to builtins.ceil");
|
||||
v.mkInt(ceil(value));
|
||||
auto ceilValue = ceil(value);
|
||||
bool isInt = args[0]->type() == nInt;
|
||||
constexpr NixFloat int_min = std::numeric_limits<NixInt::Inner>::min(); // power of 2, so that no rounding occurs
|
||||
if (ceilValue >= int_min && ceilValue < -int_min) {
|
||||
v.mkInt(ceilValue);
|
||||
} else if (isInt) {
|
||||
// a NixInt, e.g. INT64_MAX, can be rounded to -int_min due to the cast to NixFloat
|
||||
state.error<EvalError>("Due to a bug (see https://github.com/NixOS/nix/issues/12899) the NixInt argument %1% caused undefined behavior in previous Nix versions.\n\tFuture Nix versions might implement the correct behavior.", args[0]->integer().value).atPos(pos).debugThrow();
|
||||
} else {
|
||||
state.error<EvalError>("NixFloat argument %1% is not in the range of NixInt", args[0]->fpoint()).atPos(pos).debugThrow();
|
||||
}
|
||||
// `forceFloat` casts NixInt to NixFloat, but instead NixInt args shall be returned unmodified
|
||||
if (isInt) {
|
||||
auto arg = args[0]->integer();
|
||||
auto res = v.integer();
|
||||
if (arg != res) {
|
||||
state.error<EvalError>("Due to a bug (see https://github.com/NixOS/nix/issues/12899) a loss of precision occured in previous Nix versions because the NixInt argument %1% was rounded to %2%.\n\tFuture Nix versions might implement the correct behavior.", arg, res).atPos(pos).debugThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_ceil({
|
||||
.name = "__ceil",
|
||||
.args = {"double"},
|
||||
.args = {"number"},
|
||||
.doc = R"(
|
||||
Converts an IEEE-754 double-precision floating-point number (*double*) to
|
||||
the next higher integer.
|
||||
Rounds and converts *number* to the next higher NixInt value if possible, i.e. `ceil *number* >= *number*` and
|
||||
`ceil *number* - *number* < 1`.
|
||||
|
||||
If the datatype is neither an integer nor a "float", an evaluation error will be
|
||||
thrown.
|
||||
An evaluation error is thrown, if there exists no such NixInt value `ceil *number*`.
|
||||
Due to bugs in previous Nix versions an evaluation error might be thrown, if the datatype of *number* is
|
||||
a NixInt and if `*number* < -9007199254740992` or `*number* > 9007199254740992`.
|
||||
|
||||
If the datatype of *number* is neither a NixInt (signed 64-bit integer) nor a NixFloat
|
||||
(IEEE-754 double-precision floating-point number), an evaluation error will be thrown.
|
||||
)",
|
||||
.fun = prim_ceil,
|
||||
});
|
||||
|
|
@ -914,18 +936,40 @@ static RegisterPrimOp primop_ceil({
|
|||
static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "while evaluating the first argument passed to builtins.floor");
|
||||
v.mkInt(floor(value));
|
||||
auto floorValue = floor(value);
|
||||
bool isInt = args[0]->type() == nInt;
|
||||
constexpr NixFloat int_min = std::numeric_limits<NixInt::Inner>::min(); // power of 2, so that no rounding occurs
|
||||
if (floorValue >= int_min && floorValue < -int_min) {
|
||||
v.mkInt(floorValue);
|
||||
} else if (isInt) {
|
||||
// a NixInt, e.g. INT64_MAX, can be rounded to -int_min due to the cast to NixFloat
|
||||
state.error<EvalError>("Due to a bug (see https://github.com/NixOS/nix/issues/12899) the NixInt argument %1% caused undefined behavior in previous Nix versions.\n\tFuture Nix versions might implement the correct behavior.", args[0]->integer().value).atPos(pos).debugThrow();
|
||||
} else {
|
||||
state.error<EvalError>("NixFloat argument %1% is not in the range of NixInt", args[0]->fpoint()).atPos(pos).debugThrow();
|
||||
}
|
||||
// `forceFloat` casts NixInt to NixFloat, but instead NixInt args shall be returned unmodified
|
||||
if (isInt) {
|
||||
auto arg = args[0]->integer();
|
||||
auto res = v.integer();
|
||||
if (arg != res) {
|
||||
state.error<EvalError>("Due to a bug (see https://github.com/NixOS/nix/issues/12899) a loss of precision occured in previous Nix versions because the NixInt argument %1% was rounded to %2%.\n\tFuture Nix versions might implement the correct behavior.", arg, res).atPos(pos).debugThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_floor({
|
||||
.name = "__floor",
|
||||
.args = {"double"},
|
||||
.args = {"number"},
|
||||
.doc = R"(
|
||||
Converts an IEEE-754 double-precision floating-point number (*double*) to
|
||||
the next lower integer.
|
||||
Rounds and converts *number* to the next lower NixInt value if possible, i.e. `floor *number* <= *number*` and
|
||||
`*number* - floor *number* < 1`.
|
||||
|
||||
If the datatype is neither an integer nor a "float", an evaluation error will be
|
||||
thrown.
|
||||
An evaluation error is thrown, if there exists no such NixInt value `floor *number*`.
|
||||
Due to bugs in previous Nix versions an evaluation error might be thrown, if the datatype of *number* is
|
||||
a NixInt and if `*number* < -9007199254740992` or `*number* > 9007199254740992`.
|
||||
|
||||
If the datatype of *number* is neither a NixInt (signed 64-bit integer) nor a NixFloat
|
||||
(IEEE-754 double-precision floating-point number), an evaluation error will be thrown.
|
||||
)",
|
||||
.fun = prim_floor,
|
||||
});
|
||||
|
|
@ -2813,7 +2857,13 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * *
|
|||
|
||||
static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
|
||||
.name = "__unsafeGetAttrPos",
|
||||
.args = {"s", "set"},
|
||||
.arity = 2,
|
||||
.doc = R"(
|
||||
`unsafeGetAttrPos` returns the position of the attribute named *s*
|
||||
from *set*. This is used by Nixpkgs to provide location information
|
||||
in error messages.
|
||||
)",
|
||||
.fun = prim_unsafeGetAttrPos,
|
||||
});
|
||||
|
||||
|
|
@ -4299,9 +4349,7 @@ struct RegexCache
|
|||
{
|
||||
struct State
|
||||
{
|
||||
// TODO use C++20 transparent comparison when available
|
||||
std::unordered_map<std::string_view, std::regex> cache;
|
||||
std::list<std::string> keys;
|
||||
std::unordered_map<std::string, std::regex, StringViewHash, std::equal_to<>> cache;
|
||||
};
|
||||
|
||||
Sync<State> state_;
|
||||
|
|
@ -4312,8 +4360,14 @@ struct RegexCache
|
|||
auto it = state->cache.find(re);
|
||||
if (it != state->cache.end())
|
||||
return it->second;
|
||||
state->keys.emplace_back(re);
|
||||
return state->cache.emplace(state->keys.back(), std::regex(state->keys.back(), std::regex::extended)).first->second;
|
||||
/* No std::regex constructor overload from std::string_view, but can be constructed
|
||||
from a pointer + size or an iterator range. */
|
||||
return state->cache
|
||||
.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(re),
|
||||
std::forward_as_tuple(/*s=*/re.data(), /*count=*/re.size(), std::regex::extended))
|
||||
.first->second;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -4697,13 +4751,9 @@ static RegisterPrimOp primop_splitVersion({
|
|||
*************************************************************/
|
||||
|
||||
|
||||
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
||||
|
||||
|
||||
RegisterPrimOp::RegisterPrimOp(PrimOp && primOp)
|
||||
{
|
||||
if (!primOps) primOps = new PrimOps;
|
||||
primOps->push_back(std::move(primOp));
|
||||
primOps().push_back(std::move(primOp));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4957,14 +5007,18 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
|
|||
)",
|
||||
});
|
||||
|
||||
if (RegisterPrimOp::primOps)
|
||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||
if (experimentalFeatureSettings.isEnabled(primOp.experimentalFeature))
|
||||
{
|
||||
auto primOpAdjusted = primOp;
|
||||
primOpAdjusted.arity = std::max(primOp.args.size(), primOp.arity);
|
||||
addPrimOp(std::move(primOpAdjusted));
|
||||
}
|
||||
for (auto & primOp : RegisterPrimOp::primOps())
|
||||
if (experimentalFeatureSettings.isEnabled(primOp.experimentalFeature)) {
|
||||
auto primOpAdjusted = primOp;
|
||||
primOpAdjusted.arity = std::max(primOp.args.size(), primOp.arity);
|
||||
addPrimOp(std::move(primOpAdjusted));
|
||||
}
|
||||
|
||||
for (auto & primOp : evalSettings.extraPrimOps) {
|
||||
auto primOpAdjusted = primOp;
|
||||
primOpAdjusted.arity = std::max(primOp.args.size(), primOp.arity);
|
||||
addPrimOp(std::move(primOpAdjusted));
|
||||
}
|
||||
|
||||
for (auto & primOp : evalSettings.extraPrimOps) {
|
||||
auto primOpAdjusted = primOp;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include "nix/expr/primops.hh"
|
||||
#include "nix/store/store-api.hh"
|
||||
#include "nix/store/store-open.hh"
|
||||
#include "nix/store/realisation.hh"
|
||||
#include "nix/store/make-content-addressed.hh"
|
||||
#include "nix/util/url.hh"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace nix {
|
|||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// TODO: rename. It doesn't print.
|
||||
json printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue