简体   繁体   English

赛格。 std :: unique_ptr和ctor错误

[英]Seg. fault with std::unique_ptr and ctor

For a parser I am actually implementing I partially have these private functions within the parser: 对于解析器,我实际上正在实现,我在解析器中部分地拥有这些私有函数:

Parser private methods: 解析器私有方法:

    Token const* current_token() const;
    Token const* next_token();
    Token const* peek_token();

    std::unique_ptr<ast::Expression> parse_expression();
    std::unique_ptr<ast::TypeSpecifier> parse_type_specifier();
    std::unique_ptr<ast::VariableDeclarationStatement> parse_variable_declaration();
    std::unique_ptr<ast::Statement> parse_function_definition();
    std::unique_ptr<ast::Statement> parse_top_level_statement();

the implementation of the parse_variable_declaration method is this: parse_variable_declaration方法的实现是这样的:

parse_variable_declaration(): parse_variable_declaration():

std::unique_ptr<ast::VariableDeclarationStatement> Parser::parse_variable_declaration() {
    next_token(); // consume 'var'

    if (current_token()->get_type() != TokenTypes::identifier) {
        throw parser_error(current_token(), "", "expected identifier\n");
    }
    const auto id = current_token(); // store identifier
    next_token(); // consume identifier

    std::unique_ptr<ast::TypeSpecifier> type;
    std::unique_ptr<ast::Expression> expr;

    auto assignment_required = true;
    if (current_token()->get_type() == TokenTypes::op_colon) { // optional type specifier
        next_token(); // consume ':'

        type = parse_type_specifier();
        assignment_required = false;
    }

    if (assignment_required && current_token()->get_type() != TokenTypes::op_equals) {
        throw parser_error(current_token(), "", "expected equals operator\n");
    }

    if (current_token()->get_type() == TokenTypes::op_equals) {
        next_token(); // consume '='

        expr = parse_expression();
    }

    if (current_token()->get_type() != TokenTypes::op_semi_colon) {
        throw parser_error(current_token(), "", "expected semi-colon\n");
    }

    next_token(); // consume ';'

    DEBUG_STDERR("parsed: variable_declaration_statement\n");
    return std::make_unique<ast::VariableDeclarationStatement>(
        id->get_string(), std::move(type), std::move(expr));
}

the last line (the return) ends in a segmentation fault. 最后一行(返回)以分段错误结束。 it basically calls the constructor of VariableDeclarationStatement: 它基本上会调用VariableDeclarationStatement的构造函数:

VariableDeclarationStatement ctor: VariableDeclarationStatement ctor:

VariableDeclarationStatement::VariableDeclarationStatement(
    std::string const& name,
    std::unique_ptr<TypeSpecifier> type_specifier,
    std::unique_ptr<Expression> expr
):
    m_name{name},
    m_type_specifier{std::move(type_specifier)},
    m_expr{std::move(expr)}
{}

I am debugging this things since yesterday and can't seem to find out why this does not work as intended. 自昨天以来,我一直在调试这些东西,似乎无法找出为什么这不能按预期进行。 I want to build the Abstract Syntax Tree (parser output) with unique pointers to their child nodes (because they are the only owner of their childs which makes sense) - this is why I am try-harding to work with them. 我想用指向其子节点的唯一指针来构建抽象语法树(解析器输出)(因为它们是有意义的子节点的唯一所有者)-这就是为什么我努力尝试与它们一起工作的原因。

Console output: DEBUG_STDERR 控制台输出:DEBUG_STDERR

parsed: primitive_type_int // from parse_type_specifier()
parsed: integral_expression // from parse_expression()
parsed: variable_declaration_statement
[1]    12638 segmentation fault (core dumped)  ./cion_compiler

The move operations on unique pointers basically boil down to simple pointer copies. 唯一指针的移动操作基本上可以归结为简单的指针副本。 There is no reason why any implementation of unique_ptr would dereference the pointers in the process of moving them. 没有任何理由可以使unique_ptr任何实现在移动它们的过程中取消引用指针。 Therefore, the likelihood that this operation is responsible for the seg-fault is virtually zero. 因此,此操作导致段错误的可能性实际上为零。

In your return-statement / constructor-call, you do have one (or more) very obvious pointer de-referencing, as part of the id->get_string() call. 在return-statement / constructor-call中,您确实有一个(或多个)非常明显的指针取消引用,这是id->get_string()调用的一部分。

For one, the id pointer is created as so: 首先, id指针的创建方式如下:

  const Token* const id = current_token(); // store identifier
  next_token(); // consume identifier

Unless there is a guarantee that any pointer returned by current_token() will be valid until the end of time (or within the life-time of the current parsing operation), it is very possible that after the call to next_token() , the id pointer is invalid, ie, pointing to a non-existent or defunct Token object. 除非能够保证current_token()返回的任何指针在时间结束之前(或在当前解析操作的生命周期内current_token()都有效,否则很有可能在调用next_token()id指针无效,即指向不存在或已失效的Token对象。

Even if the id pointer still points to an existing Token object, it is possible that it is in a "zombie" state, and that obtaining a string from it, through get_string() , is an invalid operation. 即使id指针仍然指向现有的Token对象,它也可能处于“僵尸”状态,并且通过get_string()从中获取字符串是无效的操作。

If I were you, that is where I would be looking for the source of the seg-fault. 如果我是你,那将是我寻找段错误的来源。 You might also want to run this in a (memory-)debugger to get to the source of it, it will likely point you to the get_string function as the source of it, either during the dereferencing of the this pointer (the id pointer) or during the construction of the string itself. 您可能还想在(内存-)调试器中运行它来获取其源代码,它可能会将您指向get_string函数作为其源代码,无论是在this指针( id指针)的取消引用期间。或在琴弦本身的构造过程中。 It could also point you towards the virtual-table look-up, if get_string is a virtual function in the Token class. 如果get_stringToken类中的虚拟函数,它也可能将您引向虚拟表查找。 Either way, I highly suspect that this is the cause of the seg-fault, because it is the only overtly dangerous code in what you have posted. 无论哪种方式,我都高度怀疑这是段错误的原因,因为它是您所发布内容中唯一公开的危险代码。

As you guys correctly suggested the error was hidden in the suspicious id pointer. 如您所愿,错误被隐藏在可疑的id指针中。 The parser in my program receives Tokens via unique_ptr from the lexer and stores them right as the current token. 我程序中的解析器通过词法分析器通过unique_ptr接收令牌,并将其作为当前令牌正确存储。 Therefore the method current_token() returned a pointer to a unique_ptr which gets removed as soon as the next call to next_token() takes place. 因此,方法current_token()返回了一个指向unique_ptr的指针,该指针将在下次调用next_token()时立即删除。 Storing the invalid pointer to the already removed Token in id caused the problem. 在id中存储指向已删除的令牌的无效指针会导致此问题。

I fixed the code in several different ways. 我以几种不同的方式修复了代码。

First I changed the return types from the helper methods above from "Token const*" to "Token const&" and the id variable now only copies the get_string value and does no other pointer related operations. 首先,我将上述帮助方法的返回类型从“ Token const *”更改为“ Token const&”,并且id变量现在仅复制get_string值,并且不执行其他与指针相关的操作。

With these changes the segmentation fault problem was successfully solved! 通过这些更改,成功解决了分割错误问题! =) =)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM