Skip to content

C++ front-end: support constexpr #8386

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

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions regression/cpp/constexpr1/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ constexpr int some_other_value =

static_assert(some_other_value == 2, "some_other_value == 2");

constexpr int some_function2(int a)
{
int b;
a = a + 1;
b = a;
return b + 1;
}

constexpr int some_other_value2 = some_function2(1);

static_assert(some_other_value2 == 3, "some_other_value == 2");

int main()
{
}
2 changes: 1 addition & 1 deletion regression/cpp/constexpr1/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
KNOWNBUG
CORE
main.cpp
-std=c++11
^EXIT=0$
Expand Down
2 changes: 0 additions & 2 deletions src/cpp/cpp_convert_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ void cpp_convert_typet::read_rec(const typet &type)
++char16_t_count;
else if(type.id()==ID_char32_t)
++char32_t_count;
else if(type.id()==ID_constexpr)
c_qualifiers.is_constant = true;
else if(type.id()==ID_function_type)
{
read_function_type(type);
Expand Down
17 changes: 11 additions & 6 deletions src/cpp/cpp_declarator_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ symbolt &cpp_declarator_convertert::convert_new_symbol(
symbol.is_weak = storage_spec.is_weak();
symbol.module=cpp_typecheck.module;
symbol.is_type=is_typedef;
symbol.is_macro=is_typedef && !is_template_parameter;
symbol.is_macro =
(is_typedef && !is_template_parameter) || storage_spec.is_constexpr();
symbol.pretty_name=pretty_name;

if(is_code && !symbol.is_type)
Expand Down Expand Up @@ -493,7 +494,7 @@ symbolt &cpp_declarator_convertert::convert_new_symbol(
storage_spec.is_thread_local();

symbol.is_file_local =
symbol.is_macro ||
(symbol.is_macro && !storage_spec.is_constexpr()) ||
(!cpp_typecheck.cpp_scopes.current_scope().is_global_scope() &&
!storage_spec.is_extern()) ||
(cpp_typecheck.cpp_scopes.current_scope().is_global_scope() &&
Expand Down Expand Up @@ -553,10 +554,14 @@ symbolt &cpp_declarator_convertert::convert_new_symbol(
// do the value
if(!new_symbol->is_type)
{
if(is_code && declarator.type().id()!=ID_template)
cpp_typecheck.add_method_body(new_symbol);

if(!is_code)
if(is_code)
{
if(new_symbol->is_macro)
cpp_typecheck.convert_function(*new_symbol);
else if(declarator.type().id() != ID_template)
cpp_typecheck.add_method_body(new_symbol);
}
else
cpp_typecheck.convert_initializer(*new_symbol);
}

Expand Down
2 changes: 2 additions & 0 deletions src/cpp/cpp_storage_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@
set_asm();
else if(type.id() == ID_weak)
set_weak();
else if(type.id() == ID_constexpr)
set_constexpr();

Check warning on line 37 in src/cpp/cpp_storage_spec.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_storage_spec.cpp#L37

Added line #L37 was not covered by tests
}
13 changes: 12 additions & 1 deletion src/cpp/cpp_storage_spec.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
{
return get_bool(ID_weak);
}
bool is_constexpr() const
{
return get_bool(ID_constexpr);
}

void set_static() { set(ID_static, true); }
void set_extern() { set(ID_extern, true); }
Expand All @@ -59,11 +63,16 @@
{
set(ID_weak, true);
}
void set_constexpr()
{
set(ID_constexpr, true);
}

bool is_empty() const
{
return !is_static() && !is_extern() && !is_auto() && !is_register() &&
!is_mutable() && !is_thread_local() && !is_asm() && !is_weak();
!is_mutable() && !is_thread_local() && !is_asm() && !is_weak() &&
!is_constexpr();
}

cpp_storage_spect &operator|=(const cpp_storage_spect &other)
Expand All @@ -84,6 +93,8 @@
set_asm();
if(other.is_weak())
set_weak();
if(other.is_constexpr())
set_constexpr();

Check warning on line 97 in src/cpp/cpp_storage_spec.h

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_storage_spec.h#L97

Added line #L97 was not covered by tests

return *this;
}
Expand Down
66 changes: 66 additions & 0 deletions src/cpp/cpp_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <util/mathematical_types.h>
#include <util/pointer_expr.h>
#include <util/pointer_offset_size.h>
#include <util/replace_symbol.h>
#include <util/symbol_table_base.h>

#include <ansi-c/c_qualifiers.h>
Expand Down Expand Up @@ -1825,6 +1826,71 @@

add_implicit_dereference(expr);

if(auto sym_expr = expr_try_dynamic_cast<symbol_exprt>(expr.function()))
{
const auto &symbol = lookup(sym_expr->get_identifier());
if(symbol.is_macro)
{
// constexpr functions evaluated using a mini interpreter
const auto &code_type = to_code_type(symbol.type);
// PRECONDITION(code_type.return_type().id() != ID_empty);
PRECONDITION(expr.arguments().size() == code_type.parameters().size());
replace_symbolt value_map;
auto param_it = code_type.parameters().begin();
for(const auto &arg : expr.arguments())
{
value_map.insert(
symbol_exprt{param_it->get_identifier(), param_it->type()},
typecast_exprt::conditional_cast(arg, param_it->type()));
++param_it;
}
const auto &block = to_code_block(to_code(symbol.value));
for(const auto &stmt : block.statements())
{
if(
auto return_stmt = expr_try_dynamic_cast<code_frontend_returnt>(stmt))
{
PRECONDITION(return_stmt->has_return_value());
exprt tmp = return_stmt->return_value();
value_map.replace(tmp);
expr.swap(tmp);
return;
}
else if(auto expr_stmt = expr_try_dynamic_cast<code_expressiont>(stmt))
{
// C++14 and later only

Check warning on line 1861 in src/cpp/cpp_typecheck_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_typecheck_expr.cpp#L1861

Added line #L1861 was not covered by tests
if(
auto assign = expr_try_dynamic_cast<side_effect_expr_assignt>(
expr_stmt->expression()))
{
PRECONDITION(assign->lhs().id() == ID_symbol);
exprt rhs = assign->rhs();
value_map.replace(rhs);
value_map.set(to_symbol_expr(assign->lhs()), rhs);
}
else
UNIMPLEMENTED_FEATURE(
"constexpr with " + expr_stmt->expression().pretty());

Check warning on line 1873 in src/cpp/cpp_typecheck_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_typecheck_expr.cpp#L1872-L1873

Added lines #L1872 - L1873 were not covered by tests
}
else if(stmt.get_statement() == ID_decl_block)
{
// C++14 and later only
for(const auto &expect_decl : stmt.operands())
{
PRECONDITION(to_code(expect_decl).get_statement() == ID_decl);
PRECONDITION(!to_code_frontend_decl(to_code(expect_decl))
.initial_value()
.has_value());

Check warning on line 1883 in src/cpp/cpp_typecheck_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_typecheck_expr.cpp#L1882-L1883

Added lines #L1882 - L1883 were not covered by tests
}
}
else
{
UNIMPLEMENTED_FEATURE("constexpr with " + stmt.pretty());

Check warning on line 1888 in src/cpp/cpp_typecheck_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_typecheck_expr.cpp#L1888

Added line #L1888 was not covered by tests
}
}
}
}

// we will deal with some 'special' functions here
exprt tmp=do_special_functions(expr);
if(tmp.is_not_nil())
Expand Down
12 changes: 10 additions & 2 deletions src/cpp/cpp_typecheck_resolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,16 @@
}
else if(symbol.is_macro)
{
e=symbol.value;
PRECONDITION(e.is_not_nil());
if(symbol.type.id() == ID_code)
{
// constexpr function
e = cpp_symbol_expr(symbol);
}
else

Check warning on line 367 in src/cpp/cpp_typecheck_resolve.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/cpp_typecheck_resolve.cpp#L367

Added line #L367 was not covered by tests
{
e = symbol.value;
PRECONDITION(e.is_not_nil());
}
}
else
{
Expand Down
32 changes: 14 additions & 18 deletions src/cpp/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -764,11 +764,10 @@
{
int t=lex.LookAhead(0);

return is_identifier(t) || t == TOK_SCOPE || t == TOK_CONSTEXPR ||
t == TOK_CONST || t == TOK_VOLATILE || t == TOK_RESTRICT ||
t == TOK_CHAR || t == TOK_INT || t == TOK_SHORT || t == TOK_LONG ||
t == TOK_CHAR16_T || t == TOK_CHAR32_T || t == TOK_WCHAR_T ||
t == TOK_COMPLEX // new !!!
return is_identifier(t) || t == TOK_SCOPE || t == TOK_CONST ||
t == TOK_VOLATILE || t == TOK_RESTRICT || t == TOK_CHAR ||
t == TOK_INT || t == TOK_SHORT || t == TOK_LONG || t == TOK_CHAR16_T ||
t == TOK_CHAR32_T || t == TOK_WCHAR_T || t == TOK_COMPLEX // new !!!

Check warning on line 770 in src/cpp/parse.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/parse.cpp#L767-L770

Added lines #L767 - L770 were not covered by tests
|| t == TOK_SIGNED || t == TOK_UNSIGNED || t == TOK_FLOAT ||
t == TOK_DOUBLE || t == TOK_INT8 || t == TOK_INT16 || t == TOK_INT32 ||
t == TOK_INT64 || t == TOK_GCC_INT128 || t == TOK_PTR32 ||
Expand Down Expand Up @@ -2018,7 +2017,7 @@

/*
storage.spec : STATIC | EXTERN | AUTO | REGISTER | MUTABLE | ASM |
THREAD_LOCAL
THREAD_LOCAL | CONSTEXPR
*/
bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
{
Expand All @@ -2027,7 +2026,7 @@
if(
t == TOK_STATIC || t == TOK_EXTERN || (t == TOK_AUTO && !cpp11) ||
t == TOK_REGISTER || t == TOK_MUTABLE || t == TOK_GCC_ASM ||
t == TOK_THREAD_LOCAL)
t == TOK_THREAD_LOCAL || t == TOK_CONSTEXPR)
{
cpp_tokent tk;
lex.get_token(tk);
Expand All @@ -2041,6 +2040,9 @@
case TOK_MUTABLE: storage_spec.set_mutable(); break;
case TOK_GCC_ASM: storage_spec.set_asm(); break;
case TOK_THREAD_LOCAL: storage_spec.set_thread_local(); break;
case TOK_CONSTEXPR:
storage_spec.set_constexpr();
break;
default: UNREACHABLE;
}

Expand All @@ -2051,30 +2053,24 @@
}

/*
cv.qualify : (CONSTEXPR | CONST | VOLATILE | RESTRICT)+
cv.qualify : (CONST | VOLATILE | RESTRICT)+

Check warning on line 2056 in src/cpp/parse.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/parse.cpp#L2056

Added line #L2056 was not covered by tests
*/
bool Parser::optCvQualify(typet &cv)
{
for(;;)
{
int t=lex.LookAhead(0);
if(t==TOK_CONSTEXPR ||
t==TOK_CONST || t==TOK_VOLATILE || t==TOK_RESTRICT ||
t==TOK_PTR32 || t==TOK_PTR64 ||
t==TOK_GCC_ATTRIBUTE || t==TOK_GCC_ASM)
if(
t == TOK_CONST || t == TOK_VOLATILE || t == TOK_RESTRICT ||
t == TOK_PTR32 || t == TOK_PTR64 || t == TOK_GCC_ATTRIBUTE ||
t == TOK_GCC_ASM)

Check warning on line 2066 in src/cpp/parse.cpp

View check run for this annotation

Codecov / codecov/patch

src/cpp/parse.cpp#L2066

Added line #L2066 was not covered by tests
{
cpp_tokent tk;
lex.get_token(tk);
typet p;

switch(t)
{
case TOK_CONSTEXPR:
p=typet(ID_constexpr);
set_location(p, tk);
merge_types(p, cv);
break;

case TOK_CONST:
p=typet(ID_const);
set_location(p, tk);
Expand Down
Loading