Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
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.
*/
#include "config.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "exp_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "access.h"
#include "allocate.h"
#include "assign.h"
#include "basetype.h"
#include "capsule.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 "copy.h"
#include "chktype.h"
#include "declare.h"
#include "derive.h"
#include "destroy.h"
#include "dump.h"
#include "exception.h"
#include "expression.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "label.h"
#include "namespace.h"
#include "operator.h"
#include "overload.h"
#include "predict.h"
#include "statement.h"
#include "syntax.h"
#include "template.h"
#include "token.h"
/*
SET AN INFERRED FUNCTION RETURN TYPE
If t is a function type with an inferred return type then the actual
return type is set according to id. The routine returns the original
inferred return type.
*/
TYPE inferred_return
PROTO_N ( ( t, id ) )
PROTO_T ( TYPE t X IDENTIFIER id )
{
if ( IS_type_func ( t ) ) {
TYPE r = DEREF_type ( type_func_ret ( t ) ) ;
if ( is_type_inferred ( r ) == INFERRED_EMPTY ) {
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
switch ( TAG_hashid ( nm ) ) {
case hashid_constr_tag :
case hashid_destr_tag : {
/* Constructors and destructors */
COPY_type ( type_func_ret ( t ), type_void ) ;
break ;
}
case hashid_conv_tag : {
/* Conversion functions */
TYPE s = DEREF_type ( hashid_conv_type ( nm ) ) ;
COPY_type ( type_func_ret ( t ), s ) ;
break ;
}
}
return ( r ) ;
}
}
return ( NULL_type ) ;
}
/*
CHECK A COPY CONSTRUCTOR OR ASSIGNMENT OPERATOR TYPE
This routine checks whether the function type fn has first parameter
of type '[volatile] ct &', 'const [volatile] ct &' or '[const]
[volatile] ct' with any subsequent parameters being optional. It
returns 1, 2 and 3 respectively in these cases, or 0 if fn does not
match this description.
*/
static int check_copy_constr
PROTO_N ( ( fn, ct ) )
PROTO_T ( TYPE fn X CLASS_TYPE ct )
{
int c = 0 ;
if ( IS_type_templ ( fn ) ) {
/* Allow for template constructors */
in_template_decl++ ;
fn = DEREF_type ( type_templ_defn ( fn ) ) ;
c = check_copy_constr ( fn, ct ) ;
in_template_decl-- ;
} else {
LIST ( IDENTIFIER ) pids = DEREF_list ( type_func_pids ( fn ) ) ;
if ( !IS_NULL_list ( pids ) ) {
/* Have at least one parameter */
IDENTIFIER pid = DEREF_id ( HEAD_list ( pids ) ) ;
TYPE t = DEREF_type ( id_parameter_type ( pid ) ) ;
/* Check for second parameter */
pids = TAIL_list ( pids ) ;
if ( !IS_NULL_list ( pids ) ) {
/* Second parameter must have a default argument */
EXP e ;
pid = DEREF_id ( HEAD_list ( pids ) ) ;
e = DEREF_exp ( id_parameter_init ( pid ) ) ;
if ( IS_NULL_exp ( e ) ) return ( 0 ) ;
}
/* Check parameter type */
if ( IS_type_ref ( t ) ) {
TYPE s = DEREF_type ( type_ref_sub ( t ) ) ;
if ( IS_type_compound ( s ) ) {
CLASS_TYPE cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
if ( eq_ctype ( cs, ct ) ) {
/* Reference to same class */
CV_SPEC qual = DEREF_cv ( type_qual ( s ) ) ;
c = ( ( qual & cv_const ) ? 2 : 1 ) ;
}
} else if ( is_templ_type ( s ) ) {
/* Reference to template parameter */
CV_SPEC qual = DEREF_cv ( type_qual ( s ) ) ;
c = ( ( qual & cv_const ) ? 2 : 1 ) ;
}
} else if ( IS_type_compound ( t ) ) {
CLASS_TYPE cs = DEREF_ctype ( type_compound_defn ( t ) ) ;
if ( eq_ctype ( cs, ct ) ) c = 3 ;
} else if ( is_templ_type ( t ) ) {
/* Template parameter */
c = 1 ;
}
}
}
return ( c ) ;
}
/*
CHECK A CONSTRUCTOR FUNCTION TYPE
This routine checks the function type t for the constructor id declared
in namespace ns. The check that the constructor is a non-static member
function is carried out elsewhere. Note that no return type can be given
for id - it is implicitly void. Remedial action is taken to transform
illegal copy constructors such as 'X::X ( X )' into valid constructors
such as 'X::X ( X & )'. This is to avoid having to check for infinite
loops later.
*/
TYPE check_constr
PROTO_N ( ( t, id, ns ) )
PROTO_T ( TYPE t X IDENTIFIER id X NAMESPACE ns )
{
if ( IS_type_templ ( t ) ) {
/* Allow for template types */
TYPE s = DEREF_type ( type_templ_defn ( t ) ) ;
s = check_constr ( s, id, ns ) ;
COPY_type ( type_templ_defn ( t ), s ) ;
} else {
/* Decompose function type */
TYPE r = DEREF_type ( type_func_ret ( t ) ) ;
CV_SPEC cv = DEREF_cv ( type_func_mqual ( t ) ) ;
/* Find underlying class */
CLASS_TYPE ct = namespace_class ( ns ) ;
/* No return type can be given for a constructor */
if ( is_type_inferred ( r ) != INFERRED_EMPTY ) {
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
report ( crt_loc, ERR_class_ctor_ret ( nm ) ) ;
}
COPY_type ( type_func_ret ( t ), type_void ) ;
/* Check for invalid copy constructors */
if ( check_copy_constr ( t, ct ) == 3 ) {
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
report ( crt_loc, ERR_class_copy_bad ( nm ) ) ;
}
/* A constructor cannot be cv-qualified */
if ( cv & cv_qual ) {
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
report ( crt_loc, ERR_class_ctor_qual ( nm, cv ) ) ;
}
}
return ( t ) ;
}
/*
CHECK A DESTRUCTOR FUNCTION TYPE
This routine checks the function type t for the destructor id. The
check that the destructor is a non-static member function is carried
out elsewhere. Note that no return type can be given for id - it is
implicitly void.
*/
TYPE check_destr
PROTO_N ( ( t, id, ns ) )
PROTO_T ( TYPE t X IDENTIFIER id X NAMESPACE ns )
{
if ( IS_type_templ ( t ) ) {
/* Allow for template types */
TYPE s = DEREF_type ( type_templ_defn ( t ) ) ;
s = check_destr ( s, id, ns ) ;
COPY_type ( type_templ_defn ( t ), s ) ;
} else {
/* Decompose function type */
TYPE r = DEREF_type ( type_func_ret ( t ) ) ;
CV_SPEC cv = DEREF_cv ( type_func_mqual ( t ) ) ;
int ell = DEREF_int ( type_func_ellipsis ( t ) ) ;
LIST ( TYPE ) p = DEREF_list ( type_func_ptypes ( t ) ) ;
/* Check namespace */
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
TYPE dt = DEREF_type ( hashid_destr_type ( nm ) ) ;
if ( IS_type_compound ( dt ) ) {
/* Check inheritance */
CLASS_TYPE dct = DEREF_ctype ( type_compound_defn ( dt ) ) ;
NAMESPACE dns = DEREF_nspace ( ctype_member ( dct ) ) ;
if ( !EQ_nspace ( dns, ns ) ) {
report ( crt_loc, ERR_class_dtor_inherit ( nm, ns ) ) ;
}
}
/* No return type can be given for a destructor */
if ( is_type_inferred ( r ) != INFERRED_EMPTY ) {
report ( crt_loc, ERR_class_dtor_ret ( nm ) ) ;
}
COPY_type ( type_func_ret ( t ), type_void ) ;
/* No parameter types can be given for a destructor */
if ( !IS_NULL_list ( p ) || ell ) {
report ( crt_loc, ERR_class_dtor_pars ( nm ) ) ;
}
/* A destructor cannot be cv-qualified */
if ( cv & cv_qual ) {
report ( crt_loc, ERR_class_dtor_qual ( nm, cv ) ) ;
}
}
return ( t ) ;
}
/*
CHECK A CONVERSION FUNCTION TYPE
This routine checks the function type t for the conversion function id.
The check that the converter is a non-static member function is carried
out elsewhere. Note that no return type can be given for id - it is
inferred from the type used in id. This may not be a legal return type,
so inject_pre_type is used to check it.
*/
TYPE check_conv
PROTO_N ( ( t, id ) )
PROTO_T ( TYPE t X IDENTIFIER id )
{
if ( IS_type_templ ( t ) ) {
/* Allow for template types */
TYPE s = DEREF_type ( type_templ_defn ( t ) ) ;
s = check_conv ( s, id ) ;
COPY_type ( type_templ_defn ( t ), s ) ;
} else {
/* Find the conversion type */
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
TYPE s = DEREF_type ( hashid_conv_type ( nm ) ) ;
/* Decompose function type */
TYPE r = DEREF_type ( type_func_ret ( t ) ) ;
int ell = DEREF_int ( type_func_ellipsis ( t ) ) ;
LIST ( TYPE ) p = DEREF_list ( type_func_ptypes ( t ) ) ;
/* No return type can be given for a conversion function */
if ( is_type_inferred ( r ) != INFERRED_EMPTY ) {
/* If a return type is given it might as well be right */
if ( eq_type ( r, s ) ) {
report ( crt_loc, ERR_class_conv_fct_ret ( nm ) ) ;
} else {
report ( crt_loc, ERR_class_conv_fct_ret_bad ( nm, r ) ) ;
}
}
COPY_type ( type_func_ret ( t ), NULL_type ) ;
t = inject_pre_type ( t, s, 0 ) ;
/* No parameter types can be given for a conversion function */
if ( !IS_NULL_list ( p ) || ell ) {
report ( crt_loc, ERR_class_conv_fct_pars ( nm ) ) ;
}
/* Can't have conversion to cv-qualified void */
if ( IS_type_top_etc ( s ) ) {
report ( crt_loc, ERR_class_conv_fct_void ( nm ) ) ;
}
}
return ( t ) ;
}
/*
LOOK UP AN OVERLOADED OPERATOR FUNCTION
This routine looks up the overloaded operator function 'operator op'
in the class ct.
*/
IDENTIFIER find_operator
PROTO_N ( ( ct, op ) )
PROTO_T ( CLASS_TYPE ct X int op )
{
HASHID nm = lookup_op ( op ) ;
NAMESPACE cns = DEREF_nspace ( ctype_member ( ct ) ) ;
IDENTIFIER id = search_field ( cns, nm, 0, 0 ) ;
return ( id ) ;
}
/*
FIND A DEFAULT CONSTRUCTOR OR DESTRUCTOR
This routine finds either a default constructor, a copy constructor,
a default destructor or a copy assignment operator of the class ct,
depending on the value of n. The null identifier is returned and
an error is added to err if such a function does not exist (including
for incomplete types).
*/
static IDENTIFIER find_constr
PROTO_N ( ( ct, n, err ) )
PROTO_T ( CLASS_TYPE ct X int n X ERROR *err )
{
CLASS_INFO ci ;
int match = 0 ;
int nargs = 0 ;
int kind = KIND_FUNC ;
IDENTIFIER id = NULL_id ;
IDENTIFIER qid = NULL_id ;
/* Check for complete types */
complete_class ( ct, 1 ) ;
ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
if ( !( ci & cinfo_complete ) ) return ( NULL_id ) ;
/* Find the basic identifier */
switch ( n ) {
case DEFAULT_CONSTR :
case DEFAULT_COPY :
case DEFAULT_USR : {
/* Find constructors */
id = DEREF_id ( ctype_constr ( ct ) ) ;
if ( n == DEFAULT_COPY ) nargs = 1 ;
kind = KIND_CONSTR ;
break ;
}
case DEFAULT_DESTR :
case DEFAULT_DELETE : {
/* Find destructors */
id = DEREF_id ( ctype_destr ( ct ) ) ;
nargs = 1 ;
break ;
}
case DEFAULT_ASSIGN : {
/* Find 'operator =' */
id = find_operator ( ct, lex_assign ) ;
nargs = 2 ;
break ;
}
}
/* Find appropriate function */
if ( !IS_NULL_id ( id ) && IS_id_mem_func ( id ) ) {
DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( ds & dspec_template ) {
/* Allow for template constructors */
match = 2 ;
} else {
switch ( n ) {
case DEFAULT_CONSTR :
case DEFAULT_DESTR :
case DEFAULT_DELETE :
case DEFAULT_USR : {
/* Should take no arguments */
IDENTIFIER pid = id ;
while ( !IS_NULL_id ( pid ) ) {
TYPE t = DEREF_type ( id_mem_func_type ( pid ) ) ;
if ( min_no_args ( t ) == 1 ) {
qid = pid ;
match++ ;
}
pid = DEREF_id ( id_mem_func_over ( pid ) ) ;
}
break ;
}
case DEFAULT_COPY :
case DEFAULT_ASSIGN : {
/* Should be a copy constructor */
IDENTIFIER pid = id ;
while ( !IS_NULL_id ( pid ) ) {
TYPE t = DEREF_type ( id_mem_func_type ( pid ) ) ;
if ( check_copy_constr ( t, ct ) ) {
qid = pid ;
match++ ;
}
pid = DEREF_id ( id_mem_func_over ( pid ) ) ;
}
break ;
}
}
}
} else {
/* This can only happen for dummy classes */
return ( NULL_id ) ;
}
/* Deal with ambiguous cases */
if ( match > 1 ) {
CANDIDATE_LIST *p = &candidates ;
p->size = 0 ;
add_candidates ( p, id, 1, KIND_CONSTR ) ;
if ( p->size ) {
CANDIDATE *q ;
unsigned rank ;
TYPE t = make_class_type ( ct ) ;
LIST ( EXP ) args = NULL_list ( EXP ) ;
while ( nargs ) {
/* Create dummy arguments */
EXP a ;
MAKE_exp_value ( t, a ) ;
CONS_exp ( a, args, args ) ;
nargs-- ;
}
if ( kind == KIND_CONSTR ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
q = resolve_overload ( p, args, t, 0 ) ;
if ( kind == KIND_CONSTR ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
rank = q->rank ;
if ( rank >= RANK_VIABLE ) {
qid = q->func ;
if ( rank == RANK_BEST ) {
/* Best match */
if ( match_no_viable > 1 ) {
ERROR err2 ;
if ( kind == KIND_CONSTR ) {
err2 = ERR_over_match_ctor_ok ( qid ) ;
} else {
err2 = ERR_over_match_call_ok ( qid ) ;
}
add_error ( err, err2 ) ;
}
match = 1 ;
} else {
/* Ambiguous call */
ERROR err2 ;
if ( kind == KIND_CONSTR ) {
err2 = ERR_over_match_ctor_ambig ( qid ) ;
} else {
err2 = ERR_over_match_call_ambig ( qid ) ;
}
err2 = list_candidates ( err2, p, RANK_VIABLE ) ;
add_error ( err, err2 ) ;
}
} else {
/* No viable call */
qid = NULL_id ;
match = 0 ;
}
if ( !IS_NULL_list ( args ) ) free_exp_list ( args, 1 ) ;
} else {
/* No viable call */
qid = NULL_id ;
match = 0 ;
}
}
/* Report error */
if ( match == 0 ) {
switch ( n ) {
case DEFAULT_CONSTR :
case DEFAULT_USR : {
add_error ( err, ERR_class_ctor_default ( ct ) ) ;
break ;
}
case DEFAULT_COPY : {
add_error ( err, ERR_class_copy_constr ( ct ) ) ;
break ;
}
case DEFAULT_DESTR :
case DEFAULT_DELETE : {
add_error ( err, ERR_class_dtor_default ( ct ) ) ;
break ;
}
case DEFAULT_ASSIGN : {
add_error ( err, ERR_class_copy_assign ( ct ) ) ;
break ;
}
}
}
return ( qid ) ;
}
/*
FIND NUMBER OF EXTRA CONSTRUCTOR ARGUMENTS
This routine finds the number of extra constructor arguments required
by the member function id of the class ct. For constructors and
assignment operators for classes with a virtual base the extra
argument is true to indicate that the virtual components should be
initialised. For non-trivial destructors bit 1 of the extra argument
is used to indicate that any virtual components should be destroyed,
while bit 0 is used to indicate that 'operator delete' should be
called on the argument. The macros EXTRA_* are used to represent
these values.
*/
unsigned extra_constr_args
PROTO_N ( ( id, ct ) )
PROTO_T ( IDENTIFIER id X CLASS_TYPE ct )
{
DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( !( ds & dspec_trivial ) ) {
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
unsigned tag = TAG_hashid ( nm ) ;
if ( tag == hashid_constr_tag ) {
/* Constructors */
CLASS_INFO ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
if ( ci & cinfo_virtual_base ) return ( 1 ) ;
} else if ( tag == hashid_destr_tag ) {
/* Destructors */
return ( 1 ) ;
}
}
return ( 0 ) ;
}
/*
CONSTRUCTOR BASE CLASS INITIALISER FLAG
This flag is set to true in a constructor base class initialiser.
This overrides the value of any extra arguments passed to the
constructor.
*/
static int in_ctor_base_init = 0 ;
/*
ADD EXTRA CONSTRUCTOR ARGUMENTS
This routine adds any necessary extra constructor arguments to the
member function call expression e. ct gives the associate class
type and v is the value to be passed to such arguments.
*/
EXP add_constr_args
PROTO_N ( ( e, ct, v ) )
PROTO_T ( EXP e X CLASS_TYPE ct X int v )
{
if ( IS_exp_func_id ( e ) ) {
IDENTIFIER id = DEREF_id ( exp_func_id_id ( e ) ) ;
unsigned n = extra_constr_args ( id, ct ) ;
if ( n ) {
LIST ( EXP ) args = DEREF_list ( exp_func_id_args ( e ) ) ;
LIST ( EXP ) extra = NULL_list ( EXP ) ;
if ( in_ctor_base_init ) v = 0 ;
while ( n ) {
EXP a ;
NAT c = small_nat [v] ;
MAKE_exp_int_lit ( type_sint, c, exp_int_lit_tag, a ) ;
CONS_exp ( a, extra, extra ) ;
n-- ;
}
args = APPEND_list ( args, extra ) ;
COPY_list ( exp_func_id_args ( e ), args ) ;
}
}
return ( e ) ;
}
/*
CALL A CONSTRUCTOR OR DESTRUCTOR
This routine constructs a default call of the constructor or destructor
id of type n. ct gives the corresponding class type and pb gives the
second argument for copy constructors and assignment operators.
*/
static EXP call_constr
PROTO_N ( ( id, pb, n, v, ct ) )
PROTO_T ( IDENTIFIER id X EXP *pb X int n X int v X CLASS_TYPE ct )
{
/* Create argument list */
TYPE t ;
EXP e, a, b ;
LIST ( EXP ) args = NULL_list ( EXP ) ;
IDENTIFIER cid = DEREF_id ( ctype_name ( ct ) ) ;
MAKE_type_compound ( cv_lvalue, ct, t ) ;
COPY_id ( type_name ( t ), cid ) ;
MAKE_exp_dummy ( t, NULL_exp, LINK_NONE, NULL_off, 0, a ) ;
if ( n == DEFAULT_ASSIGN || n == DEFAULT_DELETE ) {
/* Don't know object type */
COPY_int ( exp_dummy_virt ( a ), 1 ) ;
}
if ( n == DEFAULT_COPY || n == DEFAULT_ASSIGN ) {
/* These have two arguments */
TYPE s = t ;
int virt = 1 ;
b = *pb ;
if ( !IS_NULL_exp ( b ) ) {
s = DEREF_type ( exp_type ( b ) ) ;
while ( IS_type_array ( s ) ) {
s = DEREF_type ( type_array_sub ( s ) ) ;
}
s = lvalue_type ( s ) ;
if ( know_type ( b ) ) virt = 0 ;
}
MAKE_exp_dummy ( s, b, LINK_NONE, NULL_off, DUMMY_copy, b ) ;
COPY_int ( exp_dummy_virt ( b ), virt ) ;
CONS_exp ( b, args, args ) ;
*pb = b ;
} else {
/* These have one argument */
b = a ;
}
CONS_exp ( a, args, args ) ;
/* Call the function */
use_func_id ( id, 0, suppress_usage ) ;
e = apply_func_id ( id, qual_none, NULL_graph, args ) ;
e = add_constr_args ( e, ct, v ) ;
if ( n == DEFAULT_DESTR || n == DEFAULT_DELETE ) {
MAKE_exp_destr ( type_void, e, a, e ) ;
} else {
t = make_class_type ( ct ) ;
MAKE_exp_constr ( t, e, a, b, n, e ) ;
}
return ( e ) ;
}
/*
CREATE A DEFAULT INITIALISER
This routine creates a default initialiser for an object of type t
in a constructor, destructor or assignment operator (as indicated
by n). Any resultant errors are added to err.
*/
EXP init_default
PROTO_N ( ( t, pa, n, v, err ) )
PROTO_T ( TYPE t X EXP *pa X int n X int v X ERROR *err )
{
CV_SPEC cv ;
EXP e = NULL_exp ;
unsigned tag = TAG_type ( t ) ;
/* Deal with classes */
if ( tag == type_compound_tag ) {
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
IDENTIFIER id = find_constr ( ct, n, err ) ;
if ( !IS_NULL_id ( id ) ) {
DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( !( ds & dspec_trivial ) ) {
/* Non-trivial constructor */
e = call_constr ( id, pa, n, v, ct ) ;
} else if ( ds & dspec_implicit ) {
if ( !( ds & dspec_defn ) ) {
/* Trivial constructor */
implicit_defn ( id, n ) ;
}
}
}
return ( e ) ;
}
/* Deal with arrays */
if ( tag == type_array_tag ) {
NAT m = DEREF_nat ( type_array_size ( t ) ) ;
TYPE s = DEREF_type ( type_array_sub ( t ) ) ;
e = init_default ( s, pa, n, v, err ) ;
if ( !IS_NULL_exp ( e ) ) {
/* Apply to each array element */
MAKE_exp_nof ( t, NULL_exp, m, e, NULL_exp, e ) ;
}
return ( e ) ;
}
/* Everything else is alright in these cases */
if ( n == DEFAULT_COPY || n == DEFAULT_DESTR || n == DEFAULT_DELETE ) {
return ( NULL_exp ) ;
}
/* Deal with references */
if ( tag == type_ref_tag ) {
add_error ( err, ERR_dcl_init_ref_none () ) ;
if ( n == DEFAULT_ASSIGN ) return ( NULL_exp ) ;
MAKE_exp_null ( t, e ) ;
return ( e ) ;
}
/* Deal with const objects */
cv = DEREF_cv ( type_qual ( t ) ) ;
if ( cv & cv_const ) add_error ( err, ERR_dcl_init_const () ) ;
return ( NULL_exp ) ;
}
/*
REPORT AN INITIALISATION ERROR
This routine reports the error err which occurred in the initialisation
of id in constructor or destructor fn of type n.
*/
static void constr_error
PROTO_N ( ( err, id, fn, n ) )
PROTO_T ( ERROR err X IDENTIFIER id X IDENTIFIER fn X int n )
{
ERROR err1 ;
ERROR err2 = ERR_dcl_init_decl ( id, NULL_string ) ;
if ( n == DEFAULT_USR ) {
err1 = ERR_class_base_init_err ( fn ) ;
} else {
err1 = ERR_class_base_init_impl ( fn ) ;
}
err = concat_error ( err2, err ) ;
err = concat_error ( err1, err ) ;
report ( crt_loc, err ) ;
return ;
}
/*
CREATE A BASE CLASS INITIALISER
This routine creates a default initialiser for the base class gr
for the constructor or destructor fn of type n. For copy constructors
and assignment operators a dummy expression is used to indicate that
the base should be initialised from the corresponding base of the
second argument (see enc_ctor_exp).
*/
static EXP init_empty_base
PROTO_N ( ( gr, fn, n, m ) )
PROTO_T ( GRAPH gr X IDENTIFIER fn X int n X int m )
{
EXP e = NULL_exp ;
ERROR err = NULL_err ;
CLASS_TYPE ct = DEREF_ctype ( graph_head ( gr ) ) ;
TYPE t = make_class_type ( ct ) ;
e = init_default ( t, &e, m, EXTRA_NONE, &err ) ;
if ( IS_NULL_exp ( e ) ) {
if ( n == DEFAULT_USR && m != DEFAULT_DESTR ) {
/* Warn about uninitialised bases */
if ( !is_empty_class ( t ) ) {
IDENTIFIER id = DEREF_id ( ctype_name ( ct ) ) ;
err = concat_error ( err, ERR_class_base_init_none ( id ) ) ;
}
}
if ( m == DEFAULT_COPY || m == DEFAULT_ASSIGN ) {
/* Dummy value for copy constructor */
MAKE_exp_value ( t, e ) ;
}
}
if ( !IS_NULL_err ( err ) ) {
IDENTIFIER id = DEREF_id ( ctype_name ( ct ) ) ;
constr_error ( err, id, fn, n ) ;
}
return ( e ) ;
}
/*
CREATE A CLASS MEMBER INITIALISER
This routine creates a default initialiser for a class member id
for the constructor or destructor fn of type n. Again a dummy value
is used in copy constructors and assignment operators.
*/
static EXP init_empty_mem
PROTO_N ( ( id, fn, n, m ) )
PROTO_T ( IDENTIFIER id X IDENTIFIER fn X int n X int m )
{
EXP e = NULL_exp ;
ERROR err = NULL_err ;
TYPE t = DEREF_type ( id_member_type ( id ) ) ;
int v = ( m == DEFAULT_DESTR ? EXTRA_DESTR : EXTRA_CONSTR ) ;
e = init_default ( t, &e, m, v, &err ) ;
if ( IS_NULL_exp ( e ) ) {
if ( m == DEFAULT_COPY || m == DEFAULT_ASSIGN ) {
/* Dummy value for copy constructor */
if ( IS_type_ptr ( t ) ) {
DECL_SPEC ds = DEREF_dspec ( id_storage ( fn ) ) ;
if ( !( ds & dspec_trivial ) ) {
/* Warn about shallow copies */
err = concat_error ( err, ERR_class_copy_ptr () ) ;
}
}
MAKE_exp_value ( t, e ) ;
}
}
if ( !IS_NULL_err ( err ) ) {
constr_error ( err, id, fn, n ) ;
}
return ( e ) ;
}
/*
LISTS OF CONSTRUCTOR INITIALISERS
These lists are built up by the constructor initialisers.
*/
static LIST ( GRAPH ) init_bases = NULL_list ( GRAPH ) ;
static LIST ( EXP ) val_bases = NULL_list ( EXP ) ;
static LIST ( IDENTIFIER ) init_mems = NULL_list ( IDENTIFIER ) ;
static LIST ( EXP ) val_mems = NULL_list ( EXP ) ;
static unsigned long no_ctor_init = 0 ;
static int init_base_last = 1 ;
/*
CLEAR THE LISTS OF CONSTRUCTOR INITIALISERS
This routine clears the lists of constructor initialisers above.
*/
static void destroy_ctor_lists
PROTO_Z ()
{
IGNORE check_value ( OPT_VAL_ctor_initializers, no_ctor_init ) ;
DESTROY_list ( init_bases, SIZE_graph ) ;
DESTROY_list ( val_bases, SIZE_exp ) ;
DESTROY_list ( init_mems, SIZE_id ) ;
DESTROY_list ( val_mems, SIZE_exp ) ;
init_bases = NULL_list ( GRAPH ) ;
val_bases = NULL_list ( EXP ) ;
init_mems = NULL_list ( IDENTIFIER ) ;
val_mems = NULL_list ( EXP ) ;
init_base_last = 1 ;
no_ctor_init = 0 ;
return ;
}
/*
FIND A BASE CLASS INITIALISER
This routine finds the base class initialiser for gr in the lists
given above. If gr is a member of init_bases then the corresponding
element of val_bases is returned, otherwise the null expression is
returned.
*/
static EXP find_base_init
PROTO_N ( ( gr ) )
PROTO_T ( GRAPH gr )
{
LIST ( GRAPH ) p = init_bases ;
LIST ( EXP ) q = val_bases ;
while ( !IS_NULL_list ( p ) ) {
GRAPH gs = DEREF_graph ( HEAD_list ( p ) ) ;
if ( eq_graph ( gs, gr ) ) {
/* Found graph - return corresponding expression */
EXP e = DEREF_exp ( HEAD_list ( q ) ) ;
return ( e ) ;
}
q = TAIL_list ( q ) ;
p = TAIL_list ( p ) ;
}
return ( NULL_exp ) ;
}
/*
FIND A CLASS MEMBER INITIALISER
This routine finds the class member initialiser for id in the lists
given above. If id is a member of init_mems then the corresponding
element of val_mems is returned, otherwise the null expression is
returned.
*/
static EXP find_mem_init
PROTO_N ( ( id ) )
PROTO_T ( IDENTIFIER id )
{
LIST ( IDENTIFIER ) p = init_mems ;
LIST ( EXP ) q = val_mems ;
while ( !IS_NULL_list ( p ) ) {
IDENTIFIER mid = DEREF_id ( HEAD_list ( p ) ) ;
if ( EQ_id ( mid, id ) ) {
/* Found identifier - return corresponding expression */
EXP e = DEREF_exp ( HEAD_list ( q ) ) ;
return ( e ) ;
}
q = TAIL_list ( q ) ;
p = TAIL_list ( p ) ;
}
return ( NULL_exp ) ;
}
/*
MARK A DESTRUCTOR
This routine marks the initialiser expression e for an object of type t
if t has a non-trivial destructor by enclosing it in parentheses.
*/
static EXP destr_init
PROTO_N ( ( t, e ) )
PROTO_T ( TYPE t X EXP e )
{
TYPE s = t ;
while ( IS_type_array ( s ) ) {
s = DEREF_type ( type_array_sub ( s ) ) ;
}
if ( IS_type_compound ( s ) ) {
CLASS_TYPE cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
IDENTIFIER id = DEREF_id ( ctype_destr ( cs ) ) ;
if ( IS_id_mem_func ( id ) ) {
DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( !( ds & dspec_trivial ) ) {
if ( ds & dspec_implicit ) {
if ( !( ds & dspec_defn ) ) {
/* Trivial constructor */
implicit_defn ( id, DEFAULT_DESTR ) ;
}
}
MAKE_exp_paren ( t, e, e ) ;
}
}
}
return ( e ) ;
}
/*
CREATE A CONSTRUCTOR OR DESTRUCTOR
This routine creates a constructor or destructor initialiser list.
cns gives the corresponding class namespace and fn is the function
name. n and m indicate the constructor or destructor type. This can
be an implicitly declared constructor, destructor or assignment
function, or an explicitly declared constructor. In the latter
case the ctor-initialiser lists above are used to indicate the
initialiser values.
*/
static EXP make_constr
PROTO_N ( ( cns, fn, n, m ) )
PROTO_T ( NAMESPACE cns X IDENTIFIER fn X int n X int m )
{
EXP r ;
int usr = 0 ;
int str = 1 ;
unsigned nv = 0 ;
unsigned nb = 0 ;
unsigned long no = 0 ;
int templ = in_template_decl ;
LIST ( EXP ) p = NULL_list ( EXP ) ;
LIST ( OFFSET ) q = NULL_list ( OFFSET ) ;
/* Deconstruct class type */
CLASS_TYPE ct = namespace_class ( cns ) ;
CLASS_INFO ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
GRAPH gr = DEREF_graph ( ctype_base ( ct ) ) ;
LIST ( GRAPH ) br = DEREF_list ( graph_tails ( gr ) ) ;
LIST ( GRAPH ) bv = DEREF_list ( ctype_vbase ( ct ) ) ;
MEMBER mem = DEREF_member ( nspace_ctype_first ( cns ) ) ;
/* Mark 'this' parameter as used */
crt_access_list.inherit++ ;
IGNORE this_param ( fn, 1 ) ;
/* Check for user-defined constructors */
if ( n == DEFAULT_USR && m == DEFAULT_CONSTR ) usr = 1 ;
/* Initialise virtual bases */
if ( m != DEFAULT_ASSIGN ) {
while ( !IS_NULL_list ( bv ) ) {
EXP e = NULL_exp ;
GRAPH gs = DEREF_graph ( HEAD_list ( bv ) ) ;
DECL_SPEC acc = DEREF_dspec ( graph_access ( gs ) ) ;
if ( usr ) e = find_base_init ( gs ) ;
if ( IS_NULL_exp ( e ) && !( acc & dspec_template ) ) {
e = init_empty_base ( gs, fn, n, m ) ;
if ( templ ) {
/* Only testing for template classes */
free_exp ( e, 1 ) ;
e = NULL_exp ;
}
}
if ( m == DEFAULT_CONSTR && !templ ) {
CLASS_TYPE cs = DEREF_ctype ( graph_head ( gs ) ) ;
TYPE s = make_class_type ( cs ) ;
e = destr_init ( s, e ) ;
}
if ( !IS_NULL_exp ( e ) ) {
OFFSET off = DEREF_off ( graph_off ( gs ) ) ;
CONS_off ( off, q, q ) ;
CONS_exp ( e, p, p ) ;
nv++ ;
}
bv = TAIL_list ( bv ) ;
}
}
/* Initialise direct bases */
while ( !IS_NULL_list ( br ) ) {
EXP e = NULL_exp ;
GRAPH gs = DEREF_graph ( HEAD_list ( br ) ) ;
DECL_SPEC acc = DEREF_dspec ( graph_access ( gs ) ) ;
if ( !( acc & dspec_virtual ) || m == DEFAULT_ASSIGN ) {
if ( usr ) e = find_base_init ( gs ) ;
if ( IS_NULL_exp ( e ) && !( acc & dspec_template ) ) {
e = init_empty_base ( gs, fn, n, m ) ;
if ( templ ) {
/* Only testing for template classes */
free_exp ( e, 1 ) ;
e = NULL_exp ;
}
}
if ( m == DEFAULT_CONSTR && !templ ) {
CLASS_TYPE cs = DEREF_ctype ( graph_head ( gs ) ) ;
TYPE s = make_class_type ( cs ) ;
e = destr_init ( s, e ) ;
}
if ( !IS_NULL_exp ( e ) ) {
OFFSET off = DEREF_off ( graph_off ( gs ) ) ;
CONS_off ( off, q, q ) ;
CONS_exp ( e, p, p ) ;
nb++ ;
}
}
br = TAIL_list ( br ) ;
}
/* Initialise data members */
member_no = no ;
mem = next_data_member ( mem, 2 ) ;
if ( usr && ( ci & cinfo_union ) ) {
/* Check union initialisers */
unsigned ni = LENGTH_list ( init_mems ) ;
if ( ni ) {
if ( ni > 1 ) {
/* More than one initialiser for union */
report ( crt_loc, ERR_class_base_init_union ( ct ) ) ;
}
str = 0 ;
}
}
while ( !IS_NULL_member ( mem ) ) {
EXP e = NULL_exp ;
IDENTIFIER id = DEREF_id ( member_id ( mem ) ) ;
if ( usr ) {
e = find_mem_init ( id ) ;
if ( !IS_NULL_exp ( e ) ) {
if ( no == member_no ) {
/* More than one initialiser for anonymous union */
report ( crt_loc, ERR_class_base_init_anon ( id ) ) ;
}
no = member_no ;
}
}
if ( IS_NULL_exp ( e ) && str ) {
e = init_empty_mem ( id, fn, n, m ) ;
if ( templ ) {
/* Only testing for template classes */
free_exp ( e, 1 ) ;
e = NULL_exp ;
}
}
if ( m == DEFAULT_CONSTR && !templ ) {
TYPE s = DEREF_type ( id_member_type ( id ) ) ;
e = destr_init ( s, e ) ;
}
if ( !IS_NULL_exp ( e ) ) {
OFFSET off = DEREF_off ( id_member_off ( id ) ) ;
e = check_init ( e ) ;
CONS_off ( off, q, q ) ;
CONS_exp ( e, p, p ) ;
}
if ( ci & cinfo_union ) {
if ( !usr ) break ;
str = 0 ;
}
mem = DEREF_member ( member_next ( mem ) ) ;
mem = next_data_member ( mem, 2 ) ;
}
/* Construct the result */
crt_access_list.inherit-- ;
if ( IS_NULL_list ( p ) ) {
DECL_SPEC ds = DEREF_dspec ( id_storage ( fn ) ) ;
if ( ds & dspec_trivial ) return ( NULL_exp ) ;
if ( m != DEFAULT_DESTR && !templ ) {
if ( ci & ( cinfo_virtual_base | cinfo_polymorphic ) ) {
/* These require an initialiser */
/* EMPTY */
} else {
return ( NULL_exp ) ;
}
}
}
if ( m != DEFAULT_DESTR ) {
/* Initialisers are built in reverse order */
p = REVERSE_list ( p ) ;
q = REVERSE_list ( q ) ;
}
MAKE_exp_initialiser ( type_void, p, q, m, nv, nb, r ) ;
return ( r ) ;
}
/*
CREATE A DESTRUCTOR PRELUDE EXPRESSION
The destruction of the base classes and members of a class takes
place at the end of the destructor, and it is this which is handled
by make_constr. However for polymorphic classes some action is
also required at the start of the destructor, namely the resetting
of the virtual function tables. This routine creates a dummy
initialiser expression which performs this action.
*/
static EXP make_destr_prelude
PROTO_N ( ( cns ) )
PROTO_T ( NAMESPACE cns )
{
EXP r = NULL_exp ;
CLASS_TYPE ct = namespace_class ( cns ) ;
CLASS_INFO ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
if ( ci & cinfo_polymorphic ) {
/* Create dummy initialiser */
MAKE_exp_initialiser ( type_void, NULL_list ( EXP ),
NULL_list ( OFFSET ), DEFAULT_PRELUDE, 0, 0, r ) ;
}
return ( r ) ;
}
/*
COPY A CONSTRUCTOR INITIALISER LIST
This routine copies the constructor initialiser list of type m.
*/
EXP copy_ctor
PROTO_N ( ( e, m ) )
PROTO_T ( EXP e X int m )
{
IDENTIFIER fn = crt_func_id ;
NAMESPACE cns = DEREF_nspace ( id_parent ( fn ) ) ;
LIST ( EXP ) p = DEREF_list ( exp_initialiser_args ( e ) ) ;
LIST ( OFFSET ) q = DEREF_list ( exp_initialiser_offs ( e ) ) ;
if ( !IS_NULL_list ( p ) ) {
/* Copy initialisers */
while ( !IS_NULL_list ( p ) && !IS_NULL_list ( q ) ) {
int redo = 0 ;
IDENTIFIER id = NULL_id ;
EXP a = DEREF_exp ( HEAD_list ( p ) ) ;
OFFSET off = DEREF_off ( HEAD_list ( q ) ) ;
EXP b = implicit_cast_exp ( a ) ;
if ( !IS_NULL_exp ( b ) && !EQ_exp ( b, a ) ) {
redo = 1 ;
a = b ;
}
a = copy_exp ( a, NULL_type, NULL_type ) ;
if ( redo && !IS_exp_initialiser ( a ) ) {
/* Turn expression into ctor-initialiser list */
LIST ( EXP ) r = NULL_list ( EXP ) ;
LIST ( OFFSET ) s = NULL_list ( OFFSET ) ;
CONS_exp ( a, r, r ) ;
MAKE_exp_initialiser ( type_void, r, s, 0, 0, 0, a ) ;
}
off = copy_offset ( off, lex_plus ) ;
switch ( TAG_off ( off ) ) {
case off_member_tag : {
/* Member initialiser */
id = DEREF_id ( off_member_id ( off ) ) ;
break ;
}
case off_base_tag : {
/* Direct base class initialiser */
GRAPH gr = DEREF_graph ( off_base_graph ( off ) ) ;
CLASS_TYPE ct = DEREF_ctype ( graph_head ( gr ) ) ;
id = DEREF_id ( ctype_name ( ct ) ) ;
break ;
}
case off_deriv_tag : {
/* Indirect base class initialiser */
GRAPH gr = DEREF_graph ( off_deriv_graph ( off ) ) ;
CLASS_TYPE ct = DEREF_ctype ( graph_head ( gr ) ) ;
id = DEREF_id ( ctype_name ( ct ) ) ;
break ;
}
}
if ( !IS_NULL_id ( id ) ) {
/* Add initialiser to list */
ctor_initialise ( cns, id, a ) ;
}
q = TAIL_list ( q ) ;
p = TAIL_list ( p ) ;
}
}
e = make_constr ( cns, fn, DEFAULT_USR, m ) ;
destroy_ctor_lists () ;
return ( e ) ;
}
/*
BEGIN A CONSTRUCTOR INITIALISER LIST
This routine is called at the start of a ctor-initialiser list to
check that the current function is a constructor. If so it returns
the corresponding class namespace. Otherwise the null namespace
is returned.
*/
NAMESPACE ctor_begin
PROTO_Z ()
{
IDENTIFIER id = crt_func_id ;
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
if ( IS_hashid_constr ( nm ) ) {
TYPE t = DEREF_type ( hashid_constr_type ( nm ) ) ;
if ( IS_type_compound ( t ) ) {
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
NAMESPACE cns = DEREF_nspace ( ctype_member ( ct ) ) ;
return ( cns ) ;
}
}
report ( crt_loc, ERR_class_base_init_bad ( id ) ) ;
return ( NULL_nspace ) ;
}
/*
END A CONSTRUCTOR INITIALISER LIST
This routine is called at the end of a ctor-initialiser list. cns
gives the class namespace, e is the compound statement to which
the initialisers are to be added and elem is true if the list was
not empty. The routine returns the resultant compound statement.
*/
EXP ctor_end
PROTO_N ( ( cns, e, elem ) )
PROTO_T ( NAMESPACE cns X EXP e X int elem )
{
if ( !IS_NULL_nspace ( cns ) ) {
/* Construct the constructor */
IDENTIFIER id = crt_func_id ;
if ( !elem ) report ( crt_loc, ERR_class_base_init_empty ( id ) ) ;
if ( IS_id_mem_func ( id ) ) {
EXP c = make_constr ( cns, id, DEFAULT_USR, DEFAULT_CONSTR ) ;
if ( !IS_NULL_exp ( c ) ) {
e = add_compound_stmt ( e, c ) ;
}
}
destroy_ctor_lists () ;
}
return ( e ) ;
}
/*
HANDLE AN ABSENT CONSTRUCTOR INITIALISER LIST
This routine is called for a function definition which contains no
ctor-initialiser. e gives the compound statement giving the function
body. The routine returns the value of e after any initialisers
required for constructors have been added. Any destructors which need
to be called at the end of the function are returned via p.
*/
EXP ctor_none
PROTO_N ( ( e, p ) )
PROTO_T ( EXP e X EXP *p )
{
IDENTIFIER id = crt_func_id ;
if ( IS_id_mem_func ( id ) ) {
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
switch ( TAG_hashid ( nm ) ) {
case hashid_constr_tag : {
/* Constructor declarations */
NAMESPACE cns = DEREF_nspace ( id_parent ( id ) ) ;
EXP c = make_constr ( cns, id, DEFAULT_USR, DEFAULT_CONSTR ) ;
if ( !IS_NULL_exp ( c ) ) {
/* Add initialiser to function body */
e = add_compound_stmt ( e, c ) ;
}
break ;
}
case hashid_destr_tag : {
/* Destructor declarations */
NAMESPACE cns = DEREF_nspace ( id_parent ( id ) ) ;
EXP c = make_destr_prelude ( cns ) ;
EXP d = make_constr ( cns, id, DEFAULT_USR, DEFAULT_DESTR ) ;
if ( !IS_NULL_exp ( c ) ) {
/* Add destructor prelude to function body */
e = add_compound_stmt ( e, c ) ;
}
if ( !IS_NULL_exp ( d ) ) {
/* Set up destructor postlude */
IDENTIFIER lab = postlude_label () ;
EXP a = begin_label_stmt ( lab, lex_return ) ;
*p = end_label_stmt ( a, d ) ;
}
break ;
}
}
}
return ( e ) ;
}
/*
ADD A POSTLUDE TO A FUNCTION BODY
This routine adds the postlude expression d to the end of the
function body e. This is used in user declared destructors where the
default destructors need to be called after the main function body.
For convenience the code for falling out of a normal function body
is also handled by this routine.
*/
EXP ctor_postlude
PROTO_N ( ( e, d ) )
PROTO_T ( EXP e X EXP d )
{
if ( !IS_NULL_exp ( d ) ) {
/* Add postlude expression */
EXP r ;
unreached_code = 0 ;
e = add_compound_stmt ( e, d ) ;
unreached_code = 0 ;
MAKE_exp_return_stmt ( type_bottom, NULL_exp, r ) ;
e = add_compound_stmt ( e, r ) ;
unreached_code = 1 ;
unreached_last = 0 ;
} else {
/* Fall out of function */
if ( !unreached_code ) {
EXP r = fall_return_stmt () ;
e = add_compound_stmt ( e, r ) ;
}
}
return ( e ) ;
}
/*
CREATE AN EXCEPTION POSTLUDE EXPRESSION
This routine creates an expression which will be called in the exception
specifier for the function id. This is used in constructors to destroy
the partially complete object.
*/
EXP except_postlude
PROTO_N ( ( id ) )
PROTO_T ( IDENTIFIER id )
{
EXP e = NULL_exp ;
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
if ( IS_hashid_constr ( nm ) ) {
/* Have a constructor */
TYPE t = DEREF_type ( hashid_constr_type ( nm ) ) ;
if ( IS_type_compound ( t ) ) {
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
NAMESPACE cns = DEREF_nspace ( ctype_member ( ct ) ) ;
DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( !( ds & ( dspec_friend | dspec_trivial ) ) ) {
/* Constructor may throw an exception */
int ac = do_access_checks ;
EXP a = DEREF_exp ( id_function_etc_defn ( id ) ) ;
if ( !IS_NULL_exp ( a ) && IS_exp_initialiser ( a ) ) {
LIST ( EXP ) p ;
p = DEREF_list ( exp_initialiser_args ( a ) ) ;
if ( LENGTH_list ( p ) == 1 ) {
/* Single initialiser in constructor */
a = DEREF_exp ( HEAD_list ( p ) ) ;
if ( !IS_NULL_exp ( a ) ) {
TYPE s = DEREF_type ( exp_type ( a ) ) ;
if ( !IS_type_array ( s ) ) return ( e ) ;
}
}
}
do_access_checks = 0 ;
COPY_dspec ( id_storage ( id ), ( ds | dspec_trivial ) ) ;
start_try_check ( univ_type_set ) ;
e = make_constr ( cns, id, DEFAULT_DESTR, DEFAULT_DESTR ) ;
if ( !IS_NULL_exp ( e ) ) {
/* Add destructor prelude */
EXP d = make_destr_prelude ( cns ) ;
e = join_exp ( d, e ) ;
}
e = end_try_check ( id, e ) ;
COPY_dspec ( id_storage ( id ), ds ) ;
do_access_checks = ac ;
}
}
}
return ( e ) ;
}
/*
FIND INITIALISATION ORDER OF TWO BASE GRAPHS
This routine compares the order of initialisation of the base graphs
gr and gs. It returns 1 if gr is initialised before gs, -1 if gr is
initialised before gr and 0 if they are equal. Note that virtual
bases are initialised before direct bases.
*/
static int compare_base
PROTO_N ( ( gr, gs ) )
PROTO_T ( GRAPH gr X GRAPH gs )
{
DECL_SPEC ar, as ;
LIST ( GRAPH ) br ;
if ( eq_graph ( gr, gs ) ) return ( 0 ) ;
ar = DEREF_dspec ( graph_access ( gr ) ) ;
as = DEREF_dspec ( graph_access ( gs ) ) ;
if ( ar & dspec_virtual ) {
if ( as & dspec_virtual ) {
GRAPH gt = DEREF_graph ( graph_top ( gr ) ) ;
CLASS_TYPE ct = DEREF_ctype ( graph_head ( gt ) ) ;
br = DEREF_list ( ctype_vbase ( ct ) ) ;
} else {
return ( 1 ) ;
}
} else {
if ( as & dspec_virtual ) {
return ( -1 ) ;
} else {
GRAPH gt = DEREF_graph ( graph_top ( gr ) ) ;
br = DEREF_list ( graph_tails ( gt ) ) ;
}
}
while ( !IS_NULL_list ( br ) ) {
GRAPH gt = DEREF_graph ( HEAD_list ( br ) ) ;
if ( eq_graph ( gt, gr ) ) return ( 1 ) ;
if ( eq_graph ( gt, gs ) ) return ( -1 ) ;
br = TAIL_list ( br ) ;
}
return ( 0 ) ;
}
/*
FIND INITIALISATION ORDER OF TWO MEMBERS
This routine compares the order of initialisation of the members pid
and mid of the class namespace ns. It returns 1 if mid is initialised
before pid, -1 if pid is initialised before mid and 0 if they are equal.
*/
static int compare_mem
PROTO_N ( ( ns, mid, pid ) )
PROTO_T ( NAMESPACE ns X IDENTIFIER mid X IDENTIFIER pid )
{
MEMBER mem ;
if ( EQ_id ( mid, pid ) ) return ( 0 ) ;
mem = DEREF_member ( nspace_ctype_first ( ns ) ) ;
while ( !IS_NULL_member ( mem ) ) {
IDENTIFIER id = DEREF_id ( member_id ( mem ) ) ;
if ( EQ_id ( id, mid ) ) return ( 1 ) ;
if ( EQ_id ( id, pid ) ) return ( -1 ) ;
mem = DEREF_member ( member_next ( mem ) ) ;
}
return ( 0 ) ;
}
/*
NAME LOOK-UP FOR CONSTRUCTOR INITIALISER
This routine looks up the name id as a ctor-initialiser for the class
cns. If it denotes a base class then the corresponding graph is
returned via pgr. If id is the null identifier then this is an
anachronistic way of initialising the unique direct base class of
cns, if this exists. The null identifier is returned for illegal
initialisers.
*/
static IDENTIFIER ctor_field
PROTO_N ( ( cns, id, pgr ) )
PROTO_T ( NAMESPACE cns X IDENTIFIER id X GRAPH *pgr )
{
if ( !IS_NULL_nspace ( cns ) ) {
if ( IS_NULL_id ( id ) ) {
/* Search for unique base class */
ERROR err = ERR_class_base_init_old ( crt_func_id ) ;
CLASS_TYPE ct = namespace_class ( cns ) ;
GRAPH gr = uniq_base_class ( ct, &err ) ;
if ( !IS_NULL_graph ( gr ) ) {
ct = DEREF_ctype ( graph_head ( gr ) ) ;
id = DEREF_id ( ctype_name ( ct ) ) ;
*pgr = gr ;
} else {
id = NULL_id ;
}
report ( crt_loc, err ) ;
} else {
/* Look up name as class member */
GRAPH gr ;
int check_base = 1 ;
IDENTIFIER fid = id ;
NAMESPACE fns = DEREF_nspace ( id_parent ( fid ) ) ;
if ( EQ_nspace ( fns, crt_namespace ) ) {
/* Rescan constructor parameters in enclosing scope */
HASHID nm = DEREF_hashid ( id_name ( fid ) ) ;
int c = cache_lookup ;
cache_lookup = 0 ;
remove_namespace () ;
fid = find_id ( nm ) ;
add_namespace ( fns ) ;
cache_lookup = c ;
id = fid ;
}
/* Check for class members */
gr = is_subfield ( cns, fid ) ;
if ( !IS_NULL_graph ( gr ) ) {
check_base = 0 ;
fid = search_subfield ( cns, gr, fid ) ;
switch ( TAG_id ( fid ) ) {
case id_member_tag : {
/* Non-static data members */
DECL_SPEC ds = DEREF_dspec ( id_storage ( fid ) ) ;
if ( ds & dspec_inherit ) {
/* Can't denote an inherited member */
ERROR err ;
err = ERR_class_base_init_inherit ( fid ) ;
report ( crt_loc, err ) ;
id = NULL_id ;
} else {
id = fid ;
}
break ;
}
case id_class_name_tag :
case id_class_alias_tag : {
/* Check for base classes */
check_base = 1 ;
break ;
}
case id_undef_tag : {
/* Undeclared members */
HASHID nm = DEREF_hashid ( id_name ( fid ) ) ;
ERROR err = ERR_lookup_qual_undef ( nm, fns ) ;
report ( crt_loc, err ) ;
id = NULL_id ;
break ;
}
case id_ambig_tag : {
/* Ambiguous members */
id = report_ambiguous ( fid, 0, 1, 1 ) ;
break ;
}
default : {
/* Other members */
ERROR err = ERR_class_base_init_static ( fid ) ;
report ( crt_loc, err ) ;
id = NULL_id ;
break ;
}
}
}
/* Check for base classes */
if ( check_base ) {
CLASS_TYPE cs = find_class ( fid ) ;
if ( IS_NULL_ctype ( cs ) && IS_id_undef ( fid ) ) {
TYPE s = check_typename ( fns, fid, btype_class ) ;
if ( !IS_NULL_type ( s ) && IS_type_compound ( s ) ) {
cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
}
}
if ( !IS_NULL_ctype ( cs ) ) {
/* Class name found */
ERROR err = NULL_err ;
CLASS_TYPE ct = namespace_class ( cns ) ;
gr = direct_base_class ( ct, cs, &err ) ;
if ( !IS_NULL_err ( err ) ) {
/* Report invalid bases */
report ( crt_loc, err ) ;
}
if ( !IS_NULL_graph ( gr ) ) {
id = DEREF_id ( ctype_name ( cs ) ) ;
*pgr = gr ;
} else {
id = NULL_id ;
}
} else {
/* Invalid ctor-initialiser */
ERROR err = ERR_lookup_qual_bad ( id, cns ) ;
report ( crt_loc, err ) ;
id = NULL_id ;
}
}
}
} else {
/* Invalid class namespace */
id = NULL_id ;
}
return ( id ) ;
}
/*
PROCESS A CONSTRUCTOR INITIALISER
This routine processes a ctor-initialiser which sets the id component
of the current constructor function to the initialiser expression init.
cns gives the value returned by ctor_check. id may be the null
identifier indicating the anachronistic single inheritance base class
initialisation.
*/
void ctor_initialise
PROTO_N ( ( cns, id, init ) )
PROTO_T ( NAMESPACE cns X IDENTIFIER id X EXP init )
{
TYPE t ;
int n = 0 ;
int ok = 1 ;
GRAPH gr = NULL_graph ;
/* Check member identifier */
IDENTIFIER mid = ctor_field ( cns, id, &gr ) ;
if ( !IS_NULL_id ( mid ) ) {
EXP e ;
if ( IS_id_member ( mid ) ) {
/* Member initialiser */
e = find_mem_init ( mid ) ;
t = DEREF_type ( id_member_type ( mid ) ) ;
n = 1 ;
} else {
/* Base class initialiser */
e = find_base_init ( gr ) ;
t = DEREF_type ( id_class_name_etc_defn ( mid ) ) ;
if ( in_template_decl ) {
/* Allow for template parameters */
DECL_SPEC acc = DEREF_dspec ( graph_access ( gr ) ) ;
if ( acc & dspec_template ) {
CLASS_TYPE ct = DEREF_ctype ( graph_head ( gr ) ) ;
t = DEREF_type ( ctype_form ( ct ) ) ;
}
}
n = 2 ;
}
if ( !IS_NULL_exp ( e ) ) {
/* Initialiser already given */
report ( crt_loc, ERR_class_base_init_dup ( mid ) ) ;
}
} else {
/* Invalid initialiser */
HASHID nm = KEYWORD ( lex_zzzz ) ;
mid = DEREF_id ( hashid_id ( nm ) ) ;
t = type_error ;
}
/* Check for order of initialisers */
if ( init_base_last ) {
/* Previous initialiser was a base */
if ( n == 2 ) {
if ( !IS_NULL_list ( init_bases ) ) {
GRAPH gp = DEREF_graph ( HEAD_list ( init_bases ) ) ;
int cmp = compare_base ( gr, gp ) ;
if ( cmp > 0 ) ok = 0 ;
}
}
} else {
/* Previous initialiser was a member */
if ( n == 1 ) {
if ( !IS_NULL_list ( init_mems ) ) {
IDENTIFIER pid = DEREF_id ( HEAD_list ( init_mems ) ) ;
int cmp = compare_mem ( cns, mid, pid ) ;
if ( cmp > 0 ) ok = 0 ;
}
} else if ( n == 2 ) {
ok = 0 ;
}
}
if ( !ok ) {
/* Initialisers out of sequence */
report ( crt_loc, ERR_class_base_init_order ( mid ) ) ;
}
/* Perform initialisation */
if ( IS_exp_initialiser ( init ) ) {
crt_access_list.inherit++ ;
decl_loc = crt_loc ;
if ( n == 2 ) in_ctor_base_init = 1 ;
init = init_general ( t, init, mid, 0 ) ;
in_ctor_base_init = 0 ;
crt_access_list.inherit-- ;
}
if ( n == 1 ) {
CONS_id ( mid, init_mems, init_mems ) ;
CONS_exp ( init, val_mems, val_mems ) ;
init_base_last = 0 ;
} else if ( n == 2 ) {
CONS_graph ( gr, init_bases, init_bases ) ;
CONS_exp ( init, val_bases, val_bases ) ;
init_base_last = 1 ;
}
no_ctor_init++ ;
return ;
}
/*
CONSTRUCT A PSEUDO-DESTRUCTOR
This routine creates a pseudo-destructor with class name given by
id1 and b1 and destructor name given by id2 and b2.
*/
IDENTIFIER make_pseudo_destr
PROTO_N ( ( id1, b1, id2, b2 ) )
PROTO_T ( IDENTIFIER id1 X BASE_TYPE b1 X IDENTIFIER id2 X BASE_TYPE b2 )
{
HASHID nm ;
int create = 0 ;
int rescan = 0 ;
TYPE t1 = NULL_type ;
TYPE t2 = NULL_type ;
NAMESPACE ns = NULL_nspace ;
/* Check class name */
if ( b1 == btype_none ) {
if ( !IS_NULL_id ( id1 ) ) {
ns = find_namespace ( id1 ) ;
if ( IS_id_class_name_etc ( id1 ) ) {
t1 = DEREF_type ( id_class_name_etc_defn ( id1 ) ) ;
t1 = copy_typedef ( id1, t1, cv_none ) ;
COPY_id ( type_name ( t1 ), id1 ) ;
if ( IS_NULL_nspace ( ns ) ) {
ns = DEREF_nspace ( id_parent ( id1 ) ) ;
} else {
/* Have class name */
rescan = 1 ;
}
use_id ( id1, 0 ) ;
} else {
if ( !IS_NULL_nspace ( ns ) && !IS_NULL_id ( id2 ) ) {
/* Namespace qualifier allowed */
create = 1 ;
} else {
report ( crt_loc, ERR_dcl_type_simple_undef ( id1 ) ) ;
}
}
}
} else {
t1 = make_base_type ( b1 ) ;
}
/* Check destructor name */
if ( b2 == btype_none ) {
if ( !EQ_id ( id2, id1 ) ) {
nm = DEREF_hashid ( id_name ( id2 ) ) ;
if ( !IS_NULL_nspace ( ns ) ) {
/* Rescan id2 in the context of id1 */
IDENTIFIER id3 = find_qual_id ( ns, nm, create, 1 ) ;
if ( !IS_NULL_id ( id3 ) ) id2 = id3 ;
}
if ( IS_id_class_name_etc ( id2 ) ) {
t2 = DEREF_type ( id_class_name_etc_defn ( id2 ) ) ;
t2 = copy_typedef ( id2, t2, cv_none ) ;
COPY_id ( type_name ( t2 ), id2 ) ;
use_id ( id2, 0 ) ;
} else {
report ( crt_loc, ERR_dcl_type_simple_undef ( id2 ) ) ;
}
}
} else {
if ( b2 != b1 ) t2 = make_base_type ( b2 ) ;
}
/* Form pseudo-destructor name */
if ( IS_NULL_type ( t2 ) ) {
t2 = t1 ;
if ( IS_NULL_type ( t2 ) ) t2 = type_error ;
} else {
if ( rescan ) {
/* Error will be caught in search_field */
/* EMPTY */
} else {
if ( !IS_NULL_type ( t1 ) && !eq_type ( t1, t2 ) ) {
/* If both specified, types should match */
report ( crt_loc, ERR_expr_pseudo_type ( t1, t2 ) ) ;
}
}
}
nm = lookup_destr ( t2, id2 ) ;
id2 = DEREF_id ( hashid_id ( nm ) ) ;
return ( id2 ) ;
}
/*
DECLARATION SPECIFIERS FOR IMPLICIT CONSTRUCTORS
This value gives the declaration specifiers used for implicitly declared
constructors and destructors.
*/
#define dspec_constr ( dspec_implicit | dspec_ignore | dspec_inline )
/*
ADD THE EXCEPTIONS THROWN BY A CONSTRUCTOR TO A LIST
This routine adds the list of exceptions thrown by the implicit
constructor or destructor (given by n) of the class ct to the list p.
*/
static LIST ( TYPE ) add_constr_except
PROTO_N ( ( p, ct, n ) )
PROTO_T ( LIST ( TYPE ) p X CLASS_TYPE ct X int n )
{
IDENTIFIER id = find_constr ( ct, n, KILL_err ) ;
if ( !IS_NULL_id ( id ) && IS_id_mem_func ( id ) ) {
TYPE fn = DEREF_type ( id_mem_func_type ( id ) ) ;
if ( IS_type_func ( fn ) ) {
LIST ( TYPE ) q = DEREF_list ( type_func_except ( fn ) ) ;
p = union_type_set ( p, q ) ;
}
}
return ( p ) ;
}
/*
FIND EXCEPTIONS THROWN BY AN IMPLICIT CONSTRUCTOR
This routine finds the list of exceptions thrown by the implicit
constructor or destructor (given by n) of the class ct.
*/
static LIST ( TYPE ) constr_except
PROTO_N ( ( ct, n ) )
PROTO_T ( CLASS_TYPE ct X int n )
{
LIST ( TYPE ) res = NULL_list ( TYPE ) ;
GRAPH gr = DEREF_graph ( ctype_base ( ct ) ) ;
LIST ( GRAPH ) br = DEREF_list ( graph_tails ( gr ) ) ;
NAMESPACE cns = DEREF_nspace ( ctype_member ( ct ) ) ;
MEMBER mem = DEREF_member ( nspace_ctype_first ( cns ) ) ;
/* Scan through virtual bases */
if ( n != DEFAULT_ASSIGN ) {
LIST ( GRAPH ) bv = DEREF_list ( ctype_vbase ( ct ) ) ;
while ( !IS_NULL_list ( bv ) ) {
GRAPH gs = DEREF_graph ( HEAD_list ( bv ) ) ;
CLASS_TYPE cs = DEREF_ctype ( graph_head ( gs ) ) ;
res = add_constr_except ( res, cs, n ) ;
if ( EQ_list ( res, univ_type_set ) ) return ( res ) ;
bv = TAIL_list ( bv ) ;
}
}
/* Scan through direct bases */
while ( !IS_NULL_list ( br ) ) {
GRAPH gs = DEREF_graph ( HEAD_list ( br ) ) ;
DECL_SPEC acc = DEREF_dspec ( graph_access ( gs ) ) ;
if ( !( acc & dspec_virtual ) || n == DEFAULT_ASSIGN ) {
CLASS_TYPE cs = DEREF_ctype ( graph_head ( gs ) ) ;
res = add_constr_except ( res, cs, n ) ;
if ( EQ_list ( res, univ_type_set ) ) return ( res ) ;
}
br = TAIL_list ( br ) ;
}
/* Scan through data members */
mem = next_data_member ( mem, 2 ) ;
while ( !IS_NULL_member ( mem ) ) {
IDENTIFIER id = DEREF_id ( member_id ( mem ) ) ;
TYPE s = DEREF_type ( id_member_type ( id ) ) ;
while ( IS_type_array ( s ) ) {
s = DEREF_type ( type_array_sub ( s ) ) ;
}
if ( IS_type_compound ( s ) ) {
CLASS_TYPE cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
res = add_constr_except ( res, cs, n ) ;
if ( EQ_list ( res, univ_type_set ) ) return ( res ) ;
}
mem = DEREF_member ( member_next ( mem ) ) ;
mem = next_data_member ( mem, 2 ) ;
}
return ( res ) ;
}
/*
DECLARE IMPLICIT CONSTRUCTORS AND DESTRUCTORS
This routine is called at the end of a class definition to implicitly
declare any necessary default constructors, copy constructors, assignment
operators or destructors. Note that these are only actually defined
if they are used, and errors are only detected at this definition stage.
The routine returns information on the class being defined.
*/
CLASS_INFO implicit_decl
PROTO_N ( ( ct, ci, cds ) )
PROTO_T ( CLASS_TYPE ct X CLASS_INFO ci X DECL_SPEC cds )
{
TYPE t ;
HASHID nm ;
IDENTIFIER id ;
TYPE pars [2] ;
CLASS_INFO cj ;
LIST ( TYPE ) ex ;
IDENTIFIER cid = DEREF_id ( ctype_name ( ct ) ) ;
NAMESPACE ns = DEREF_nspace ( ctype_member ( ct ) ) ;
LIST ( IDENTIFIER ) pals = DEREF_list ( ctype_pals ( ct ) ) ;
/* Options being checked */
int need_copy_constr = 1 ;
int need_default_constr = 1 ;
int need_assignment_op = 1 ;
int need_destructor = 1 ;
int noncopy_constr = 0 ;
int access_constr = 0 ;
int access_destr = 0 ;
int three_rule = 0 ;
/* Find accumulated information */
cj = ( ci & cinfo_implicit ) ;
if ( ci & ( cinfo_virtual_base | cinfo_polymorphic ) ) {
cj &= ~cinfo_trivial_make ;
}
ci &= ~cinfo_implicit ;
/* Check constructors */
id = DEREF_id ( ctype_constr ( ct ) ) ;
if ( IS_id_mem_func ( id ) ) {
IDENTIFIER fid = id ;
while ( !IS_NULL_id ( fid ) ) {
TYPE fn = DEREF_type ( id_mem_func_type ( fid ) ) ;
int c = check_copy_constr ( fn, ct ) ;
if ( c ) {
/* Copy constructor */
if ( c == 2 ) ci |= cinfo_const_copy ;
need_copy_constr = 0 ;
three_rule++ ;
} else {
/* Check access for other constructors */
DECL_SPEC acc = DEREF_dspec ( id_storage ( fid ) ) ;
if ( ( acc & dspec_access ) != dspec_private ) {
access_constr = 1 ;
}
noncopy_constr = 1 ;
}
fid = DEREF_id ( id_mem_func_over ( fid ) ) ;
}
ci |= cinfo_usr_constr ;
need_default_constr = 0 ;
} else {
/* Delete non-function meanings */
nm = DEREF_hashid ( id_name ( id ) ) ;
clear_member ( ns, nm ) ;
}
/* Implicit declaration of default constructor */
if ( need_default_constr ) {
DECL_SPEC ds = ( dspec_constr | cds ) ;
if ( cj & cinfo_trivial_constr ) {
ci |= cinfo_trivial_constr ;
ds |= dspec_trivial ;
}
pars [0] = NULL_type ;
ex = constr_except ( ct, DEFAULT_CONSTR ) ;
IGNORE declare_func ( ds, id, NULL_type, pars, FUNC_NONE, ex ) ;
noncopy_constr = 1 ;
access_constr = 1 ;
}
/* Implicit declaration of copy constructor */
if ( need_copy_constr ) {
CV_SPEC cv = cv_lvalue ;
DECL_SPEC ds = ( dspec_constr | cds ) ;
if ( cj & cinfo_trivial_copy ) {
ci |= cinfo_trivial_copy ;
ds |= dspec_trivial ;
}
if ( cj & cinfo_const_copy ) {
ci |= cinfo_const_copy ;
cv |= cv_const ;
}
MAKE_type_compound ( cv, ct, t ) ;
COPY_id ( type_name ( t ), cid ) ;
MAKE_type_ref ( cv_none, t, t ) ;
pars [0] = t ;
pars [1] = NULL_type ;
ex = constr_except ( ct, DEFAULT_COPY ) ;
IGNORE declare_func ( ds, id, NULL_type, pars, FUNC_NONE, ex ) ;
}
/* Check assignment operators (ignore inheritance) */
nm = lookup_op ( lex_assign ) ;
id = search_id ( ns, nm, 0, 0 ) ;
if ( !IS_NULL_id ( id ) ) {
if ( IS_id_mem_func ( id ) ) {
IDENTIFIER fid = id ;
while ( !IS_NULL_id ( fid ) ) {
TYPE fn = DEREF_type ( id_mem_func_type ( fid ) ) ;
int c = check_copy_constr ( fn, ct ) ;
if ( c ) {
if ( c == 2 || c == 3 ) ci |= cinfo_const_assign ;
need_assignment_op = 0 ;
three_rule++ ;
}
fid = DEREF_id ( id_mem_func_over ( fid ) ) ;
}
} else {
/* Delete non-function meanings */
clear_member ( ns, nm ) ;
}
} else {
/* Create a dummy identifier */
id = DEREF_id ( hashid_id ( nm ) ) ;
}
/* Implicit declaration of copy assignment operator */
if ( need_assignment_op ) {
CV_SPEC cv = cv_lvalue ;
DECL_SPEC ds = ( dspec_constr | cds ) ;
if ( cj & cinfo_trivial_assign ) {
ci |= cinfo_trivial_assign ;
ds |= dspec_trivial ;
}
if ( cj & cinfo_const_assign ) {
ci |= cinfo_const_assign ;
cv |= cv_const ;
}
MAKE_type_compound ( cv, ct, t ) ;
COPY_id ( type_name ( t ), cid ) ;
MAKE_type_ref ( cv_none, t, t ) ;
pars [0] = t ;
pars [1] = NULL_type ;
MAKE_type_compound ( cv_lvalue, ct, t ) ;
COPY_id ( type_name ( t ), cid ) ;
MAKE_type_ref ( cv_none, t, t ) ;
ex = constr_except ( ct, DEFAULT_ASSIGN ) ;
IGNORE declare_func ( ds, id, t, pars, FUNC_NONE, ex ) ;
}
/* Check destructors */
id = DEREF_id ( ctype_destr ( ct ) ) ;
if ( IS_id_mem_func ( id ) ) {
DECL_SPEC acc = DEREF_dspec ( id_storage ( id ) ) ;
if ( ( acc & dspec_access ) != dspec_private ) {
access_destr = 1 ;
}
need_destructor = 0 ;
three_rule++ ;
} else {
/* Delete non-function meanings */
nm = DEREF_hashid ( id_name ( id ) ) ;
clear_member ( ns, nm ) ;
}
/* Implicit declaration of default destructor */
if ( need_destructor ) {
/* Check for trivial destructors */
DECL_SPEC ds = ( dspec_constr | cds ) ;
if ( cj & cinfo_trivial_destr ) {
ci |= cinfo_trivial_destr ;
ds |= dspec_trivial ;
}
pars [0] = NULL_type ;
ex = constr_except ( ct, DEFAULT_DESTR ) ;
IGNORE declare_func ( ds, id, NULL_type, pars, FUNC_NONE, ex ) ;
access_destr = 1 ;
}
/* Report inaccessible constructors or destructors */
if ( three_rule != 0 && three_rule != 3 ) {
report ( crt_loc, ERR_class_dtor_three ( ct ) ) ;
}
if ( !noncopy_constr ) {
report ( crt_loc, ERR_class_ctor_make ( ct ) ) ;
access_constr = 1 ;
}
if ( IS_NULL_list ( pals ) ) {
if ( !access_constr ) {
report ( crt_loc, ERR_class_ctor_private ( ct ) ) ;
}
if ( !access_destr ) {
report ( crt_loc, ERR_class_dtor_private ( ct ) ) ;
}
}
return ( ci ) ;
}
/*
FIND A CONSTRUCTOR KIND
This routine finds the kind of constructor for the function id of
type t.
*/
int constr_kind
PROTO_N ( ( id, t ) )
PROTO_T ( IDENTIFIER id X TYPE t )
{
int n = DEFAULT_USR ;
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
switch ( TAG_hashid ( nm ) ) {
case hashid_constr_tag : {
if ( min_no_args ( t ) == 1 ) {
n = DEFAULT_CONSTR ;
} else {
n = DEFAULT_COPY ;
}
break ;
}
case hashid_destr_tag : {
/* Implicit default destructor */
n = DEFAULT_DESTR ;
break ;
}
case hashid_op_tag : {
/* Implicit assignment operator */
n = DEFAULT_ASSIGN ;
break ;
}
}
return ( n ) ;
}
/*
DEFINE AN IMPLICIT CONSTRUCTOR OR DESTRUCTOR
This routine defines the function id, which will be one of the
implicitly declared constructors or destructors declared in
implicit_decl. n gives the constructor type or DEFAULT_USR if this
is not known.
*/
void implicit_defn
PROTO_N ( ( id, n ) )
PROTO_T ( IDENTIFIER id X int n )
{
TYPE t ;
EXP e, r ;
DECL_SPEC ds ;
IDENTIFIER fn ;
NAMESPACE cns ;
int in_func, in_decl ;
/* Check for previous definition */
if ( !IS_id_mem_func ( id ) ) return ;
ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( ds & dspec_inherit ) {
/* Inherited functions */
id = DEREF_id ( id_alias ( id ) ) ;
ds = DEREF_dspec ( id_storage ( id ) ) ;
}
if ( !( ds & dspec_implicit ) ) return ;
if ( ds & dspec_defn ) return ;
ds |= dspec_defn ;
COPY_dspec ( id_storage ( id ), ds ) ;
/* Find constructor type */
t = DEREF_type ( id_mem_func_type ( id ) ) ;
if ( !IS_type_func ( t ) ) return ;
if ( n == DEFAULT_USR ) n = constr_kind ( id, t ) ;
/* Force immediate access checks */
in_func = in_function_defn ;
in_decl = in_declaration ;
fn = crt_func_id ;
in_function_defn = 1 ;
in_declaration = 0 ;
crt_func_id = id ;
/* Make initialiser list */
start_try_check ( univ_type_set ) ;
cns = DEREF_nspace ( type_func_pars ( t ) ) ;
push_namespace ( cns ) ;
r = make_this_decl ( id ) ;
cns = DEREF_nspace ( id_parent ( id ) ) ;
e = make_constr ( cns, id, n, n ) ;
if ( n == DEFAULT_DESTR ) {
/* Allow for destructor prelude */
if ( !( ds & dspec_trivial ) ) {
EXP d = make_destr_prelude ( cns ) ;
e = join_exp ( d, e ) ;
}
} else if ( n == DEFAULT_ASSIGN ) {
/* Allow for return value */
r = make_indir_exp ( r ) ;
MAKE_exp_return_stmt ( type_bottom, r, r ) ;
e = join_exp ( e, r ) ;
}
IGNORE pop_namespace () ;
e = end_try_check ( id, e ) ;
COPY_exp ( id_mem_func_defn ( id ), e ) ;
define_id ( id ) ;
/* Compile the function definition */
if ( !( ds & dspec_trivial ) ) {
if ( do_dump ) {
dump_implicit = 1 ;
dump_declare ( id, &crt_loc, 1 ) ;
}
compile_function ( id, 0 ) ;
}
/* Restore old values */
in_function_defn = in_func ;
in_declaration = in_decl ;
crt_func_id = fn ;
return ;
}
/*
EXPLICIT CONSTRUCTOR FLAGS
These flags are used to indicate that an explicit constructor or
conversion function has been declared.
*/
int have_conv_expl = 0 ;
int have_constr_expl = 0 ;
/*
REMOVE EXPLICIT CONSTRUCTORS
This routine removes all explicit constructors from the candidate list
p beginning with the mth element.
*/
static void remove_explicit
PROTO_N ( ( p, m ) )
PROTO_T ( CANDIDATE_LIST *p X unsigned m )
{
unsigned i, n = p->size ;
CANDIDATE *q = p->elem + m ;
for ( i = m ; i < n ; i++ ) {
DECL_SPEC ds = DEREF_dspec ( id_storage ( q->base ) ) ;
if ( ds & dspec_explicit ) q->rank = RANK_IGNORE ;
q++ ;
}
return ;
}
/*
CONSTRUCT CONSTRUCTOR CANDIDATES LIST
This routine constructs the candidate list consisting of the constructors
for the class type t (including the explicit constructors only if cast
indicates an explicit conversion). The candidates are added to the
list p. The routine returns the first constructor for t.
*/
static IDENTIFIER constr_candidates
PROTO_N ( ( p, t, cast ) )
PROTO_T ( CANDIDATE_LIST *p X TYPE t X unsigned cast )
{
/* Search for constructor candidates */
IDENTIFIER cid ;
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
complete_class ( ct, 1 ) ;
cid = DEREF_id ( ctype_constr ( ct ) ) ;
if ( IS_id_mem_func ( cid ) ) {
unsigned i = p->size ;
add_candidates ( p, cid, 1, KIND_CONSTR ) ;
swap_candidates ( p, i ) ;
if ( cast == CAST_IMPLICIT && have_constr_expl ) {
remove_explicit ( p, i ) ;
}
} else {
/* Can happen for incomplete types and in C */
cid = NULL_id ;
}
return ( cid ) ;
}
/*
CONSTRUCT CONVERSION CANDIDATES LIST
This routine constructs the candidate list for the conversion functions
for the type s which can be implicitly converted to type t, plus the
constructors of t if t is a class type. The candidates are added to
the end of p. The routine returns the first constructor for t if t is
a class type, and the null identifier otherwise.
*/
IDENTIFIER conv_candidates
PROTO_N ( ( p, t, s, cast ) )
PROTO_T ( CANDIDATE_LIST *p X TYPE t X TYPE s X unsigned cast )
{
/* Add constructors */
IDENTIFIER cid = NULL_id ;
if ( IS_type_compound ( t ) ) {
cid = constr_candidates ( p, t, cast ) ;
}
/* Add conversion functions */
if ( IS_type_compound ( s ) ) {
LIST ( IDENTIFIER ) conv ;
CLASS_TYPE cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
complete_class ( cs, 1 ) ;
conv = DEREF_list ( ctype_conv ( cs ) ) ;
while ( !IS_NULL_list ( conv ) ) {
IDENTIFIER id = DEREF_id ( HEAD_list ( conv ) ) ;
TYPE fn = DEREF_type ( id_function_etc_type ( id ) ) ;
if ( IS_type_templ ( fn ) ) {
/* Allow for template functions */
fn = deduce_conv ( fn, t ) ;
if ( !IS_NULL_type ( fn ) ) {
int eq = 0 ;
id = deduce_func ( id, fn, &eq ) ;
} else {
id = NULL_id ;
}
}
if ( !IS_NULL_id ( id ) ) {
unsigned r ;
CONVERSION c ;
HASHID nm = DEREF_hashid ( id_name ( id ) ) ;
c.from = DEREF_type ( hashid_conv_type ( nm ) ) ;
c.to = t ;
r = std_convert_seq ( &c, NULL_exp, 0, 0 ) ;
if ( r != CONV_NONE ) {
unsigned i = p->size ;
add_candidates ( p, id, 1, KIND_CONV ) ;
if ( cast == CAST_IMPLICIT && have_conv_expl ) {
remove_explicit ( p, i ) ;
}
}
}
conv = TAIL_list ( conv ) ;
}
}
return ( cid ) ;
}
/*
CONSTRUCT GENERIC CONVERSION CANDIDATES LIST
This routine adds all the conversion functions from the type t to
types matching the type category mask kind to the candidate list p.
Note that template conversion functions are excluded - they cannot
give a non-ambiguous resolution and are beaten by any non-template
conversion function.
*/
static void gen_candidates
PROTO_N ( ( p, t, kind ) )
PROTO_T ( CANDIDATE_LIST *p X TYPE t X unsigned kind )
{
if ( IS_type_compound ( t ) ) {
LIST ( IDENTIFIER ) conv ;
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
complete_class ( ct, 1 ) ;
conv = DEREF_list ( ctype_conv ( ct ) ) ;
while ( !IS_NULL_list ( conv ) ) {
IDENTIFIER id = DEREF_id ( HEAD_list ( conv ) ) ;
TYPE fn = DEREF_type ( id_function_etc_type ( id ) ) ;
if ( IS_type_func ( fn ) ) {
TYPE r = DEREF_type ( type_func_ret ( fn ) ) ;
unsigned c = type_category ( &r ) ;
if ( c & kind ) {
unsigned i = p->size ;
add_candidates ( p, id, 1, KIND_CONV ) ;
if ( have_conv_expl ) remove_explicit ( p, i ) ;
}
}
conv = TAIL_list ( conv ) ;
}
}
return ;
}
/*
APPLY A TRIVIAL DESTRUCTOR
This routine applies a trivial destructor to the expression a. It
is used in explicit destructor and pseudo-destructor calls.
*/
EXP trivial_destr
PROTO_N ( ( a ) )
PROTO_T ( EXP a )
{
EXP e = make_discard_exp ( a ) ;
MAKE_exp_cast ( type_void, CONV_ELLIPSIS, e, e ) ;
return ( e ) ;
}
/*
APPLY A TRIVIAL FUNCTION
This routine applies the trivial constructor or destructor id to
the arguments args. The null expression is returned for invalid
arguments.
*/
EXP apply_trivial_func
PROTO_N ( ( id, args ) )
PROTO_T ( IDENTIFIER id X LIST ( EXP ) args )
{
EXP e = NULL_exp ;
TYPE t = DEREF_type ( id_function_etc_type ( id ) ) ;
int n = constr_kind ( id, t ) ;
switch ( n ) {
case DEFAULT_CONSTR : {
if ( LENGTH_list ( args ) == 1 ) {
/* Trivial constructor */
CLASS_TYPE ct = parent_class ( id ) ;
t = make_class_type ( ct ) ;
e = make_null_exp ( t ) ;
}
break ;
}
case DEFAULT_COPY : {
if ( LENGTH_list ( args ) == 2 ) {
/* Trivial copy constructor */
ERROR err = NULL_err ;
LIST ( TYPE ) p = DEREF_list ( type_func_mtypes ( t ) ) ;
TYPE ta = DEREF_type ( HEAD_list ( p ) ) ;
ta = DEREF_type ( type_ref_sub ( ta ) ) ;
e = DEREF_exp ( HEAD_list ( TAIL_list ( args ) ) ) ;
e = convert_reference ( e, REF_NORMAL ) ;
e = convert_class ( ta, e, &err ) ;
if ( !IS_NULL_err ( err ) ) {
err = concat_warning ( err, ERR_expr_ass_conv () ) ;
report ( crt_loc, err ) ;
}
}
break ;
}
case DEFAULT_DESTR : {
if ( LENGTH_list ( args ) == 1 ) {
/* Trivial destructor */
EXP a = DEREF_exp ( HEAD_list ( args ) ) ;
e = trivial_destr ( a ) ;
}
break ;
}
case DEFAULT_ASSIGN : {
if ( LENGTH_list ( args ) == 2 ) {
/* Trivial assignment */
EXP a = DEREF_exp ( HEAD_list ( args ) ) ;
EXP b = DEREF_exp ( HEAD_list ( TAIL_list ( args ) ) ) ;
e = make_assign_exp ( a, b, 1 ) ;
}
break ;
}
}
return ( e ) ;
}
/*
APPLY A CONSTRUCTOR
This routine applies the constructor id to the arguments args, using a
dummy expression for the object the constructor is applied to. The
definition of this object, whether an existing object or a temporary,
is filled in later.
*/
EXP apply_constr
PROTO_N ( ( id, args ) )
PROTO_T ( IDENTIFIER id X LIST ( EXP ) args )
{
TYPE t ;
EXP e, a ;
DECL_SPEC ds ;
CLASS_TYPE ct ;
IDENTIFIER cid ;
CONS_exp ( NULL_exp, args, args ) ;
/* Check for trivial constructors */
ds = DEREF_dspec ( id_storage ( id ) ) ;
if ( ds & dspec_trivial ) {
e = apply_trivial_func ( id, args ) ;
if ( !IS_NULL_exp ( e ) ) {
DESTROY_list ( args, SIZE_exp ) ;
return ( e ) ;
}
}
/* Create dummy argument */
ct = parent_class ( id ) ;
cid = DEREF_id ( ctype_name ( ct ) ) ;
MAKE_type_compound ( cv_lvalue, ct, t ) ;
COPY_id ( type_name ( t ), cid ) ;
MAKE_exp_dummy ( t, NULL_exp, LINK_NONE, NULL_off, 0, a ) ;
COPY_exp ( HEAD_list ( args ), a ) ;
/* Apply the constructor */
e = apply_func_id ( id, qual_none, NULL_graph, args ) ;
if ( IS_exp_func_id ( e ) ) {
int n = DEFAULT_USR ;
TYPE fn = DEREF_type ( id_function_etc_type ( id ) ) ;
if ( check_copy_constr ( fn, ct ) ) {
/* Mark copy constructor calls */
n = DEFAULT_COPY ;
}
e = add_constr_args ( e, ct, EXTRA_CONSTR ) ;
t = make_class_type ( ct ) ;
MAKE_exp_constr ( t, e, a, a, n, e ) ;
}
return ( e ) ;
}
/*
INITIALISATION BY CONSTRUCTOR
This routine converts the argument list args to the class type t by
constructor. The cast parameter indicates if this is an explicit
conversion. The routine is used, for example, in initialisations of
the form 't id ( args )'.
*/
EXP convert_constr
PROTO_N ( ( t, args, err, cast ) )
PROTO_T ( TYPE t X LIST ( EXP ) args X ERROR *err X unsigned cast )
{
EXP e ;
IDENTIFIER cid ;
CANDIDATE_LIST *p ;
/* Check for template parameters */
if ( dependent_conv ( t, args ) ) {
MAKE_exp_opn ( t, lex_static_Hcast, args, e ) ;
return ( e ) ;
}
/* Construct list of candidates */
p = &candidates ;
p->size = 0 ;
cid = constr_candidates ( p, t, cast ) ;
/* Perform overload resolution */
if ( p->size ) {
CANDIDATE *q = resolve_overload ( p, args, NULL_type, 0 ) ;
IDENTIFIER qid = q->func ;
unsigned rank = q->rank ;
int kind = q->kind ;
if ( rank == RANK_BEST ) {
/* Unambiguous resolution */
if ( !IS_NULL_id ( cid ) ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
if ( match_no_viable > 1 && overload_warn ) {
add_error ( err, ERR_over_match_ctor_ok ( qid ) ) ;
}
} else if ( rank == RANK_VIABLE ) {
/* Ambiguous resolution */
q = resolve_ambiguous ( p, args, NULL_type, 0 ) ;
qid = q->func ;
rank = q->rank ;
if ( !IS_NULL_id ( cid ) ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
if ( rank == RANK_TARGET ) {
ERROR err2 = ERR_over_match_ctor_target ( qid ) ;
qid = make_ambig_func ( p, qid, args, qual_none, &err2 ) ;
kind = KIND_FUNC ;
add_error ( err, err2 ) ;
} else if ( rank == RANK_VIABLE ) {
ERROR err2 = ERR_over_match_ctor_ambig ( qid ) ;
err2 = list_candidates ( err2, p, RANK_VIABLE ) ;
add_error ( err, err2 ) ;
}
} else {
/* No resolution */
if ( !IS_NULL_id ( cid ) ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
goto error_lab ;
}
use_func_id ( qid, 0, suppress_usage ) ;
if ( kind == KIND_CONSTR ) {
e = apply_constr ( qid, args ) ;
} else {
/* Can only happen for target dependent resolutions */
e = apply_func_id ( qid, qual_none, NULL_graph, args ) ;
if ( IS_exp_constr ( e ) ) {
/* Introduce temporary variable if necessary */
TYPE s = DEREF_type ( exp_type ( e ) ) ;
e = make_temporary ( s, e, NULL_exp, 0, err ) ;
e = convert_lvalue ( e ) ;
}
}
return ( e ) ;
}
/* Deal with incomplete structures */
if ( IS_NULL_id ( cid ) ) {
ERROR err2 = check_incomplete ( t ) ;
if ( !IS_NULL_err ( err2 ) ) {
add_error ( err, err2 ) ;
add_error ( err, ERR_expr_type_conv_incompl () ) ;
e = make_null_exp ( t ) ;
return ( e ) ;
}
#if LANGUAGE_C
if ( IS_NULL_list ( args ) ) {
/* C default initialisation */
e = make_null_exp ( t ) ;
return ( e ) ;
}
if ( IS_NULL_list ( TAIL_list ( args ) ) ) {
/* C copy initialisation */
EXP a = DEREF_exp ( HEAD_list ( args ) ) ;
a = convert_none ( a ) ;
a = convert_class ( t, a, err ) ;
a = remove_temporary ( a, NULL_exp ) ;
return ( a ) ;
}
#endif
}
/* No candidates */
error_lab : {
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
cid = DEREF_id ( ctype_constr ( ct ) ) ;
add_error ( err, ERR_over_match_ctor_none ( cid ) ) ;
e = make_null_exp ( t ) ;
}
return ( e ) ;
}
/*
CHECK FOR INITIALISATION BY USER-DEFINED CONVERSION
This routine checks for user-defined conversions of a to type t.
It is identical to convert_conv except that it returns the null
expression is there are no viable conversion functions.
*/
EXP convert_conv_aux
PROTO_N ( ( t, a, err, cast ) )
PROTO_T ( TYPE t X EXP a X ERROR *err X unsigned cast )
{
IDENTIFIER cid ;
CANDIDATE_LIST *p ;
LIST ( EXP ) args ;
TYPE s = DEREF_type ( exp_type ( a ) ) ;
if ( IS_type_error ( s ) ) {
EXP e = make_error_exp ( 0 ) ;
return ( e ) ;
}
/* Check for template parameters */
CONS_exp ( a, NULL_list ( EXP ), args ) ;
if ( dependent_conv ( t, args ) ) {
EXP e = cast_templ_type ( t, a, cast ) ;
DESTROY_list ( args, SIZE_exp ) ;
return ( e ) ;
}
/* Construct list of candidates */
p = &candidates ;
p->size = 0 ;
cid = conv_candidates ( p, t, s, cast ) ;
/* Perform overload resolution */
if ( p->size ) {
EXP e ;
int kind ;
DECL_SPEC ds ;
CANDIDATE *q ;
unsigned rank ;
IDENTIFIER qid ;
match_this++ ;
q = resolve_overload ( p, args, t, 0 ) ;
match_this-- ;
qid = q->func ;
rank = q->rank ;
kind = q->kind ;
if ( rank == RANK_BEST ) {
/* Unambiguous resolution */
if ( !IS_NULL_id ( cid ) ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
if ( match_no_viable > 1 && overload_warn ) {
add_error ( err, ERR_over_match_conv_ok ( qid ) ) ;
}
} else if ( rank == RANK_VIABLE ) {
/* Ambiguous resolution */
q = resolve_ambiguous ( p, args, t, 0 ) ;
qid = q->func ;
rank = q->rank ;
if ( !IS_NULL_id ( cid ) ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
if ( rank == RANK_TARGET ) {
ERROR err2 = ERR_over_match_conv_target ( s, t ) ;
qid = make_ambig_func ( p, qid, args, qual_none, &err2 ) ;
kind = KIND_FUNC ;
add_error ( err, err2 ) ;
} else if ( rank == RANK_VIABLE ) {
ERROR err2 = ERR_over_match_conv_ambig ( s, t ) ;
err2 = list_candidates ( err2, p, RANK_VIABLE ) ;
add_error ( err, err2 ) ;
}
} else {
/* No resolution */
if ( !IS_NULL_id ( cid ) ) {
swap_candidates ( p, ( unsigned ) 0 ) ;
}
DESTROY_list ( args, SIZE_exp ) ;
return ( NULL_exp ) ;
}
/* Check conversion function */
ds = DEREF_dspec ( id_storage ( qid ) ) ;
if ( ds & dspec_trivial ) {
/* Trivial copy constructor */
if ( !( ds & dspec_defn ) ) {
implicit_defn ( qid, DEFAULT_COPY ) ;
}
CONS_exp ( NULL_exp, args, args ) ;
a = apply_trivial_func ( qid, args ) ;
DESTROY_list ( args, SIZE_exp ) ;
return ( a ) ;
}
/* Apply conversion function */
use_func_id ( qid, 0, suppress_usage ) ;
if ( kind == KIND_CONSTR ) {
if ( eq_type_unqual ( t, s ) ) {
/* Force initialisation in copy constructor */
int depth = init_ref_force ;
if ( depth > 2 && IS_type_compound ( t ) ) {
TYPE fn = DEREF_type ( id_function_etc_type ( qid ) ) ;
CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
int c = check_copy_constr ( fn, ct ) ;
if ( c == 3 ) {
/* Bad copy constructor */
HASHID nm = DEREF_hashid ( id_name ( qid ) ) ;
add_error ( err, ERR_class_copy_bad ( nm ) ) ;
DESTROY_list ( args, SIZE_exp ) ;
return ( a ) ;
}
}
init_ref_force = depth + 1 ;
e = apply_constr ( qid, args ) ;
init_ref_force = depth ;
} else {
e = apply_constr ( qid, args ) ;
}
} else {
e = apply_func_id ( qid, qual_none, NULL_graph, args ) ;
if ( IS_exp_constr ( e ) ) {
/* Introduce temporary variable if necessary */
s = DEREF_type ( exp_type ( e ) ) ;
e = make_temporary ( s, e, NULL_exp, 0, err ) ;
}
}
return ( e ) ;
}
/* No candidates */
DESTROY_list ( args, SIZE_exp ) ;
return ( NULL_exp ) ;
}
/*
INITIALISATION BY USER-DEFINED CONVERSION
This routine converts the expression a to the type t by user-defined
conversions. It is used, for example, in initialisations of the form
't id = a'.
*/
EXP convert_conv
PROTO_N ( ( t, a, err, cast ) )
PROTO_T ( TYPE t X EXP a X ERROR *err X unsigned cast )
{
EXP e = convert_conv_aux ( t, a, err, cast ) ;
if ( IS_NULL_exp ( e ) ) {
/* No viable conversion functions */
TYPE s ;
a = convert_lvalue ( a ) ;
s = DEREF_type ( exp_type ( a ) ) ;
if ( !IS_type_error ( s ) && !IS_type_error ( t ) ) {
add_error ( err, check_incomplete ( t ) ) ;
if ( check_int_type ( t, btype_bool ) ) {
e = convert_boolean ( a, exp_paren_tag, err ) ;
return ( e ) ;
}
add_error ( err, ERR_expr_cast_invalid ( s, t ) ) ;
}
e = make_null_exp ( t ) ;
} else {
/* Deal with further implicit conversions */
TYPE s = DEREF_type ( exp_type ( e ) ) ;
if ( IS_type_ref ( s ) ) {
/* Reference conversion */
s = DEREF_type ( type_ref_sub ( s ) ) ;
MAKE_exp_indir ( s, e, e ) ;
}
if ( !IS_type_ref ( t ) ) {
CV_SPEC cv = DEREF_cv ( type_qual ( s ) ) ;
if ( cv & cv_lvalue ) {
/* lvalue conversion */
s = rvalue_type ( s ) ;
MAKE_exp_contents ( s, e, e ) ;
}
}
if ( !eq_type ( t, s ) ) {
cast = ( CAST_STANDARD | CAST_IMPLICIT ) ;
e = cast_exp ( t, e, err, cast ) ;
}
}
return ( e ) ;
}
/*
GENERIC INITIALISATION BY USER-DEFINED CONVERSION
This routine selects a user-defined conversion from the expression a
to a type with category matching kind. It returns the null expression
if no such conversion exists.
*/
EXP convert_gen
PROTO_N ( ( kind, a, err ) )
PROTO_T ( unsigned kind X EXP a X ERROR *err )
{
EXP e = NULL_exp ;
TYPE t = DEREF_type ( exp_type ( a ) ) ;
/* Construct list of candidates */
CANDIDATE_LIST *p = &candidates ;
p->size = 0 ;
gen_candidates ( p, t, kind ) ;
/* Perform overload resolution */
if ( p->size ) {
CANDIDATE *q ;
unsigned rank ;
IDENTIFIER qid ;
LIST ( EXP ) args ;
CONS_exp ( a, NULL_list ( EXP ), args ) ;
match_this++ ;
q = resolve_overload ( p, args, NULL_type, 0 ) ;
match_this-- ;
qid = q->func ;
rank = q->rank ;
if ( rank == RANK_BEST ) {
/* Unambiguous resolution */
if ( match_no_viable > 1 && overload_warn ) {
add_error ( err, ERR_over_match_conv_ok ( qid ) ) ;
}
} else if ( rank == RANK_VIABLE ) {
/* Ambiguous resolution */
q = resolve_ambiguous ( p, args, NULL_type, 0 ) ;
qid = q->func ;
rank = q->rank ;
if ( rank != RANK_BEST ) {
/* Can't be target dependent */
ERROR err2 = ERR_over_match_conv_dup ( t ) ;
err2 = list_candidates ( err2, p, RANK_VIABLE ) ;
add_error ( err, err2 ) ;
}
} else {
/* No viable candidates */
return ( e ) ;
}
use_func_id ( qid, 0, suppress_usage ) ;
e = apply_func_id ( qid, qual_none, NULL_graph, args ) ;
e = convert_reference ( e, REF_NORMAL ) ;
e = convert_lvalue ( e ) ;
}
/* No candidates */
return ( e ) ;
}