Skip to content

Commit 575c84a

Browse files
committed
C++ front-end: support constexpr
Mark `constexpr` symbols as macros and use direct replacement (non-function symbols) or in-place evaluation (uses of `constexpr` function symbols).
1 parent 2523f2c commit 575c84a

9 files changed

+128
-30
lines changed

regression/cpp/constexpr1/main.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ constexpr int some_other_value =
1414

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

17+
constexpr int some_function2(int a)
18+
{
19+
int b;
20+
a = a + 1;
21+
b = a;
22+
return b + 1;
23+
}
24+
25+
constexpr int some_other_value2 = some_function2(1);
26+
27+
static_assert(some_other_value2 == 3, "some_other_value == 2");
28+
1729
int main()
1830
{
1931
}

regression/cpp/constexpr1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
main.cpp
33
-std=c++11
44
^EXIT=0$

src/cpp/cpp_convert_type.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void cpp_convert_typet::read_rec(const typet &type)
6363
++char16_t_count;
6464
else if(type.id()==ID_char32_t)
6565
++char32_t_count;
66-
else if(type.id()==ID_constexpr)
67-
c_qualifiers.is_constant = true;
6866
else if(type.id()==ID_function_type)
6967
{
7068
read_function_type(type);

src/cpp/cpp_declarator_converter.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,8 @@ symbolt &cpp_declarator_convertert::convert_new_symbol(
456456
symbol.is_weak = storage_spec.is_weak();
457457
symbol.module=cpp_typecheck.module;
458458
symbol.is_type=is_typedef;
459-
symbol.is_macro=is_typedef && !is_template_parameter;
459+
symbol.is_macro =
460+
(is_typedef && !is_template_parameter) || storage_spec.is_constexpr();
460461
symbol.pretty_name=pretty_name;
461462

462463
if(is_code && !symbol.is_type)
@@ -493,7 +494,7 @@ symbolt &cpp_declarator_convertert::convert_new_symbol(
493494
storage_spec.is_thread_local();
494495

495496
symbol.is_file_local =
496-
symbol.is_macro ||
497+
(symbol.is_macro && !storage_spec.is_constexpr()) ||
497498
(!cpp_typecheck.cpp_scopes.current_scope().is_global_scope() &&
498499
!storage_spec.is_extern()) ||
499500
(cpp_typecheck.cpp_scopes.current_scope().is_global_scope() &&
@@ -553,10 +554,14 @@ symbolt &cpp_declarator_convertert::convert_new_symbol(
553554
// do the value
554555
if(!new_symbol->is_type)
555556
{
556-
if(is_code && declarator.type().id()!=ID_template)
557-
cpp_typecheck.add_method_body(new_symbol);
558-
559-
if(!is_code)
557+
if(is_code)
558+
{
559+
if(new_symbol->is_macro)
560+
cpp_typecheck.convert_function(*new_symbol);
561+
else if(declarator.type().id() != ID_template)
562+
cpp_typecheck.add_method_body(new_symbol);
563+
}
564+
else
560565
cpp_typecheck.convert_initializer(*new_symbol);
561566
}
562567

src/cpp/cpp_storage_spec.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ void cpp_storage_spect::read(const typet &type)
3333
set_asm();
3434
else if(type.id() == ID_weak)
3535
set_weak();
36+
else if(type.id() == ID_constexpr)
37+
set_constexpr();
3638
}

src/cpp/cpp_storage_spec.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class cpp_storage_spect:public irept
4747
{
4848
return get_bool(ID_weak);
4949
}
50+
bool is_constexpr() const
51+
{
52+
return get_bool(ID_constexpr);
53+
}
5054

5155
void set_static() { set(ID_static, true); }
5256
void set_extern() { set(ID_extern, true); }
@@ -59,11 +63,16 @@ class cpp_storage_spect:public irept
5963
{
6064
set(ID_weak, true);
6165
}
66+
void set_constexpr()
67+
{
68+
set(ID_constexpr, true);
69+
}
6270

6371
bool is_empty() const
6472
{
6573
return !is_static() && !is_extern() && !is_auto() && !is_register() &&
66-
!is_mutable() && !is_thread_local() && !is_asm() && !is_weak();
74+
!is_mutable() && !is_thread_local() && !is_asm() && !is_weak() &&
75+
!is_constexpr();
6776
}
6877

6978
cpp_storage_spect &operator|=(const cpp_storage_spect &other)
@@ -84,6 +93,8 @@ class cpp_storage_spect:public irept
8493
set_asm();
8594
if(other.is_weak())
8695
set_weak();
96+
if(other.is_constexpr())
97+
set_constexpr();
8798

8899
return *this;
89100
}

src/cpp/cpp_typecheck_expr.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Author: Daniel Kroening, [email protected]
2222
#include <util/mathematical_types.h>
2323
#include <util/pointer_expr.h>
2424
#include <util/pointer_offset_size.h>
25+
#include <util/replace_symbol.h>
2526
#include <util/symbol_table_base.h>
2627

2728
#include <ansi-c/c_qualifiers.h>
@@ -1825,6 +1826,71 @@ void cpp_typecheckt::typecheck_side_effect_function_call(
18251826

18261827
add_implicit_dereference(expr);
18271828

1829+
if(auto sym_expr = expr_try_dynamic_cast<symbol_exprt>(expr.function()))
1830+
{
1831+
const auto &symbol = lookup(sym_expr->get_identifier());
1832+
if(symbol.is_macro)
1833+
{
1834+
// constexpr functions evaluated using a mini interpreter
1835+
const auto &code_type = to_code_type(symbol.type);
1836+
// PRECONDITION(code_type.return_type().id() != ID_empty);
1837+
PRECONDITION(expr.arguments().size() == code_type.parameters().size());
1838+
replace_symbolt value_map;
1839+
auto param_it = code_type.parameters().begin();
1840+
for(const auto &arg : expr.arguments())
1841+
{
1842+
value_map.insert(
1843+
symbol_exprt{param_it->get_identifier(), param_it->type()},
1844+
typecast_exprt::conditional_cast(arg, param_it->type()));
1845+
++param_it;
1846+
}
1847+
const auto &block = to_code_block(to_code(symbol.value));
1848+
for(const auto &stmt : block.statements())
1849+
{
1850+
if(
1851+
auto return_stmt = expr_try_dynamic_cast<code_frontend_returnt>(stmt))
1852+
{
1853+
PRECONDITION(return_stmt->has_return_value());
1854+
exprt tmp = return_stmt->return_value();
1855+
value_map.replace(tmp);
1856+
expr.swap(tmp);
1857+
return;
1858+
}
1859+
else if(auto expr_stmt = expr_try_dynamic_cast<code_expressiont>(stmt))
1860+
{
1861+
// C++14 and later only
1862+
if(
1863+
auto assign = expr_try_dynamic_cast<side_effect_expr_assignt>(
1864+
expr_stmt->expression()))
1865+
{
1866+
PRECONDITION(assign->lhs().id() == ID_symbol);
1867+
exprt rhs = assign->rhs();
1868+
value_map.replace(rhs);
1869+
value_map.set(to_symbol_expr(assign->lhs()), rhs);
1870+
}
1871+
else
1872+
UNIMPLEMENTED_FEATURE(
1873+
"constexpr with " + expr_stmt->expression().pretty());
1874+
}
1875+
else if(stmt.get_statement() == ID_decl_block)
1876+
{
1877+
// C++14 and later only
1878+
for(const auto &expect_decl : stmt.operands())
1879+
{
1880+
PRECONDITION(to_code(expect_decl).get_statement() == ID_decl);
1881+
PRECONDITION(!to_code_frontend_decl(to_code(expect_decl))
1882+
.initial_value()
1883+
.has_value());
1884+
}
1885+
}
1886+
else
1887+
{
1888+
UNIMPLEMENTED_FEATURE("constexpr with " + stmt.pretty());
1889+
}
1890+
}
1891+
}
1892+
}
1893+
18281894
// we will deal with some 'special' functions here
18291895
exprt tmp=do_special_functions(expr);
18301896
if(tmp.is_not_nil())

src/cpp/cpp_typecheck_resolve.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,16 @@ exprt cpp_typecheck_resolvet::convert_identifier(
359359
}
360360
else if(symbol.is_macro)
361361
{
362-
e=symbol.value;
363-
PRECONDITION(e.is_not_nil());
362+
if(symbol.type.id() == ID_code)
363+
{
364+
// constexpr function
365+
e = cpp_symbol_expr(symbol);
366+
}
367+
else
368+
{
369+
e = symbol.value;
370+
PRECONDITION(e.is_not_nil());
371+
}
364372
}
365373
else
366374
{

src/cpp/parse.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -764,11 +764,10 @@ bool Parser::isTypeSpecifier()
764764
{
765765
int t=lex.LookAhead(0);
766766

767-
return is_identifier(t) || t == TOK_SCOPE || t == TOK_CONSTEXPR ||
768-
t == TOK_CONST || t == TOK_VOLATILE || t == TOK_RESTRICT ||
769-
t == TOK_CHAR || t == TOK_INT || t == TOK_SHORT || t == TOK_LONG ||
770-
t == TOK_CHAR16_T || t == TOK_CHAR32_T || t == TOK_WCHAR_T ||
771-
t == TOK_COMPLEX // new !!!
767+
return is_identifier(t) || t == TOK_SCOPE || t == TOK_CONST ||
768+
t == TOK_VOLATILE || t == TOK_RESTRICT || t == TOK_CHAR ||
769+
t == TOK_INT || t == TOK_SHORT || t == TOK_LONG || t == TOK_CHAR16_T ||
770+
t == TOK_CHAR32_T || t == TOK_WCHAR_T || t == TOK_COMPLEX // new !!!
772771
|| t == TOK_SIGNED || t == TOK_UNSIGNED || t == TOK_FLOAT ||
773772
t == TOK_DOUBLE || t == TOK_INT8 || t == TOK_INT16 || t == TOK_INT32 ||
774773
t == TOK_INT64 || t == TOK_GCC_INT128 || t == TOK_PTR32 ||
@@ -2018,7 +2017,7 @@ bool Parser::optMemberSpec(cpp_member_spect &member_spec)
20182017

20192018
/*
20202019
storage.spec : STATIC | EXTERN | AUTO | REGISTER | MUTABLE | ASM |
2021-
THREAD_LOCAL
2020+
THREAD_LOCAL | CONSTEXPR
20222021
*/
20232022
bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20242023
{
@@ -2027,7 +2026,7 @@ bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20272026
if(
20282027
t == TOK_STATIC || t == TOK_EXTERN || (t == TOK_AUTO && !cpp11) ||
20292028
t == TOK_REGISTER || t == TOK_MUTABLE || t == TOK_GCC_ASM ||
2030-
t == TOK_THREAD_LOCAL)
2029+
t == TOK_THREAD_LOCAL || t == TOK_CONSTEXPR)
20312030
{
20322031
cpp_tokent tk;
20332032
lex.get_token(tk);
@@ -2041,6 +2040,9 @@ bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20412040
case TOK_MUTABLE: storage_spec.set_mutable(); break;
20422041
case TOK_GCC_ASM: storage_spec.set_asm(); break;
20432042
case TOK_THREAD_LOCAL: storage_spec.set_thread_local(); break;
2043+
case TOK_CONSTEXPR:
2044+
storage_spec.set_constexpr();
2045+
break;
20442046
default: UNREACHABLE;
20452047
}
20462048

@@ -2051,30 +2053,24 @@ bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20512053
}
20522054

20532055
/*
2054-
cv.qualify : (CONSTEXPR | CONST | VOLATILE | RESTRICT)+
2056+
cv.qualify : (CONST | VOLATILE | RESTRICT)+
20552057
*/
20562058
bool Parser::optCvQualify(typet &cv)
20572059
{
20582060
for(;;)
20592061
{
20602062
int t=lex.LookAhead(0);
2061-
if(t==TOK_CONSTEXPR ||
2062-
t==TOK_CONST || t==TOK_VOLATILE || t==TOK_RESTRICT ||
2063-
t==TOK_PTR32 || t==TOK_PTR64 ||
2064-
t==TOK_GCC_ATTRIBUTE || t==TOK_GCC_ASM)
2063+
if(
2064+
t == TOK_CONST || t == TOK_VOLATILE || t == TOK_RESTRICT ||
2065+
t == TOK_PTR32 || t == TOK_PTR64 || t == TOK_GCC_ATTRIBUTE ||
2066+
t == TOK_GCC_ASM)
20652067
{
20662068
cpp_tokent tk;
20672069
lex.get_token(tk);
20682070
typet p;
20692071

20702072
switch(t)
20712073
{
2072-
case TOK_CONSTEXPR:
2073-
p=typet(ID_constexpr);
2074-
set_location(p, tk);
2075-
merge_types(p, cv);
2076-
break;
2077-
20782074
case TOK_CONST:
20792075
p=typet(ID_const);
20802076
set_location(p, tk);

0 commit comments

Comments
 (0)