/*ident "@(#)cls4:src/typ.c 1.18" */ /******************************************************************************* C++ source for the C++ Language System, Release 3.0. This product is a new release of the original cfront developed in the computer science research center of AT&T Bell Laboratories. Copyright (c) 1993 UNIX System Laboratories, Inc. Copyright (c) 1991, 1992 AT&T and UNIX System Laboratories, Inc. Copyright (c) 1984, 1989, 1990 AT&T. All Rights Reserved. THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System Laboratories, Inc. The copyright notice above does not evidence any actual or intended publication of such source code. *******************************************************************************/ #include "cfront.h" #include "size.h" Pbase short_type; Pbase int_type; Pbase char_type; Pbase long_type; Pbase uchar_type; Pbase ushort_type; Pbase uint_type; Pbase ulong_type; Pbase zero_type; Pbase float_type; Pbase double_type; Pbase ldouble_type; Pbase void_type; Pbase any_type; Ptype Pint_type; Ptype Pchar_type; Ptype Pvoid_type; Ptype Pfctvec_type; Ptable gtbl; Ptable ptbl; Pname Cdcl; Pstmt Cstmt; bit new_type; Pclass type::classtype() { return (base==COBJ)?Pclass(Pbase(this)->b_name->tp) : (error('i',"T::classtype(): %k cobjX",base),Pclass(0)); } Ptype np_promote(TOK oper, TOK r1, TOK r2, Ptype t1, Ptype t2, TOK p, bit perr) /* an arithmetic operator "oper" is applied to "t1" and "t2", types t1 and t2 has been checked and belongs to catagories "r1" and "r2", respectively: A ANY Z ZERO I CHAR, SHORT, INT, LONG, FIELD, or EOBJ F FLOAT DOUBLE LDOUBLE P PTR (to something) or VEC (of something) test for compatability of the operands, if (p) return the promoted result type */ { if (r2 == 'A') return t1; switch (r1) { case 'A': return t2; case 'Z': switch (oper) { case ASMOD: case ASAND: case ASOR: case ASLS: case ASRS: case ASPLUS: case ASMINUS: return any_type; } switch (r2) { case 'Z': return int_type; case 'F': switch (oper) { case MOD: case AND: case ER: case OR: case LS: case RS: return any_type; } // no break case 'I': switch (oper) { case DEREF: return any_type; case LS: // result type is that of promoted left op case RS: return int_type; } return (p) ? Pbase(t2->skiptypedefs())->arit_conv(0) : 0; case 'P': switch (oper) { case MOD: case AND: case ER: case OR: case LS: case RS: case GE: case GT: case LE: case LT: return any_type; case PLUS: case ASPLUS: if(t2!=Pvoid_type) break; case EQ: case NE: case QUEST: break; default: return any_type; } return t2; case FCT: switch (oper) { case QUEST: return any_type; case EQ: case NE: return t2; } if(perr) error("zero%kF",oper); return any_type; default: error('i',"zero(%d)",r2); } case 'I': switch (r2) { case 'Z': t2 = 0; switch (oper) { case DEREF: return any_type; } return (p) ? Pbase(t1->skiptypedefs())->arit_conv(Pbase(t2)) : 0; case 'F': switch (oper) { case MOD: case AND: case ER: case OR: case LS: case RS: case ASMOD: case ASAND: case ASOR: case ASLS: case ASRS: return any_type; } // no break; case 'I': switch (oper) { case DEREF: return any_type; case LS: // result type is that of promoted left op case RS: return (p) ? Pbase(t1->skiptypedefs())->arit_conv(Pbase(0)) : 0; } return (p) ? Pbase(t1->skiptypedefs())->arit_conv(Pbase(t2)) : 0; case 'P': switch (oper) { case DEREF: break; case PLUS: case ASPLUS: if(t2!=Pvoid_type) break; default: if(perr) error("int%kP",oper); return any_type; } return t2; case FCT: if(perr) error("int%kF",oper); return any_type; default: error('i',"int(%d)",r2); return any_type; } case 'F': switch (oper) { case MOD: case ASMOD: case AND: case ER: case OR: case ASAND: case ASOR: case LS: case RS: case ASLS: case ASRS: return any_type; } switch (r2) { case 'Z': t2 = 0; case 'I': case 'F': if(oper==DEREF) return any_type; return (p) ? Pbase(t1->skiptypedefs())->arit_conv(Pbase(t2)) : 0; case 'P': if(perr) error("float%kP",oper); return any_type; case FCT: if(perr) error("float%kF",oper); return any_type; default: error('i',"float(%d)",r2); return any_type; } case 'P': switch(oper) { case LS: case RS: case ASLS: case ASRS: case MOD: case ASMOD: case ER: case OR: case ASOR: case AND: case ASAND: case DIV: case MUL: return any_type; } switch (r2) { case 'Z': switch (oper) { case GE: case GT: case LE: case LT: return any_type; } return t1; case 'I': switch (oper) { case DEREF: case PLUS: case MINUS: case ASPLUS: case ASMINUS: if (t1->check(Pvoid_type,0)==0) { return any_type; } break; default: if(perr) error("P%k int",oper); return any_type; } return t1; case 'F': if(perr) error("P%k float",oper); return any_type; case 'P': if (t1->check(t2,ASSIGN)) { Ptype tt1 = t1->is_ptr()->typ->skiptypedefs(); Ptype tt2 = t2->is_ptr()->typ->skiptypedefs(); switch (oper) { case EQ: case NE: case LE: case GE: case GT: case LT: case MINUS: case QUEST: if (tt1 && tt2 && tt1->base==COBJ && tt2->base==COBJ ) { Pclass c1=tt1->classtype(); Pclass c2=tt2->classtype(); if (c1 && c2 && c1->baseof(c2) || c2->baseof(c1) ) goto zz; } if (t2->check(t1,ASSIGN) == 0) { if (oper == QUEST) return t2; goto zz; } break; case REFMUL: { Pname cn = tt1->is_cl_obj(); if (cn && tt2->base == FCT && same_class(Pclass(cn->tp),Pfct(tt2)->memof) ) return t2; } } if(perr) error("T mismatch:%t %k%t",t1,oper,t2); return any_type; } zz: switch (oper) { case MINUS: return (t2!=Pvoid_type) ? int_type : any_type; case ASMINUS: if(perr) error("P -=P"); return any_type; case PLUS: if(perr) error("P +P"); return any_type; case ASPLUS: if(perr) error("P +=P"); return any_type; case MOD: case ASMOD: case AND: case ER: case OR: case ASAND: case ASOR: case ASLS: case ASRS: case LS: case RS: case DEREF: return any_type; default: return t1; } case FCT: return t1; default: error('i',"P(%d)",r2); } case FCT: if(oper == QUEST) { switch (r2) { case 'Z': return any_type; case 'P': return t2; case 'I': case 'F': if(perr) error("F%k%t",oper,t2); default: return t1; } } if(Pfct(t1)->memof && r2=='P' && t2->memptr()) return t2; if((oper == EQ || oper == NE) && r2=='Z') return t1; if(perr) error("F%k%t",oper,t2); return any_type; default: error('i',"np_promote(%d,%d)",r1,r2); return 0; } } TOK type::kind(TOK oper, TOK v, bit perr) /* v == 'I' integral 'N' numeric 'P' numeric or pointer */ { Ptype t = this; if (this == 0) error('i',"type::kind(): this==0"); t = t->skiptypedefs(); switch (t->base) { case ANY: return 'A'; case ZTYPE: return 'Z'; case FIELD: case CHAR: case SHORT: case INT: case LONG: case EOBJ: return 'I'; case FLOAT: case LDOUBLE: case DOUBLE: if (v == 'I') if(perr) error("float operand for %k",oper); return 'F'; case VEC: case PTR: if (v != 'P') if(perr) error("P operand for %k",oper); switch (oper) { case INCR: case DECR: case MINUS: case PLUS: case ASMINUS: case ASPLUS: if ( t->base==PTR && ( Pptr(t)->memof || Pptr(t)->typ->base==FCT ) ) { if(perr) error("%t operand of%k",this,oper); } else { Pptr(t)->typ->tsizeof(); // get increment } break; default: if ( t->base==PTR && ( Pptr(t)->memof || Pptr(t)->typ->base==FCT ) ) if(perr) error("%t operand of%k",this,oper); case LT: case LE: case GT: case GE: case ANDAND: case OROR: case ASSIGN: case NE: case EQ: case IF: case WHILE: case DO: case FOR: case QUEST: case NOT: break; } return 'P'; case RPTR: if(perr) error("R operand for %k",oper); return 'A'; case FCT: if (v != 'P') if(perr) error("F operand for %k",oper); return FCT; case OVERLOAD: if(perr) error("overloaded operand for %k",oper); return 'A'; case CLASS: case ENUM: if(perr) error("%k operand for %k",base,oper); return 'A'; default: if(perr) error("%t operand for %k",this,oper); return 'A'; } } void type::dcl(Ptable tbl) /* go through the type (list) and (1) evaluate vector dimensions (2) evaluate field sizes (3) lookup struct tags, etc. (4) handle implicit tag declarations */ { Ptype t = this; // processing_sizeof suppresses errors for refs to names in the arg // to sizeof. Turn errors back on for exprs within type specs // (such as array subscripts) int os = processing_sizeof; processing_sizeof = 0; if (this == 0) error('i',"T::dcl(this==0)"); if (tbl->base != TABLE) error('i',"T::dcl(%d)",tbl->base); xx: switch (t->base) { case TYPE: t = Pbase(t)->b_name->tp; goto xx; case PTR: case RPTR: { Pptr p = Pptr(t); if(p->memof == 0 && p->ptname) { // T::*, where T is a template formal Ptype tp = p->ptname->tp->skiptypedefs(); switch (tp->base) { case COBJ: { p->memof = tp->classtype(); if (p->typ) { Ptype t = p->typ->skiptypedefs(); if (t && t->base==FCT) { Pfct(t)->memof = p->memof; } } break; } case CLASS: { p->memof = Pclass(tp); Ptype t = p->typ->skiptypedefs(); Pfct f = Pfct(t); // safe??? f->memof = p->memof; break; } default: error("illegalZizedP toM %t::*",tp); break; } } t = p->typ; if (t->base == TYPE) { Ptype tt = Pbase(t)->b_name->tp; if (tt->base == FCT) p->typ = tt; goto done; } goto xx; } case VEC: { Pvec v = Pvec(t); Pexpr e = v->dim; if (e) { Ptype et; v->dim = e = e->typ(tbl); if (e->tp->skiptypedefs()->base == COBJ) { e = check_cond(e,DEREF,tbl); v->dim = e; } et = e->tp; if (et->integral(0) == 'A') { error("UN in array dimension"); } else { long i; Neval = 0; i = e->eval(); if (Neval == 0) { if (largest_intsize = int(i); DEL(v->dim); v->dim = 0; } if (new_type) { if (Neval) ; else if (i == 0) v->dim = zero; else if (i < 0) { error("negative array dimension"); i = 1; } } else { if (Neval) error("%s",Neval); else if (i == 0) { error("array dimension == 0"); v->dim=e; } else if (i < 0) { error("negative array dimension"); i = 1; } } } } t = v->typ; llx: switch (t->base) { case TYPE: t = Pbase(t)->b_name->tp; goto llx; case FCT: v->typ = t; break; case VEC: if (Pvec(t)->dim==0 && Pvec(t)->size==0) error("null dimension (something like [][] seen)"); } goto xx; } case FCT: { Pfct f = Pfct(t); void dargs(Pname, Pfct, Ptable); if (f->argtype) dargs(0,f,tbl); for (Pname n=f->argtype; n; n = n->n_list) { Ptype t = n->tp; n->tp->dcl(tbl); while(t->base==TYPE) t = Pbase(t)->b_name->tp; if(t->base==VEC) n->tp = new ptr(PTR,Pvec(t)->typ); } Pname cn = f->returns->is_cl_obj(); if (cn && Pclass(cn->tp)->has_itor()) make_res(f); else if (f->f_this == 0) f->f_args = f->argtype; t = f->returns; goto xx; } case FIELD: { Pbase f = Pbase(t); Pexpr e = Pexpr(f->b_name); long i; Ptype et; e = e->typ(tbl); f->b_name = Pname(e); et = e->tp; if (et->integral(0) == 'A') { error("UN in field size"); i = 1; } else { Neval = 0; i = e->eval(); if (Neval) error("%s",Neval); else if (i < 0) { error("negative field size"); i = 1; } else if (f->b_fieldtype->tsizeof()*BI_IN_BYTE < i) error("field size > sizeof(%t)",f->b_fieldtype); DEL(e); } f->b_bits = int(i); f->b_name = 0; break; } } done: processing_sizeof = os; return; } static bit fm_same_class(Pclass c1, Pclass c2) /* Predicate to determine whether two classes are indeed the same. * as arguments to a template function: uninstantiated: t * template void f(X t)); * X results in an UNINSTANTIATED template class * friend X void foo( X ) results in a CL_TEMPLATE * because the template nature is extrapolated from the declaration * and class objects referenced within friend declarations are not * placed on the class templ_ref list for instantiation */ { // error('d',"fm_same_class: %t %d %t %d",c1,c1,c2,c2); // error('d',"fm_same_class: c1->cl_base %d c2->cl_base %d",c1->class_base,c2->class_base); if (same_class(c1,c2)) return 1; Templ_type t1 = c1->class_base; Templ_type t2 = c2->class_base; if ((t1 == UNINSTANTIATED || t1 == CL_TEMPLATE) && (t2 == UNINSTANTIATED || t2 == CL_TEMPLATE) && (strcmp(c1->string,c2->string)==0)) return 1; return 0; } bit const_problem; // types differ only in const bit return_error; // functions differ only in return static bit pt_ptm; // template pointer to member static bit pt_over; // strong check of template_classes int Vcheckerror; extern int template_hier; // permit derived/base conversion in templates bit type::check(Ptype t, TOK oper, bit level) /* check if "this" can be combined with "t" by the operator "oper" used for check of assignment types (oper==ASSIGN) declaration compatability (oper==0) decl. compatibility without "const"ness (oper=254) (oper == IGNORE_CONST) parameterized type formals (oper==255) as for (oper==0) but special checking for ANY types argument types (oper==ARG) return types (oper==RETURN) overloaded function name match (oper==OVERLOAD) overloaded function coercion (oper==COERCE) virtual function match (oper==VIRTUAL) NOT for arithmetic operators return 1 if the check failed */ { register Ptype t1 = this, t2 = t; bit cnst1 = 0, cnst2 = 0; TOK b1, b2; bit vv=0, over=0, strict_any_check = 0; TOK rec_oper; // value of oper for recursive calls to type::check // oper, 255, or PT_OVERLOAD TOK rec_oper0; // value of oper for recursive calls to type::check // 0, 255, or PT_OVERLOAD if (t1==0 || t2==0) error('i',"check(%p,%p,%d)",t1,t2,oper); if(t1==t2) return 0; switch(oper) { case VIRTUAL: vv = 1; Vcheckerror = 0; oper = 0; break; case OVERLOAD: over = 1; oper = 0; break; case PT_OVERLOAD: over = 1; // no break case 255: oper = 0; strict_any_check=1; break; } if(level==0) { const_problem = 0; return_error = 0; pt_ptm = 0; pt_over = over; } rec_oper = strict_any_check ? ( over ? PT_OVERLOAD : 255 ) : oper; rec_oper0 = strict_any_check ? ( over ? PT_OVERLOAD : 255 ) : 0; t1 = t1->skiptypedefs(cnst1); /* ** GLOG: the following test on ANY must be done before unrolling ** t2, to accommodate templates (in a way I don't yet understand) */ if (t1->base == ANY || t2->base == ANY) if (over==0 || strict_any_check==0) return strict_any_check ? t1!=t2 : 0; t2 = t2->skiptypedefs(cnst2); if(t1==t2) goto const_check; b1 = t1->base; b2 = t2->base; if (b1 != b2) { switch (b1) { case PTR: switch (b2) { case VEC: if ( level>0 || (oper == 0 || oper == IGNORE_CONST) && over==0 || Pptr(t1)->memof || Pptr(t1)->ptname || Pptr(t1)->typ->check(Pvec(t2)->typ,rec_oper,level+1) ) return 1; goto const_check; case FCT: if ( level>0 || Pptr(t1)->typ->check(t2,rec_oper,level+1) ) return 1; goto const_check; } break; case FCT: if( b2==PTR ) { if ( level>0 || t1->check(Pptr(t2)->typ,rec_oper,level+1) ) return 1; goto const_check; } break; case VEC: if (b2==PTR) { if ( level>0 || (oper==0 || oper == IGNORE_CONST) && over==0 || Pptr(t2)->memof || Pptr(t2)->ptname || Pvec(t1)->typ->check(Pptr(t2)->typ,rec_oper,level+1) ) return 1; goto const_check; } break; } if(level>0) { if((oper != 0 && oper != IGNORE_CONST) && b1==VOID && level==1) { if(b2==FCT) { Pfct f = Pfct(t2); if(f->memof && f->f_static==0) return 1; } goto const_check; } return 1; } switch (oper) { case 0: case IGNORE_CONST: if ( b2 == ZTYPE && b1==INT && Pbase(t1)->b_unsigned==0 || b1 == ZTYPE && b2==INT && Pbase(t2)->b_unsigned==0 ) goto const_check; return 1; case ARG: case ASSIGN: case RETURN: case COERCE: switch (b1) { case ZTYPE: case CHAR: case SHORT: case INT: case LONG: case FLOAT: case DOUBLE: case LDOUBLE: case FIELD: switch (b2) { case LONG: case FLOAT: case DOUBLE: case LDOUBLE: case EOBJ: case ZTYPE: case CHAR: case SHORT: case INT: case FIELD: if(oper==COERCE) Nstd++; goto const_check; } return 1; case PTR: case VEC: if (b2==ZTYPE) { if(oper==COERCE) Nstd++; goto const_check; } case RPTR: case COBJ: case FCT: case EOBJ: default: return 1; } } goto const_check; } switch (b1) { case VEC: if ( Pvec(t1)->size!=Pvec(t2)->size && ( level>0 || (oper==0 || oper==IGNORE_CONST) && strict_any_check==0 && Pvec(t1)->size && Pvec(t2)->size ) ) return 1; if(Pvec(t1)->typ->check(Pvec(t2)->typ,rec_oper,level+1)) return 1; break; case PTR: case RPTR: { Pptr p1 = Pptr(t1); Pptr p2 = Pptr(t2); if ((p1->ptname && p2->ptname) && (!p1->memof || !p2->memof)) return 1; if (!same_class(p1->memof,p2->memof)) { // T::* requires we defer setting up memof // until instantiation of the Template type T // ptname holds the formal parameter T // can't merge with other if because of memof usage if( p1->memof==0 && p1->ptname || p2->memof==0 && p2->ptname ) pt_ptm = 1; else if( p1->memof==0 || p2->memof==0 || (p1->memof->baseof(p2->memof)==0 && same_class(p1->memof,p2->memof,1)==0) ) return 1; if (pt_ptm == 0 && (oper==0 || oper==IGNORE_CONST) && same_class(p1->memof,p2->memof)==0) return 1; if(oper==COERCE) Nstd++; } #if 0 if (!level && (oper == 0 || oper == ASSIGN || oper == COERCE || oper == ARG || oper == RETURN)) { Ptype t11; Ptype t22; Ptype ta; Ptype tb; int i = 0; int j = 0; int k = 0; ta = t1; while ((ta->base == PTR || ta->base == RPTR) && (tb = ta->is_ptr_or_ref())) { ta = Pptr(tb)->typ; i++; k += ta->tconst(); } t11 = ta; ta = t2; while ((ta->base == PTR || ta->base == RPTR) && (tb = ta->is_ptr_or_ref())) { ta = Pptr(tb)->typ; j++; } t22 = ta; if (i >= 2 && j == i && t11->tconst() && !t22->tconst() && k != i) return const_problem = 1; } #endif if(p1->typ->check(p2->typ,rec_oper,level+1)) return 1; break; } case FCT: { Pfct f1 = Pfct(t1); Pfct f2 = Pfct(t2); Pname a1 = f1->argtype; Pname a2 = f2->argtype; TOK k1 = f1->nargs_known; TOK k2 = f2->nargs_known; int n1 = f1->nargs; int n2 = f2->nargs; // if pt_ptm, want to check arguments and return type // but template ptm has no memof until instantiation if (!same_class(f1->memof,f2->memof) && pt_ptm == 0) { if (f1->memof==0 && f2->f_static) goto sss; if (vv == 0) // match even if private base class if ( f1->memof==0 || f2->memof==0 || ( level > 1 || f1->memof->baseof(f2->memof)==0 ) && same_class(f1->memof,f2->memof)==0 ) return 1; if(oper==COERCE) Nstd++; sss:; //SSS } if (k1 != k2) return 1; if (n1!=n2 && k1 && k2) { goto aaa; } else if (a1 && a2) { while (a1 && a2) { if (a1->tp->check(a2->tp,rec_oper0,level+1)) return 1; a1 = a1->n_list; a2 = a2->n_list; } if (a1 || a2) goto aaa; } else if (a1 || a2) { aaa: if (k1 == ELLIPSIS) { switch (oper) { case 0: case IGNORE_CONST: if (a2 && k2==0) break; return 1; case ASSIGN: if (a2 && k2==0) break; return 1; case ARG: if (a1) return 1; break; case COERCE: return 1; } } else if (k2 == ELLIPSIS) { return 1; } else if (k1 || k2) { return 1; } } cnst1 = f2->f_const; cnst2 = f1->f_const; if(f1->returns->check(f2->returns,rec_oper0,level+1)) { if(vv && cnst1==cnst2) { bit fail = 1; Ptype t1 = f1->returns; Ptype t2 = f2->returns; if ((t1->is_ptr() && t2->is_ptr()) || (t1->is_ref() && t2->is_ref())) { t1 = Pptr(t1->is_ptr_or_ref())->typ; t2 = Pptr(t2->is_ptr_or_ref())->typ; if (!t1->is_ptr_or_ref() && !t2->is_ptr_or_ref()) { t1 = t1->skiptypedefs(); t2 = t2->skiptypedefs(); if (t1->base == COBJ && t2->base == COBJ) { Pclass c1 = t1->classtype(); Pclass c2 = t2->classtype(); Nvis = 0; if (c2->has_base(c1,0,1) && !Nvis) fail = 0; } } } if (fail) Vcheckerror = 1; else break; } if (rec_oper0 == PT_OVERLOAD && level == 0) return_error = 1; return 1; } break; } case FIELD: switch (oper) { case 0: case IGNORE_CONST: case ARG: error('i',"check field?"); } return 0; case FLOAT: case DOUBLE: case LDOUBLE: case CHAR: case SHORT: case INT: case LONG: if (Pbase(t1)->b_unsigned != Pbase(t2)->b_unsigned) { if (level>0 || (oper==0 || oper==IGNORE_CONST)) return 1; if (oper==COERCE) Nstd++; } goto const_check; case EOBJ: if (Pbase(t1)->b_name->tp != Pbase(t2)->b_name->tp) return 1; goto const_check; case CLASS: case COBJ: { Pname n1, n2; if (b1 == COBJ) { n1 = Pbase(t1)->b_name; n2 = Pbase(t2)->b_name; if (n1 == n2) goto const_check; } // once again, a more comprehensive check for classes, // since they may be parameterized. // same_class: handles class templates: instantiated // fm_same_class: handles matching uninstantiated templates // used as formal arguments for template functions // hack: see comment on classdef::same_class in template.c extern int is_arg; int access = template_hier || pt_over; if ( same_class(Pclass(b1==COBJ?n1->tp:t1),Pclass(b1==COBJ?n2->tp:t2),access||is_arg) || rec_oper==PT_OVERLOAD && fm_same_class(Pclass(b1==COBJ?n1->tp:t1),Pclass(b1==COBJ?n2->tp:t2)) ) goto const_check; // permit a derived class to match public base class if (template_hier != 0) goto pt_hack; switch (oper) { case ARG: case ASSIGN: case RETURN: case COERCE: { pt_hack: ppbase = PUBLIC; if (level<=1 && ((Pclass(b1==COBJ?n2->tp:t2))->is_base(b1==COBJ?n1->string:Pclass(t1)->string))) { if (ppbase!=PUBLIC) { const_problem = 0; return 1; // private or protected base } if(oper==COERCE) Nstd++; goto const_check; } } // no break case 0: case IGNORE_CONST: const_problem = 0; return 1; } goto const_check; } case ZTYPE: case VOID: goto const_check; default: error('i',"T::check(o=%d %d %d)",oper,b1,b2); } const_check: if(cnst1==cnst2) return 0; switch(oper) { case IGNORE_CONST: return 0;//ignore "const"ness for oper=IGNORE_CONST case 0: const_problem=1; return 1; case ASSIGN: case COERCE: case ARG: case RETURN: if(level>0) { if(cnst2) const_problem=1; return cnst2; } return 0; default: error('i',"oper = %k in type::check()",oper); } return 0; }