/*******************************************************************\
Module: C++ Language Type Checking
Author: Daniel Kroening, [email protected]
\*******************************************************************/
/// \file
/// C++ Language Type Checking
#include "cpp_typecheck.h"
#include
#include
#include
/// \param source_location: source location for generated code
/// \param object: non-typechecked object
/// \param operands: non-typechecked operands
/// \return typechecked code
std::optional cpp_typecheckt::cpp_constructor(
const source_locationt &source_location,
const exprt &object,
const exprt::operandst &operands)
{
exprt object_tc=object;
typecheck_expr(object_tc);
elaborate_class_template(object_tc.type());
CHECK_RETURN(!is_reference(object_tc.type()));
if(object_tc.type().id() == ID_array)
{
// We allow only one operand and it must be tagged with '#array_ini'.
// Note that the operand is an array that is used for copy-initialization.
// In the general case, a program is not allowed to use this form of
// construct. This way of initializing an array is used internally only.
// The purpose of the tag #array_ini is to rule out ill-formed
// programs.
if(!operands.empty() && !operands.front().get_bool(ID_C_array_ini))
{
error().source_location=source_location;
error() << "bad array initializer" << eom;
throw 0;
}
DATA_INVARIANT(
operands.empty() || operands.size() == 1,
"array constructor must have at most one operand");
if(operands.empty() && cpp_is_pod(object_tc.type()))
return {};
const exprt &size_expr = to_array_type(object_tc.type()).size();
if(size_expr.id() == ID_infinity)
return {}; // don't initialize
exprt tmp_size=size_expr;
make_constant_index(tmp_size);
mp_integer s;
if(to_integer(to_constant_expr(tmp_size), s))
{
error().source_location=source_location;
error() << "array size '" << to_string(size_expr) << "' is not a constant"
<< eom;
throw 0;
}
/*if(cpp_is_pod(object_tc.type()))
{
code_expressiont new_code;
exprt op_tc=operands.front();
typecheck_expr(op_tc);
// Override constantness
object_tc.type().set("ID_C_constant", false);
object_tc.set("ID_C_lvalue", true);
side_effect_exprt assign(ID_assign);
assign.add_source_location()=source_location;
assign.copy_to_operands(object_tc, op_tc);
typecheck_side_effect_assignment(assign);
new_code.expression()=assign;
return new_code;
}
else*/
{
code_blockt new_code;
// for each element of the array, call the default constructor
for(mp_integer i=0; i < s; ++i)
{
exprt::operandst tmp_operands;
exprt constant = from_integer(i, c_index_type());
constant.add_source_location()=source_location;
index_exprt index = index_exprt(object_tc, constant);
index.add_source_location()=source_location;
if(!operands.empty())
{
index_exprt operand(operands.front(), constant);
operand.add_source_location()=source_location;
tmp_operands.push_back(operand);
}
auto i_code = cpp_constructor(source_location, index, tmp_operands);
if(i_code.has_value())
new_code.add(std::move(i_code.value()));
}
return std::move(new_code);
}
}
else if(cpp_is_pod(object_tc.type()))
{
exprt::operandst operands_tc=operands;
for(auto &op : operands_tc)
{
typecheck_expr(op);
add_implicit_dereference(op);
}
if(operands_tc.empty())
{
// a POD is NOT initialized
return {};
}
else if(operands_tc.size()==1)
{
// Override constantness
object_tc.type().set(ID_C_constant, false);
object_tc.set(ID_C_lvalue, true);
side_effect_expr_assignt assign(
object_tc, operands_tc.front(), typet(), source_location);
typecheck_side_effect_assignment(assign);
return code_expressiont(std::move(assign));
}
else
{
error().source_location=source_location;
error() << "initialization of POD requires one argument, "
"but got " << operands.size() << eom;
throw 0;
}
}
else if(object_tc.type().id() == ID_union_tag)
{
UNREACHABLE; // Todo: union
}
else if(object_tc.type().id() == ID_struct_tag)
{
exprt::operandst operands_tc=operands;
for(auto &op : operands_tc)
{
typecheck_expr(op);
add_implicit_dereference(op);
}
const struct_typet &struct_type =
follow_tag(to_struct_tag_type(object_tc.type()));
// set most-derived bits
code_blockt block;
for(const auto &component : struct_type.components())
{
if(component.get_base_name() != "@most_derived")
continue;
member_exprt member(object_tc, component.get_name(), bool_typet());
member.add_source_location()=source_location;
member.set(ID_C_lvalue, object_tc.get_bool(ID_C_lvalue));
exprt val=false_exprt();
if(!component.get_bool(ID_from_base))
val=true_exprt();
side_effect_expr_assignt assign(
std::move(member), std::move(val), typet(), source_location);
typecheck_side_effect_assignment(assign);
block.add(code_expressiont(std::move(assign)));
}
// enter struct scope
cpp_save_scopet save_scope(cpp_scopes);
cpp_scopes.set_scope(struct_type.get(ID_name));
// find name of constructor
const struct_typet::componentst &components=
struct_type.components();
irep_idt constructor_name;
for(const auto &c : components)
{
const typet &type = c.type();
if(
!c.get_bool(ID_from_base) && type.id() == ID_code &&
to_code_type(type).return_type().id() == ID_constructor)
{
constructor_name = c.get_base_name();
break;
}
}
INVARIANT(!constructor_name.empty(), "non-PODs should have a constructor");
side_effect_expr_function_callt function_call(
cpp_namet(constructor_name, source_location).as_expr(),
operands_tc,
uninitialized_typet(),
source_location);
typecheck_side_effect_function_call(function_call);
CHECK_RETURN(function_call.get(ID_statement) == ID_temporary_object);
exprt &initializer =
static_cast(function_call.add(ID_initializer));
DATA_INVARIANT(
initializer.id() == ID_code &&
initializer.get(ID_statement) == ID_expression,
"initializer must be expression statement");
auto &statement_expr = to_code_expression(to_code(initializer));
side_effect_expr_function_callt &func_ini =
to_side_effect_expr_function_call(statement_expr.expression());
exprt &tmp_this=func_ini.arguments().front();
DATA_INVARIANT(
to_address_of_expr(tmp_this).object().id() == ID_new_object,
"expected new_object operand in address_of expression");
tmp_this=address_of_exprt(object_tc);
const auto &initializer_code=to_code(initializer);
if(block.statements().empty())
return initializer_code;
else
{
block.add(initializer_code);
return std::move(block);
}
}
else
UNREACHABLE;
return {};
}
void cpp_typecheckt::new_temporary(
const source_locationt &source_location,
const typet &type,
const exprt::operandst &ops,
exprt &temporary)
{
// create temporary object
side_effect_exprt tmp_object_expr(ID_temporary_object, type, source_location);
tmp_object_expr.set(ID_mode, ID_cpp);
exprt new_object(ID_new_object);
new_object.add_source_location()=tmp_object_expr.source_location();
new_object.set(ID_C_lvalue, true);
new_object.type()=tmp_object_expr.type();
already_typechecked_exprt::make_already_typechecked(new_object);
auto new_code = cpp_constructor(source_location, new_object, ops);
if(new_code.has_value())
{
if(new_code->get_statement() == ID_assign)
tmp_object_expr.add_to_operands(std::move(new_code->op1()));
else
tmp_object_expr.add(ID_initializer) = *new_code;
}
temporary.swap(tmp_object_expr);
}
void cpp_typecheckt::new_temporary(
const source_locationt &source_location,
const typet &type,
const exprt &op,
exprt &temporary)
{
exprt::operandst ops;
ops.push_back(op);
new_temporary(source_location, type, ops, temporary);
}