Puppy
Puppy

Reputation: 146920

Illegal use of type as an expression

I've got a nasty problem with some code and can't see the error. I have a template function

template<typename T, typename F> void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
    CheckedIncrement();
    auto new_expr = Wide::make_unique<T>();
    new_expr->lhs = std::move(cur_expr);
    new_expr->rhs = f();
    cur_expr = std::move(new_expr);
}

with the obvious implementation of make_unique. There are other overloads, but they differ in obvious ways like different parameter numbers, so I know that this is the only one being called. Then I call it, like so (this is the error line, 315):

RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression>(current_expression, [&] { return this_ptr->RecursiveParseExpression(); });

But the compiler insists that I am using a type as an expression. I'm pretty sure that this code is completely legal. Am I missing something? The error is

1>parser.cpp(315): error C2275: 'Wide::Parser::AccessExpression' : illegal use of this type as an expression

Edit: These are the definitions of the other overloads.

template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST> RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseSingleToken;
    RecursiveParseSingleToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseSingleToken();
        }
    };
    RecursiveParseSingleToken();
    return std::move(cur_expr);
}

template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseDualToken;
    RecursiveParseDualToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseDualToken();
        case second:
            RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
            return RecursiveParseDualToken();
        }
    };
    RecursiveParseDualToken();

    return std::move(cur_expr);
}

However, they clearly differ in requiring additional explicit template arguments, and so cannot be the call resolved.

Edit: The definition of make_unique:

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }
};

Just to be certain, the calling code isn't in any template of any kind, and there are absolutely no dependent names. Wide is a namespace.

Another edit: The error is repeated for all the direct calls to this overload, but not for the ones which are called through the other overloads, curiously.

Edit: This 100line test case reproduces the problem.

#include <memory>
#include <functional>
#include <vector>

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }
    class Lexer {
    public:
        enum TokenType {
            Namespace,
            For,
            Identifier
        };
        struct Token {
            TokenType type;
        };
    };
    class Parser {
    public:
        struct ExpressionAST {};
        struct BinaryExpressionAST : public ExpressionAST {
            std::unique_ptr<ExpressionAST> lhs;
            std::unique_ptr<ExpressionAST> rhs;
        };
        struct AccessExpression : public BinaryExpressionAST {};
        struct IdentifierExpression : public ExpressionAST {};
    };
}

typedef std::vector<Wide::Lexer::Token>::iterator Iterator;

struct inner_parser {
    Iterator begin, end;

    std::unique_ptr<Wide::Parser::ExpressionAST> RecursiveParsePrimaryExpression() {
        if (begin->type == Wide::Lexer::TokenType::Identifier) {
            CheckedIncrement();
            return Wide::make_unique<Wide::Parser::IdentifierExpression>();
        }
        throw std::runtime_error("aah");
    }
    template<typename T, typename F> void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
        CheckedIncrement();
        auto new_expr = Wide::make_unique<T>();
        new_expr->lhs = std::move(cur_expr);
        new_expr->rhs = f();
        cur_expr = std::move(new_expr);
    }
    template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
    RecursiveParseLeftAssociativeBinaryExpression(F next) {
        auto cur_expr = next();
        std::function<void()> RecursiveParseSingleToken;
        RecursiveParseSingleToken = [&] {
            switch(begin->type) {
            case first:
                RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                return RecursiveParseSingleToken();
            }
        };
        RecursiveParseSingleToken();
        return std::move(cur_expr);
    }

    template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F>
    std::unique_ptr<Wide::Parser::ExpressionAST>
        RecursiveParseLeftAssociativeBinaryExpression(F next) {
            auto cur_expr = next();
            std::function<void()> RecursiveParseDualToken;
            RecursiveParseDualToken = [&] {
                switch(begin->type) {
                case first:
                    RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                    return RecursiveParseDualToken();
                case second:
                    RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
                    return RecursiveParseDualToken();
                }
            };
            RecursiveParseDualToken();

            return std::move(cur_expr);
    }

    void CheckedIncrement() {
        if (begin == end)
            throw std::runtime_error("aaaaaah!");
        begin++;
    }
};
int main() {
    std::vector<Wide::Lexer::Token> tokens;
    inner_parser ip;
    ip.begin = tokens.begin();
    ip.end = tokens.end();
    auto ret = ip.RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression, Wide::Lexer::TokenType::For>([&] { return ip.RecursiveParsePrimaryExpression(); });
    return 0;
}

Upvotes: 3

Views: 4813

Answers (2)

ildjarn
ildjarn

Reputation: 62975

Sprinkling this-> liberally inside of the template member functions' lambdas seems to fix your test case:

