Skip to content

Commit

Permalink
builder
Browse files Browse the repository at this point in the history
  • Loading branch information
9999years committed Jan 24, 2024
1 parent 95d7834 commit c70d608
Show file tree
Hide file tree
Showing 29 changed files with 291 additions and 235 deletions.
8 changes: 4 additions & 4 deletions src/libexpr/attr-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
if (!attrIndex) {

if (v->type() != nAttrs)
throw TypeError(
EvalErrorBuilder<TypeError>(
state,
"the expression selected by the selection path '%1%' should be a set but is %2%",
attrPath,
showType(*v));
showType(*v)).debugThrow();
if (attr.empty())
throw Error("empty attribute name in selection path '%1%'", attrPath);

Expand All @@ -89,11 +89,11 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
else {

if (!v->isList())
throw TypeError(
EvalErrorBuilder<TypeError>(
state,
"the expression selected by the selection path '%1%' should be a list but is %2%",
attrPath,
showType(*v));
showType(*v)).debugThrow();
if (*attrIndex >= v->listSize())
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);

Expand Down
28 changes: 14 additions & 14 deletions src/libexpr/eval-cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -500,15 +500,15 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
// evaluate to see whether 'name' exists
} else
return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
//EvalErrorBuilder<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
}
}

auto & v = forceValue();

if (v.type() != nAttrs)
return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
//EvalErrorBuilder<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();

auto attr = v.attrs->get(name);

Expand Down Expand Up @@ -574,14 +574,14 @@ std::string AttrCursor::getString()
debug("using cached string attribute '%s'", getAttrPathStr());
return s->first;
} else
TypeError(root->state, "'%s' is not a string", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a string", getAttrPathStr()).debugThrow();
}
}

auto & v = forceValue();

if (v.type() != nString && v.type() != nPath)
TypeError(root->state, "'%s' is not a string but %s", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a string but %s", getAttrPathStr()).debugThrow();

return v.type() == nString ? v.c_str() : v.path().to_string();
}
Expand Down Expand Up @@ -616,7 +616,7 @@ string_t AttrCursor::getStringWithContext()
return *s;
}
} else
TypeError(root->state, "'%s' is not a string", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a string", getAttrPathStr()).debugThrow();
}
}

Expand All @@ -630,7 +630,7 @@ string_t AttrCursor::getStringWithContext()
else if (v.type() == nPath)
return {v.path().to_string(), {}};
else
TypeError(root->state, "'%s' is not a string but %s", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a string but %s", getAttrPathStr()).debugThrow();
}

bool AttrCursor::getBool()
Expand All @@ -643,14 +643,14 @@ bool AttrCursor::getBool()
debug("using cached Boolean attribute '%s'", getAttrPathStr());
return *b;
} else
TypeError(root->state, "'%s' is not a Boolean", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a Boolean", getAttrPathStr()).debugThrow();
}
}

auto & v = forceValue();

if (v.type() != nBool)
TypeError(root->state, "'%s' is not a Boolean", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a Boolean", getAttrPathStr()).debugThrow();

return v.boolean;
}
Expand All @@ -665,14 +665,14 @@ NixInt AttrCursor::getInt()
debug("using cached integer attribute '%s'", getAttrPathStr());
return i->x;
} else
throw TypeError(root->state, "'%s' is not an integer", getAttrPathStr());
EvalErrorBuilder<TypeError>(root->state, "'%s' is not an integer", getAttrPathStr()).debugThrow();
}
}

auto & v = forceValue();

if (v.type() != nInt)
throw TypeError(root->state, "'%s' is not an integer", getAttrPathStr());
EvalErrorBuilder<TypeError>(root->state, "'%s' is not an integer", getAttrPathStr()).debugThrow();

return v.integer;
}
Expand All @@ -687,7 +687,7 @@ std::vector<std::string> AttrCursor::getListOfStrings()
debug("using cached list of strings attribute '%s'", getAttrPathStr());
return *l;
} else
throw TypeError(root->state, "'%s' is not a list of strings", getAttrPathStr());
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a list of strings", getAttrPathStr()).debugThrow();
}
}

