Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.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,
* 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.
* 3. Neither the name of The TenDRA Project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 COPYRIGHT HOLDERS OR
* CONTRIBUTORS 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.
*
* $Id$
*/
/*
Crown Copyright (c) 1997
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 "hashid_ops.h"
#include "graph_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 "access.h"
#include "basetype.h"
#include "cast.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "constant.h"
#include "convert.h"
#include "copy.h"
#include "derive.h"
#include "expression.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "member.h"
#include "namespace.h"
#include "operator.h"
#include "parse.h"
#include "predict.h"
#include "statement.h"
#include "syntax.h"
#include "template.h"
#include "token.h"
/*
FORM THE ADDRESS OF AN EXPRESSION
This routine forms the address of the expression e. If e is not an
lvalue then a temporary is introduced and its address is returned.
*/
static EXP
member_address(EXP e)
{
TYPE t = DEREF_type(exp_type(e));
CV_SPEC qual = DEREF_cv(type_qual(t));
if (qual & cv_lvalue) {
t = rvalue_type(t);
} else {
ERROR err = NULL_err;
e = make_temporary(t, e, NULL_exp, 0, &err);
if (!IS_NULL_err(err)) {
report(crt_loc, err);
}
}
MAKE_type_ptr(cv_none, t, t);
MAKE_exp_address(t, e, e);
return (e);
}
/*
BEGIN A FIELD SELECTOR EXPRESSION
This routine begins the construction of the field selector expressions
'a.b' and 'a->b' (as indicated by op). The base type of a (after
removing pointers in the '->' case) is returned via pt. Note that
it is not checked that this is a class type at this stage because the
explicit destructor notation can apply to any type. Note that '->'
can be overloaded (albeit in a different way to most other operations)
but '.' can't.
*/
EXP
begin_field_exp(int op, EXP a, TYPE *pt, NAMESPACE *pns)
{
TYPE t, s;
unsigned c;
int ptr = 0;
unsigned tag;
/* Do operand conversion */
if (op == lex_arrow) {
/* Check for overloading with '->' */
#if LANGUAGE_CPP
int go = 1;
LIST(TYPE) prev = NULL_list(TYPE);
do {
/* Perform reference conversions */
a = convert_reference(a, REF_NORMAL);
t = DEREF_type(exp_type(a));
c = type_category(&t);
if (overload_depth) {
break;
}
if (IS_TYPE_TEMPL(c)) {
/* Template parameter type */
EXP e;
MAKE_exp_op(t, op, a, a, e);
cache_lookup = 0;
*pns = NULL_nspace;
*pt = t;
return (e);
}
if (IS_TYPE_OVERLOAD(c)) {
EXP e = unary_overload(lex_arrow, a);
if (IS_NULL_exp(e)) {
/* '->' isn't overloaded */
go = 0;
} else {
/* '->' is overloaded, try again */
LIST(TYPE) p = prev;
while (!IS_NULL_list(p)) {
/* Check for cycles */
s = DEREF_type(HEAD_list(p));
if (eq_type(s, t)) {
ERROR err = ERR_over_match_oper_arrow();
report(crt_loc, err);
go = 0;
break;
}
p = TAIL_list(p);
}
CONS_type(t, prev, prev);
a = e;
}
} else {
/* '->' can't be overloaded */
go = 0;
}
} while (go);
DESTROY_list(prev, SIZE_type);
#else
/* Perform reference conversions */
a = convert_reference(a, REF_NORMAL);
t = DEREF_type(exp_type(a));
c = type_category(&t);
#endif
/* Do lvalue conversions if necessary */
if (IS_TYPE_ADDRESS(c)) {
a = convert_lvalue(a);
t = DEREF_type(exp_type(a));
}
} else {
/* For '.' just do reference conversions */
a = convert_reference(a, REF_NORMAL);
t = DEREF_type(exp_type(a));
#if LANGUAGE_CPP
c = type_category(&t);
if (IS_TYPE_TEMPL(c)) {
/* Template parameter type */
EXP e;
MAKE_exp_op(t, op, a, a, e);
cache_lookup = 0;
*pns = NULL_nspace;
*pt = t;
return (e);
}
#endif
}
/* Check operand type */
tag = TAG_type(t);
if (tag == type_token_tag) {
t = expand_type(t, 0);
tag = TAG_type(t);
}
if (tag == type_ptr_tag) {
s = DEREF_type(type_ptr_sub(t));
tag = TAG_type(s);
if (tag == type_token_tag) {
if (op == lex_arrow && is_templ_type(s)) {
/* Template parameter type */
EXP e;
MAKE_exp_op(s, op, a, a, e);
cache_lookup = 0;
*pns = NULL_nspace;
*pt = s;
return (e);
}
s = expand_type(s, 0);
tag = TAG_type(s);
}
ptr = 1;
} else {
s = t;
}
if (tag == type_compound_tag) {
/* Operand is a class or a pointer to a class */
ERROR err;
CLASS_TYPE ct = DEREF_ctype(type_compound_defn(s));
NAMESPACE ns = DEREF_nspace(ctype_member(ct));
/* Check that the correct operator has been used */
if (op == lex_arrow) {
if (!ptr) {
report(crt_loc, ERR_expr_ref_arrow_dot(t));
}
} else {
if (ptr) {
report(crt_loc, ERR_expr_ref_dot_arrow(t));
}
}
/* Check that the class is complete */
err = check_incomplete(s);
cache_lookup = 0;
if (!IS_NULL_err(err)) {
/* Incomplete class */
BASE_TYPE key = find_class_key(ct);
err = concat_error(err, ERR_expr_ref_incompl(op, key));
report(crt_loc, err);
ns = NULL_nspace;
s = type_error;
} else {
/* Complete class */
add_namespace(ns);
}
*pns = ns;
*pt = s;
/* Check for null pointers */
if (IS_exp_null(a) && ptr) {
report(crt_loc, ERR_expr_unary_op_indir_null(op));
}
} else {
/* Bad operand type */
if (!IS_type_error(s)) {
if (op == lex_arrow) {
if (!ptr) {
string key = find_vocab(lex_class);
report(crt_loc,
ERR_expr_ref_arrow_op(t, key));
s = type_error;
}
} else {
s = t;
}
}
cache_lookup = 0;
*pns = NULL_nspace;
*pt = s;
}
return (a);
}
/*
CREATE A FIELD SELECTOR EXPRESSION
This routine completes the field selector expression constructed by
begin_field_exp. The a arguments is the outputs of this routine,
ns represents the class namespace of the type of a, op indicated which
selector is being used, and fld indicates the field being selected.
*/
static EXP
apply_field_op(int op, EXP a, NAMESPACE ns, IDENTIFIER fld, int templ)
{
EXP e;
GRAPH gr;
HASHID nm;
TYPE t, s;
string key;
CV_SPEC qual;
ERROR err = NULL_err;
LIST(TOKEN) args = NULL_list(TOKEN);
/* Find the type of a */
s = DEREF_type(exp_type(a));
qual = find_cv_qual(s);
/* Check for template members */
if (templ && IS_id_undef(fld)) {
TYPE form = DEREF_type(id_undef_form(fld));
if (!IS_NULL_type(form) && IS_type_token(form)) {
fld = DEREF_id(type_token_tok(form));
args = DEREF_list(type_token_args(form));
templ = 2;
}
}
/* Check that fld is a member of ns */
gr = is_subfield(ns, fld);
if (!IS_NULL_graph(gr)) {
IDENTIFIER orig = fld;
fld = search_subfield(ns, gr, orig);
if (templ == 2) {
/* Apply template arguments */
fld = apply_template(fld, args, 0, 0);
}
switch (TAG_id(fld)) {
case id_stat_member_tag:
/* Static data members */
e = make_id_exp(fld);
e = join_exp(a, e);
return (e);
case id_mem_func_tag:
case id_stat_mem_func_tag:
/* Member functions */
if (!templ) {
IDENTIFIER tid = find_template(fld, 0);
if (!IS_NULL_id(tid)) {
/* Should have 'template' prefix */
report(crt_loc,
ERR_temp_names_mem(fld));
}
}
/* Form '*a' for pointers */
if (IS_type_ptr(s)) {
s = DEREF_type(type_ptr_sub(s));
s = lvalue_type(s);
MAKE_exp_indir(s, a, a);
}
/* Construct the result */
e = make_id_exp(fld);
t = DEREF_type(exp_type(e));
MAKE_exp_call(t, e, a, gr, e);
return (e);
case id_member_tag: {
/* Non-static data members */
TYPE p;
OFFSET off;
int virt = 0;
/* Allow for pointer types */
if (IS_type_ptr(s)) {
/* Form '*a' for pointers */
s = DEREF_type(type_ptr_sub(s));
qual = DEREF_cv(type_qual(s));
qual |= cv_lvalue;
} else {
/* Form '&a' for non-pointers */
a = member_address(a);
}
/* Find result type */
use_id(fld, 0);
t = DEREF_type(id_member_type(fld));
if (!IS_type_ref(t)) {
CV_SPEC cv = find_cv_qual(t);
cv &= cv_qual;
if (qual & cv_const) {
DECL_SPEC ds =
DEREF_dspec(id_storage(fld));
if (!(ds & dspec_mutable)) {
/* 'mutable' cancels out
* 'const' */
cv |= cv_const;
}
}
if (qual & cv_volatile) {
cv |= cv_volatile;
}
t = qualify_type(t, cv, 0);
}
/* Check for virtual base members */
gr = DEREF_graph(id_member_base(fld));
if (!IS_NULL_graph(gr)) {
DECL_SPEC acc = DEREF_dspec(graph_access(gr));
if (acc & dspec_mutable) {
/* Contained in virtual base */
if (know_type(a) != 1) {
virt = 1;
}
}
}
/* Form the result */
off = DEREF_off(id_member_off(fld));
MAKE_type_ptr(cv_none, t, p);
MAKE_exp_add_ptr(p, a, off, virt, e);
if (qual & cv_lvalue) {
/* The result is an lvalue if s is */
t = lvalue_type(t);
MAKE_exp_indir(t, e, e);
} else {
MAKE_exp_contents(t, e, e);
}
return (e);
}
case id_enumerator_tag: {
/* Enumerator members */
NAT n;
unsigned etag;
use_id(fld, 0);
e = DEREF_exp(id_enumerator_value(fld));
DECONS_exp_int_lit(t, n, etag, e);
MAKE_exp_int_lit(t, n, etag, e);
e = join_exp(a, e);
return (e);
}
case id_ambig_tag:
/* Ambiguous members */
IGNORE report_ambiguous(fld, 0, 1, 0);
e = make_error_exp(1);
e = join_exp(a, e);
return (e);
case id_class_name_tag:
case id_class_alias_tag:
case id_enum_name_tag:
case id_enum_alias_tag:
case id_type_alias_tag:
/* Type members */
err = ERR_expr_ref_type(fld);
break;
case id_undef_tag:
/* Undefined members */
nm = DEREF_hashid(id_name(fld));
err = ERR_lookup_qual_undef(nm, ns);
break;
}
}
/* Report any errors */
nm = DEREF_hashid(id_name(fld));
if (IS_hashid_destr(nm)) {
/* Pseudo-destructor call */
destroy_error(err, 1);
return (NULL_exp);
}
key = find_vocab(lex_class);
if (IS_NULL_err(err)) {
err = ERR_lookup_qual_bad(fld, ns);
}
err = concat_error(err, ERR_expr_ref_select(op, key));
report(crt_loc, err);
e = make_error_exp(1);
e = join_exp(a, e);
return (e);
}
/*
COMPLETE A FIELD SELECTOR EXPRESSION
This routine gives the main interface to apply_field_op, allowing for
the error case and adjusting the name look-up stack. a is the return
value from begin_field_exp, while t is the returned base type. The
field identifier is given by fld, this being prefixed by template if
templ is true.
*/
EXP
end_field_exp(int op, EXP a, TYPE t, NAMESPACE ns, IDENTIFIER fld, int templ)
{
EXP e;
QUALIFIER cq = crt_id_qualifier;
if (cq == qual_full || cq == qual_top) {
/* Bad field selector */
report(crt_loc, ERR_expr_ref_qual(cq, fld));
}
if (!IS_NULL_nspace(ns)) {
/* Class types */
remove_namespace();
cache_lookup = old_cache_lookup;
if (cq != qual_none) {
templ = 1;
}
e = apply_field_op(op, a, ns, fld, templ);
if (!IS_NULL_exp(e)) {
return (e);
}
} else {
cache_lookup = old_cache_lookup;
}
if (IS_exp_op(a)) {
/* Template parameter types */
EXP b;
if (cq == qual_none) {
fld = underlying_id(fld);
}
MAKE_exp_member(t, fld, qual_nested, b);
COPY_exp(exp_op_arg2(a), b);
e = a;
} else {
/* Other types */
if (!IS_type_error(t)) {
HASHID nm = DEREF_hashid(id_name(fld));
if (IS_hashid_destr(nm)) {
/* Allow for pseudo-destructor call */
TYPE fn = type_func_void;
TYPE r = DEREF_type(hashid_destr_type(nm));
TYPE s = expand_type(r, 2);
if (!EQ_type(s, r)) {
nm = lookup_destr(s, NULL_id);
fld = DEREF_id(hashid_id(nm));
t = expand_type(t, 2);
r = s;
}
/* NOT YET IMPLEMENTED - scalar type */
if (!eq_type_unqual(t, r) &&
!IS_type_error(r)) {
/* Destructor types should match */
report(crt_loc,
ERR_expr_pseudo_obj(nm, t));
}
MAKE_exp_undeclared(fn, fld, cq, e);
MAKE_exp_call(fn, e, a, NULL_graph, e);
} else {
/* Anything else is illegal */
string key = find_vocab(lex_class);
TYPE s = DEREF_type(exp_type(a));
if (op == lex_arrow) {
report(crt_loc,
ERR_expr_ref_arrow_op(s, key));
} else {
report(crt_loc,
ERR_expr_ref_dot_op(s, key));
}
e = make_error_exp(1);
}
} else {
e = make_error_exp(1);
}
}
return (e);
}
/*
CONSTRUCT A FIELD SELECTOR EXPRESSION
This routine is an alternative interface to the field selector
expression routines used in the instantiation of template functions.
It constructs 'a.b' or 'a->b' depending on op, where b is an
identifier expression giving the member name.
*/
EXP
make_field_exp(int op, EXP a, EXP b)
{
TYPE t = NULL_type;
NAMESPACE ns = NULL_nspace;
QUALIFIER cq = crt_id_qualifier;
EXP e = begin_field_exp(op, a, &t, &ns);
IDENTIFIER fld = DEREF_id(exp_member_id(b));
QUALIFIER qual = DEREF_qual(exp_member_qual(b));
crt_id_qualifier = qual;
if (!IS_NULL_nspace(ns)) {
/* Rescan field for class namespaces */
fld = rescan_id(fld, qual, 0);
}
e = end_field_exp(op, e, t, ns, fld, 1);
crt_id_qualifier = cq;
return (e);
}
/*
CONSTRUCT A MEMBER SELECTOR EXPRESSION
This routine constructs the member selector expressions 'a.*b' and
'a->*b' (as indicated by op). Note that '->*' can be overloaded but
'.*' can't.
*/
#if LANGUAGE_CPP
EXP
make_member_exp(int op, EXP a, EXP b)
{
EXP e;
int is_ptr;
CV_SPEC qual;
TYPE ta, tap;
unsigned sa, sb;
TYPE tb, tbr, tbp;
CLASS_TYPE ca, cb;
/* Do reference conversions */
a = convert_reference(a, REF_NORMAL);
b = convert_reference(b, REF_NORMAL);
/* Find operand types */
ta = DEREF_type(exp_type(a));
sa = type_category(&ta);
tb = DEREF_type(exp_type(b));
sb = type_category(&tb);
/* Check for overloading */
if (op == lex_arrow_Hstar) {
if (IS_TYPE_OVERLOAD(sa) || IS_TYPE_OVERLOAD(sb)) {
if (overload_depth == 0) {
e = binary_overload(op, a, b);
return (e);
}
}
} else {
if (IS_TYPE_TEMPL(sa) || IS_TYPE_TEMPL(sb)) {
if (overload_depth == 0) {
e = binary_overload(op, a, b);
return (e);
}
}
}
/* Do lvalue conversion */
if (IS_TYPE_ADDRESS(sb)) {
b = convert_lvalue(b);
tb = DEREF_type(exp_type(b));
}
/* Second operand must be pointer to member */
if (!IS_TYPE_PTR_MEM(sb)) {
if (!IS_TYPE_ERROR(sa) && !IS_TYPE_ERROR(sb)) {
report(crt_loc, ERR_expr_mptr_oper_op2(op, tb));
}
e = make_error_exp(1);
return (e);
}
cb = DEREF_ctype(type_ptr_mem_of(tb));
tbr = DEREF_type(type_ptr_mem_sub(tb));
if (in_template_decl) {
/* Allow for template parameters */
TYPE fb = DEREF_type(ctype_form(cb));
if (is_templ_type(fb)) {
MAKE_exp_op(fb, op, a, b, e);
return (e);
}
}
/* Check first operand type */
if (IS_type_ptr(ta)) {
a = convert_lvalue(a);
ta = DEREF_type(exp_type(a));
tap = DEREF_type(type_ptr_sub(ta));
qual = DEREF_cv(type_qual(tap));
qual |= cv_lvalue;
is_ptr = 1;
} else {
tap = ta;
qual = DEREF_cv(type_qual(ta));
is_ptr = 0;
}
if (IS_type_compound(tap)) {
ERROR err;
ca = DEREF_ctype(type_compound_defn(tap));
if (eq_ctype(ca, cb)) {
/* Equal base types */
/* EMPTY */
} else {
GRAPH gr = find_base_class(ca, cb, 1);
if (!IS_NULL_graph(gr)) {
/* cb is a base class of ca */
OFFSET off;
err = check_ambig_base(gr);
if (!IS_NULL_err(err)) {
ERROR err2 =
ERR_expr_mptr_oper_ambig(op);
err = concat_error(err, err2);
report(crt_loc, err);
}
check_base_access(gr);
off = DEREF_off(graph_off(gr));
tap = make_class_type(cb);
MAKE_type_ptr(cv_none, tap, ta);
if (is_ptr) {
a = make_base_cast(ta, a, off);
} else {
a = member_address(a);
a = make_base_cast(ta, a, off);
ta = lvalue_type(tap);
MAKE_exp_indir(ta, a, a);
}
} else {
/* cb is not a base class of ca */
goto error_lab;
}
}
err = check_incomplete(tap);
if (!IS_NULL_err(err)) {
/* Class should be complete */
err = concat_error(err, ERR_expr_mptr_oper_compl(op));
report(crt_loc, err);
}
} else {
/* Invalid base type */
if (!IS_type_error(tap)) {
error_lab: {
ERROR err;
IDENTIFIER cid = DEREF_id(ctype_name(cb));
if (op == lex_arrow_Hstar) {
err = ERR_expr_mptr_oper_arrow_op(cid, ta);
} else {
err = ERR_expr_mptr_oper_dot_op(cid, ta);
}
report(crt_loc, err);
}
}
e = make_error_exp(1);
return (e);
}
/* Correct base type */
if (op == lex_arrow_Hstar) {
if (!is_ptr) {
report(crt_loc, ERR_expr_mptr_oper_arrow_dot(ta));
}
} else {
if (is_ptr) {
report(crt_loc, ERR_expr_mptr_oper_dot_arrow(ta));
}
}
/* Check for null pointers */
if (IS_exp_null(a) && is_ptr) {
report(crt_loc, ERR_expr_unary_op_indir_null(op));
}
if (IS_exp_null(b)) {
report(crt_loc, ERR_expr_mptr_oper_null(op));
}
/* Construct the result */
if (IS_type_func(tbr)) {
/* Member functions */
if (is_ptr) {
ta = lvalue_type(ta);
MAKE_exp_indir(ta, a, a);
}
MAKE_exp_call(tbr, b, a, NULL_graph, e);
} else {
/* Data members */
OFFSET off;
CV_SPEC cv1 = (qual & cv_qual);
if (cv1) {
/* The result type inherits both qualifiers */
CV_SPEC cv2 = DEREF_cv(type_qual(tbr));
tbr = qualify_type(tbr,(cv1 | cv2), 0);
}
if (!is_ptr) {
/* For non-pointers construct '&a' */
a = member_address(a);
}
MAKE_off_ptr_mem(b, off);
MAKE_type_ptr(cv_none, tbr, tbp);
MAKE_exp_add_ptr(tbp, a, off, 0, e);
if (qual & cv_lvalue) {
/* The result is an lvalue if tap is */
tbr = lvalue_type(tbr);
MAKE_exp_indir(tbr, e, e);
} else {
MAKE_exp_contents(tbr, e, e);
}
}
return (e);
}
#endif
/*
LOOK UP A MEMBER IN A MEMBER FUNCTION DEFINITION
This routine looks up the member fld. In a member function definition
the routine returns 'this->fld' for non-static data members and the
parameter corresponding to 'this' for non-static function members,
otherwise the null expression is returned.
*/
EXP
make_this_field(IDENTIFIER fld)
{
IDENTIFIER fn = crt_func_id;
if (in_function_defn && IS_id_mem_func(fn)) {
NAMESPACE ns = DEREF_nspace(id_parent(fn));
GRAPH gr = is_subfield(ns, fld);
if (!IS_NULL_graph(gr)) {
switch (TAG_id(fld)) {
case id_member_tag:
case id_mem_func_tag: {
EXP e;
TYPE t;
IDENTIFIER id = this_param(fn, 1);
if (in_default_arg) {
/* Can't use in default argument */
ERROR err =
ERR_dcl_fct_default_param(fld);
report(crt_loc, err);
}
e = DEREF_exp(id_parameter_init(id));
t = DEREF_type(exp_type(e));
MAKE_exp_copy(t, e, e);
e = apply_field_op(lex_arrow, e, ns, fld, 1);
return (e);
}
}
}
}
return (NULL_exp);
}
/*
DECOMPOSE A BITFIELD OFFSET
This routine decomposes the bitfield member offset off into its member
component, which is returned, and its base class component, which is
assigned to off.
*/
OFFSET
decons_bitf_off(OFFSET *off)
{
OFFSET off1 = *off;
if (!IS_NULL_off(off1) && IS_off_plus(off1)) {
*off = DEREF_off(off_plus_arg1(off1));
off1 = DEREF_off(off_plus_arg2(off1));
} else {
*off = NULL_off;
}
return (off1);
}
/*
DECOMPOSE A BITFIELD EXPRESSION
This routine decomposes the lvalue bitfield expression e into its
bitfield member offset, which is returned, and its class pointer
component, which is assigned to e.
*/
OFFSET
decons_bitf_exp(EXP *e)
{
EXP a = *e;
OFFSET off = NULL_off;
switch (TAG_exp(a)) {
case exp_indir_tag: {
/* Simple field access */
a = DEREF_exp(exp_indir_ptr(a));
if (IS_exp_add_ptr(a)) {
TYPE t;
int virt;
OFFSET off1;
DECONS_exp_add_ptr(t, a, off1, virt, a);
UNUSED(t);
t = DEREF_type(exp_type(a));
off = decons_bitf_off(&off1);
if (!IS_NULL_off(off1)) {
MAKE_exp_add_ptr(t, a, off1, virt, a);
}
if (IS_exp_address(a)) {
a = DEREF_exp(exp_address_arg(a));
} else {
if (IS_type_ptr(t)) {
t = DEREF_type(type_ptr_sub(t));
}
t = lvalue_type(t);
MAKE_exp_indir(t, a, a);
}
*e = a;
}
break;
}
case exp_comma_tag: {
/* Comma expression */
TYPE t = DEREF_type(exp_type(a));
LIST(EXP) p = DEREF_list(exp_comma_args(a));
LIST(EXP) q = NULL_list(EXP);
while (!IS_NULL_list(p)) {
EXP b = DEREF_exp(HEAD_list(p));
p = TAIL_list(p);
if (IS_NULL_list(p)) {
off = decons_bitf_exp(&b);
t = DEREF_type(exp_type(b));
}
CONS_exp(b, q, q);
}
q = REVERSE_list(q);
MAKE_exp_comma(t, q, a);
*e = a;
break;
}
case exp_if_stmt_tag: {
/* Conditional expression */
TYPE t;
EXP c = DEREF_exp(exp_if_stmt_cond(a));
EXP b1 = DEREF_exp(exp_if_stmt_true_code(a));
EXP b2 = DEREF_exp(exp_if_stmt_false_code(a));
OFFSET off1 = decons_bitf_exp(&b1);
OFFSET off2 = decons_bitf_exp(&b2);
if (eq_offset(off1, off2, 0)) {
off = off1;
} else {
/* NOT YET IMPLEMENTED */
off = off1;
}
t = DEREF_type(exp_type(b1));
MAKE_exp_if_stmt(t, c, b1, b2, NULL_id, a);
*e = a;
break;
}
}
return (off);
}
/*
SET MEMBER OFFSET LOOK-UP NAMESPACE
This routine sets the look-up namespace to that for the class type t.
If t is not a class type then the null namespace is returned.
*/
NAMESPACE
offset_nspace(TYPE t)
{
NAMESPACE ns;
if (IS_type_compound(t)) {
CLASS_TYPE ct = DEREF_ctype(type_compound_defn(t));
complete_class(ct, 1);
ns = DEREF_nspace(ctype_member(ct));
cache_lookup = 0;
add_namespace(ns);
} else {
ns = NULL_nspace;
cache_lookup = 0;
}
return (ns);
}
/*
FIND A MEMBER OFFSET
This routine finds the offset of the member id of the class type t.
ns gives the namespace associated with t. The member type is
returned via pt. id must denote a simple, non-virtual data member.
*/
OFFSET
offset_member(TYPE t, IDENTIFIER id, TYPE *pt, NAMESPACE ns, int adjust)
{
if (!IS_NULL_nspace(ns)) {
GRAPH gr;
if (adjust) {
remove_namespace();
cache_lookup = old_cache_lookup;
}
gr = is_subfield(ns, id);
if (!IS_NULL_graph(gr)) {
IDENTIFIER fld = search_subfield(ns, gr, id);
switch (TAG_id(fld)) {
case id_member_tag: {
/* Data members */
TYPE s = DEREF_type(id_member_type(fld));
OFFSET off = DEREF_off(id_member_off(fld));
gr = DEREF_graph(id_member_base(fld));
if (!IS_NULL_graph(gr)) {
/* Check for virtual bases */
DECL_SPEC acc =
DEREF_dspec(graph_access(gr));
if (acc & dspec_mutable) {
goto default_lab;
}
}
use_id(fld, 0);
*pt = s;
return (off);
}
case id_ambig_tag:
/* Ambiguous members */
IGNORE report_ambiguous(fld, 0, 1, 0);
break;
case id_undef_tag: {
/* Undefined members */
HASHID nm = DEREF_hashid(id_name(fld));
report(crt_loc, ERR_lookup_qual_undef(nm, ns));
break;
}
default:
default_lab: {
/* Other members */
ERROR err = ERR_expr_const_off_mem(fld);
err = concat_error(err, ERR_token_mem_off());
report(crt_loc, err);
break;
}
}
} else {
/* id is not a member of ns */
report(crt_loc, ERR_lookup_qual_bad(id, ns));
}
} else {
if (!IS_type_error(t)) {
/* t is not a class type */
string key = find_vocab(lex_class);
report(crt_loc, ERR_expr_ref_dot_op(t, key));
}
cache_lookup = old_cache_lookup;
}
*pt = type_error;
return (NULL_off);
}
/*
FIND AN INDEX OFFSET
This routine finds the offset of the eth member of the array type t.
The member type is returned via pt. e must denote an integer constant
expression.
*/
OFFSET
offset_index(TYPE t, EXP e, TYPE *pt)
{
if (IS_type_array(t)) {
OFFSET off;
ERROR err = NULL_err;
TYPE s = DEREF_type(type_array_sub(t));
IGNORE make_nat_exp(e, &err);
if (!IS_NULL_err(err)) {
err = concat_error(err, ERR_expr_const_off_dim());
err = concat_error(err, ERR_token_mem_off());
report(crt_loc, err);
}
check_bounds(lex_array_Hop, t, e);
off = make_off_mult(s, e, 0);
*pt = s;
return (off);
}
if (!IS_type_error(t)) {
ERROR err = ERR_expr_const_off_array(t);
err = concat_error(err, ERR_token_mem_off());
report(crt_loc, err);
}
*pt = type_error;
return (NULL_off);
}
/*
ADD TWO OFFSETS
This routine adds the two offsets a and b, either of which may be
null.
*/
OFFSET
offset_add(OFFSET a, OFFSET b)
{
OFFSET off;
if (is_zero_offset(a)) {
return (b);
}
if (is_zero_offset(b)) {
return (a);
}
MAKE_off_plus(a, b, off);
return (off);
}