Compare commits

...

1 Commits

Author SHA1 Message Date
Sergei Zimmerman
2ded675e56 treewide: Make exceptions cloneable
This is needed to make it possible to store exceptions in failed values
with each new rethrow getting a fresh copy of the exception object.
2026-02-18 19:45:49 +03:00
25 changed files with 83 additions and 63 deletions

View File

@@ -11,7 +11,7 @@
namespace nix::eval_cache {
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
: CloneableError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
, cursor(cursor)
, attr(attr)
{

View File

@@ -14,7 +14,7 @@ namespace nix::eval_cache {
struct AttrDb;
class AttrCursor;
struct CachedEvalError : EvalError
struct CachedEvalError : CloneableError<CachedEvalError, EvalError>
{
const ref<AttrCursor> cursor;
const Symbol attr;

View File

@@ -18,7 +18,7 @@ class EvalErrorBuilder;
*
* Most subclasses should inherit from `EvalError` instead of this class.
*/
class EvalBaseError : public Error
class EvalBaseError : public CloneableError<EvalBaseError, Error>
{
template<class T>
friend class EvalErrorBuilder;
@@ -26,14 +26,14 @@ public:
EvalState & state;
EvalBaseError(EvalState & state, ErrorInfo && errorInfo)
: Error(errorInfo)
: CloneableError(errorInfo)
, state(state)
{
}
template<typename... Args>
explicit EvalBaseError(EvalState & state, const std::string & formatString, const Args &... formatArgs)
: Error(formatString, formatArgs...)
: CloneableError(formatString, formatArgs...)
, state(state)
{
}
@@ -60,23 +60,23 @@ MakeError(InfiniteRecursionError, EvalError);
* Inherits from EvalBaseError (not EvalError) because resource exhaustion
* should not be cached.
*/
struct StackOverflowError : public EvalBaseError
struct StackOverflowError : public CloneableError<StackOverflowError, EvalBaseError>
{
StackOverflowError(EvalState & state)
: EvalBaseError(state, "stack overflow; max-call-depth exceeded")
: CloneableError(state, "stack overflow; max-call-depth exceeded")
{
}
};
MakeError(IFDError, EvalBaseError);
struct InvalidPathError : public EvalError
struct InvalidPathError : public CloneableError<InvalidPathError, EvalError>
{
public:
Path path;
InvalidPathError(EvalState & state, const Path & path)
: EvalError(state, "path '%s' is not valid", path)
: CloneableError(state, "path '%s' is not valid", path)
{
}
};

View File

@@ -9,14 +9,14 @@
namespace nix {
class BadNixStringContextElem : public Error
class BadNixStringContextElem final : public CloneableError<BadNixStringContextElem, Error>
{
public:
std::string_view raw;
template<typename... Args>
BadNixStringContextElem(std::string_view raw_, const Args &... args)
: Error("")
: CloneableError("")
{
raw = raw_;
auto hf = HintFmt(args...);

View File

@@ -1312,11 +1312,12 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value ** args, Value
state.forceString(*args[0], pos, "while evaluating the first argument; the message passed to builtins.warn");
{
BaseError msg(std::string{msgStr});
msg.atPos(state.positions[pos]);
auto info = msg.info();
info.level = lvlWarn;
info.isFromExpr = true;
ErrorInfo info{
.level = lvlWarn,
.msg = HintFmt(std::string(msgStr)),
.pos = state.positions[pos],
.isFromExpr = true,
};
logWarning(info);
}

View File

@@ -74,11 +74,11 @@ namespace nix {
struct GitSourceAccessor;
struct GitError : public Error
struct GitError final : public CloneableError<GitError, Error>
{
template<typename... Ts>
GitError(const git_error & error, Ts &&... args)
: Error("")
: CloneableError("")
{
auto hf = HintFmt(std::forward<Ts>(args)...);
err.msg = HintFmt("%1%: %2% (libgit2 error code = %3%)", Uncolored(hf.str()), error.message, error.klass);

View File

@@ -28,7 +28,7 @@
namespace nix {
AwsAuthError::AwsAuthError(int errorCode)
: Error("AWS authentication error: '%s' (%d)", aws_error_str(errorCode), errorCode)
: CloneableError("AWS authentication error: '%s' (%d)", aws_error_str(errorCode), errorCode)
, errorCode(errorCode)
{
}

View File

@@ -1,11 +1,11 @@
#include "nix/store/build/goal.hh"
#include "nix/store/build/worker.hh"
#include "nix/store/globals.hh"
#include "nix/store/worker-settings.hh"
namespace nix {
TimedOut::TimedOut(time_t maxDuration)
: BuildError(BuildResult::Failure::TimedOut, "timed out after %1% seconds", maxDuration)
: CloneableError(BuildResult::Failure::TimedOut, "timed out after %1% seconds", maxDuration)
, maxDuration(maxDuration)
{
}

View File

@@ -60,12 +60,12 @@ namespace {
using curlSList = std::unique_ptr<::curl_slist, decltype([](::curl_slist * list) { ::curl_slist_free_all(list); })>;
using curlMulti = std::unique_ptr<::CURLM, decltype([](::CURLM * multi) { ::curl_multi_cleanup(multi); })>;
struct curlMultiError : Error
struct curlMultiError final : CloneableError<curlMultiError, Error>
{
::CURLMcode code;
curlMultiError(::CURLMcode code)
: Error{"unexpected curl multi error: %s", ::curl_multi_strerror(code)}
: CloneableError{"unexpected curl multi error: %s", ::curl_multi_strerror(code)}
{
assert(code != CURLM_OK);
}
@@ -1212,7 +1212,7 @@ void FileTransfer::download(
template<typename... Args>
FileTransferError::FileTransferError(
FileTransfer::Error error, std::optional<std::string> response, const Args &... args)
: Error(args...)
: CloneableError(args...)
, error(error)
, response(response)
{

View File

@@ -34,12 +34,13 @@ struct AwsCredentials
}
};
class AwsAuthError : public Error
class AwsAuthError final : public CloneableError<AwsAuthError, Error>
{
std::optional<int> errorCode;
public:
using Error::Error;
using CloneableError::CloneableError;
AwsAuthError(int errorCode);
std::optional<int> getErrorCode() const

View File

@@ -58,7 +58,7 @@ enum struct BuildResultFailureStatus : uint8_t {
* This is both an exception type (inherits from Error) and serves as
* the failure variant in BuildResult::inner.
*/
struct BuildError : public Error
struct BuildError : public CloneableError<BuildError, Error>
{
using Status = BuildResultFailureStatus;
using enum Status;
@@ -80,7 +80,7 @@ public:
*/
template<typename... Args>
BuildError(Status status, const Args &... args)
: Error(args...)
: CloneableError(args...)
, status{status}
{
}
@@ -97,7 +97,7 @@ public:
* Also used for deserialization.
*/
BuildError(Args args)
: Error(std::move(args.msg))
: CloneableError(std::move(args.msg))
, status{args.status}
, isNonDeterministic{args.isNonDeterministic}
@@ -108,7 +108,7 @@ public:
* Default constructor for deserialization.
*/
BuildError()
: Error("")
: CloneableError("")
{
}

View File

@@ -20,14 +20,14 @@ namespace nix {
* Denotes a build failure that stemmed from the builder exiting with a
* failing exist status.
*/
struct BuilderFailureError : BuildError
struct BuilderFailureError final : CloneableError<BuilderFailureError, BuildError>
{
int builderStatus;
std::string extraMsgAfter;
BuilderFailureError(BuildResult::Failure::Status status, int builderStatus, std::string extraMsgAfter)
: BuildError{
: CloneableError{
status,
/* No message for now, because the caller will make for
us, with extra context */

View File

@@ -10,7 +10,7 @@
namespace nix {
struct TimedOut : BuildError
struct TimedOut final : CloneableError<TimedOut, BuildError>
{
time_t maxDuration;

View File

@@ -22,7 +22,7 @@ struct Package
}
};
class BuildEnvFileConflictError : public Error
class BuildEnvFileConflictError final : public CloneableError<BuildEnvFileConflictError, Error>
{
public:
const Path fileA;
@@ -30,7 +30,7 @@ public:
int priority;
BuildEnvFileConflictError(const Path fileA, const Path fileB, int priority)
: Error(
: CloneableError(
"Unable to build profile. There is a conflict for the following files:\n"
"\n"
" %1%\n"

View File

@@ -403,7 +403,7 @@ ref<FileTransfer> getFileTransfer();
*/
ref<FileTransfer> makeFileTransfer(const FileTransferSettings & settings = fileTransferSettings);
class FileTransferError : public Error
class FileTransferError final : public CloneableError<FileTransferError, Error>
{
public:
FileTransfer::Error error;

View File

@@ -146,7 +146,7 @@ struct RealisedPath
auto operator<=>(const RealisedPath &) const = default;
};
class MissingRealisation : public Error
class MissingRealisation final : public CloneableError<MissingRealisation, Error>
{
public:
MissingRealisation(DrvOutput & outputId)
@@ -155,7 +155,7 @@ public:
}
MissingRealisation(std::string_view drv, OutputName outputName)
: Error(
: CloneableError(
"cannot operate on output '%s' of the "
"unbuilt derivation '%s'",
outputName,

View File

@@ -166,7 +166,7 @@ struct SQLiteTxn
~SQLiteTxn();
};
struct SQLiteError : Error
struct SQLiteError : CloneableError<SQLiteError, Error>
{
std::string path;
std::string errMsg;

View File

@@ -17,7 +17,7 @@ namespace nix {
SQLiteError::SQLiteError(
const char * path, const char * errMsg, int errNo, int extendedErrNo, int offset, HintFmt && hf)
: Error("")
: CloneableError("")
, path(path)
, errMsg(errMsg)
, errNo(errNo)

View File

@@ -18,11 +18,11 @@ static std::string parsePublicHostKey(std::string_view host, std::string_view ss
}
}
class InvalidSSHAuthority : public Error
class InvalidSSHAuthority final : public CloneableError<InvalidSSHAuthority, Error>
{
public:
InvalidSSHAuthority(const ParsedURL::Authority & authority, std::string_view reason)
: Error("invalid SSH authority: '%s': %s", authority.to_string(), reason)
: CloneableError("invalid SSH authority: '%s': %s", authority.to_string(), reason)
{
}
};

View File

@@ -55,10 +55,10 @@
namespace nix {
struct NotDeterministic : BuildError
struct NotDeterministic final : CloneableError<NotDeterministic, BuildError>
{
NotDeterministic(auto &&... args)
: BuildError(BuildResult::Failure::NotDeterministic, args...)
: CloneableError(BuildResult::Failure::NotDeterministic, args...)
{
isNonDeterministic = true;
}

View File

@@ -378,7 +378,7 @@ std::set<ExperimentalFeature> parseFeatures(const StringSet & rawFeatures)
}
MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature, std::string reason)
: Error(
: CloneableError(
"experimental Nix feature '%1%' is disabled%2%; add '--extra-experimental-features %1%' to enable it",
showExperimentalFeature(feature),
Uncolored(optionalBracket(" (", reason, ")")))

View File

@@ -226,13 +226,31 @@ public:
{
return err;
};
[[noreturn]] virtual void throwClone() const = 0;
};
#define MakeError(newClass, superClass) \
class newClass : public superClass \
{ \
public: \
using superClass::superClass; \
template<typename Derived, typename Base>
class CloneableError : public Base
{
public:
using Base::Base;
/**
* Rethrow a copy of this exception. Useful when the exception can get
* modified when appending traces.
*/
[[noreturn]] void throwClone() const override
{
throw Derived(static_cast<const Derived &>(*this));
}
};
#define MakeError(newClass, superClass) \
class newClass : public CloneableError<newClass, superClass> \
{ \
public: \
using CloneableError<newClass, superClass>::CloneableError; \
}
MakeError(Error, BaseError);
@@ -244,21 +262,21 @@ MakeError(UnimplementedError, Error);
* std::error_code. Use when you want to catch and check an error condition like
* no_such_file_or_directory (ENOENT) without ifdefs.
*/
class SystemError : public Error
class SystemError : public CloneableError<SystemError, Error>
{
std::error_code errorCode;
public:
template<typename... Args>
SystemError(std::errc posixErrNo, Args &&... args)
: Error(std::forward<Args>(args)...)
: CloneableError(std::forward<Args>(args)...)
, errorCode(std::make_error_code(posixErrNo))
{
}
template<typename... Args>
SystemError(std::error_code errorCode, Args &&... args)
: Error(std::forward<Args>(args)...)
: CloneableError(std::forward<Args>(args)...)
, errorCode(errorCode)
{
}
@@ -290,7 +308,7 @@ public:
* support is too WIP to justify the code churn, but if it is finished
* then a better identifier becomes moe worth it.
*/
class SysError : public SystemError
class SysError final : public CloneableError<SysError, SystemError>
{
public:
int errNo;
@@ -301,7 +319,7 @@ public:
*/
template<typename... Args>
SysError(int errNo, const Args &... args)
: SystemError(static_cast<std::errc>(errNo), "")
: CloneableError(static_cast<std::errc>(errNo), "")
, errNo(errNo)
{
auto hf = HintFmt(args...);
@@ -369,7 +387,7 @@ namespace windows {
* Unless you need to catch a specific error number, don't catch this in
* portable code. Catch `SystemError` instead.
*/
class WinError : public SystemError
class WinError : public CloneableError<WinError, SystemError>
{
public:
DWORD lastError;
@@ -381,7 +399,7 @@ public:
*/
template<typename... Args>
WinError(DWORD lastError, const Args &... args)
: SystemError(std::error_code(lastError, std::system_category()), "")
: CloneableError(std::error_code(lastError, std::system_category()), "")
, lastError(lastError)
{
auto hf = HintFmt(args...);

View File

@@ -80,7 +80,7 @@ std::set<ExperimentalFeature> parseFeatures(const StringSet &);
* An experimental feature was required for some (experimental)
* operation, but was not enabled.
*/
class MissingExperimentalFeature : public Error
class MissingExperimentalFeature final : public CloneableError<MissingExperimentalFeature, Error>
{
public:
/**

View File

@@ -133,14 +133,14 @@ std::pair<int, std::string> runProgram(RunOptions && options);
void runProgram2(const RunOptions & options);
class ExecError : public Error
class ExecError final : public CloneableError<ExecError, Error>
{
public:
int status;
template<typename... Args>
ExecError(int status, const Args &... args)
: Error(args...)
: CloneableError(args...)
, status(status)
{
}

View File

@@ -231,19 +231,19 @@ ref<SourceAccessor> makeEmptySourceAccessor();
*/
MakeError(RestrictedPathError, Error);
struct SymlinkNotAllowed : public Error
struct SymlinkNotAllowed final : public CloneableError<SymlinkNotAllowed, Error>
{
CanonPath path;
SymlinkNotAllowed(CanonPath path)
: Error("relative path '%s' points to a symlink, which is not allowed", path.rel())
: CloneableError("relative path '%s' points to a symlink, which is not allowed", path.rel())
, path(std::move(path))
{
}
template<typename... Args>
SymlinkNotAllowed(CanonPath path, const std::string & fs, Args &&... args)
: Error(fs, std::forward<Args>(args)...)
: CloneableError(fs, std::forward<Args>(args)...)
, path(std::move(path))
{
}