/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997, 1998
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/producers/common/construct/initialise.c,v 1.15 2005/11/07 18:42:37 stefanf Exp $
*/
#include "config.h"
#include "producer.h"
#include "msgcat.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "etype_ops.h"
#include "exp_ops.h"
#include "ftype_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nat_ops.h"
#include "nspace_ops.h"
#include "str_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "tdf.h"
#include "access.h"
#include "assign.h"
#include "basetype.h"
#include "buffer.h"
#include "cast.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "constant.h"
#include "construct.h"
#include "convert.h"
#include "declare.h"
#include "derive.h"
#include "destroy.h"
#include "dump.h"
#include "exception.h"
#include "expression.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "inttype.h"
#include "literal.h"
#include "namespace.h"
#include "overload.h"
#include "parse.h"
#include "predict.h"
#include "print.h"
#include "statement.h"
#include "syntax.h"
#include "template.h"
#include "tok.h"
#include "tokdef.h"
#include "token.h"
#include "ustring.h"
/*
* INLINE MEMBER DEFINITIONS
*
* A static member can be defined inline in its class. This is only a
* provisional definition for use in constant expressions etc. The real
* definition (which cannot contain an initialiser) must be provided
* elsewhere. This value is used to indicate such members.
*/
#define dspec_stat_inline dspec_explicit
#define dspec_ignore_mem (dspec_alias | dspec_inherit | dspec_token)
/*
* MEMBER NUMBER
*
* This variable is used to keep track of the number of data members
* within next_data_member. Anonymous unions, rather than their members,
* are counted.
*/
unsigned long member_no = 0;
/*
* FIND NEXT DATA MEMBER
*
* This routine returns the first non-static, non-function member of a
* class following mem. Anonymous unions are included if bit 1 of bf
* is false, but their members if it is true. Anonymous bitfields are
* included if bit 0 of bf is true. The null member is returned if there
* are no further members.
*/
MEMBER
next_data_member(MEMBER mem, int bf)
{
while (!IS_NULL_member (mem)) {
IDENTIFIER id = DEREF_id (member_id (mem));
if (!IS_NULL_id (id) && IS_id_member (id)) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (!(ds & dspec_ignore_mem)) {
int ok = 1;
HASHID nm = DEREF_hashid (id_name (id));
if (ds & dspec_reserve) {
/* Anonymous union members */
if (!(bf & 2)) ok = 0;
} else {
member_no++;
}
if (IS_hashid_anon (nm)) {
TYPE t = DEREF_type (id_member_type (id));
unsigned tag = TAG_type (t);
if (tag == type_bitfield_tag) {
/* Anonymous bitfield */
if (!(bf & 1)) ok = 0;
} else if (tag == type_compound_tag) {
/* Anonymous union */
if (bf & 2) ok = 0;
}
}
if (ok) return (mem);
}
}
mem = DEREF_member (member_next (mem));
}
return (NULL_member);
}
/*
* CONSTRUCT A DYNAMIC INITIALISER
*
* This routine checks whether the expression e is a non-constant
* initialiser for id (or a component of id if off is not null). If so
* it is embedded in a dynamic initialiser expression and an error is
* reported.
*/
EXP
dynamic_init(IDENTIFIER id, string off, EXP e)
{
int fs = 0;
int c = -1;
if (!IS_NULL_id (id)) {
switch (TAG_id (id)) {
case id_variable_tag : {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_auto) {
if (off == NULL) return (e);
} else {
if (!(ds & dspec_linkage)) fs = 1;
}
break;
}
case id_stat_member_tag : {
/* Check static members */
break;
}
default : {
/* Ignore other identifiers */
return (e);
}
}
}
if (option (OPT_init_dynamic)) {
/* Dynamic initialisation not allowed */
c = 1;
}
if (!is_const_exp (e, c)) {
/* Non-constant initialiser */
TYPE t = DEREF_type (exp_type (e));
if (!is_templ_type (t)) {
ERROR err;
if (off) {
err = ERR_dcl_init_aggr_dynamic ();
} else {
err = ERR_dcl_init_dynamic ();
}
if (!IS_NULL_err (err)) {
ERROR err2 = ERR_dcl_init_decl (id, off);
err = concat_error (err2, err);
report (crt_loc, err);
}
if (fs) {
/* Check function statics */
e = check_return_exp (e, lex_static);
}
MAKE_exp_dynamic (t, e, e);
}
}
return (e);
}
/*
* CHECK A VARIABLE INITIALISER
*
* This routine is called to check the initialiser for a variable or static
* data member. Its primary purpose is to mark those temporaries which
* are bound to variables.
*/
EXP
check_init(EXP e)
{
if (!IS_NULL_exp (e)) {
switch (TAG_exp (e)) {
case exp_identifier_tag : {
IDENTIFIER id = DEREF_id (exp_identifier_id (e));
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_temp) {
ds &= ~dspec_register;
COPY_dspec (id_storage (id), ds);
}
break;
}
case exp_init_tag : {
IDENTIFIER id = DEREF_id (exp_init_id (e));
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_temp) {
ds &= ~dspec_register;
COPY_dspec (id_storage (id), ds);
}
break;
}
case exp_indir_tag : {
EXP a = DEREF_exp (exp_indir_ptr (e));
a = check_init (a);
COPY_exp (exp_indir_ptr (e), a);
break;
}
case exp_address_tag : {
EXP a = DEREF_exp (exp_address_arg (e));
a = check_init (a);
COPY_exp (exp_address_arg (e), a);
break;
}
case exp_base_cast_tag : {
EXP a = DEREF_exp (exp_base_cast_arg (e));
a = check_init (a);
COPY_exp (exp_base_cast_arg (e), a);
break;
}
case exp_add_ptr_tag : {
EXP a = DEREF_exp (exp_add_ptr_ptr (e));
a = check_init (a);
COPY_exp (exp_add_ptr_ptr (e), a);
break;
}
case exp_dynamic_tag : {
EXP a = DEREF_exp (exp_dynamic_arg (e));
a = check_init (a);
COPY_exp (exp_dynamic_arg (e), a);
break;
}
case exp_aggregate_tag : {
LIST (EXP) p = DEREF_list (exp_aggregate_args (e));
while (!IS_NULL_list (p)) {
EXP a = DEREF_exp (HEAD_list (p));
a = check_init (a);
COPY_exp (HEAD_list (p), a);
p = TAIL_list (p);
}
break;
}
case exp_nof_tag : {
EXP a = DEREF_exp (exp_nof_start (e));
EXP b = DEREF_exp (exp_nof_pad (e));
EXP c = DEREF_exp (exp_nof_end (e));
a = check_init (a);
b = check_init (b);
c = check_init (c);
COPY_exp (exp_nof_start (e), a);
COPY_exp (exp_nof_pad (e), b);
COPY_exp (exp_nof_end (e), c);
break;
}
}
}
return (e);
}
/*
* TEMPORARY VARIABLE FLAG
*
* The variable made_temporary is set to the temporary variable whenever
* a temporary variable is created. temp_storage is used to determine
* the storage class for a local variable.
*/
IDENTIFIER made_temporary = NULL_id;
int keep_temporary = 0;
static DECL_SPEC temp_storage = dspec_auto;
/*
* DECLARE A TEMPORARY VARIABLE
*
* This routine declares a temporary variable of type t, initial value
* e, and destructor d (the implicit destructor will be created if this
* is the null expression). It returns an expression giving the value of
* this temporary. Note the use of an assignment expression to ensure
* that the temporary gets initialised in the right place.
*/
EXP
make_temporary(TYPE t, EXP e, EXP d, int ref, ERROR *err)
{
LOCATION loc;
DECL_SPEC ds;
EXP c = NULL_exp;
HASHID nm = lookup_anon ();
NAMESPACE ns = crt_namespace;
QUALIFIER cq = crt_id_qualifier;
int tq = crt_templ_qualifier;
int fn = in_function_defn;
IDENTIFIER id = DEREF_id (hashid_id (nm));
loc = decl_loc;
decl_loc = crt_loc;
crt_id_qualifier = qual_none;
crt_templ_qualifier = 0;
/* Declare the temporary object */
if (IS_type_ref (t)) t = DEREF_type (type_ref_sub (t));
if (in_default_arg) {
/* NOT YET IMPLEMENTED */
crt_namespace = global_namespace;
in_function_defn = 0;
}
t = qualify_type (t, cv_none, 0);
if (IS_type_compound (t)) {
add_error (err, ERR_dcl_init_ref_tmp (t));
}
id = make_object_decl (dspec_temp, t, id, 0);
ds = DEREF_dspec (id_storage (id));
if (temp_storage != dspec_auto) {
/* Set storage class */
ds &= ~dspec_storage;
ds |= temp_storage;
}
if (ds & dspec_auto) ds |= dspec_register;
COPY_dspec (id_storage (id), ds);
made_temporary = id;
/* Check initialiser */
if (!is_const_exp (e, -1)) {
if (ds & dspec_auto) {
c = e;
e = NULL_exp;
} else if (ref) {
TYPE s = DEREF_type (exp_type (e));
MAKE_exp_dynamic (s, e, e);
add_error (err, ERR_dcl_init_dynamic ());
} else {
TYPE s = DEREF_type (exp_type (e));
c = e;
e = make_null_exp (s);
}
}
if (IS_NULL_exp (d)) {
/* Create destructor */
int du = do_usage;
do_usage = 0;
d = init_default (t, &d, DEFAULT_DESTR, EXTRA_DESTR, err);
do_usage = du;
}
COPY_exp (id_variable_init (id), e);
COPY_exp (id_variable_term (id), d);
define_id (id);
/* Construct the result expression */
if (!IS_NULL_exp (c)) {
/* Assign initial value */
t = DEREF_type (id_variable_type (id));
MAKE_exp_init (t, id, c, e);
ds = DEREF_dspec (id_storage (id));
ds |= dspec_explicit;
COPY_dspec (id_storage (id), ds);
} else {
e = make_id_exp (id);
}
/* Define variable */
if (!(ds & dspec_auto)) {
if (!really_in_function_defn && !in_template_decl) {
/* Compile variable definition */
compile_variable (id, 0);
}
}
if (do_dump) {
dump_implicit = 1;
dump_declare (id, &decl_loc, 1);
}
crt_templ_qualifier = tq;
crt_id_qualifier = cq;
in_function_defn = fn;
crt_namespace = ns;
decl_loc = loc;
return (e);
}
/*
* IS A VARIABLE ALIASED IN AN EXPRESSION
*
* This routine checks whether the variable expression d is aliased in
* the expression e.
*/
static int
involves_alias(EXP e, EXP d)
{
if (!IS_NULL_exp (d)) {
/* NOT YET IMPLEMENTED */
UNUSED (e);
return (1);
}
return (0);
}
/*
* ELIMINATE A TEMPORARY VARIABLE
*
* This routine is called to eliminate any temporary variables from the
* value e which is to be assigned to a variable given by d. d is the
* null expression in a variable initialisation, otherwise care needs
* to be taken if d is aliased in e. Note that creating a temporary and
* then removing it ensures that the accesses for the copy constructor
* and destructor are checked correctly.
*/
EXP
remove_temporary(EXP e, EXP d)
{
if (!IS_NULL_exp (e)) {
EXP a = NULL_exp;
unsigned tag = TAG_exp (e);
if (tag == exp_constr_tag) {
LIST (EXP) p;
int info = DEREF_int (exp_constr_info (e));
if (info != DEFAULT_COPY) return (e);
a = DEREF_exp (exp_constr_call (e));
p = DEREF_list (exp_func_id_args (a));
if (IS_NULL_list (p)) return (e);
p = TAIL_list (p);
if (IS_NULL_list (p)) return (e);
a = DEREF_exp (HEAD_list (p));
if (!IS_exp_address (a)) return (e);
a = DEREF_exp (exp_address_arg (a));
} else if (tag == exp_contents_tag) {
a = DEREF_exp (exp_contents_ptr (e));
}
if (!IS_NULL_exp (a) && IS_exp_init (a)) {
/* Check for temporary variable */
IDENTIFIER id = DEREF_id (exp_init_id (a));
if (IS_id_variable (id)) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if ((ds & dspec_temp) && !keep_temporary) {
/* Eliminate temporary variable */
EXP b = DEREF_exp (exp_init_arg (a));
if (IS_NULL_exp (b)) {
b = DEREF_exp (id_variable_init (id));
}
if (!involves_alias (b, d)) {
ds |= dspec_ignore;
COPY_dspec (id_storage (id), ds);
return (b);
}
}
}
}
}
return (e);
}
/*
* FORCE A REFERENCE INITIALISATION
*
* This flag forces a reference to be initialised by a less cv-qualified
* version of the same type. Values of greater than 1 can arise in
* copy constructors.
*/
int init_ref_force = 1;
/*
* INITIALISE A REFERENCE WITH AN LVALUE
*
* This routine checks whether e is a suitable lvalue initialiser for a
* reference to type t. If so a suitably converted version of e is
* returned. The null expression is returned otherwise.
*/
EXP
init_ref_lvalue(TYPE t, EXP e, ERROR *err)
{
EXP a = NULL_exp;
if (!IS_NULL_exp (e)) {
TYPE s = DEREF_type (exp_type (e));
CV_SPEC qual = DEREF_cv (type_qual (s));
if (qual & cv_lvalue) {
/* Check whether t is reference compatible with s */
unsigned nt = TAG_type (t);
unsigned ns = TAG_type (s);
CV_SPEC cv = cv_compare (t, s);
if (nt == ns) {
if (nt == type_compound_tag) {
/* Check for base class conversions */
if (cv == cv_none || init_ref_force) {
a = cast_class_class (t, e, err, CAST_IMPLICIT, 1);
}
} else if (nt == type_func_tag) {
/* Allow for overloading */
LIST (IDENTIFIER) pids = NULL_list (IDENTIFIER);
e = resolve_cast (t, e, err, 1, 0, pids);
if (!IS_exp_member (e)) {
s = DEREF_type (exp_type (e));
if (eq_type_unqual (t, s)) {
if (eq_except (t, s) != 2) {
add_error (err, ERR_except_spec_init ());
}
a = e;
}
}
} else {
/* Otherwise check for equal types */
if (eq_type_unqual (t, s)) {
a = e;
if (cv != cv_none) {
add_error (err, ERR_dcl_init_ref_qual (cv));
}
}
}
}
}
if (is_templ_type (s)) {
/* Allow for template parameters */
a = cast_templ_type (t, e, CAST_IMPLICIT);
}
}
return (a);
}
/*
* INITIALISE A REFERENCE WITH AN RVALUE
*
* This routine checks whether e is a suitable rvalue initialiser for a
* reference to type t. It is firstly checked that t is a const reference
* and not a reference to function. If t is reference compatible with the
* type of s then a temporary is created to hold the value of e and the
* contents of this temporary are returned. Otherwise the null expression
* is returned.
*/
static EXP
init_ref_rvalue(TYPE t, EXP e, ERROR *err)
{
/* Check for reference to functions */
CV_SPEC qual;
unsigned nt = TAG_type (t);
if (nt == type_func_tag) {
add_error (err, ERR_dcl_init_ref_func ());
e = make_null_exp (t);
return (e);
}
/* Check for const references */
qual = find_cv_qual (t);
qual &= cv_qual;
if (qual != cv_const) {
int ok = 0;
if (!IS_NULL_exp (e)) {
TYPE s = DEREF_type (exp_type (e));
if (IS_type_error (s)) ok = 1;
}
if (!ok) add_error (err, ERR_dcl_init_ref_const ());
}
/* Check the initialiser */
if (!IS_NULL_exp (e)) {
/* Check whether t is reference compatible with s */
int force = init_ref_force;
TYPE s = DEREF_type (exp_type (e));
unsigned ns = TAG_type (s);
CV_SPEC cv = cv_compare (t, s);
if (nt == ns && (cv == cv_none || force)) {
TYPE r = t;
if (nt == type_compound_tag) {
/* t must be a base class of s */
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
CLASS_TYPE cs = DEREF_ctype (type_compound_defn (s));
GRAPH gr = find_base_class (cs, ct, 1);
if (IS_NULL_graph (gr)) return (NULL_exp);
r = qualify_type (s, qual, 0);
/* NOT YET IMPLEMENTED: copy e */
} else {
/* Otherwise check for equal types */
if (!eq_type_unqual (t, s)) return (NULL_exp);
}
if (cv != cv_none) {
/* Binding from more qualified type */
add_error (err, ERR_dcl_init_ref_qual (cv));
}
e = make_temporary (r, e, NULL_exp, 1, err);
if (nt == type_compound_tag) {
/* Bind temporary to reference */
e = cast_class_class (t, e, err, CAST_IMPLICIT, 1);
}
return (e);
}
}
return (NULL_exp);
}
/*
* CREATE A REFERENCE INITIALISER
*
* This routine creates a reference initialiser of type t out of the
* expression e.
*/
EXP
make_ref_init(TYPE t, EXP e)
{
if (!IS_NULL_exp (e)) {
if (IS_exp_op (e)) {
/* Allow for template parameters */
COPY_type (exp_type (e), t);
} else {
TYPE s = t;
unsigned tag = TAG_type (s);
if (tag == type_ref_tag) {
s = DEREF_type (type_ref_sub (s));
tag = TAG_type (s);
}
if (tag == type_token_tag && is_templ_type (s)) {
/* Check again later */
/* EMPTY */
} else {
MAKE_exp_address (t, e, e);
}
}
}
return (e);
}
/*
* CREATE A NULL EXPRESSION
*
* This routine creates a null expression (i.e. all zeros) for the type t.
* This is the default value for a non-explicitly initialised variable with
* internal or external linkage.
*/
EXP
make_null_exp(TYPE t)
{
EXP e;
switch (TAG_type (t)) {
case type_integer_tag :
case type_enumerate_tag : {
NAT n = small_nat [0];
MAKE_exp_int_lit (t, n, exp_int_lit_tag, e);
break;
}
case type_floating_tag : {
FLOAT f = get_float (t, 0);
MAKE_exp_float_lit (t, f, e);
break;
}
case type_bitfield_tag : {
TYPE s = find_bitfield_type (t);
e = make_null_exp (s);
MAKE_exp_cast (t, (CONV_BITFIELD | CONV_REVERSE), e, e);
break;
}
default : {
MAKE_exp_null (t, e);
break;
}
}
return (e);
}
/*
* CREATE A UNIT EXPRESSION
*
* This routine creates a unit expression (i.e. one) for the type t.
*/
EXP
make_unit_exp(TYPE t)
{
EXP e;
switch (TAG_type (t)) {
case type_integer_tag :
case type_enumerate_tag : {
NAT n = small_nat [1];
MAKE_exp_int_lit (t, n, exp_int_lit_tag, e);
break;
}
case type_floating_tag : {
FLOAT f = get_float (t, 1);
MAKE_exp_float_lit (t, f, e);
break;
}
case type_bitfield_tag : {
TYPE s = find_bitfield_type (t);
e = make_unit_exp (s);
MAKE_exp_cast (t, (CONV_BITFIELD | CONV_REVERSE), e, e);
break;
}
default : {
FAIL (Invalid unit type);
MAKE_exp_null (t, e);
break;
}
}
return (e);
}
/*
* IS AN EXPRESSION NULL?
*
* This routine checks whether the expression e is a null expression.
*/
int
is_null_exp(EXP e)
{
if (IS_NULL_exp (e)) return (1);
switch (TAG_exp (e)) {
case exp_int_lit_tag : {
NAT n = DEREF_nat (exp_int_lit_nat (e));
return (is_zero_nat (n));
}
case exp_float_lit_tag : {
FLOAT f = DEREF_flt (exp_float_lit_flt (e));
return (is_zero_float (f));
}
case exp_null_tag :
case exp_zero_tag : {
return (1);
}
case exp_aggregate_tag : {
LIST (EXP) p = DEREF_list (exp_aggregate_args (e));
while (!IS_NULL_list (p)) {
EXP a = DEREF_exp (HEAD_list (p));
if (!is_null_exp (a)) return (0);
p = TAIL_list (p);
}
return (1);
}
case exp_nof_tag : {
EXP a = DEREF_exp (exp_nof_start (e));
EXP b = DEREF_exp (exp_nof_pad (e));
EXP c = DEREF_exp (exp_nof_end (e));
if (!is_null_exp (a)) return (0);
if (!is_null_exp (b)) return (0);
return (is_null_exp (c));
}
}
return (0);
}
/*
* CREATE AN EMPTY INITIALISER
*
* This routine creates an empty initialiser for the type t. Basically
* this is the same as make_null_exp except that it also checks for
* uninitialised references and const objects. Also if force is false
* then a value is only created if absolutely necessary.
*/
EXP
init_empty(TYPE t, CV_SPEC cv, int force, ERROR *err)
{
EXP e = NULL_exp;
switch (TAG_type (t)) {
case type_array_tag : {
NAT n = DEREF_nat (type_array_size (t));
TYPE s = DEREF_type (type_array_sub (t));
e = init_empty (s, cv, force, err);
if (!IS_NULL_exp (e)) {
MAKE_exp_nof (t, NULL_exp, n, e, NULL_exp, e);
}
break;
}
case type_ref_tag : {
/* References must be initialised */
TYPE s = DEREF_type (type_ref_sub (t));
add_error (err, ERR_dcl_init_ref_none ());
if (IS_type_func (s)) {
e = make_null_exp (s);
} else {
e = init_empty (s, cv_none, 1, err);
e = make_temporary (s, e, NULL_exp, 1, err);
e = make_ref_init (t, e);
}
break;
}
case type_compound_tag : {
/* Call default constructor for classes */
e = init_default (t, &e, DEFAULT_CONSTR, EXTRA_CONSTR, err);
if (IS_NULL_exp (e)) goto default_lab;
break;
}
case type_enumerate_tag : {
if (force) {
/* Check for zero enumerator */
ENUM_TYPE et = DEREF_etype (type_enumerate_defn (t));
CLASS_INFO ei = DEREF_cinfo (etype_info (et));
if (!(ei & cinfo_usr_constr)) {
add_error (err, ERR_dcl_enum_zero (t));
}
}
goto default_lab;
}
case type_token_tag : {
if (is_templ_type (t)) {
/* Allow for template parameters */
if (force) {
MAKE_exp_op (t, lex_cast, NULL_exp, NULL_exp, e);
}
break;
}
goto default_lab;
}
default :
default_lab : {
CV_SPEC qual = find_cv_qual (t);
qual |= cv;
if (qual & cv_const) {
/* Const objects must be initialised */
add_error (err, ERR_dcl_init_const ());
}
if (force) e = make_null_exp (t);
break;
}
}
return (e);
}
/*
* LAST ARRAY INITIALISER SIZE
*
* This variable is used to hold the number of elements in the last array
* initialiser processed. It is subsequently used to calculate the bound
* for an unbounded, but initialised, array type.
*/
static NAT last_array_size = NULL_nat;
/*
* IS A TYPE A CHARACTER ARRAY?
*
* This routine checks whether the type t is an array of 'char', 'signed
* char', 'unsigned char' 'wchar_t', and so may be initialised by a single
* literal. It returns 1 for character arrays, 2 for wide character
* arrays, and 3 for types compatible with wide character arrays.
*/
static int
is_char_array(TYPE t)
{
if (IS_type_array (t)) {
TYPE s = DEREF_type (type_array_sub (t));
if (check_int_type (s, btype_char)) return (1);
if (check_int_type (s, btype_wchar_t)) return (2);
if (!basetype_info [ ntype_wchar_t ].key) {
s = type_composite (s, type_wchar_t, 1, 0, KILL_err, 0);
if (!IS_NULL_type (s)) return (3);
}
}
return (0);
}
/*
* PAD AN ARRAY INITIALISER
*
* This routine pads the array initialiser e, which contains m elements,
* with zeros until it matches the type t. n gives the bound size of t.
*/
static EXP
pad_array(EXP e, NAT m, TYPE t, NAT n, int pad, ERROR *err)
{
EXP a;
int eq;
unsigned long c;
ERROR err2 = NULL_err;
TYPE s = DEREF_type (type_array_sub (t));
/* Check for equality */
eq = compare_nat (n, m);
if (eq == 0) {
return (e);
} else if (eq == 1) {
if (!pad) return (NULL_exp);
} else if (eq == -1) {
/* Too many initialisers */
if (!pad) return (NULL_exp);
add_error (err, ERR_dcl_init_aggr_excess (t));
return (e);
} else {
/* Allow for token definitions */
force_tokdef++;
eq = eq_nat (n, m);
force_tokdef--;
if (eq) return (e);
if (!pad) return (NULL_exp);
}
/* Find number of uninitialised elements */
c = get_nat_value (m);
if (c != 0) {
EXP en = calc_nat_value (n, type_size_t);
EXP em = calc_nat_value (m, type_size_t);
en = make_minus_exp (en, em);
if (IS_exp_int_lit (en)) {
n = DEREF_nat (exp_int_lit_nat (en));
if (pad && c > 1 && is_calc_nat (n)) {
/* Warn about potentially dubious initialisers */
err2 = ERR_dcl_init_aggr_array_ti (m, t);
}
c = get_nat_value (n);
if (c == 0) return (e);
}
}
/* Form initialiser */
if (IS_NULL_err (err2)) err2 = ERR_dcl_init_aggr_pad (n, t);
if (!IS_NULL_err (err2)) add_error (err, err2);
a = init_empty (s, cv_none, 1, err);
if (!IS_NULL_exp (e) && IS_exp_aggregate (e)) {
if (c <= ARRAY_PADDING) {
/* Explicitly pad small arrays */
LIST (EXP) p = DEREF_list (exp_aggregate_args (e));
LIST (EXP) q = NULL_list (EXP);
while (c) {
CONS_exp (a, q, q);
c--;
}
p = APPEND_list (p, q);
COPY_list (exp_aggregate_args (e), p);
COPY_type (exp_type (e), t);
return (e);
}
}
MAKE_exp_nof (t, e, n, a, NULL_exp, e);
return (e);
}
/*
* CHECK AN ASSIGNMENT STYLE ARRAY INITIALISER
*
* This routine checks the assignment style initialiser 't id = e ;' where
* t is an array type. If arr is true then e is allowed to be another
* array expression of compatible type. The routine returns the
* initialising expression for id.
*/
EXP
init_array(TYPE t, CV_SPEC cv, EXP e, int arr, ERROR *err)
{
TYPE r = DEREF_type (exp_type (e));
NAT n = DEREF_nat (type_array_size (t));
TYPE s = DEREF_type (type_array_sub (t));
if (IS_type_array (r)) {
unsigned tag = TAG_exp (e);
NAT m = DEREF_nat (type_array_size (r));
TYPE u = DEREF_type (type_array_sub (r));
if (IS_NULL_nat (n)) n = m;
last_array_size = m;
/* Check for templates */
if (in_template_decl) {
if (is_templ_type (s) || is_templ_type (u)) {
e = cast_templ_type (t, e, CAST_IMPLICIT);
return (e);
}
}
/* Initialisation by string literal */
if (tag == exp_string_lit_tag) {
unsigned long na, ma;
int ca = is_char_array (t);
STRING str = DEREF_str (exp_string_lit_str (e));
unsigned kind = DEREF_unsigned (str_simple_kind (str));
if (kind & STRING_WIDE) {
/* Wide string literals */
if (ca == 2) {
u = s;
} else if (ca == 3) {
if (IS_type_enumerate (s)) {
/* It could happen ... */
EXP a;
ENUM_TYPE es;
MAKE_exp_value (u, a);
a = cast_int_int (s, a, err, CAST_IMPLICIT, -1);
es = DEREF_etype (type_enumerate_defn (s));
s = DEREF_type (etype_rep (es));
free_exp (a, 1);
}
} else {
add_error (err, ERR_dcl_init_string_wchar ());
}
} else {
/* Normal string literals */
if (ca == 1) {
u = s;
} else {
add_error (err, ERR_dcl_init_string_char ());
}
}
if (!EQ_type (u, s)) {
/* Deal with invalid cases */
if (IS_type_integer (s)) {
/* Cast string to appropriate type */
MAKE_type_array (cv_none, s, m, r);
MAKE_exp_string_lit (r, str, e);
} else {
/* Don't take any initialisers from the string */
e = NULL_exp;
}
}
/* Check array bound */
na = get_nat_value (n);
ma = get_nat_value (m);
if (na != EXTENDED_MAX) {
/* Known array bounds */
if (ma > na) {
/* Too many initialisers - trim string */
if (ma == na + 1) {
add_error (err, ERR_dcl_init_string_zero (t));
} else {
add_error (err, ERR_dcl_init_string_excess (t));
}
MAKE_exp_string_lit (t, str, e);
} else if (ma < na) {
/* Not enough initialisers */
NAT d = make_nat_value (na - ma);
add_error (err, ERR_dcl_init_aggr_pad (d, t));
MAKE_exp_string_lit (t, str, e);
}
} else {
/* Unknown array bounds */
e = pad_array (e, m, t, n, 1, err);
}
return (e);
}
/* Check array initialisers */
if (tag == exp_token_tag) {
/* Allow rvalue array tokens */
CV_SPEC qual = DEREF_cv (type_qual (r));
if (!(qual & cv_lvalue)) arr = 2;
}
e = convert_reference (e, REF_ASSIGN);
if (arr == 0) {
/* Invalid array initialiser */
report (crt_loc, ERR_dcl_init_aggr_array_bad ());
if (tag == exp_paren_tag) {
/* Parenthesised initialiser */
e = init_array (t, cv, e, arr, err);
return (e);
}
arr = 1;
}
if (eq_type_unqual (s, u)) {
if (arr != 2) {
EXP d;
d = init_default (r, &e, DEFAULT_COPY, EXTRA_CONSTR, err);
if (IS_NULL_exp (d)) {
MAKE_exp_contents (r, e, e);
} else {
MAKE_exp_preinc (r, e, d, lex_array, e);
}
}
e = pad_array (e, m, t, n, arr - 1, err);
if (!IS_NULL_exp (e)) return (e);
}
add_error (err, ERR_basic_link_incompat (t, r));
} else {
/* Other array initialisations are not allowed */
report (crt_loc, ERR_dcl_init_aggr_array_bad ());
last_array_size = NULL_nat;
}
e = init_empty (t, cv, 1, err);
return (e);
}
/*
* REPORT AN INITIALISATION ERROR
*
* In C there is no distinction between conversion by initialisation and
* conversion by assignment. This routine adds a suitable error message
* to err which says that the conversion cannot be done by initialisation.
*/
ERROR
init_error(ERROR err, int init)
{
ERROR ferr;
#if LANGUAGE_CPP
ferr = ERR_dcl_init_conv ();
UNUSED (init);
#else
ferr = ERR_expr_ass_conv ();
if (init) ferr = concat_error (ferr, ERR_dcl_init_assign ());
#endif
err = concat_warning (err, ferr);
return (err);
}
/*
* CHECK AN ASSIGNMENT STYLE INITIALISER
*
* This routine checks the assignment style initialiser 'cv t id = e ;'.
* It returns a suitably converted version of e.
*/
EXP
init_assign(TYPE t, CV_SPEC cv, EXP e, ERROR *err)
{
switch (TAG_type (t)) {
case type_array_tag : {
/* Array initialisers */
e = init_array (t, cv, e, 0, err);
break;
}
case type_ref_tag : {
/* Reference initialisers */
EXP a;
TYPE s = DEREF_type (type_ref_sub (t));
TYPE r = DEREF_type (exp_type (e));
if (IS_type_compound (r)) {
if (IS_type_compound (s)) {
/* Check base class conversions first */
a = init_ref_lvalue (s, e, err);
if (!IS_NULL_exp (a)) {
e = make_ref_init (t, a);
break;
}
}
a = convert_conv_aux (t, e, err, CAST_IMPLICIT);
if (!IS_NULL_exp (a)) {
e = a;
r = DEREF_type (exp_type (e));
if (eq_type (r, t)) break;
e = convert_reference (e, REF_ASSIGN);
}
}
a = init_ref_lvalue (s, e, err);
if (IS_NULL_exp (a)) {
a = init_ref_rvalue (s, e, err);
if (IS_NULL_exp (a)) {
e = init_assign (s, cv_none, e, err);
if (!IS_exp_null (e)) {
e = make_temporary (s, e, NULL_exp, 1, err);
}
} else {
e = a;
}
} else {
e = a;
}
e = make_ref_init (t, e);
break;
}
case type_compound_tag : {
/* Class initialisers */
TYPE s = DEREF_type (exp_type (e));
if (IS_type_compound (s)) {
/* Check for base class initialisers */
CLASS_TYPE cs = DEREF_ctype (type_compound_defn (s));
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
GRAPH gr = find_base_class (cs, ct, 1);
if (!IS_NULL_graph (gr)) {
e = init_direct (t, e, err);
break;
}
}
e = convert_conv (t, e, err, CAST_IMPLICIT);
if (!IS_exp_null (e)) {
e = make_temporary (t, e, NULL_exp, 0, err);
e = init_direct (t, e, err);
e = remove_temporary (e, NULL_exp);
}
break;
}
default : {
/* Do conversion by initialisation */
e = convert_assign (t, e, err);
break;
}
}
return (e);
}
/*
* CHECK A CONSTRUCTOR STYLE INITIALISER
*
* This routine checks the constructor style initialiser 't id (args) ;'.
* It returns an expression representing the result of converting args to
* type t.
*/
EXP
init_constr(TYPE t, LIST (EXP) args, ERROR *err)
{
EXP e;
unsigned tag = TAG_type (t);
switch (tag) {
case type_ref_tag : {
/* Reference initialisers */
EXP a;
TYPE s = DEREF_type (type_ref_sub (t));
if (LENGTH_list (args) == 1) {
a = DEREF_exp (HEAD_list (args));
a = init_ref_lvalue (s, a, err);
} else {
a = NULL_exp;
}
if (IS_NULL_exp (a)) {
a = init_ref_rvalue (s, a, err);
if (IS_NULL_exp (a)) {
e = init_constr (s, args, err);
if (!IS_exp_null (e)) {
e = make_temporary (s, e, NULL_exp, 1, err);
}
} else {
e = a;
}
} else {
e = a;
}
e = make_ref_init (t, e);
break;
}
case type_compound_tag : {
/* Class constructor initialisers */
e = convert_constr (t, args, err, CAST_STATIC);
e = remove_temporary (e, NULL_exp);
break;
}
case type_token_tag : {
/* Check for template parameters */
if (is_templ_type (t)) {
LIST (OFFSET) offs = NULL_list (OFFSET);
MAKE_exp_initialiser (t, args, offs, 0, 0, 0, e);
e = cast_templ_type (t, e, CAST_IMPLICIT);
break;
}
goto default_lab;
}
default :
default_lab : {
/* Should have at most one argument otherwise */
unsigned nargs = LENGTH_list (args);
if (nargs == 0) {
e = init_empty (t, cv_none, 1, err);
} else {
EXP a = DEREF_exp (HEAD_list (args));
DESTROY_list (args, SIZE_exp);
if (nargs > 1) {
/* Can't have more than one initialiser */
add_error (err, ERR_dcl_init_ctor (t));
}
if (tag == type_array_tag) {
e = init_array (t, cv_none, a, 0, err);
} else {
TYPE s = DEREF_type (exp_type (a));
if (IS_type_compound (s)) {
e = convert_conv (t, a, err, CAST_STATIC);
} else {
e = convert_assign (t, a, err);
}
}
}
break;
}
}
return (e);
}
/*
* CHECK A DIRECT INITIALISER
*
* This routine checks the direct initialiser 't id (a) ;'. It is
* a special case of init_constr in which there is only one initialiser.
*/
EXP
init_direct(TYPE t, EXP a, ERROR *err)
{
LIST (EXP) args;
CONS_exp (a, NULL_list (EXP), args);
a = init_constr (t, args, err);
return (a);
}
/*
* FIELD NAME BUFFER
*
* This buffer is used to build up field names for use in error reporting.
*/
BUFFER field_buff = NULL_buff;
/*
* SET LOCATION FROM AN AGGREGATE INITIALISER
*
* Because aggregate initialisers may be spread over several lines each
* component is embedded in a location expression. This routine gets
* the first element of the aggregate list p, setting the current location
* as appropriate. It returns the tag of e (ignoring parentheses) via
* ptag.
*/
static EXP
get_aggr_elem(LIST (EXP) p, unsigned *ptag)
{
EXP a = DEREF_exp (HEAD_list (p));
if (!IS_NULL_exp (a)) {
if (IS_exp_location (a)) {
TYPE t;
DESTROY_exp_location (destroy, t, crt_loc, a, a);
UNUSED (t);
COPY_exp (HEAD_list (p), a);
}
if (!IS_NULL_exp (a)) {
EXP b = a;
unsigned tag = TAG_exp (b);
while (tag == exp_paren_tag) {
b = DEREF_exp (exp_paren_arg (b));
tag = TAG_exp (b);
}
*ptag = tag;
}
}
return (a);
}
/*
* CHECK AN AGGREGATE INITIALISER
*
* This routine checks the aggregate initialiser expression list pointed
* to by r against the type t. The argument start is 1 to indicate the
* presence of a open brace immediately preceding r and 2 to indicate
* the top-level aggregate. The result is a structured aggregate
* initialiser expression for compound types t or a suitably converted
* initialiser expression.
*/
static EXP
init_aggr_aux(TYPE t, CV_SPEC cv, LIST (EXP) *r, int start, IDENTIFIER id,
ERROR *err)
{
EXP e;
LIST (EXP) p = *r;
ERROR cerr = NULL_err;
CLASS_INFO ci = cinfo_none;
unsigned tag = TAG_type (t);
switch (tag) {
case type_array_tag : {
/* Array types */
NAT nc;
LIST (EXP) a = NULL_list (EXP);
TYPE s = DEREF_type (type_array_sub (t));
int str = is_char_array (s);
BUFFER *bf = &field_buff;
unsigned boff = (unsigned) (bf->posn - bf->start);
/* Find the array size */
NAT n = DEREF_nat (type_array_size (t));
unsigned long m = get_nat_value (n);
unsigned long c = 0;
/* Report partially bracketed initialisers */
if (!start) add_error (err, ERR_dcl_init_aggr_partial ());
/* Check for string literals in braces */
if (start && !IS_NULL_list (p)) {
unsigned et = null_tag;
e = get_aggr_elem (p, &et);
if (et == exp_string_lit_tag && is_char_array (t)) {
e = init_array (t, cv, e, 0, err);
p = TAIL_list (p);
break;
}
}
/* Loop through at most m initialisers */
while (!IS_NULL_list (p) && c != m) {
LIST (EXP) p0 = p;
ERROR serr = NULL_err;
unsigned et = null_tag;
/* Build up the field name */
bfprintf (bf, " [%lu]", c);
/* Check first element of aggregate */
e = get_aggr_elem (p, &et);
if (IS_NULL_exp (e)) {
/* Can occur in template initialisers */
e = init_empty (s, cv_none, 1, &serr);
COPY_exp (HEAD_list (p), e);
}
if (et == exp_string_lit_tag && str) {
/* Check for string literals */
e = init_array (s, cv, e, 0, &serr);
p = TAIL_list (p);
} else if (et == exp_aggregate_tag && start) {
/* Check for sub-aggregates */
LIST (EXP) q;
q = DEREF_list (exp_aggregate_args (e));
e = init_aggr_aux (s, cv, &q, 1, id, &serr);
p = TAIL_list (p);
} else {
/* Otherwise read constituents from p */
e = init_aggr_aux (s, cv, &p, 0, id, &serr);
}
/* Report any errors for this member */
if (!IS_NULL_err (serr)) {
ERROR ferr = ERR_dcl_init_decl (id, bf->start);
serr = concat_error (ferr, serr);
report (crt_loc, serr);
}
/* Check for dynamic initialisers */
e = dynamic_init (id, bf->start, e);
/* Restore the field name */
bf->posn = bf->start + boff;
bf->posn [0] = 0;
/* Check that some initialisers were used up */
if (EQ_list (p, p0)) break;
/* Build up the result (in reverse order) */
CONS_exp (e, a, a);
c++;
}
/* Construct the result */
a = REVERSE_list (a);
nc = make_nat_value (c);
MAKE_type_array (cv_none, s, nc, s);
MAKE_exp_aggregate (s, a, NULL_list (OFFSET), e);
/* Check array size */
if (!IS_NULL_nat (n)) {
e = pad_array (e, nc, t, n, 1, err);
}
last_array_size = nc;
break;
}
case type_ref_tag : {
/* Reference types */
TYPE s = DEREF_type (type_ref_sub (t));
e = init_ref_rvalue (s, NULL_exp, err);
if (IS_NULL_exp (e)) {
e = init_aggr_aux (s, cv, r, start, id, err);
e = make_temporary (s, e, NULL_exp, 1, err);
e = make_ref_init (t, e);
}
return (e);
}
case type_compound_tag : {
/* Compound types */
MEMBER mem;
NAMESPACE ns;
unsigned long pads = 0;
LIST (EXP) a = NULL_list (EXP);
LIST (OFFSET) b = NULL_list (OFFSET);
CV_SPEC cv1 = (DEREF_cv (type_qual (t)) | cv);
CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
GRAPH gr = DEREF_graph (ctype_base (ct));
LIST (GRAPH) br = DEREF_list (graph_tails (gr));
BUFFER *bf = &field_buff;
unsigned boff = (unsigned) (bf->posn - bf->start);
/* Check for non-aggregate classes */
ci = DEREF_cinfo (ctype_info (ct));
if (!(ci & cinfo_defined)) {
/* Instantiate template types if necessary */
complete_class (ct, 1);
ci = DEREF_cinfo (ctype_info (ct));
}
if (!(ci & cinfo_complete)) {
/* Incomplete types can't be initialised */
goto incomplete_lab;
}
if (ci & cinfo_non_aggregate) {
/* Types with these properties can't be initialised */
cerr = class_info (ct, cinfo_non_aggregate, 1);
if (ci & cinfo_token) goto token_lab;
if (ci & cinfo_usr_constr) goto non_aggregate_lab;
add_error (err, cerr);
add_error (err, ERR_dcl_init_aggr_type (t));
cerr = NULL_err;
}
/* Check for non-aggregate initialisations */
if (!IS_NULL_list (p)) {
unsigned rank;
CONVERSION conv;
unsigned et = null_tag;
e = get_aggr_elem (p, &et);
conv.from = DEREF_type (exp_type (e));
conv.to = t;
rank = std_convert_seq (&conv, e, 0, 0);
if (rank != CONV_NONE) goto non_aggregate_lab;
}
/* Report partially bracketed initialisers */
if (!start) add_error (err, ERR_dcl_init_aggr_partial ());
/* Loop through base classes */
while (!IS_NULL_list (br)) {
ERROR serr = NULL_err;
GRAPH gs = DEREF_graph (HEAD_list (br));
OFFSET off = DEREF_off (graph_off (gs));
CLASS_TYPE cs = DEREF_ctype (graph_head (gs));
TYPE s = make_class_type (cs);
/* Build up field name */
IDENTIFIER sid = DEREF_id (ctype_name (cs));
HASHID snm = DEREF_hashid (id_name (sid));
if (!IS_hashid_anon (snm)) {
bfputc (bf, '.');
IGNORE print_hashid (snm, 1, 0, bf, 0);
}
/* Check next initialiser */
if (!IS_NULL_list (p)) {
unsigned et = null_tag;
e = get_aggr_elem (p, &et);
if (et == exp_aggregate_tag && start) {
/* Check for sub-aggregates */
LIST (EXP) q;
q = DEREF_list (exp_aggregate_args (e));
e = init_aggr_aux (s, cv1, &q, 1, id, &serr);
p = TAIL_list (p);
} else {
/* Otherwise read constituents from p */
e = init_aggr_aux (s, cv1, &p, 0, id, &serr);
}
} else {
e = init_empty (s, cv1, 1, &serr);
pads++;
}
/* Report any errors for this field */
if (!IS_NULL_err (serr)) {
ERROR ferr = ERR_dcl_init_decl (id, bf->start);
serr = concat_error (ferr, serr);
report (crt_loc, serr);
}
/* Check for dynamic initialisers */
e = dynamic_init (id, bf->start, e);
/* Restore field name */
bf->posn = bf->start + boff;
bf->posn [0] = 0;
/* Build up the result (in reverse order) */
CONS_exp (e, a, a);
CONS_off (off, b, b);
br = TAIL_list (br);
}
/* Find list of class members */
ns = DEREF_nspace (ctype_member (ct));
mem = DEREF_member (nspace_ctype_first (ns));
mem = next_data_member (mem, 0);
/* Loop through structure members */
while (!IS_NULL_member (mem)) {
ERROR serr = NULL_err;
CV_SPEC cv2 = cv1;
IDENTIFIER sid = DEREF_id (member_id (mem));
TYPE s = DEREF_type (id_member_type (sid));
DECL_SPEC ds = DEREF_dspec (id_storage (sid));
OFFSET off = DEREF_off (id_member_off (sid));
/* Build up field name */
HASHID snm = DEREF_hashid (id_name (sid));
if (!IS_hashid_anon (snm)) {
bfputc (bf, '.');
IGNORE print_hashid (snm, 1, 0, bf, 0);
}
/* Adjust cv-qualifiers */
if (ds & dspec_mutable) cv2 = cv_none;
/* Check next initialiser */
if (!IS_NULL_list (p)) {
unsigned et = null_tag;
e = get_aggr_elem (p, &et);
if (et == exp_string_lit_tag && is_char_array (s)) {
/* Check for string literals */
e = init_array (s, cv2, e, 0, &serr);
p = TAIL_list (p);
} else if (et == exp_aggregate_tag && start) {
/* Check for sub-aggregates */
LIST (EXP) q;
q = DEREF_list (exp_aggregate_args (e));
e = init_aggr_aux (s, cv2, &q, 1, id, &serr);
p = TAIL_list (p);
} else {
/* Otherwise read constituents from p */
e = init_aggr_aux (s, cv2, &p, 0, id, &serr);
}
/* Check flexible array members */
if (sid == DEREF_id (ctype_flex_mem (ct))) {
ERROR ferr = ERR_dcl_init_flex_mem ();
serr = concat_error (ferr, serr);
}
} else {
/* Pad rest of structure */
e = init_empty (s, cv2, 1, &serr);
pads++;
}
/* Report any errors for this field */
if (!IS_NULL_err (serr)) {
ERROR ferr = ERR_dcl_init_decl (id, bf->start);
serr = concat_error (ferr, serr);
report (crt_loc, serr);
}
/* Check for dynamic initialisers */
e = dynamic_init (id, bf->start, e);
/* Restore field name */
bf->posn = bf->start + boff;
bf->posn [0] = 0;
/* Build up the result (in reverse order) */
CONS_exp (e, a, a);
CONS_off (off, b, b);
/* Examine next member */
if (ci & cinfo_union) break;
mem = DEREF_member (member_next (mem));
mem = next_data_member (mem, 0);
}
/* Report padded structures */
if (pads) {
NAT n = make_nat_value (pads);
add_error (err, ERR_dcl_init_aggr_pad (n, t));
}
/* Construct the result */
a = REVERSE_list (a);
b = REVERSE_list (b);
MAKE_exp_aggregate (t, a, b, e);
break;
}
case type_integer_tag :
case type_floating_tag :
case type_enumerate_tag :
case type_ptr_tag :
case type_ptr_mem_tag : {
/* Scalar types */
if (IS_NULL_list (p)) {
/* Can't have empty initialiser */
add_error (err, ERR_dcl_init_aggr_no_scalar ());
e = init_empty (t, cv, 1, err);
} else {
/* The first element must be a scalar */
unsigned et = null_tag;
e = get_aggr_elem (p, &et);
if (et == exp_aggregate_tag) {
LIST (EXP) q;
q = DEREF_list (exp_aggregate_args (e));
e = init_aggr_aux (t, cv, &q, 1, id, err);
} else {
ERROR ferr = NULL_err;
if (start == 1) {
/* Can only have aggregate at top level */
ferr = ERR_dcl_init_aggr_nest ();
}
e = convert_reference (e, REF_ASSIGN);
e = init_assign (t, cv, e, &ferr);
if (!IS_NULL_err (ferr)) {
ferr = init_error (ferr, 1);
add_error (err, ferr);
}
}
p = TAIL_list (p);
}
break;
}
case type_token_tag :
token_lab : {
/* Tokenised types */
TYPE s = expand_type (t, 0);
if (EQ_type (s, t)) goto non_aggregate_lab;
e = init_aggr_aux (s, cv, r, start, id, err);
return (e);
}
case type_top_tag :
case type_bottom_tag :
incomplete_lab : {
/* Incomplete types */
add_error (err, ERR_basic_types_incompl (t));
add_error (err, ERR_dcl_init_incompl ());
e = init_empty (t, cv, 1, err);
break;
}
default :
non_aggregate_lab : {
/* Other types */
if (start) {
/* Can't have aggregate initialisers */
ERROR ferr = ERR_dcl_init_decl (id, NULL_string);
ferr = concat_error (ferr, cerr);
ferr = concat_error (ferr, ERR_dcl_init_aggr_type (t));
report (crt_loc, ferr);
if (ci & cinfo_usr_constr) {
/* Map to constructor call */
LIST (EXP) q = p;
while (!IS_NULL_list (q)) {
unsigned et = null_tag;
IGNORE get_aggr_elem (q, &et);
q = TAIL_list (q);
}
e = init_constr (t, p, err);
return (e);
}
} else {
if (!IS_NULL_err (cerr)) destroy_error (cerr, 1);
}
if (IS_NULL_list (p)) {
/* Empty initialiser list */
e = init_empty (t, cv, 1, err);
} else {
/* Get next initialiser from list */
unsigned et = null_tag;
e = get_aggr_elem (p, &et);
if (et == exp_aggregate_tag) {
LIST (EXP) q;
q = DEREF_list (exp_aggregate_args (e));
e = init_aggr_aux (t, cv, &q, 1, id, err);
} else {
e = convert_reference (e, REF_ASSIGN);
if (tag == type_token_tag && is_zero_exp (e)) {
/* Special concession to tokenised types */
e = init_empty (t, cv, 1, err);
} else {
ERROR ferr = NULL_err;
e = init_assign (t, cv, e, &ferr);
if (!IS_NULL_err (ferr)) {
ferr = init_error (ferr, 1);
add_error (err, ferr);
}
}
}
p = TAIL_list (p);
}
break;
}
}
/* Check for end of initialiser list */
if (start && !IS_NULL_list (p)) {
ERROR ferr = ERR_dcl_init_decl (id, NULL_string);
ferr = concat_error (ferr, ERR_dcl_init_aggr_excess (t));
report (crt_loc, ferr);
p = NULL_list (EXP);
}
*r = p;
return (e);
}
/*
* CHECK AN AGGREGATE INITIALISER
*
* This is the top-level routine for analysing the aggregate initialiser
* e for the object id of type t. Any errors are added to err.
*/
EXP
init_aggregate(TYPE t, EXP e, IDENTIFIER id, ERROR *err)
{
LOCATION loc;
LIST (EXP) args = DEREF_list (exp_aggregate_args (e));
if (IS_NULL_list (args)) {
/* Report empty aggregate initialisers */
add_error (err, ERR_dcl_init_aggr_empty ());
}
bad_crt_loc++;
loc = crt_loc;
IGNORE clear_buffer (&field_buff, NULL);
e = init_aggr_aux (t, cv_none, &args, 2, id, err);
crt_loc = loc;
bad_crt_loc--;
return (e);
}
/*
* CHECK ANY INITIALISER
*
* This routine calls init_assign, init_constr or init_aggregate depending
* on the value of e. If tentative is true then an absent initialiser
* (i.e. when e is null) is treated as a tentative definition.
*/
EXP
init_general(TYPE t, EXP e, IDENTIFIER id, int tentative)
{
/* Check the initialiser */
ERROR err = NULL_err;
if (IS_NULL_exp (e)) {
/* Empty initialisers */
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_auto) {
e = init_empty (t, cv_none, 0, &err);
} else if (tentative) {
MAKE_exp_zero (t, e);
} else {
e = init_empty (t, cv_none, 1, &err);
}
} else {
switch (TAG_exp (e)) {
case exp_aggregate_tag : {
/* Aggregate initialisers */
if (is_templ_type (t)) {
e = cast_templ_type (t, e, CAST_IMPLICIT);
} else {
e = init_aggregate (t, e, id, &err);
}
break;
}
case exp_nof_tag : {
/* Padded aggregate initialisers */
e = DEREF_exp (exp_nof_start (e));
e = init_general (t, e, id, 0);
return (e);
}
case exp_initialiser_tag : {
/* Function style initialisers */
LIST (EXP) args;
args = DEREF_list (exp_initialiser_args (e));
args = convert_args (args);
e = init_constr (t, args, &err);
if (!IS_NULL_err (err)) err = init_error (err, 1);
break;
}
default : {
/* Simple initialisers */
unsigned etag = TAG_exp (e);
e = convert_reference (e, REF_ASSIGN);
if (etag == exp_paren_tag && IS_type_array (t)) {
e = make_paren_exp (e);
}
e = init_assign (t, cv_none, e, &err);
if (!IS_NULL_err (err)) err = init_error (err, 1);
break;
}
}
}
/* Report any errors */
if (!IS_NULL_err (err)) {
ERROR ferr = ERR_dcl_init_decl (id, NULL_string);
err = concat_error (ferr, err);
report (crt_loc, err);
}
/* Check for dynamic initialisers */
if (!IS_NULL_exp (e)) {
e = dynamic_init (id, NULL_string, e);
}
return (e);
}
/*
* DESTROY AN OBJECT
*
* This routine creates a destructor for the object id of type t,
* reporting any errors.
*/
EXP
destroy_general(TYPE t, IDENTIFIER id)
{
EXP d = NULL_exp;
int du = do_usage;
ERROR err = NULL_err;
do_usage = 0;
d = init_default (t, &d, DEFAULT_DESTR, EXTRA_DESTR, &err);
if (!IS_NULL_err (err)) {
/* Report any destructor errors */
ERROR ferr = ERR_dcl_init_decl (id, NULL_string);
err = concat_error (ferr, err);
report (decl_loc, err);
}
do_usage = du;
return (d);
}
/*
* INITIALISE AN OBJECT
*
* This routine initialises the object given by id to the value e. Note
* that e can be the null expression, indicating that no initialiser is
* given. The routine returns 1 if the object is defined, 2 if it is
* tentatively defined, and 0 if it is just declared (see dump_declare).
*/
int
init_object(IDENTIFIER id, EXP e)
{
EXP d;
TYPE t;
int def = 0;
unsigned tag;
DECL_SPEC ds;
unsigned itag;
int ignore = 0;
/* Check for non-object declarations */
if (IS_NULL_id (id)) return (0);
itag = TAG_id (id);
switch (itag) {
case id_variable_tag :
case id_stat_member_tag : {
/* Variables and static data members */
break;
}
case id_parameter_tag : {
/* Function parameters */
if (!in_default_arg) {
if (!IS_NULL_exp (e)) {
report (crt_loc, ERR_dcl_fct_default_weak (id));
}
return (0);
}
break;
}
case id_function_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Check for function declarations */
if (!IS_NULL_exp (e)) {
/* Can't initialise functions */
report (crt_loc, ERR_dcl_init_func (id));
}
/* Check previous definition */
d = DEREF_exp (id_function_etc_defn (id));
if (!IS_NULL_exp (d)) {
ds = DEREF_dspec (id_storage (id));
ds |= dspec_defn;
COPY_dspec (id_storage (id), ds);
}
return (0);
}
case id_member_tag : {
/* Check for non-static members (shouldn't be reached) */
report (crt_loc, ERR_class_mem_init_mem (id));
return (0);
}
default : {
/* The declaration could have been a typedef */
if (!IS_NULL_exp (e)) {
/* Can't initialise a typedef */
report (crt_loc, ERR_dcl_init_typedef (id));
}
return (1);
}
}
/* Get declaration data */
t = DEREF_type (id_variable_etc_type (id));
tag = TAG_type (t);
ds = DEREF_dspec (id_storage (id));
temp_storage = (ds & dspec_storage);
/* Remember modifiable objects with static storage duration */
if (!(ds & dspec_auto) && in_function_defn) {
CV_SPEC qual = find_cv_qual (t);
if (!(qual & cv_const)) {
IDENTIFIER fn = crt_func_id;
if (IS_NULL_id (DEREF_id (id_function_etc_static_def (fn)))) {
COPY_id (id_function_etc_static_def (fn), id);
}
}
}
/* Check array initialisers */
if (tag == type_array_tag) {
int chk = 0;
if (!IS_NULL_exp (e)) {
/* Initialisation of array types */
NAT n = DEREF_nat (type_array_size (t));
if ((ds & dspec_auto) && itag == id_variable_tag) {
/* Local aggregate initialiser */
report (crt_loc, ERR_dcl_init_aggr_auto (id));
}
last_array_size = NULL_nat;
e = init_general (t, e, id, 0);
if (IS_NULL_nat (n)) {
/* Complete unbounded arrays */
n = last_array_size;
if (!IS_NULL_nat (n)) {
CV_SPEC qual = DEREF_cv (type_qual (t));
TYPE s = DEREF_type (type_array_sub (t));
n = check_array_dim (n);
MAKE_type_array (qual, s, n, t);
COPY_type (id_variable_etc_type (id), t);
}
}
ds |= dspec_defn;
chk = 1;
} else {
/* Allow for tentative definitions */
if (ds & dspec_defn) chk = LANGUAGE_CPP;
/* They are only allowed at file scope */
if (ds & dspec_auto) chk = 1;
/* Arrays with internal linkage must have a complete type */
if (ds & dspec_static) chk = 1;
}
/* Can only spot incomplete arrays at this stage */
if (chk) {
ERROR err = check_complete (t);
if (!IS_NULL_err (err)) {
ERROR err2 = ERR_basic_types_def_incompl (id);
err = concat_error (err, err2);
report (decl_loc, err);
}
}
} else {
/* Other initialisers */
if (!IS_NULL_exp (e)) {
if (tag == type_compound_tag) {
if ((ds & dspec_auto) && itag == id_variable_tag) {
/* Local aggregate initialiser */
report (crt_loc, ERR_dcl_init_aggr_auto (id));
}
}
if (ds & dspec_defn) {
/* Type already check for completeness */
/* EMPTY */
} else if (tag == type_ref_tag) {
/* Reference types don't need checking */
/* EMPTY */
} else {
/* Type not yet checked for completeness */
ERROR err = check_complete (t);
if (!IS_NULL_err (err)) {
ERROR err2 = ERR_basic_types_def_incompl (id);
err = concat_error (err, err2);
report (decl_loc, err);
}
ds |= dspec_defn;
}
/* Examine initialiser */
e = init_general (t, e, id, 0);
}
}
/* Check on definition */
if (ds & dspec_stat_inline) {
/* Check for definitions of inline static members */
if (ds & dspec_defn) {
if (!IS_NULL_exp (e)) {
/* Can't give a value in the definition */
PTR (LOCATION) loc = id_loc (id);
report (decl_loc, ERR_class_static_data_def (id, loc));
} else {
/* Force definition to existing value */
e = DEREF_exp (id_variable_etc_init (id));
}
/* Mark as defined and no longer inline */
ds &= ~dspec_stat_inline;
}
} else {
/* Provide default definition if necessary */
if (ds & dspec_defn) {
def = 1;
if (IS_NULL_exp (e)) {
e = init_general (t, e, id, LANGUAGE_C);
if (!IS_NULL_exp (e) && IS_exp_zero (e)) def = 2;
}
}
/* Check previous definition */
d = DEREF_exp (id_variable_etc_init (id));
if (!IS_NULL_exp (d)) {
if (!IS_NULL_exp (e)) {
/* Defined twice */
ERROR err;
PTR (LOCATION) loc = id_loc (id);
if (def == 2) {
/* This definition is tentative */
err = ERR_basic_odr_tentative (id, loc);
ignore = 1;
} else if (IS_exp_zero (d)) {
/* Previous definition was tentative */
err = ERR_basic_odr_tentative (id, loc);
} else {
/* Neither definition is tentative */
err = ERR_basic_odr_def (id, loc);
}
report (decl_loc, err);
}
ds |= dspec_defn;
}
}
/* Update declaration data */
COPY_type (id_variable_etc_type (id), t);
COPY_dspec (id_storage (id), ds);
if (!IS_NULL_exp (e)) {
/* Define object */
if (!ignore) {
if ((ds & dspec_auto) && (ds & dspec_used)) {
/* Local variable initialised in terms of itself */
TYPE s = DEREF_type (exp_type (e));
MAKE_exp_dynamic (s, e, e);
}
if (itag != id_parameter_tag) e = check_init (e);
COPY_exp (id_variable_etc_init (id), e);
COPY_loc (id_loc (id), decl_loc);
define_id (id);
}
if (ds & dspec_linkage) {
/* Check enclosing namespace */
NAMESPACE ns = DEREF_nspace (id_parent (id));
check_decl_nspace (id, ns, 1, crt_namespace);
}
if (def == 0) def = 1;
}
if (def) {
/* Create destructor */
EXP d1 = DEREF_exp (id_variable_etc_term (id));
d = destroy_general (t, id);
if (!IS_NULL_exp (d1) && IS_exp_paren (d1)) {
/* Preserve parenthesised type information */
TYPE t1 = DEREF_type (exp_type (d1));
MAKE_exp_paren (t1, d, d);
}
COPY_exp (id_variable_etc_term (id), d);
}
if (!IS_NULL_id (unify_id_pending)) {
/* Deal with any pending identifiers */
IGNORE unify_id (unify_id_pending, id, 1);
}
temp_storage = dspec_auto;
if (def && !(ds & dspec_auto)) {
if (!really_in_function_defn && !in_template_decl) {
/* Compile variable definition */
compile_variable (id, 0);
}
}
return (def);
}
/*
* INITIALISE A FUNCTION PARAMETER
*
* This routine initialises the function parameter id with the expression
* e (i.e. sets up a default argument value). Note that the class
* definition case, where e is initially just skipped over, is dealt
* with here.
*/
void
init_param(IDENTIFIER id, EXP e)
{
if (!IS_NULL_exp (e)) {
if (!IS_NULL_id (id)) {
if (IS_id_token (id)) {
/* Template parameter */
init_exp_param (id, e);
} else {
/* Function parameter */
if (IS_exp_uncompiled (e)) {
if (in_class_defn) {
LIST (IDENTIFIER) ft;
CLASS_TYPE ct = crt_class;
ft = DEREF_list (ctype_nest (ct));
CONS_id (id, ft, ft);
COPY_list (ctype_nest (ct), ft);
}
COPY_exp (id_parameter_init (id), e);
} else {
in_default_arg++;
IGNORE init_object (id, e);
in_default_arg--;
}
}
}
}
return;
}
/*
* INITIALISE A CLASS MEMBER
*
* This routine initialises the class member id with the expression e
* (which since it is a constant-expression has already undergone the
* appropriate operand conversions). Note that the pure specifier is
* indistinguishable syntactically from a member initialiser, and so is
* only spotted at this stage. See above for inline definitions of static
* data members. The routine returns 1 for a definition and 0 for a
* declaration (see dump_declare).
*/
int
init_member(IDENTIFIER id, EXP e)
{
int def;
unsigned tag;
/* Check for definitions */
if (IS_NULL_id (id)) return (0);
tag = TAG_id (id);
if (have_access_decl) {
def = 2;
have_access_decl = 0;
} else {
switch (tag) {
case id_stat_member_tag :
case id_mem_func_tag :
case id_stat_mem_func_tag : {
def = 0;
break;
}
default : {
def = 1;
break;
}
}
}
/* Check for function declarations */
if (IS_NULL_exp (e)) {
if (tag == id_mem_func_tag || tag == id_stat_mem_func_tag) {
if (really_in_function_defn) {
NAMESPACE ns = DEREF_nspace (id_parent (id));
if (EQ_nspace (ns, crt_namespace)) {
DECL_SPEC ds, mds;
ds = DEREF_dspec (id_storage (id));
mds = (dspec_inherit | dspec_implicit | dspec_defn);
if (!(ds & mds)) {
/* Local functions should be defined */
report (decl_loc, ERR_class_local_func (id));
}
}
}
}
return (def);
}
/* Check the various types of member */
switch (tag) {
case id_stat_member_tag : {
/* Static data members may be initialised */
DECL_SPEC ds;
TYPE t = DEREF_type (id_stat_member_type (id));
CV_SPEC cv = find_cv_qual (t);
switch (TAG_type (t)) {
case type_integer_tag :
case type_enumerate_tag : {
ERROR err = NULL_err;
IGNORE make_nat_exp (e, &err);
if (!IS_NULL_err (err)) {
/* Initialiser should be a constant expression */
ERROR err2 = ERR_class_mem_init_const ();
err = concat_error (err, err2);
report (crt_loc, err);
}
break;
}
case type_token_tag : {
if (!is_templ_type (t)) goto default_lab;
break;
}
default :
default_lab : {
/* Only integral types allowed */
report (crt_loc, ERR_class_static_data_init (id, t));
break;
}
}
if (cv != (cv_const | cv_lvalue)) {
/* Only const types allowed */
report (crt_loc, ERR_class_static_data_const (id, t));
}
e = init_general (t, e, id, 0);
e = dynamic_init (id, NULL_string, e);
e = check_init (e);
COPY_exp (id_stat_member_init (id), e);
/* Mark inline member definition */
ds = DEREF_dspec (id_storage (id));
ds |= dspec_stat_inline;
COPY_dspec (id_storage (id), ds);
break;
}
case id_mem_func_tag :
case id_stat_mem_func_tag : {
/* Check for pure specifiers */
if (is_zero_exp (e)) {
DECL_SPEC ds = DEREF_dspec (id_storage (id));
if (ds & dspec_virtual) {
CLASS_TYPE ct = crt_class;
CLASS_INFO ci = DEREF_cinfo (ctype_info (ct));
/* Pure specifier should be precisely '0' */
int tok = last_lex_token;
if (tok != lex_integer_Hexp || is_literal (e) != 2) {
report (crt_loc, ERR_class_abstract_zero ());
}
/* Mark class as abstract */
ci |= cinfo_abstract;
COPY_cinfo (ctype_info (ct), ci);
/* Mark function as pure */
ds |= dspec_pure;
COPY_dspec (id_storage (id), ds);
} else {
/* Only virtual functions can be pure */
report (crt_loc, ERR_class_abstract_virt ());
}
} else {
/* Can't initialise functions otherwise */
report (crt_loc, ERR_dcl_init_func (id));
}
break;
}
case id_member_tag : {
/* Can't initialise non-static members */
report (crt_loc, ERR_class_mem_init_mem (id));
break;
}
default : {
/* Can't initialise a typedef */
report (crt_loc, ERR_dcl_init_typedef (id));
break;
}
}
return (def);
}
/*
* ALLOW A TOKEN AS AN INITIALISER
*
* This routine enables the token id as an initialiser.
*/
void
allow_initialiser(IDENTIFIER id)
{
id = find_token (id);
if (IS_id_token (id)) {
/* NOT YET IMPLEMENTED */
/* EMPTY */
} else {
report (crt_loc, ERR_token_undecl (id));
}
return;
}
/*
* INITIALISE THE FIELD BUFFER
*
* This routine initialises the field buffer.
*/
void
init_initialise(void)
{
field_buff.posn = extend_buffer (&field_buff, field_buff.posn);
return;
}
syntax highlighted by Code2HTML, v. 0.9.1