Expand All @@ -697,7 +697,7 @@ std::vector<std::string> AttrCursor::getListOfStrings()
root->state.forceValue(v, noPos);

if (v.type() != nList)
throw TypeError(root->state, "'%s' is not a list", getAttrPathStr());
EvalErrorBuilder<TypeError>(root->state, "'%s' is not a list", getAttrPathStr()).debugThrow();

std::vector<std::string> res;

Expand All @@ -720,14 +720,14 @@ std::vector<Symbol> AttrCursor::getAttrs()
debug("using cached attrset attribute '%s'", getAttrPathStr());
return *attrs;
} else
TypeError(root->state, "'%s' is not an attribute set", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not an attribute set", getAttrPathStr()).debugThrow();
}
}

auto & v = forceValue();

if (v.type() != nAttrs)
TypeError(root->state, "'%s' is not an attribute set", getAttrPathStr()).debugThrow();
EvalErrorBuilder<TypeError>(root->state, "'%s' is not an attribute set", getAttrPathStr()).debugThrow();

std::vector<Symbol> attrs;
for (auto & attr : *getValue().attrs)
Expand Down
69 changes: 49 additions & 20 deletions src/libexpr/eval-error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,97 @@

namespace nix {

EvalError & EvalError::atPos(PosIdx pos)
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus)
{
err.errPos = state.positions[pos];
error.withExitStatus(exitStatus);
return *this;
}

EvalError & EvalError::withTrace(PosIdx pos, const std::string_view text)
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::atPos(PosIdx pos)
{
err.traces.push_front(Trace{.pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false});
error.err.pos = error.state.positions[pos];
return *this;
}

EvalError & EvalError::withFrameTrace(PosIdx pos, const std::string_view text)
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text)
{
err.traces.push_front(Trace{.pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true});
error.err.traces.push_front(
Trace{.pos = error.state.positions[pos], .hint = hintformat(std::string(text)), .frame = false});
return *this;
}

EvalError & EvalError::withSuggestions(Suggestions & s)
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrameTrace(PosIdx pos, const std::string_view text)
{
err.suggestions = s;
error.err.traces.push_front(
Trace{.pos = error.state.positions[pos], .hint = hintformat(std::string(text)), .frame = true});
return *this;
}

EvalError & EvalError::withFrame(const Env & env, const Expr & expr)
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withSuggestions(Suggestions & s)
{
error.err.suggestions = s;
return *this;
}

template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr & expr)
{
// NOTE: This is abusing side-effects.
// TODO: check compatibility with nested debugger calls.
// TODO: What side-effects??
state.debugTraces.push_front(DebugTrace{
.pos = state.positions[expr.getPos()],
error.state.debugTraces.push_front(DebugTrace{
.pos = error.state.positions[expr.getPos()],
.expr = expr,
.env = env,
.hint = hintformat("Fake frame for debugging purposes"),
.isError = true});
return *this;
}

EvalError & EvalError::addTrace(PosIdx pos, hintformat hint, bool frame)
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, hintformat hint, bool frame)
{
BaseError::addTrace(state.positions[pos], hint, frame);
error.addTrace(error.state.positions[pos], hint, frame);
return *this;
}

template<class T>
template<typename... Args>
EvalError & EvalError::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs)
EvalErrorBuilder<T> &
EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs)
{

addTrace(state.positions[pos], hintfmt(std::string(formatString), formatArgs...));
addTrace(error.state.positions[pos], hintfmt(std::string(formatString), formatArgs...));
return *this;
}

