diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 4f6c5fe8e..b2d510ce0 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -11,7 +11,7 @@ namespace nix::eval_cache { CachedEvalError::CachedEvalError(ref 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) { diff --git a/src/libexpr/include/nix/expr/eval-cache.hh b/src/libexpr/include/nix/expr/eval-cache.hh index 6d82f8c7e..165b27420 100644 --- a/src/libexpr/include/nix/expr/eval-cache.hh +++ b/src/libexpr/include/nix/expr/eval-cache.hh @@ -14,7 +14,7 @@ namespace nix::eval_cache { struct AttrDb; class AttrCursor; -struct CachedEvalError : EvalError +struct CachedEvalError : CloneableError { const ref cursor; const Symbol attr; diff --git a/src/libexpr/include/nix/expr/eval-error.hh b/src/libexpr/include/nix/expr/eval-error.hh index 06a7790d5..24717d40b 100644 --- a/src/libexpr/include/nix/expr/eval-error.hh +++ b/src/libexpr/include/nix/expr/eval-error.hh @@ -18,7 +18,7 @@ class EvalErrorBuilder; * * Most subclasses should inherit from `EvalError` instead of this class. */ -class EvalBaseError : public Error +class EvalBaseError : public CloneableError { template friend class EvalErrorBuilder; @@ -26,14 +26,14 @@ public: EvalState & state; EvalBaseError(EvalState & state, ErrorInfo && errorInfo) - : Error(errorInfo) + : CloneableError(errorInfo) , state(state) { } template 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(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 { 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) { } }; diff --git a/src/libexpr/include/nix/expr/value/context.hh b/src/libexpr/include/nix/expr/value/context.hh index 054516bc2..9884c2705 100644 --- a/src/libexpr/include/nix/expr/value/context.hh +++ b/src/libexpr/include/nix/expr/value/context.hh @@ -9,14 +9,14 @@ namespace nix { -class BadNixStringContextElem : public Error +class BadNixStringContextElem final : public CloneableError { public: std::string_view raw; template BadNixStringContextElem(std::string_view raw_, const Args &... args) - : Error("") + : CloneableError("") { raw = raw_; auto hf = HintFmt(args...); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b7b80be66..db5755614 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -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); } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index e3bce13b2..d440c3c70 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -74,11 +74,11 @@ namespace nix { struct GitSourceAccessor; -struct GitError : public Error +struct GitError final : public CloneableError { template GitError(const git_error & error, Ts &&... args) - : Error("") + : CloneableError("") { auto hf = HintFmt(std::forward(args)...); err.msg = HintFmt("%1%: %2% (libgit2 error code = %3%)", Uncolored(hf.str()), error.message, error.klass); diff --git a/src/libstore/aws-creds.cc b/src/libstore/aws-creds.cc index 9b0ddefdc..5baac798c 100644 --- a/src/libstore/aws-creds.cc +++ b/src/libstore/aws-creds.cc @@ -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) { } diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index d64b869e9..ef3501b00 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -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) { } diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index b8a9a9aa8..8e055c141 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -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 { ::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 FileTransferError::FileTransferError( FileTransfer::Error error, std::optional response, const Args &... args) - : Error(args...) + : CloneableError(args...) , error(error) , response(response) { diff --git a/src/libstore/include/nix/store/aws-creds.hh b/src/libstore/include/nix/store/aws-creds.hh index 30f6592a0..0751757cb 100644 --- a/src/libstore/include/nix/store/aws-creds.hh +++ b/src/libstore/include/nix/store/aws-creds.hh @@ -34,12 +34,13 @@ struct AwsCredentials } }; -class AwsAuthError : public Error +class AwsAuthError final : public CloneableError { std::optional errorCode; public: - using Error::Error; + using CloneableError::CloneableError; + AwsAuthError(int errorCode); std::optional getErrorCode() const diff --git a/src/libstore/include/nix/store/build-result.hh b/src/libstore/include/nix/store/build-result.hh index ce63d0999..c664e6e5b 100644 --- a/src/libstore/include/nix/store/build-result.hh +++ b/src/libstore/include/nix/store/build-result.hh @@ -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 { using Status = BuildResultFailureStatus; using enum Status; @@ -80,7 +80,7 @@ public: */ template 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("") { } diff --git a/src/libstore/include/nix/store/build/derivation-builder.hh b/src/libstore/include/nix/store/build/derivation-builder.hh index a56cbe3c2..3b8f305a7 100644 --- a/src/libstore/include/nix/store/build/derivation-builder.hh +++ b/src/libstore/include/nix/store/build/derivation-builder.hh @@ -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 { 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 */ diff --git a/src/libstore/include/nix/store/build/goal.hh b/src/libstore/include/nix/store/build/goal.hh index 6a92d2828..263647fc1 100644 --- a/src/libstore/include/nix/store/build/goal.hh +++ b/src/libstore/include/nix/store/build/goal.hh @@ -10,7 +10,7 @@ namespace nix { -struct TimedOut : BuildError +struct TimedOut final : CloneableError { time_t maxDuration; diff --git a/src/libstore/include/nix/store/builtins/buildenv.hh b/src/libstore/include/nix/store/builtins/buildenv.hh index c152ab00a..136a1ab5a 100644 --- a/src/libstore/include/nix/store/builtins/buildenv.hh +++ b/src/libstore/include/nix/store/builtins/buildenv.hh @@ -22,7 +22,7 @@ struct Package } }; -class BuildEnvFileConflictError : public Error +class BuildEnvFileConflictError final : public CloneableError { 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" diff --git a/src/libstore/include/nix/store/filetransfer.hh b/src/libstore/include/nix/store/filetransfer.hh index 48c1b5a2f..e34c3692a 100644 --- a/src/libstore/include/nix/store/filetransfer.hh +++ b/src/libstore/include/nix/store/filetransfer.hh @@ -403,7 +403,7 @@ ref getFileTransfer(); */ ref makeFileTransfer(const FileTransferSettings & settings = fileTransferSettings); -class FileTransferError : public Error +class FileTransferError final : public CloneableError { public: FileTransfer::Error error; diff --git a/src/libstore/include/nix/store/realisation.hh b/src/libstore/include/nix/store/realisation.hh index 55597acb7..670f8ce49 100644 --- a/src/libstore/include/nix/store/realisation.hh +++ b/src/libstore/include/nix/store/realisation.hh @@ -146,7 +146,7 @@ struct RealisedPath auto operator<=>(const RealisedPath &) const = default; }; -class MissingRealisation : public Error +class MissingRealisation final : public CloneableError { 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, diff --git a/src/libstore/include/nix/store/sqlite.hh b/src/libstore/include/nix/store/sqlite.hh index 83839d385..be14fde19 100644 --- a/src/libstore/include/nix/store/sqlite.hh +++ b/src/libstore/include/nix/store/sqlite.hh @@ -166,7 +166,7 @@ struct SQLiteTxn ~SQLiteTxn(); }; -struct SQLiteError : Error +struct SQLiteError : CloneableError { std::string path; std::string errMsg; diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index 498e2deda..16561f3b2 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -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) diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 1a9908366..a5014b2c8 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -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 { 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) { } }; diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc index 504063da0..981da9cf2 100644 --- a/src/libstore/unix/build/derivation-builder.cc +++ b/src/libstore/unix/build/derivation-builder.cc @@ -55,10 +55,10 @@ namespace nix { -struct NotDeterministic : BuildError +struct NotDeterministic final : CloneableError { NotDeterministic(auto &&... args) - : BuildError(BuildResult::Failure::NotDeterministic, args...) + : CloneableError(BuildResult::Failure::NotDeterministic, args...) { isNonDeterministic = true; } diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 20d1a082a..727365833 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -378,7 +378,7 @@ std::set 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, ")"))) diff --git a/src/libutil/include/nix/util/error.hh b/src/libutil/include/nix/util/error.hh index d9babf34d..ea18b0a30 100644 --- a/src/libutil/include/nix/util/error.hh +++ b/src/libutil/include/nix/util/error.hh @@ -227,13 +227,31 @@ public: { return err; }; + + [[noreturn]] virtual void throwClone() const = 0; }; -#define MakeError(newClass, superClass) \ - class newClass : public superClass \ - { \ - public: \ - using superClass::superClass; \ +template +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(*this)); + } +}; + +#define MakeError(newClass, superClass) \ + class newClass : public CloneableError \ + { \ + public: \ + using CloneableError::CloneableError; \ } MakeError(Error, BaseError); @@ -245,7 +263,7 @@ 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 { std::error_code errorCode; std::string errorDetails; @@ -265,7 +283,7 @@ protected: */ template SystemError(Disambig, std::error_code errorCode, std::string_view errorDetails, Args &&... args) - : Error("") + : CloneableError("") , errorCode(errorCode) , errorDetails(errorDetails) { @@ -311,7 +329,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 { public: int errNo; @@ -322,7 +340,7 @@ public: */ template SysError(int errNo, Args &&... args) - : SystemError( + : CloneableError( Disambig{}, std::make_error_code(static_cast(errNo)), strerror(errNo), @@ -392,7 +410,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 { public: DWORD lastError; @@ -404,7 +422,7 @@ public: */ template WinError(DWORD lastError, Args &&... args) - : SystemError( + : CloneableError( Disambig{}, std::error_code(lastError, std::system_category()), renderError(lastError), diff --git a/src/libutil/include/nix/util/experimental-features.hh b/src/libutil/include/nix/util/experimental-features.hh index aca14bfbb..1a4c9b6b5 100644 --- a/src/libutil/include/nix/util/experimental-features.hh +++ b/src/libutil/include/nix/util/experimental-features.hh @@ -80,7 +80,7 @@ std::set 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 { public: /** diff --git a/src/libutil/include/nix/util/processes.hh b/src/libutil/include/nix/util/processes.hh index 44e8c2952..3b9cff22c 100644 --- a/src/libutil/include/nix/util/processes.hh +++ b/src/libutil/include/nix/util/processes.hh @@ -133,14 +133,14 @@ std::pair runProgram(RunOptions && options); void runProgram2(const RunOptions & options); -class ExecError : public Error +class ExecError final : public CloneableError { public: int status; template ExecError(int status, const Args &... args) - : Error(args...) + : CloneableError(args...) , status(status) { } diff --git a/src/libutil/include/nix/util/source-accessor.hh b/src/libutil/include/nix/util/source-accessor.hh index e1de9ece7..3a72f8005 100644 --- a/src/libutil/include/nix/util/source-accessor.hh +++ b/src/libutil/include/nix/util/source-accessor.hh @@ -231,19 +231,19 @@ ref makeEmptySourceAccessor(); */ MakeError(RestrictedPathError, Error); -struct SymlinkNotAllowed : public Error +struct SymlinkNotAllowed final : public CloneableError { 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 SymlinkNotAllowed(CanonPath path, const std::string & fs, Args &&... args) - : Error(fs, std::forward(args)...) + : CloneableError(fs, std::forward(args)...) , path(std::move(path)) { }