#include <memory>
#include <functional>
#include <vector>

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }
    class Lexer {
    public:
        enum TokenType {
            Namespace,
            For,
            Identifier
        };
        struct Token {
            TokenType type;
        };
    };
    class Parser {
    public:
        struct ExpressionAST {};
        struct BinaryExpressionAST : public ExpressionAST {
            std::unique_ptr<ExpressionAST> lhs;
            std::unique_ptr<ExpressionAST> rhs;
        };
        struct AccessExpression : public BinaryExpressionAST {};
        struct IdentifierExpression : public ExpressionAST {};
    };
}

typedef std::vector<Wide::Lexer::Token>::iterator Iterator;

struct inner_parser {
    Iterator begin, end;

    std::unique_ptr<Wide::Parser::ExpressionAST> RecursiveParsePrimaryExpression() {
        if (begin->type == Wide::Lexer::Identifier) {
            CheckedIncrement();
            return Wide::make_unique<Wide::Parser::IdentifierExpression>();
        }
        throw std::runtime_error("aah");
    }
    template<typename T, typename F> void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
        CheckedIncrement();
        auto new_expr = Wide::make_unique<T>();
        new_expr->lhs = std::move(cur_expr);
        new_expr->rhs = f();
        cur_expr = std::move(new_expr);
    }
    template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
    RecursiveParseLeftAssociativeBinaryExpression(F next) {
        auto cur_expr = next();
        std::function<void()> RecursiveParseSingleToken;
        RecursiveParseSingleToken = [&] {
            switch(this->begin->type) {
            case first:
                this->RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                return RecursiveParseSingleToken();
            }
        };
        RecursiveParseSingleToken();
        return std::move(cur_expr);
    }

    template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F>
    std::unique_ptr<Wide::Parser::ExpressionAST>
        RecursiveParseLeftAssociativeBinaryExpression(F next) {
            auto cur_expr = next();
            std::function<void()> RecursiveParseDualToken;
            RecursiveParseDualToken = [&] {
                switch(this->begin->type) {
                case first:
                    this->RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                    return RecursiveParseDualToken();
                case second:
                    this->RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
                    return RecursiveParseDualToken();
                }
            };
            RecursiveParseDualToken();

            return std::move(cur_expr);
    }

    void CheckedIncrement() {
        if (begin == end)
            throw std::runtime_error("aaaaaah!");
        begin++;
    }
};
int main() {
    std::vector<Wide::Lexer::Token> tokens;
    inner_parser ip;
    ip.begin = tokens.begin();
    ip.end = tokens.end();
    auto ret = ip.RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression, Wide::Lexer::For>([&] { return ip.RecursiveParsePrimaryExpression(); });
    return 0;
}

I'd speculate that this is a bug in VC++ 2010's lambda implementation, wherein class-scope name resolution is not performed properly for member templates.

Upvotes: 2

Howard Hinnant
Howard Hinnant

Reputation: 218750

After you've sprinkled typename everywhere you can think, sprinkle template after any qualified call that contains a < before a (. The template goes right after the ::.

Like this:

auto new_expr = Wide::template make_unique<T>();

Update

Don't know if this will help, but I reverse engineered the question to create a minimum complete compiling (but not linking) program. Unfortunately it does not replicate the symptom. But perhaps it will be helpful in tracking down the problem. Or at least serve as amusement as to how close I guessed in several places. ;-)

#include <memory>
#include <functional>

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }

namespace Lexer
{

enum TokenType {first, second};

}

namespace Parser
{

struct  ExpressionAST {};
struct AccessExpression
    : public ExpressionAST
{
    std::unique_ptr<ExpressionAST> lhs;
    std::unique_ptr<ExpressionAST> rhs;
};

}

};

struct
{
    Wide::Lexer::TokenType type;
}* begin;

template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseSingleToken;
    RecursiveParseSingleToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseSingleToken();
        }
    };
    RecursiveParseSingleToken();
    return std::move(cur_expr);
}

template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F>
std::unique_ptr<Wide::Parser::ExpressionAST>
RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseDualToken;
    RecursiveParseDualToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseDualToken();
        case second:
            RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
            return RecursiveParseDualToken();
        }
    };
    RecursiveParseDualToken();

    return std::move(cur_expr);
}

void CheckedIncrement();

template<typename T, typename F>
void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
    CheckedIncrement();
    auto new_expr = Wide::make_unique<T>();
    new_expr->lhs = std::move(cur_expr);
    new_expr->rhs = f();
    cur_expr = std::move(new_expr);
}

struct A
{
    std::unique_ptr<Wide::Parser::AccessExpression> RecursiveParseExpression();
};

int main()
{
    std::unique_ptr<Wide::Parser::ExpressionAST> current_expression;
    A* this_ptr;
    RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression>(current_expression,
        [&] { return this_ptr->RecursiveParseExpression(); });
}

Upvotes: 1

Related Questions