コード例 #1
0
ファイル: decl.c プロジェクト: bobrippling/ucc-c-compiler
unsigned decl_size(decl *d)
{
	if(type_is_void(d->ref))
		die_at(&d->where, "%s is void", d->spel);

	if(!type_is(d->ref, type_func) && d->bits.var.field_width)
		die_at(&d->where, "can't take size of a bitfield");

	return type_size(d->ref, &d->where);
}
コード例 #2
0
ファイル: backend_c.c プロジェクト: wm4/boringlang
static char *R(CTX *ctx, struct ir_inst *in, int i)
{
    struct ir_inst *r = inst_getuse(in, i);
    if (type_is_void(r->result_type))
        return VOID_VAL;
    if (r->scratch1_i < 0) {
        return gen_inst_inline(ctx, r);
    } else {
        return get_temp(ctx, r->scratch1_i);
    }
}
コード例 #3
0
ファイル: type.c プロジェクト: 8l/ucc-c-compiler
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);
}
コード例 #4
0
ファイル: backend_c.c プロジェクト: wm4/boringlang
static void gen_inst(CTX *ctx, struct ir_inst *in)
{
    struct ir_type res_t = in->result_type;
    bool is_void = type_is_void(res_t);
    // Don't write anything not needed. Note that void values are never actually
    // "used" by the generated C code, and instead are replaced with VOID_VAL.
    if (!ir_op_writes_side_effects(in->op) && !ir_op_is_branch(in->op)) {
        if (is_void)
            return;
        if (in->users_count == 0)
            return;
        // If it has 1 use, it can be generated inline.
        if (write_inline && in->users_count == 1) {
            // But if it crosses side effects, it must be generated earlier.
            // NOTE: if there are any not yet generated instructions with read
            //       side-effects, we must generate the code before crossing
            //       write side-effects as well.
            // Consider: t1=read; t2=neg(t1); call a(); call b(t2);
            // Mustn't turn into: call a(); call b(neg(read));
            bool crosses_sideffects = false;
            if (has_outstanding_sideffect_reads(in)) {
                struct ir_inst *user= in->users[0];
                for (struct ir_inst *cur = in; cur; cur = cur->next) {
                    if (cur == user)
                        break;
                    if (ir_op_writes_side_effects(cur->op)) {
                        crosses_sideffects = true;
                        break;
                    }
                }
            }
            if (!crosses_sideffects)
                return;
        }
    }

    set_loc(ctx, in->loc);
    indent(ctx);
    if (!TEST_UNION0(IR_TYPE, any, &res_t) && !is_void && in->users_count > 0)
    {
        if (in->scratch1_i == -1)
            in->scratch1_i = ctx->reg++;
        P(ctx, "%s %s = ", type(ctx, res_t), get_temp(ctx, in->scratch1_i));
    }

    write_inst(ctx, in, false);

    fprintf(ctx->f, ";\n");

    talloc_free_children(ctx->tmp);
}
コード例 #5
0
ファイル: backend_c.c プロジェクト: wm4/boringlang
static char *gen_inst_inline(CTX *ctx, struct ir_inst *in)
{
    assert(!ir_op_writes_side_effects(in->op) && !ir_op_is_branch(in->op)
           && !type_is_void(in->result_type) && in->users_count == 1);

    size_t sz = 0;
    char *data = NULL;

    FILE *oldf = ctx->f;
    ctx->f = open_memstream(&data, &sz);

    P(ctx, "(");
    write_inst(ctx, in, true);
    P(ctx, ")");

    int res = fclose(ctx->f);
    assert(res == 0);
    ctx->f = oldf;

    char *r = talloc_strdup(ctx->tmp, data);
    free(data);
    return r;
}
コード例 #6
0
ファイル: expr_if.c プロジェクト: 8l/ucc-c-compiler
void fold_expr_if(expr *e, symtable *stab)
{
	const char *desc = "?:";
	consty konst;
	type *tt_l, *tt_r;

	FOLD_EXPR(e->expr, stab);
	const_fold(e->expr, &konst);

	fold_check_expr(e->expr, FOLD_CHK_NO_ST_UN, desc);

	if(e->lhs){
		e->lhs = fold_expr_nonstructdecay(e->lhs, stab);
		fold_check_expr(e->lhs,
				FOLD_CHK_ALLOW_VOID,
				"?: left operand");
	}

	e->rhs = fold_expr_nonstructdecay(e->rhs, stab);
	fold_check_expr(e->rhs,
			FOLD_CHK_ALLOW_VOID,
			"?: right operand");

	e->freestanding = (e->lhs ? e->lhs : e->expr)->freestanding || e->rhs->freestanding;

	/*

	Arithmetic                             Arithmetic                           Arithmetic type after usual arithmetic conversions
	Structure or union type                Compatible structure or union type   Structure or union type with all the qualifiers on both operands
	void                                   void                                 void
	Pointer to compatible type             Pointer to compatible type           Pointer to type with all the qualifiers specified for the type
	Pointer to type                        NULL pointer (the constant 0)        Pointer to type
	Pointer to object or incomplete type   Pointer to void                      Pointer to void with all the qualifiers specified for the type

	GCC and Clang seem to relax the last rule:
		a) resolve if either is any pointer, not just (void *)
	  b) resolve to a pointer to the incomplete-type
	*/

	tt_l = (e->lhs ? e->lhs : e->expr)->tree_type;
	tt_r = e->rhs->tree_type;


	/* C11 6.5.15 */
	if(type_is_arith(tt_l) && type_is_arith(tt_r)){
		/* 6.5.15 p4 */
		expr **middle_op = e->lhs ? &e->lhs : &e->expr;

		expr_check_sign(desc, *middle_op, e->rhs, &e->where);

		e->tree_type = op_promote_types(
				op_unknown,
				middle_op, &e->rhs, stab,
				&e->where, desc);

	}else if(type_is_void(tt_l) || type_is_void(tt_r)){
		e->tree_type = type_nav_btype(cc1_type_nav, type_void);

	}else{
		const enum type_cmp cmp = type_cmp(tt_l, tt_r, 0);

		if((cmp & (TYPE_EQUAL_ANY | TYPE_QUAL_ADD | TYPE_QUAL_SUB))
		&& type_is_s_or_u(tt_l))
		{
			e->f_islval = expr_is_lval_struct;
			e->tree_type = type_qualify(tt_l, type_qual(tt_l) | type_qual(tt_r));

		}else{
			try_pointer_propagate(e, cmp, tt_l, tt_r);
		}
	}
}
コード例 #7
0
ファイル: backend_c.c プロジェクト: wm4/boringlang
static void gen_fn(CTX *ctx, struct ir_function *fn, char *name, bool visible)
{
    assert(!ctx->writing_types);

    if (!fn->parent)
        fn_complete_nested_calls(fn);

    for (int n = 0; n < fn->nested_functions_count; n++) {
        struct ir_function *nfn = fn->nested_functions[n];
        ctx->writing_types = true;
        char *nname = def_nested_fn(ctx, nfn);
        ctx->writing_types = false;
        gen_fn(ctx, nfn, nname, false);
    }

    fn_remove_global_ssa(fn);
    fn_verify(fn);
    //dump_fn(stderr, fn);

    for (int b = 0; b < fn->blocks_count; b++) {
        for (struct ir_inst *in = fn->blocks[b]->first; in; in = in->next)
            in->scratch1_i = -1;
    }

    // add all C types and function declarations needed for this function
    ctx->writing_types = true;
    do_fn_types(ctx, fn->type);
    for (int n = 0; n < fn->vars_count; n++)
        def_type(ctx, fn->vars[n]->type);
    for (int b = 0; b < fn->blocks_count; b++) {
        struct ir_bb *bb = fn->blocks[b];
        for (struct ir_inst *in = bb->first; in; in = in->next) {
            def_type(ctx, in->result_type);
            if (in->op == IR_OP_CALL || in->op == IR_OP_FN_PTR) {
                def_fn(ctx, in->fn);
            }
        }
    }
    ctx->writing_types = false;

    set_loc(ctx, fn->loc);
    if (!visible)
        fprintf(ctx->f, "static ");
    write_fn_type(ctx, fn->type, false, name);
    wf(ctx, " {");
    indent_in(ctx);
    for (int n = 0; n < fn->vars_count; n++) {
        struct ir_var *v = fn->vars[n];
        set_loc(ctx, v->loc);
        indent(ctx);
        P(ctx, "%s V%d", type(ctx, v->type), n);
        // void values are never assigned to (to avoid clashes with C's void);
        // since they have only one value, there's no need to. Initialize them
        // to avoid C warnings, though.
        if (type_is_void(v->type))
            P(ctx, " = {0}");
        P(ctx, ";\n");
    }
    indent(ctx);
    P(ctx, "goto B%d;\n", fn->entry->index);
    for (int b = 0; b < fn->blocks_count; b++) {
        struct ir_bb *bb = fn->blocks[b];
        indent(ctx);
        P(ctx, "B%d: {\n", b);
        indent_in(ctx);
        ctx->reg = 0;
        for (struct ir_inst *in = bb->first; in; in = in->next)
            gen_inst(ctx, in);
        indent_out(ctx);
        wf(ctx, "}");
    }
    indent_out(ctx);
    wf(ctx, "}");
}
コード例 #8
0
ファイル: backend_c.c プロジェクト: wm4/boringlang
static void write_inst(CTX *ctx, struct ir_inst *in, bool inner)
{
    if (in->comment)
        P(ctx, " /* COMMENT: %s */ ", in->comment);
    // @ALL ir_opcode
    switch (in->op) {
        case IR_OP_NOP:
            break;
        case IR_OP_COPY:
            P(ctx, "%s", R0);
            break;
        case IR_OP_GOTO:
            P(ctx, "goto B%d", BR(in, 0));
            break;
        case IR_OP_BRANCH:
            P(ctx, "if (%s) goto B%d; else goto B%d", R0, BR(in, 1), BR(in, 0));
            break;
        case IR_OP_RET:
            if (type_is_void(inst_getuse(in, 0)->result_type)) {
                P(ctx, "return");
            } else {
                P(ctx, "return %s", R0);
            }
            break;
        case IR_OP_ABORT:
            P(ctx, "abort()");
            break;
        case IR_OP_GETARG:
            P(ctx, "A%d", in->struct_member->index);
            break;
        case IR_OP_READ_VAR:
            P(ctx, "V%d", in->var->index);
            break;
        case IR_OP_WRITE_VAR:
            P(ctx, "V%d = %s", in->var->index, R0);
            break;
        case IR_OP_VAR_PTR:
            P(ctx, "&V%d", in->var->index);
            break;
        case IR_OP_GET_STRUCT_MEMBER_PTR: {
            struct name_temp t;
            P(ctx, "&(%s->%s)", R0, member_name(in->struct_member, &t));
            break;
        }
        case IR_OP_CONSTRUCT_STRUCT: {
            if (inner)
                P(ctx, "(%s)", type(ctx, in->result_type));
            P(ctx, "{");
            print_reads(ctx, in, 0);
            P(ctx, "}");
            break;
        }
        case IR_OP_GET_STRUCT_MEMBER: {
            struct name_temp t;
            P(ctx, "%s.%s", R0, member_name(in->struct_member, &t));
            break;
        }
        case IR_OP_SET_STRUCT_MEMBER: {
            //struct name_temp t;
            //bstr name = member_name(in->struct_member, &t);
            //P(ctx, "%s; %s.%.*s = %s", R0, in->write, BSTR_P(name), R1);
            abort();
            break;
        }
        case IR_OP_MAKE_CLOSURE:
            if (inner)
                P(ctx, "(%s)", type(ctx, in->result_type));
            P(ctx, "{ (void*)%s, %s }", R0, R1);
            break;
        case IR_OP_GET_CLOSURE_FN:
            P(ctx, "(%s) %s.fn", type(ctx, in->result_type), R0);
            break;
        case IR_OP_GET_CLOSURE_CTX:
            P(ctx, "%s.ctx", R0);
            break;
        case IR_OP_CONSTRUCT_SLICE:
        case IR_OP_SLICE:
        case IR_OP_SLICE_COPY:
        case IR_OP_SLICE_SET:
            assert(false); //TODO
        case IR_OP_GET_SLICE_LENGTH:
            P(ctx, "%s.len", R0);
            break;
        case IR_OP_GET_SLICE_PTR:
            P(ctx, "(%s)%s.ptr", type(ctx, in->result_type), R0);
            break;
        case IR_OP_GET_SLICE_ITEM_PTR:
            P(ctx, "(assert(%s < %s.len), ((%s)%s.ptr) + %s)", R1, R0,
              type(ctx, in->result_type), R0, R1);
            break;
        case IR_OP_CONSTRUCT_ARRAY:
            if (inner)
                P(ctx, "(%s)", type(ctx, in->result_type));
            P(ctx, "{ {");
            print_reads(ctx, in, 0);
            P(ctx, "} }");
            break;
        case IR_OP_ARRAY_TO_SLICE:
            if (inner)
                P(ctx, "(%s)", type(ctx, in->result_type));
            P(ctx, "{ &%s->a[0], %d }", R0, type_array_get_dimension
                                (type_unptr(inst_getuse(in, 0)->result_type)));
            break;
        case IR_OP_READ_PTR:
            P(ctx, "*%s", R0);
            break;
        case IR_OP_WRITE_PTR:
            P(ctx, "*%s = %s", R0, R1);
            break;
        case IR_OP_LOAD_CONST:
            if (inner)
                P(ctx, "(%s)", type(ctx, in->result_type));
            write_const(ctx, *in->const_value);
            break;
        case IR_OP_FN_PTR:
            P(ctx, "%s", def_fn(ctx, in->fn));
            break;
        case IR_OP_CALL: {
            P(ctx, "%s(", def_fn(ctx, in->fn));
            print_reads(ctx, in, 0);
            P(ctx, ")");
            break;
        }
        case IR_OP_CALL_PTR: {
            P(ctx, "%s(", R0);
            print_reads(ctx, in, 1);
            P(ctx, ")");
            break;
        }
        case IR_OP_NEG:
            P(ctx, "-%s", R0);
            break;
        case IR_OP_NOT:
            if (in->result_type.type == IR_TYPE_tbool) {
                // C promotes the bool to a larger integer, "~" won't work.
                P(ctx, "!%s", R0);
            } else {
                P(ctx, "~%s", R0);
            }
            break;
        case IR_OP_CONV_INT_SIGN:
        case IR_OP_CONV_INT_EXT:
        case IR_OP_CONV_INT_TRUNC:
        case IR_OP_CONV_TO_G_PTR:
        case IR_OP_CONV_FROM_G_PTR:
            P(ctx, "(%s)%s", type(ctx, in->result_type), R0);
            break;
        case IR_OP_ADD:
        case IR_OP_SUB:
        case IR_OP_MUL:
        case IR_OP_DIV:
        case IR_OP_MOD:
        case IR_OP_AND:
        case IR_OP_OR:
        case IR_OP_XOR:
        case IR_OP_SHIFT_R:
        case IR_OP_SHIFT_L:
        case IR_OP_EQ:
        case IR_OP_NOT_EQ:
        case IR_OP_CMP_LT:
        case IR_OP_CMP_GT:
        case IR_OP_CMP_LT_EQ:
        case IR_OP_CMP_GT_EQ:
        {
            // C integer promotion makes it hard to avoid the cast here.
            P(ctx, "(%s)(%s %s %s)", type(ctx, in->result_type), R0,
              optable[in->op], R1);
            break;
        }
        // not applicable
        case IR_OP_UPVAL_PTR:
        case IR_OP_UPVAL_CONTEXT:
        case IR_OP_PHI:
            assert(false);
        default:
            assert(false);
    }
}
コード例 #9
0
ファイル: backend_c.c プロジェクト: wm4/boringlang
static char *ret_type(CTX *ctx, struct ir_type t)
{
    // for C ABI compatibility
    return type_is_void(t) ? "void" : def_type(ctx, t);
}
コード例 #10
0
ファイル: type.c プロジェクト: 8l/ucc-c-compiler
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;
}
コード例 #11
0
ファイル: expr_if.c プロジェクト: guoguocat/ucc-c-compiler
void fold_expr_if(expr *e, symtable *stab)
{
	consty konst;
	type *tt_l, *tt_r;

	FOLD_EXPR(e->expr, stab);
	const_fold(e->expr, &konst);

	fold_check_expr(e->expr, FOLD_CHK_NO_ST_UN, "if-expr");

	if(e->lhs){
		FOLD_EXPR(e->lhs, stab);
		fold_check_expr(e->lhs,
				FOLD_CHK_NO_ST_UN | FOLD_CHK_ALLOW_VOID,
				"if-lhs");
	}

	FOLD_EXPR(e->rhs, stab);
	fold_check_expr(e->rhs,
			FOLD_CHK_NO_ST_UN | FOLD_CHK_ALLOW_VOID,
			"if-rhs");


	/*

	Arithmetic                             Arithmetic                           Arithmetic type after usual arithmetic conversions
	// Structure or union type                Compatible structure or union type   Structure or union type with all the qualifiers on both operands
	void                                   void                                 void
	Pointer to compatible type             Pointer to compatible type           Pointer to type with all the qualifiers specified for the type
	Pointer to type                        NULL pointer (the constant 0)        Pointer to type
	Pointer to object or incomplete type   Pointer to void                      Pointer to void with all the qualifiers specified for the type

	GCC and Clang seem to relax the last rule:
		a) resolve if either is any pointer, not just (void *)
	  b) resolve to a pointer to the incomplete-type
	*/

	tt_l = (e->lhs ? e->lhs : e->expr)->tree_type;
	tt_r = e->rhs->tree_type;

	if(type_is_integral(tt_l) && type_is_integral(tt_r)){
		expr **middle_op = e->lhs ? &e->lhs : &e->expr;

		expr_check_sign("?:", *middle_op, e->rhs, &e->where);

		e->tree_type = op_promote_types(
				op_unknown,
				middle_op, &e->rhs, &e->where, stab);

	}else if(type_is_void(tt_l) || type_is_void(tt_r)){
		e->tree_type = type_nav_btype(cc1_type_nav, type_void);

	}else if(type_cmp(tt_l, tt_r, 0) & TYPE_EQUAL_ANY){
		/* pointer to 'compatible' type */
		e->tree_type = type_qualify(tt_l,
				type_qual(tt_l) | type_qual(tt_r));

	}else{
		/* brace yourself. */
		int l_ptr_null = expr_is_null_ptr(
				e->lhs ? e->lhs : e->expr, NULL_STRICT_VOID_PTR);

		int r_ptr_null = expr_is_null_ptr(e->rhs, NULL_STRICT_VOID_PTR);

		int l_complete = !l_ptr_null && type_is_complete(tt_l);
		int r_complete = !r_ptr_null && type_is_complete(tt_r);

		if((l_complete && r_ptr_null) || (r_complete && l_ptr_null)){
			e->tree_type = l_ptr_null ? tt_r : tt_l;

		}else{
			int l_ptr = l_ptr_null || type_is(tt_l, type_ptr);
			int r_ptr = r_ptr_null || type_is(tt_r, type_ptr);

			if(l_ptr || r_ptr){
				fold_type_chk_warn(
						tt_l, tt_r, &e->where, "?: pointer type mismatch");

				/* qualified void * */
				e->tree_type = type_qualify(
						type_ptr_to(type_nav_btype(cc1_type_nav, type_void)),
						type_qual(tt_l) | type_qual(tt_r));

			}else{
				char buf[TYPE_STATIC_BUFSIZ];

				warn_at(&e->where, "conditional type mismatch (%s vs %s)",
						type_to_str(tt_l), type_to_str_r(buf, tt_r));

				e->tree_type = type_nav_btype(cc1_type_nav, type_void);
			}
		}
	}

	e->freestanding = (e->lhs ? e->lhs : e->expr)->freestanding || e->rhs->freestanding;
}