Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cling] Do not wrap overloaded operator function declarations #11265

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 73 additions & 18 deletions interpreter/cling/lib/Utils/SourceNormalization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ namespace {
///\brief A Lexer that exposes preprocessor directives.
class MinimalPPLexer: public Lexer {

///\brief Jump to last Identifier in a scope chain A::B::C::D
///\brief Jump to the next token after a scope chain A::B::C::D
///
bool SkipScopes(Token& Tok) {
Token LastTok;
return SkipScopes(Tok, LastTok);
}

bool SkipScopes(Token& Tok, Token& LastTok) {
LastTok = Tok;

if (getLangOpts().CPlusPlus) {
while (Tok.is(tok::coloncolon)) {
if (!LexClean(Tok) || Identifier(Tok).empty())
if (!LexClean(LastTok) || Identifier(LastTok).empty())
return false;
if (!LexClean(Tok))
return false;
Expand All @@ -38,9 +45,7 @@ class MinimalPPLexer: public Lexer {
///\brief Skips all contiguous '*' '&' tokens
///
bool SkipPointerRefs(Token& Tok) {
while (Tok.isNot(tok::raw_identifier)) {
if (!Tok.isOneOf(tok::star, tok::amp))
return false;
while (Tok.isOneOf(tok::star, tok::amp)) {
if (!LexClean(Tok))
return false;
}
Expand Down Expand Up @@ -71,14 +76,34 @@ class MinimalPPLexer: public Lexer {
return false;
}

// Function or class name should be in Tok now
if (Identifier(Tok).empty())
auto LastTok{Tok};
// skip scopes in function name
// e.g. int ::the_namespace::class_a::mem_func() { return 3; }
if ((Tok.is(tok::coloncolon) && !SkipScopes(Tok, LastTok)) ||
(!LexClean(Tok) ||
(Tok.is(tok::coloncolon) && !SkipScopes(Tok, LastTok))))
return false;

// Advance to argument list or method name
if (!LexClean(Tok))
const auto Ident{Identifier(LastTok)}; // function, operator or class name

if (Ident.empty())
return false;

if (Ident.equals("operator")) {
// Tok is the operator, e.g. <=, ==, +...
// however, for operator() and [], Tok only contains the
// left side so we need to parse the closing right side
// TODO: tok::spaceship operator<=>
if ((Tok.isOneOf(tok::l_paren, tok::l_square) && !CheckBalance(Tok)) ||
jalopezg-git marked this conversation as resolved.
Show resolved Hide resolved
// user-defined literal, e.g. double operator "" _dd(long double t)
// TODO: parse without the space right after "",
// e.g. double operator ""_dd(long double t)
(Tok.is(tok::string_literal) && !LexClean(Tok)) ||
// Advance to argument list or method name
!LexClean(Tok))
return false;
}

if (!SkipScopes(Tok))
return false;

Expand Down Expand Up @@ -163,8 +188,13 @@ class MinimalPPLexer: public Lexer {
///
/// \param Tok - Token, advanced to first token to test
/// \param First - First token identifier.
/// \param[out] HasBody - if set to `true`, the function/class body follows;
/// thus, the caller needs to consume tokens until the
/// closing `}`
/// \return - Typeof definition, function/method or class
DefinitionType IsClassOrFunction(Token& Tok, llvm::StringRef First) {
DefinitionType IsClassOrFunction(Token& Tok, llvm::StringRef First,
bool& HasBody) {
HasBody = true;
/// ###TODO: Allow preprocessor expansion
if (!Lexer::isIdentifierBodyChar(First.front(), getLangOpts()))
return kNONE;
Expand All @@ -174,6 +204,8 @@ class MinimalPPLexer: public Lexer {
return kNONE;

bool Ctor = false;
bool Dtor = false;
jiangyilism marked this conversation as resolved.
Show resolved Hide resolved

if (getLangOpts().CPlusPlus && Tok.is(tok::coloncolon)) {
// CLASS::CLASS() or CLASS::~CLASS()
// CLASS::NESTED::NESTED()
Expand Down Expand Up @@ -201,6 +233,8 @@ class MinimalPPLexer: public Lexer {
} while (Tok.is(tok::coloncolon));

if (Tok.is(tok::tilde)) {
Dtor = true;

if (!LexClean(Tok))
return kNONE;
if (!Ident.empty())
Expand All @@ -219,14 +253,15 @@ class MinimalPPLexer: public Lexer {
return kNONE;

// Advance to argument list, or next scope
if (!LexClean(Tok))
if (!SkipIdentifier(Tok))
return kNONE;

// Function name should be last on scope chain
if (!SkipScopes(Tok))
return kNONE;

Ctor = false;
Dtor = false;
}
} else {
bool SeenSignedness = false;
Expand Down Expand Up @@ -301,6 +336,19 @@ class MinimalPPLexer: public Lexer {
if (Ctor && Tok.is(tok::colon))
return !AdvanceTo(Tok, tok::l_brace) ? kFunction : kNONE;

if (Ctor || Dtor) {
// e.g. CLASS::CLASS() = default;
// CLASS::~CLASS();
if (Tok.is(tok::equal)) {
if ((!LexClean(Tok) && Tok.isNot(tok::raw_identifier)) ||
(!LexClean(Tok) && Tok.isNot(tok::semi)))
return kNONE;

HasBody = false;
return kFunction;
}
}

// class const method 'CLASS::method() const {'
if (!Ctor && Identifier(Tok).equals("const")) {
if (LexClean(Tok) && Tok.is(tok::l_brace))
Expand Down Expand Up @@ -439,9 +487,10 @@ size_t cling::utils::getWrapPoint(std::string& source,
Lex.Lex(Tok);
}

const tok::TokenKind kind = Tok.getKind();
if (Tok.getKind() == tok::coloncolon)
Lex.LexClean(Tok);

if (kind == tok::raw_identifier && !Tok.needsCleaning()) {
if (Tok.getKind() == tok::raw_identifier && !Tok.needsCleaning()) {
StringRef keyword(Tok.getRawIdentifier());
if (keyword.equals("using")) {
// FIXME: Using definitions and declarations should be decl extracted.
Expand All @@ -464,12 +513,18 @@ size_t cling::utils::getWrapPoint(std::string& source,
if (keyword.equals("template"))
return std::string::npos;

auto HasBody{false};

if (const MinimalPPLexer::DefinitionType T =
Lex.IsClassOrFunction(Tok, keyword)) {
assert(Tok.is(tok::l_brace) && "Lexer begin location invalid");
if (!Lex.CheckBalance(Tok))
return offset;
assert(Tok.is(tok::r_brace) && "Lexer end location invalid");
Lex.IsClassOrFunction(Tok, keyword, HasBody)) {
if (HasBody) {
assert(Tok.is(tok::l_brace) && "Lexer begin location invalid");

if (!Lex.CheckBalance(Tok))
return offset;

assert(Tok.is(tok::r_brace) && "Lexer end location invalid");
}

const size_t rBrace = getFileOffset(Tok);
// Wrap everything after '}'
Expand Down
39 changes: 39 additions & 0 deletions interpreter/cling/test/Prompt/decls.C
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

// RUN: cat %s | %cling -I%p | FileCheck %s
#include <cmath>
#include <iostream>

struct S{int i;} ss;
S s = {12 };
Expand All @@ -16,6 +17,44 @@ struct U{void f() const {};} uu;

struct V{V(): v(12) {}; int v; } vv;

struct typeA {
typeA(int i): num{i} {};
typeA();
~typeA();

typeA& operator()(int k);
const typeA& operator[](int k) const;

operator int() const;

int num;
};

typeA::typeA() = default;
::typeA::~typeA() {};
::typeA& ::typeA::operator()(int k) { return *this; }
const typeA& typeA::operator[](int k) const { return *this; }
bool operator>=(const typeA &lhs, int t) { return lhs.num >= t; }
bool operator<=(::typeA const &lhs, int t) { return lhs.num <= t; }
bool operator<(typeA const&lhs, int t) { return lhs.num < t; }
bool operator>(const typeA &lhs, int t) { return lhs.num > t; }
::typeA operator+(const typeA &lhs, const ::typeA &rhs) {
return typeA{lhs.num + rhs.num};
}
::typeA operator "" _tA(unsigned long long int t) { return typeA{static_cast<int>(t)}; }
std::ostream& operator<<(std::ostream& os, const typeA& a) {return (os << a.num);}
typeA::operator int() const { return num + 7; }

std::cout << 76601_tA; // CHECK:76601
6551_tA(99)[4].num // CHECK:6551
typeA{-675} > 0 // CHECK:false
99_tA >= 99 // CHECK:true
::typeA(31) < 31 // CHECK:false
(60_tA + 3_tA).num // CHECK:63
static_cast<int>(18_tA) // CHECK:25

jiangyilism marked this conversation as resolved.
Show resolved Hide resolved
::atoi("42") // CHECK:42

int i = 12;
float f = sin(12);
int j = i;
Expand Down