Skip to content

Commit 697d29f

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 5c1ace0 commit 697d29f

File tree

9 files changed

+128
-30
lines changed

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>
@@ -1820,6 +1821,71 @@ void cpp_typecheckt::typecheck_side_effect_function_call(
18201821

18211822
add_implicit_dereference(expr);
18221823

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

20182017
/*
20192018
storage.spec : STATIC | EXTERN | AUTO | REGISTER | MUTABLE | ASM |
2020-
THREAD_LOCAL
2019+
THREAD_LOCAL | CONSTEXPR
20212020
*/
20222021
bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20232022
{
@@ -2026,7 +2025,7 @@ bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20262025
if(
20272026
t == TOK_STATIC || t == TOK_EXTERN || (t == TOK_AUTO && !cpp11) ||
20282027
t == TOK_REGISTER || t == TOK_MUTABLE || t == TOK_GCC_ASM ||
2029-
t == TOK_THREAD_LOCAL)
2028+
t == TOK_THREAD_LOCAL || t == TOK_CONSTEXPR)
20302029
{
20312030
cpp_tokent tk;
20322031
lex.get_token(tk);
@@ -2040,6 +2039,9 @@ bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20402039
case TOK_MUTABLE: storage_spec.set_mutable(); break;
20412040
case TOK_GCC_ASM: storage_spec.set_asm(); break;
20422041
case TOK_THREAD_LOCAL: storage_spec.set_thread_local(); break;
2042+
case TOK_CONSTEXPR:
2043+
storage_spec.set_constexpr();
2044+
break;
20432045
default: UNREACHABLE;
20442046
}
20452047

@@ -2050,30 +2052,24 @@ bool Parser::optStorageSpec(cpp_storage_spect &storage_spec)
20502052
}
20512053

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

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

0 commit comments

Comments
 (0)