void EvalError::debugThrow()
template<class T>
void EvalErrorBuilder<T>::debugThrow()
{
if (state.debugRepl && !state.debugTraces.empty()) {
const DebugTrace & last = state.debugTraces.front();
if (error.state.debugRepl && !error.state.debugTraces.empty()) {
const DebugTrace & last = error.state.debugTraces.front();
const Env * env = &last.env;
const Expr * expr = &last.expr;
state.runDebugRepl(this, *env, *expr);
error.state.runDebugRepl(&error, *env, *expr);
}

throw std::move(*this);
throw error;
}

template class EvalErrorBuilder<EvalError>;
template class EvalErrorBuilder<AssertionError>;
template class EvalErrorBuilder<ThrownError>;
template class EvalErrorBuilder<Abort>;
template class EvalErrorBuilder<TypeError>;
template class EvalErrorBuilder<UndefinedVarError>;
template class EvalErrorBuilder<MissingArgumentError>;
template class EvalErrorBuilder<InfiniteRecursionError>;
template class EvalErrorBuilder<CachedEvalError>;
template class EvalErrorBuilder<InvalidPathError>;

}
68 changes: 48 additions & 20 deletions src/libexpr/eval-error.hh
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ struct Env;
struct Expr;

class EvalState;
template<class T>
class EvalErrorBuilder;

class EvalError : public Error
{
template<class T>
friend class EvalErrorBuilder;
public:
EvalState & state;

Expand All @@ -27,23 +31,6 @@ public:
, state(state)
{
}

[[nodiscard, gnu::noinline]] EvalError & atPos(PosIdx pos);

[[nodiscard, gnu::noinline]] EvalError & withTrace(PosIdx pos, const std::string_view text);

[[nodiscard, gnu::noinline]] EvalError & withFrameTrace(PosIdx pos, const std::string_view text);

[[nodiscard, gnu::noinline]] EvalError & withSuggestions(Suggestions & s);

[[nodiscard, gnu::noinline]] EvalError & withFrame(const Env & e, const Expr & ex);

[[nodiscard, gnu::noinline]] EvalError & addTrace(PosIdx pos, hintformat hint, bool frame = false);

template<typename... Args>
EvalError & addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs);

[[gnu::noinline, gnu::noreturn]] void debugThrow();
};

MakeError(ParseError, Error);
Expand All @@ -53,12 +40,53 @@ MakeError(Abort, EvalError);
MakeError(TypeError, EvalError);
MakeError(UndefinedVarError, EvalError);
MakeError(MissingArgumentError, EvalError);
MakeError(CachedEvalError, EvalError);
MakeError(InfiniteRecursionError, EvalError);

struct InvalidPathError : public EvalError
{
public:
Path path;
InvalidPathError(EvalState & state, const Path & path)
: EvalError(state, "path '%s' is not valid", path)
{
}
#ifdef EXCEPTION_NEEDS_THROW_SPEC
~InvalidPathError() throw(){};
#endif
};

class InfiniteRecursionError : public EvalError
template<class T>
class EvalErrorBuilder
{
friend class EvalState;
public:
using EvalError::EvalError;
T error;

template<typename... Args>
explicit EvalErrorBuilder(EvalState & state, const Args &... args)
: error(T(state, args...))
{
}

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withExitStatus(unsigned int exitStatus);

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(PosIdx pos);

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withTrace(PosIdx pos, const std::string_view text);

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrameTrace(PosIdx pos, const std::string_view text);

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withSuggestions(Suggestions & s);

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);

[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, hintformat hint, bool frame = false);

template<typename... Args>
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &
addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs);

[[gnu::noinline, gnu::noreturn]] virtual void debugThrow();
};

}
4 changes: 2 additions & 2 deletions src/libexpr/eval-inline.hh
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e
PosIdx pos = getPos();
forceValue(v, pos);
if (v.type() != nAttrs) {
TypeError(
EvalErrorBuilder<TypeError>(
*this,
"expected a set but found %1%: %2%",
showType(v),
Expand All @@ -131,7 +131,7 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e
{
forceValue(v, pos);
if (!v.isList()) {
TypeError(
EvalErrorBuilder<TypeError>(
*this,
"expected a list but found %1%: %2%",
showType(v),
Expand Down
Loading

0 comments on commit c70d608

Please sign in to comment.