This is because a dynamic_cast<nix::RootArgs *> of a (n-e-j) MyArgs
returns nullptr even though MyArgs has virtual nix::RootArgs as a
parent.
class MyArgs : virtual public nix::MixEvalArgs,
virtual public nix::MixCommonArgs,
virtual nix::RootArgs { ... };
So this should work right?? But it does not. We found out that it's
caused by -fvisibility=hidden in n-e-j, but honestly this code was bad
anyway.
The trivial solution is to simply stop relying on RTTI working properly
here, which is probably better OO architecture anyway. However, I am not
100% confident *this* is sound, since we have this horrible hierarchy:
Args (defines getRoot)
/ | \
RootArgs MixCommonArgs MixEvalArgs
(overrides)
I am not confident that this is guaranteed to resolve from Args always
in the case of this override.
Assertion failed: (res), function getRoot, file src/libutil/args.cc, line 67.
6MyArgsProcess 60503 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = hit program assert
frame #4: 0x0000000100b1a41c liblixutil.dylib`nix::Args::processArgs(std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>> const&, bool) [inlined] nix::Args::getRoot(this=0x00000001000d0688) at args.cc:67:5 [opt]
64 std::cout << typeid(*p).name();
65
66 auto * res = dynamic_cast<RootArgs *>(p);
-> 67 assert(res);
68 return *res;
69 }
70
Target 0: (nix-eval-jobs) stopped.
(lldb) p this
(MyArgs *) 0x00000001000d0688
(lldb) p *this
(nix::Args) {
longFlags = size=180 { ... }
shortFlags = size=4 { ... }
expectedArgs = size=1 { ... }
processedArgs = size=0 {}
hiddenCategories = size=1 {
[0] = "Options to override configuration settings"
}
parent = nullptr
}
We also found that if we did this:
class [[gnu::visibility("default")]] RootArgs : virtual public Args
it would work properly (???!). This is of course, very strange, because
objdump -Ct output on liblixexpr.dylib is identical both with and
without it.
Possibly related: https://www.qt.io/blog/quality-assurance/one-way-dynamic_cast-across-library-boundaries-can-fail-and-how-to-fix-it
Fixes: https://git.lix.systems/lix-project/nix-eval-jobs/issues/2
Change-Id: I6b9ed968ed56420a9c4d2dffd18999d78c2761bd
78 lines
1.9 KiB
C++
78 lines
1.9 KiB
C++
#pragma once
|
|
///@file
|
|
|
|
#include "args.hh"
|
|
|
|
namespace nix {
|
|
|
|
/**
|
|
* The concrete implementation of a collection of completions.
|
|
*
|
|
* This is exposed so that the main entry point can print out the
|
|
* collected completions.
|
|
*/
|
|
struct Completions final : AddCompletions
|
|
{
|
|
std::set<Completion> completions;
|
|
Type type = Type::Normal;
|
|
|
|
void setType(Type type) override;
|
|
void add(std::string completion, std::string description = "") override;
|
|
};
|
|
|
|
/**
|
|
* The outermost Args object. This is the one we will actually parse a command
|
|
* line with, whereas the inner ones (if they exists) are subcommands (and this
|
|
* is also a MultiCommand or something like it).
|
|
*
|
|
* This Args contains completions state shared between it and all of its
|
|
* descendent Args.
|
|
*/
|
|
class RootArgs : virtual public Args
|
|
{
|
|
public:
|
|
/** Parse the command line, throwing a UsageError if something goes
|
|
* wrong.
|
|
*/
|
|
void parseCmdline(const Strings & cmdline);
|
|
|
|
std::shared_ptr<Completions> completions;
|
|
|
|
protected:
|
|
|
|
friend class Args;
|
|
|
|
/**
|
|
* A pointer to the completion and its two arguments; a thunk;
|
|
*/
|
|
struct DeferredCompletion {
|
|
const CompleterClosure & completer;
|
|
size_t n;
|
|
std::string prefix;
|
|
};
|
|
|
|
/**
|
|
* Completions are run after all args and flags are parsed, so completions
|
|
* of earlier arguments can benefit from later arguments.
|
|
*/
|
|
std::vector<DeferredCompletion> deferredCompletions;
|
|
|
|
/**
|
|
* Experimental features needed when parsing args. These are checked
|
|
* after flag parsing is completed in order to support enabling
|
|
* experimental features coming after the flag that needs the
|
|
* experimental feature.
|
|
*/
|
|
std::set<ExperimentalFeature> flagExperimentalFeatures;
|
|
|
|
virtual std::optional<std::reference_wrapper<RootArgs>> asRootArgs() override {
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
|
|
std::optional<std::string> needsCompletion(std::string_view s);
|
|
};
|
|
|
|
}
|