enum asm_size asm_type_size(decl *d) { if(decl_ptr_depth(d)){ return ASM_SIZE_WORD; }else{ switch(d->type->primitive){ case type_enum: case type_int: return ASM_SIZE_WORD; case type_char: return ASM_SIZE_1; case type_void: ICE("type primitive is void"); case type_typedef: return asm_type_size(d->type->tdef); case type_struct: ICE("asm_type_size of a struct - can't be word nor byte"); case type_unknown: ICE("type primitive not set"); } } ICE("asm_type_size switch error"); return ASM_SIZE_WORD; }
static void instantiateType(Output& output, Ty* type) { if (UNION_CASE(Instance, t, type)) { assert(t->def || t->generic); if (t->def) { if (UNION_CASE(Struct, def, t->def)) { if (t->tyargs.size == 0) { for (auto& arg: def->tyargs) t->tyargs.push(UNION_NEW(Ty, Unknown, {})); } else { if (t->tyargs.size != def->tyargs.size) output.error(t->location, "Expected %d type arguments but given %d", int(def->tyargs.size), int(t->tyargs.size)); } } else ICE("Unknown TyDef kind %d", t->def->kind); } else { if (t->tyargs.size != 0) output.error(t->location, "Expected 0 type arguments but given %d", int(t->tyargs.size)); } } }
static void typeUnary(Output& output, Ast::Unary* n, TypeConstraints* constraints) { type(output, n->expr, constraints); if (n->op == UnaryOpNot) { n->type = UNION_NEW(Ty, Bool, {}); typeMustEqual(n->expr, n->type, constraints, output); } else if (n->op == UnaryOpDeref) { if (UNION_CASE(Pointer, t, astType(n->expr))) { n->type = t->element; } else { n->type = UNION_NEW(Ty, Unknown, {}); typeMustEqual(n->expr, UNION_NEW(Ty, Pointer, { n->type }), constraints, output); } } else if (n->op == UnaryOpNew) { n->type = UNION_NEW(Ty, Pointer, { astType(n->expr) }); } else { ICE("Unknown UnaryOp %d", n->op); } }
const char *decl_store_to_str(const enum decl_storage s) { static char buf[16]; /* "inline register" is the longest - just a fit */ if(s & STORE_MASK_EXTRA){ char *trail_space = NULL; *buf = '\0'; if((s & STORE_MASK_EXTRA) == store_inline){ strcpy(buf, "inline "); trail_space = buf + strlen("inline"); } strcpy(buf + strlen(buf), decl_store_to_str(s & STORE_MASK_STORE)); if(trail_space && trail_space[1] == '\0') *trail_space = '\0'; return buf; } switch(s){ case store_inline: ICE("inline"); case store_default: return ""; CASE_STR_PREFIX(store, auto); CASE_STR_PREFIX(store, static); CASE_STR_PREFIX(store, extern); CASE_STR_PREFIX(store, register); CASE_STR_PREFIX(store, typedef); } return NULL; }
void asm_declare_single_part(FILE *f, expr *e) { if(!e->f_gen_1) ICE("unexpected global initaliser %s (no gen_1())", e->f_str()); e->f_gen_1(e, f); }
enum visibility decl_visibility(decl *d) { attribute *visibility = attribute_present(d, attr_visibility); if(visibility) return visibility->bits.visibility; switch((d->store & STORE_MASK_STORE)){ case store_extern: /* no explicit visibility, -fvisibility doesn't affect extern decls */ return VISIBILITY_DEFAULT; case store_default: /* if it's not in this translation unit it's essentially an extern decl */ if(!decl_defined(d, 0)) return VISIBILITY_DEFAULT; break; case store_static: break; case store_auto: case store_register: case store_typedef: ICE("shouldn't be calling decl_visibility() on a %s decl", decl_store_to_str(d->store & STORE_MASK_STORE)); } return cc1_visibility_default; }
enum linkage decl_linkage(decl *d) { /* global scoped or extern */ decl *p = decl_proto(d); /* if first instance is static, we're internal */ switch((enum decl_storage)(p->store & STORE_MASK_STORE)){ case store_extern: return linkage_external; case store_static: return linkage_internal; case store_register: case store_auto: case store_typedef: return linkage_none; case store_inline: ICE("bad store"); case store_default: break; } /* either global non-static or local */ return d->sym && d->sym->type == sym_global ? linkage_external : linkage_none; }
static unsigned type_hash2( const type *t, unsigned nest_hash(const type *)) { unsigned hash = t->type << 20 | (unsigned)(unsigned long)t; switch(t->type){ case type_auto: ICE("auto type"); case type_btype: hash |= t->bits.type->primitive | sue_hash(t->bits.type->sue); break; case type_tdef: hash |= nest_hash(t->bits.tdef.type_of->tree_type); hash |= 1 << 3; break; case type_ptr: if(t->bits.ptr.decayed_from) hash |= nest_hash(t->bits.ptr.decayed_from); break; case type_array: if(t->bits.array.size) hash |= nest_hash(t->bits.array.size->tree_type); hash |= 1 << t->bits.array.is_static; hash |= 1 << t->bits.array.is_vla; break; case type_block: case type_where: /* nothing */ break; case type_func: { decl **i; for(i = t->bits.func.args->arglist; i && *i; i++) hash |= nest_hash((*i)->ref); break; } case type_cast: hash |= t->bits.cast.qual; break; case type_attr: hash |= t->bits.attr->type; break; } return hash; }
static void type(Output& output, Ast* root, TypeConstraints* constraints) { #define CALL(name, ...) else if (UNION_CASE(name, n, root)) type##name(output, n, constraints); if (false) ; UD_AST(CALL) else ICE("Unknown Ast kind %d", root->kind); #undef CALL }
unsigned type_size(type *r, const where *from) { switch(r->type){ case type_auto: ICE("__auto_type"); case type_btype: return btype_size(r->bits.type, from); case type_tdef: { decl *d = r->bits.tdef.decl; type *sub; if(d) return type_size(d->ref, from); sub = r->bits.tdef.type_of->tree_type; UCC_ASSERT(sub, "type_size for unfolded typedef"); return type_size(sub, from); } case type_attr: case type_cast: case type_where: return type_size(r->ref, from); case type_ptr: case type_block: return platform_word_size(); case type_func: /* function size is one, sizeof(main) is valid */ return 1; case type_array: { integral_t sz; if(type_is_void(r->ref)) die_at(from, "array of void"); if(!r->bits.array.size) die_at(from, "array has an incomplete size"); sz = const_fold_val_i(r->bits.array.size); return sz * type_size(r->ref, from); } } ucc_unreach(0); }
static void static_val(enum section_type sec, type *ty, expr *e) { consty k; memset(&k, 0, sizeof k); const_fold(e, &k); switch(k.type){ case CONST_NEED_ADDR: case CONST_NO: ICE("non-constant expr-%s const=%d%s", e->f_str(), k.type, k.type == CONST_NEED_ADDR ? " (needs addr)" : ""); break; case CONST_NUM: if(K_FLOATING(k.bits.num)){ /* asm fp const */ asm_out_fp(sec, ty, k.bits.num.val.f); }else{ char buf[INTEGRAL_BUF_SIZ]; asm_declare_init_type(sec, ty); integral_str(buf, sizeof buf, k.bits.num.val.i, e->tree_type); asm_out_section(sec, "%s", buf); } break; case CONST_ADDR: asm_declare_init_type(sec, ty); if(k.bits.addr.is_lbl) asm_out_section(sec, "%s", k.bits.addr.bits.lbl); else asm_out_section(sec, "%ld", k.bits.addr.bits.memaddr); break; case CONST_STRK: stringlit_use(k.bits.str->lit); /* must be before the label access */ asm_declare_init_type(sec, ty); asm_out_section(sec, "%s", k.bits.str->lit->lbl); break; } /* offset in bytes, no mul needed */ if(k.offset) asm_out_section(sec, " + %ld", k.offset); asm_out_section(sec, "\n"); }
void asm_out_fp(enum section_type sec, type *ty, floating_t f) { switch(type_primitive(ty)){ case type_float: { union { float f; unsigned u; } u; u.f = f; asm_out_section(sec, ".long %u # float %f\n", u.u, u.f); break; } case type_double: { union { double d; unsigned long ul; } u; u.d = f; asm_out_section(sec, ".quad %lu # double %f\n", u.ul, u.d); break; } case type_ldouble: ICE("TODO"); default: ICE("bad float type"); } }
static void typeBinary(Output& output, Ast::Binary* n, TypeConstraints* constraints) { type(output, n->left, constraints); type(output, n->right, constraints); if (n->op == BinaryOpAnd || n->op == BinaryOpOr) { n->type = UNION_NEW(Ty, Bool, {}); typeMustEqual(n->left, n->type, constraints, output); typeMustEqual(n->right, n->type, constraints, output); } else { ICE("Unknown BinaryOp %d", n->op); } }
static void fold_const_expr_if(expr *e, consty *k) { consty consts[3]; int res; const_fold(e->expr, &consts[0]); const_fold(e->rhs, &consts[2]); if(e->lhs) const_fold(e->lhs, &consts[1]); else consts[1] = consts[0]; /* only evaluate lhs/rhs' constness if we need to */ if(!CONST_AT_COMPILE_TIME(consts[0].type)){ k->type = CONST_NO; return; } switch(consts[0].type){ case CONST_NUM: { numeric *n = &consts[0].bits.num; res = n->suffix & VAL_FLOATING ? n->val.f : n->val.i; break; } case CONST_ADDR: case CONST_STRK: res = 1; break; case CONST_NEED_ADDR: case CONST_NO: ICE("buh"); } res = res ? 1 : 2; /* index into consts */ if(!CONST_AT_COMPILE_TIME(consts[res].type)){ k->type = CONST_NO; }else{ memcpy_safe(k, &consts[res]); k->nonstandard_const = consts[res == 1 ? 2 : 1].nonstandard_const; } }
enum flag_cmp op_to_flag(enum op_type op) { switch(op){ #define OP(x) case op_ ## x: return flag_ ## x OP(eq); OP(ne); OP(le); OP(lt); OP(ge); OP(gt); #undef OP default: break; } ICE("invalid op"); return -1; }
const out_val *gen_expr_funcall(const expr *e, out_ctx *octx) { const out_val *fn_ret; if(0){ out_comment(octx, "start manual __asm__"); ICE("same"); #if 0 fprintf(cc_out[SECTION_TEXT], "%s\n", e->funcargs[0]->data_store->data.str); #endif out_comment(octx, "end manual __asm__"); }else{ /* continue with normal funcall */ const out_val *fn, **args = NULL; fn = gen_expr(e->expr, octx); if(e->funcargs){ expr **aiter; for(aiter = e->funcargs; *aiter; aiter++){ expr *earg = *aiter; const out_val *arg; /* should be of size int or larger (for integral types) * or double (for floating types) */ arg = gen_expr(earg, octx); dynarray_add(&args, arg); } } /* consumes fn and args */ fn_ret = gen_call(e->expr, NULL, fn, args, octx, &e->expr->where); dynarray_free(const out_val **, args, NULL); if(!expr_func_passable(GEN_CONST_CAST(expr *, e))) out_ctrl_end_undefined(octx); } return fn_ret; }
int asm_table_lookup(type *r) { int sz; int i; if(!r) sz = type_primitive_size(type_long); /* or ptr */ else if(type_is(r, type_array) || type_is(r, type_func)) /* special case for funcs and arrays */ sz = platform_word_size(); else sz = type_size(r, NULL); for(i = 0; i < ASM_TABLE_LEN; i++) if(asm_type_table[i].sz == sz) return i; ICE("no asm type index for byte size %d", sz); return -1; }
static char *word_strstr(char *haystack, char *needle) { const int nlen = strlen(needle); char *i; if(!strstr(haystack, needle)) return NULL; for(i = haystack; *i; i++) if(*i == '"'){ i = str_quotefin(i + 1); if(!i) ICE("terminating quote not found\nhaystack = >>%s<<\nneedle = >>%s<<", haystack, needle); }else if(!strncmp(i, needle, nlen)){ return i; } return NULL; }
stmt *stmt_new( func_fold_stmt *f_fold, func_gen_stmt *f_gen, func_gen_stmt *f_gen_style, func_str_stmt *f_str, void (*init)(stmt *), symtable *stab) { stmt *s = umalloc(sizeof *s); where_cc1_current(&s->where); UCC_ASSERT(stab, "no symtable for statement"); s->symtab = stab; s->f_fold = f_fold; switch(cc1_backend){ case BACKEND_ASM: s->f_gen = f_gen; break; case BACKEND_PRINT: case BACKEND_STYLE: s->f_gen = f_gen_style; break; default: ICE("bad backend"); } s->f_str = f_str; init(s); s->kills_below_code = stmt_kind(s, break) || stmt_kind(s, return) || stmt_kind(s, goto) || stmt_kind(s, continue); return s; }
static void preproc_pop(void) { if(!file_stack_idx) ICE("file stack idx = 0 on pop()"); free(file_stack[file_stack_idx].fname); file_stack_idx--; #ifdef DO_CHDIR if(curwdfd != -1){ if(fchdir(curwdfd) == -1) ppdie(p, "chdir(-): %s", strerror(errno)); close(curwdfd); } #endif /* restore state */ current_line = file_stack[file_stack_idx].line_no; set_current_fname(file_stack[file_stack_idx].fname); preproc_emit_line_info_top(LINEINFO_RETURN_TO_FILE); }
void preproc_pop(void) { if(!file_stack_idx) ICE("file stack idx = 0 on pop()"); free(file_stack[file_stack_idx].fname); file_stack_idx--; #ifdef DO_CHDIR if(curwdfd != -1){ if(fchdir(curwdfd) == -1) ppdie(p, "chdir(-): %s", strerror(errno)); close(curwdfd); } #endif /* restore state */ current_line = file_stack[file_stack_idx].line_no - 1; current_fname = file_stack[file_stack_idx].fname; preproc_out_info(); }
char *word_strstr(char *haystack, char *needle) { const int nlen = strlen(needle); char *i; if(!strstr(haystack, needle)) return NULL; for(i = haystack; *i; i++) if(*i == '"'){ refind: i = strchr(i + 1, '"'); if(!i) ICE("terminating quote not found\nhaystack = >>%s<<\nneedle = >>%s<<", haystack, needle); else if(i[-1] == '\\') goto refind; }else if(!strncmp(i, needle, nlen)){ return i; } return NULL; }
void expr_mutate(expr *e, func_mutate_expr *f, func_fold *f_fold, func_str *f_str, func_gen *f_gen, func_gen *f_gen_str, func_gen *f_gen_style ) { e->f_fold = f_fold; e->f_str = f_str; switch(cc1_backend){ case BACKEND_ASM: e->f_gen = f_gen; break; case BACKEND_PRINT: e->f_gen = f_gen_str; break; case BACKEND_STYLE: e->f_gen = f_gen_style; break; default: ICE("bad backend"); } e->f_const_fold = NULL; e->f_lea = NULL; f(e); }
static void typeFnDecl(Output& output, Ast::FnDecl* n, TypeConstraints* constraints) { if (n->body) { type(output, n->body, constraints); if (UNION_CASE(Function, fnty, n->var->type)) { if (fnty->ret->kind != Ty::KindVoid) typeMustEqual(n->body, fnty->ret, constraints, output); } else ICE("FnDecl type is not Function"); } else { if (!constraints) typeMustKnow(n->var->type, output, n->var->location); } if (!n->type) n->type = UNION_NEW(Ty, Void, {}); }
static char *read_line(void) { FILE *f; char *line; int newline; re_read: if(file_stack_idx < 0) ICE("file stack idx = 0 on read()"); f = file_stack[file_stack_idx].file; line = fline(f, &newline); if(!line){ if(ferror(f)) die("read():"); fclose(f); if(file_stack_idx > 0){ free(dirname_pop()); preproc_pop(); goto re_read; }else{ if(!prev_newline){ CPP_WARN(WNEWLINE, "no newline at end-of-file"); } } return NULL; } prev_newline = newline; current_line++; return line; }
static void print_decl_init(decl_init *di) { switch(di->type){ case decl_init_scalar: idt_printf("scalar:\n"); gen_str_indent++; print_expr(di->bits.expr); gen_str_indent--; break; case decl_init_copy: ICE("copy in print"); break; case decl_init_brace: { decl_init *s; int i; idt_printf("brace\n"); gen_str_indent++; for(i = 0; (s = di->bits.ar.inits[i]); i++){ if(s == DYNARRAY_NULL){ idt_printf("[%d] = <zero init>\n", i); }else if(s->type == decl_init_copy){ idt_printf("[%d] = copy from range_store[%ld]\n", i, (long)DECL_INIT_COPY_IDX(s, di)); }else{ const int need_brace = s->type == decl_init_brace; /* ->member not printed */ #ifdef DINIT_WITH_STRUCT if(s->spel) idt_printf(".%s", s->spel); else #endif idt_printf("[%d]", i); fprintf(cc1_out, " = %s\n", need_brace ? "{" : ""); gen_str_indent++; print_decl_init(s); gen_str_indent--; if(need_brace) idt_printf("}\n"); } } gen_str_indent--; if(di->bits.ar.range_inits){ struct init_cpy *icpy; idt_printf("range store:\n"); gen_str_indent++; for(i = 0; (icpy = di->bits.ar.range_inits[i]); i++){ idt_printf("store[%d]:\n", i); gen_str_indent++; print_decl_init(icpy->range_init); gen_str_indent--; if(icpy->first_instance){ idt_printf("first expr:\n"); gen_str_indent++; print_expr(icpy->first_instance); gen_str_indent--; } } gen_str_indent--; } } } }
static void print_type_eng(type *ref) { if(!ref) return; print_type_eng(ref->ref); switch(ref->type){ case type_auto: ICE("__auto_type"); case type_cast: if(ref->bits.cast.is_signed_cast) fprintf(cc1_out, "%s ", ref->bits.cast.signed_true ? "signed" : "unsigned"); else fprintf(cc1_out, "%s", type_qual_to_str(ref->bits.cast.qual, 1)); break; case type_ptr: fprintf(cc1_out, "pointer to "); break; case type_block: fprintf(cc1_out, "block returning "); break; case type_func: { #ifdef ENGLISH_PRINT_ARGLIST funcargs *fargs = ref->bits.func.args; decl **iter; #endif fputs("function", cc1_out); #ifdef ENGLISH_PRINT_ARGLIST fputc('(', cc1_out); if(fargs->arglist){ for(iter = fargs->arglist; iter && *iter; iter++){ print_decl(*iter, PDECL_NONE); if(iter[1]) fputs(", ", cc1_out); } if(fargs->variadic) fputs("variadic", cc1_out); }else{ fprintf(cc1_out, "taking %s arguments", fargs->args_void ? "no" : "unspecified"); } fputc(')', cc1_out); #endif fputs(" returning ", cc1_out); break; } case type_array: fputs("array[", cc1_out); if(ref->bits.array.size) print_expr_val(ref->bits.array.size); fputs("] of ", cc1_out); break; case type_btype: fprintf(cc1_out, "%s", btype_to_str(ref->bits.type)); break; case type_tdef: case type_attr: ICE("TODO"); case type_where: break; } }
static bool resolveNamesNode(ResolveNames& rs, Ast* root) { // TODO: refactor if (root->kind != Ast::KindFnDecl && root->kind != Ast::KindTyDecl) visitAstTypes(root, resolveType, rs); if (UNION_CASE(Ident, n, root)) { n->targets = resolveBindings(rs.variables.findAll(n->name)); if (n->targets.size == 0) rs.output->error(n->location, "Unresolved identifier %s", n->name.str().c_str()); } else if (UNION_CASE(Block, n, root)) { auto scope = rs.top(); // Bind all declarations from this scope to allow recursive references for (auto& c: n->body) resolveDecl(rs, c); visitAstInner(root, resolveNamesNode, rs); rs.pop(scope); } else if (UNION_CASE(Module, n, root)) { assert(!rs.module); rs.module = n; for (auto& i: n->autoimports) resolveImport(rs, i); visitAstInner(root, resolveNamesNode, rs); } else if (UNION_CASE(For, n, root)) { auto scope = rs.top(); visitAst(n->expr, resolveNamesNode, rs); rs.variables.push(n->var->name, n->var); if (n->index) rs.variables.push(n->index->name, n->index); visitAst(n->body, resolveNamesNode, rs); rs.pop(scope); } else if (UNION_CASE(FnDecl, n, root)) { // establish lexical parent-child relationship n->parent = rs.functions.empty() ? nullptr : rs.functions.back(); n->module = rs.module; auto scope = rs.top(); for (auto& a: n->tyargs) { UNION_CASE(Generic, g, a); assert(g); rs.generics.push(g->name, a); } visitAstTypes(root, resolveType, rs); if (n->body) { for (auto& a: n->args) rs.variables.push(a->name, a); rs.functions.push_back(n); visitAstInner(root, resolveNamesNode, rs); rs.functions.pop_back(); } rs.pop(scope); } else if (UNION_CASE(TyDecl, n, root)) { auto scope = rs.top(); if (UNION_CASE(Struct, d, n->def)) { for (auto& a: d->tyargs) { UNION_CASE(Generic, g, a); assert(g); rs.generics.push(g->name, a); } } else ICE("Unknown TyDef kind %d", n->def->kind); visitAstTypes(root, resolveType, rs); visitAstInner(root, resolveNamesNode, rs); rs.pop(scope); } else if (UNION_CASE(VarDecl, n, root)) { visitAstInner(root, resolveNamesNode, rs); rs.variables.push(n->var->name, n->var); } else if (UNION_CASE(Import, n, root)) { resolveImport(rs, n->name); } else return false; return true; }
static void type_add_str( type *r, const char *spel, int *need_spc, char **bufp, int *sz, type *stop_at) { int need_paren; type *array_qual = NULL, *next_ty; if(!r){ /* reached the bottom/end - spel */ if(spel){ ADD_SPC(); BUF_ADD("%s", spel); *need_spc = 0; } return; } if(stop_at && r->tmp == stop_at){ type_add_str(r->tmp, spel, need_spc, bufp, sz, stop_at); return; } type_add_str_pre(r, &need_paren, need_spc, bufp, sz); next_ty = r->tmp; if(r->type == type_array && r->tmp && r->tmp->type == type_cast){ array_qual = r->tmp; next_ty = array_qual->tmp; } type_add_str(next_ty, spel, need_spc, bufp, sz, stop_at); switch(r->type){ case type_auto: ICE("__auto_type"); case type_tdef: /* tdef "aka: %s" handled elsewhere */ case type_attr: /* attribute not handled here */ case type_btype: case type_cast: case type_where: /**/ case type_block: break; case type_func: type_add_funcargs(r->bits.func.args, need_spc, bufp, sz); break; case type_ptr: #ifdef SHOW_DECAYED_ARRAYS if(!r->bits.ptr.size) #endif break; /* fall */ case type_array: { const char *sz_space = ""; BUF_ADD("["); if(r->bits.array.is_vla == 0 && r->bits.array.is_static){ BUF_ADD("static"); sz_space = " "; } if(array_qual){ BUF_ADD("%s%s", sz_space, type_qual_to_str(array_qual->bits.cast.qual, 0)); sz_space = " "; } switch(r->bits.array.is_vla){ case 0: if(r->bits.array.size){ BUF_ADD( "%s%" NUMERIC_FMT_D, sz_space, const_fold_val_i(r->bits.array.size)); } break; case VLA: BUF_ADD("vla"); break; case VLA_STAR: BUF_ADD("*"); break; } BUF_ADD("]"); break; } } if(need_paren) BUF_ADD(")"); #undef IS_PTR }
static enum type_cmp type_cmp_r( type *const orig_a, type *const orig_b, enum type_cmp_opts opts) { enum type_cmp ret; type *a, *b; int subchk = 1; if(!orig_a || !orig_b) return orig_a == orig_b ? TYPE_EQUAL : TYPE_NOT_EQUAL; a = type_skip_all(orig_a); b = type_skip_all(orig_b); /* array/func decay takes care of any array->ptr checks */ if(a->type != b->type){ /* allow _Bool <- pointer */ if(type_is_primitive(a, type__Bool) && type_is_ptr(b)) return TYPE_CONVERTIBLE_IMPLICIT; /* allow int <-> ptr (or block) */ if((type_is_ptr_or_block(a) && type_is_integral(b)) || (type_is_ptr_or_block(b) && type_is_integral(a))) { return TYPE_CONVERTIBLE_EXPLICIT; } /* allow void <- anything */ if(type_is_void(a)) return TYPE_CONVERTIBLE_IMPLICIT; /* allow block <-> fnptr */ if((type_is_fptr(a) && type_is(b, type_block)) || (type_is_fptr(b) && type_is(a, type_block))) { return TYPE_CONVERTIBLE_EXPLICIT; } return TYPE_NOT_EQUAL; } switch(a->type){ case type_auto: ICE("__auto_type"); case type_btype: subchk = 0; ret = btype_cmp(a->bits.type, b->bits.type); break; case type_array: if(a->bits.array.is_vla || b->bits.array.is_vla){ /* fine, pretend they're equal even if different expressions */ ret = TYPE_EQUAL_TYPEDEF; }else{ const int a_has_sz = !!a->bits.array.size; const int b_has_sz = !!b->bits.array.size; if(a_has_sz && b_has_sz){ integral_t av = const_fold_val_i(a->bits.array.size); integral_t bv = const_fold_val_i(b->bits.array.size); if(av != bv) return TYPE_NOT_EQUAL; }else if(a_has_sz != b_has_sz){ if((opts & TYPE_CMP_ALLOW_TENATIVE_ARRAY) == 0) return TYPE_NOT_EQUAL; } } /* next */ break; case type_block: case type_ptr: break; case type_cast: case type_tdef: case type_attr: case type_where: ICE("should've been skipped"); case type_func: switch(funcargs_cmp(a->bits.func.args, b->bits.func.args)){ case FUNCARGS_EXACT_EQUAL: case FUNCARGS_IMPLICIT_CONV: break; default: /* "void (int)" and "void (int, int)" aren't equal, * but a cast can soon fix it */ return TYPE_CONVERTIBLE_EXPLICIT; } break; } if(subchk) ret = type_cmp_r(a->ref, b->ref, opts); if(ret == TYPE_NOT_EQUAL && a->type == type_func) { /* "int (int)" and "void (int)" aren't equal - but castable */ ret = TYPE_CONVERTIBLE_EXPLICIT; } if(ret == TYPE_NOT_EQUAL && a->type == type_ptr && fopt_mode & FOPT_PLAN9_EXTENSIONS) { /* allow b to be an anonymous member of a, if pointers */ struct_union_enum_st *a_sue = type_is_s_or_u(a), *b_sue = type_is_s_or_u(b); if(a_sue && b_sue /* already know they aren't equal */){ /* b_sue has an a_sue, * the implicit cast adjusts to return said a_sue */ if(struct_union_member_find_sue(b_sue, a_sue)) return TYPE_CONVERTIBLE_IMPLICIT; } } /* allow ptr <-> ptr */ if(ret == TYPE_NOT_EQUAL && type_is_ptr(a) && type_is_ptr(b)) ret = TYPE_CONVERTIBLE_EXPLICIT; /* char * and int * are explicitly conv., * even though char and int are implicit */ if(ret == TYPE_CONVERTIBLE_IMPLICIT && a->type == type_ptr) ret = TYPE_CONVERTIBLE_EXPLICIT; if(a->type == type_ptr || a->type == type_block){ switch(ret){ #define MAP(a, b) case a: ret = b; break MAP(TYPE_QUAL_ADD, TYPE_QUAL_POINTED_ADD); MAP(TYPE_QUAL_SUB, TYPE_QUAL_POINTED_SUB); MAP(TYPE_QUAL_POINTED_ADD, TYPE_QUAL_NESTED_CHANGE); MAP(TYPE_QUAL_POINTED_SUB, TYPE_QUAL_NESTED_CHANGE); #undef MAP default: break; } } if(ret & TYPE_EQUAL_ANY){ enum type_qualifier a_qual = type_qual(orig_a); enum type_qualifier b_qual = type_qual(orig_b); if(a_qual && b_qual){ switch(type_qual_cmp(a_qual, b_qual)){ case -1: /* a has more */ ret = TYPE_QUAL_ADD; break; case 1: /* b has more */ ret = TYPE_QUAL_SUB; break; } }else if(a_qual){ ret = TYPE_QUAL_ADD; }else if(b_qual){ ret = TYPE_QUAL_SUB; } /* else neither are casts */ } if(ret == TYPE_EQUAL){ int at = orig_a->type == type_tdef; int bt = orig_b->type == type_tdef; if(at != bt){ /* one is a typedef */ ret = TYPE_EQUAL_TYPEDEF; }else if(at){ /* both typedefs */ if(orig_a->bits.tdef.decl != orig_b->bits.tdef.decl){ ret = TYPE_EQUAL_TYPEDEF; } } /* else no typedefs */ } return ret; }