static int check_arg_counts(
		funcargs *args_from_decl,
		unsigned count_decl,
		expr **exprargs,
		expr *fnexpr, char *sp)
{
	where *const loc = &fnexpr->where;

	/* this block is purely count checking */
	if(!FUNCARGS_EMPTY_NOVOID(args_from_decl)){
		const unsigned count_arg  = dynarray_count(exprargs);

		if(count_decl != count_arg
		&& (args_from_decl->variadic ? count_arg < count_decl : 1))
		{
			decl *call_decl;
			/* may be args_old_proto but also args_void if copied from
			 * another prototype elsewhere */
			int warn = args_from_decl->args_old_proto
				&& !args_from_decl->args_void;
			int warning_emitted = 1;

#define common_warning                                         \
					"too %s arguments to function %s%s(got %d, need %d)",\
					count_arg > count_decl ? "many" : "few",             \
					sp ? sp : "",                                        \
					sp ? " " : "",                                       \
					count_arg, count_decl

			if(warn){
				warning_emitted = cc1_warn_at(loc, funcall_argcount, common_warning);
			}else{
				warn_at_print_error(loc, common_warning);
			}

#undef common_warning

			if(warning_emitted
			&& (call_decl = expr_to_declref(fnexpr->expr, NULL)))
			{
				note_at(&call_decl->where, "'%s' declared here", call_decl->spel);
			}

			if(!warn){
				fold_had_error = 1;
				return 1;
			}
		}
	}else if(args_from_decl->args_void_implicit && exprargs){
		cc1_warn_at(loc, funcall_argcount,
				"too many arguments to implicitly (void)-function");
	}
	return 0;
}
static void static_array_check(
		decl *arg_decl, expr *arg_expr)
{
	/* if ty_func is x[static %d], check counts */
	type *ty_expr = arg_expr->tree_type;
	type *ty_decl = decl_is_decayed_array(arg_decl);
	consty k_decl;

	if(!ty_decl)
		return;

	assert(ty_decl->type == type_array);
	if(!ty_decl->bits.array.is_static)
		return;

	/* want to check any pointer type */
	if(expr_is_null_ptr(arg_expr, NULL_STRICT_ANY_PTR)){
		cc1_warn_at(&arg_expr->where,
				attr_nonnull,
				"passing null-pointer where array expected");
		return;
	}

	if(!ty_decl->bits.array.size)
		return;

	const_fold(ty_decl->bits.array.size, &k_decl);

	if((ty_expr = type_is_decayed_array(ty_expr))){

		assert(ty_expr->type == type_array);

		if(ty_expr->bits.array.size){
			consty k_arg;

			const_fold(ty_expr->bits.array.size, &k_arg);

			if(k_decl.type == CONST_NUM
			&& K_INTEGRAL(k_arg.bits.num)
			&& k_arg.bits.num.val.i < k_decl.bits.num.val.i)
			{
				cc1_warn_at(&arg_expr->where,
						static_array_bad,
						"array of size %" NUMERIC_FMT_D
						" passed where size %" NUMERIC_FMT_D " needed",
						k_arg.bits.num.val.i, k_decl.bits.num.val.i);
			}
		}
	}
	/* else it's a random pointer, just be quiet */
}
Beispiel #3
0
int symtab_fold(symtable *tab, int current)
{
	const int this_start = current;

	if(tab->decls){
		const int word_size = platform_word_size();
		decl **diter;
		int arg_offset;

		arg_offset = 0;

		/* need to walk backwards for args */
		for(diter = tab->decls; *diter; diter++);

		for(diter--; diter >= tab->decls; diter--){
			sym *s = (*diter)->sym;
			/*enum type_primitive last = type_int; TODO: packing */

			if(s->type == sym_local && (s->decl->type->spec & (spec_extern | spec_static)) == 0){
				int siz = decl_size(s->decl);

				if(siz <= word_size)
					s->offset = current;
				else
					s->offset = current + siz - word_size; /* an array and structs start at the bottom */

				/* need to increase by a multiple of word_size */
				if(siz % word_size)
					siz += word_size - siz % word_size;
				current += siz;

				/* static analysis on sym (only auto-vars) */
				if(s->nwrites == 0 && !decl_has_array(s->decl)){
					cc1_warn_at(&s->decl->where, 0, WARN_SYM_NEVER_WRITTEN, "\"%s\" never written to", s->decl->spel);
					s->nwrites++; /* only warn once */
				}

			}else if(s->type == sym_arg){
				s->offset = arg_offset;
				arg_offset += word_size;

			}
		}
	}

	{
		symtable **tabi;
		int subtab_max = 0;

		for(tabi = tab->children; tabi && *tabi; tabi++){
			int this = symtab_fold(*tabi, current);
			if(this > subtab_max)
				subtab_max = this;
		}

		tab->auto_total_size = current - this_start + subtab_max;
	}

	return tab->auto_total_size;
}
static attribute *parse_attr_single(const char *ident, symtable *scope)
{
    symtable_global *glob;
    int i;
    where attrloc;

    for(i = 0; attrs[i].ident; i++) {
        char buf[MAX_FMT_LEN];
        if(!strcmp(attrs[i].ident, ident)
                || (snprintf(buf, sizeof buf, "__%s__", attrs[i].ident), !strcmp(buf, ident)))
        {
            return attrs[i].parser(scope, attrs[i].ident);
        }
    }

    where_cc1_current(&attrloc);
    attrloc.chr -= strlen(ident);

    /* unrecognised - only do the warning (and map checking) if non system-header */
    if(cc1_warning.system_headers || !where_in_sysheader(&attrloc)) {
        glob = symtab_global(scope);
        if(!dynmap_exists(char *, glob->unrecog_attrs, (char *)ident)) {
            char *dup = ustrdup(ident);

            if(!glob->unrecog_attrs)
                glob->unrecog_attrs = dynmap_new(char *, strcmp, dynmap_strhash);

            dynmap_set(char *, void *, glob->unrecog_attrs, dup, NULL);

            cc1_warn_at(&attrloc, attr_unknown,
                        "ignoring unrecognised attribute \"%s\"", ident);
        }
    }
static attribute *parse_attr_section(symtable *symtab, const char *ident)
{
    /* __attribute__((section ("sectionname"))) */
    attribute *da;
    char *func;
    size_t len, i;

    (void)symtab;
    (void)ident;

    EAT(token_open_paren);

    if(curtok != token_string)
        die_at(NULL, "string expected for section");

    token_get_current_str(&func, &len, NULL, NULL);
    EAT(token_string);

    for(i = 0; i < len; i++)
        if(!isprint(func[i])) {
            if(i < len - 1 || func[i] != '\0')
                cc1_warn_at(NULL, attr_section_badchar,
                            "character 0x%x detected in section", func[i]);
            break;
        }

    da = attribute_new(attr_section);

    da->bits.section = func;

    EAT(token_close_paren);

    return da;
}
static void check_arg_voidness_and_nonnulls(
		expr *callexpr, symtable *stab,
		funcargs *args_from_decl, unsigned count_decl,
		expr **exprargs, char *sp)
{
	/* this block folds the args and type-checks */
	unsigned long nonnulls = 0;
	unsigned i;
	attribute *da;

	if((da = func_or_builtin_attr_present(callexpr, attr_nonnull)))
		nonnulls = da->bits.nonnull_args;

	for(i = 0; exprargs[i]; i++){
		expr *arg = FOLD_EXPR(exprargs[i], stab);
		char buf[64];

		ARG_BUF(buf, i, sp);

		if(fold_check_expr(arg, FOLD_CHK_NO_ST_UN, buf))
			continue;

		if(i < count_decl && (nonnulls & (1 << i))
		&& type_is_ptr(args_from_decl->arglist[i]->ref)
		&& expr_is_null_ptr(arg, NULL_STRICT_INT))
		{
			cc1_warn_at(&arg->where,
					attr_nonnull,
					"null passed where non-null required (arg %d)",
					i + 1);
		}
	}
}
Beispiel #7
0
void token_get_current_str(
		char **ps, size_t *pl, int *pwide, where *w)
{
	extern char *currentstring;
	extern size_t currentstringlen;
	extern int   currentstringwide;

	*ps = currentstring;

	if(pwide)
		*pwide = currentstringwide;
	else if(currentstringwide)
		die_at(NULL, "wide string not wanted");

	if(w){
		extern where currentstringwhere;
		memcpy_safe(w, &currentstringwhere);
	}

	if(pl){
		*pl = currentstringlen;
	}else{
		char *p = memchr(currentstring, '\0', currentstringlen);

		if(p && p < currentstring + currentstringlen - 1)
			cc1_warn_at(NULL, str_contain_nul,
					"nul-character terminates string early (%s)", p + 1);
	}

	currentstring = NULL;
	currentstringlen = 0;
}
Beispiel #8
0
static void handle_pragma_stdc(const char *pragma, where *loc)
{
	/*
	 * #pragma STDC FP_CONTRACT <arg>
	 * #pragma STDC FENV_ACCESS <arg>
	 * #pragma STDC CX_LIMITED_RANGE <arg>
	 */
	cc1_warn_at(loc, unknown_pragma, "unhandled STDC pragma '%s'", pragma);
}
static void check_implicit_funcall(expr *e, symtable *stab, char **const psp)
{
	struct symtab_entry ent;
	funcargs *args;
	decl *df, *owning_func;
	type *func_ty;

	if(e->expr->in_parens
	|| !expr_kind(e->expr, identifier)
	/* not folded yet, hence no 'e->expr->bits.ident.type != IDENT_NORM' */
	/* get the spel that parse stashes in the identifier expr: */
	|| !((*psp) = e->expr->bits.ident.bits.ident.spel))
	{
		return;
	}

	/* check for implicit function */
	if(symtab_search(stab, *psp, NULL, &ent)
	&& ent.type == SYMTAB_ENT_DECL)
	{
		e->expr->bits.ident.bits.ident.sym = ent.bits.decl->sym;
		return;
	}

	args = funcargs_new();

	/* set up the funcargs as if it's "x()" - i.e. any args */
	funcargs_empty(args);

	func_ty = type_func_of(
			type_nav_btype(cc1_type_nav, type_int),
			args,
			symtab_new(stab, &e->where) /*new symtable for args*/);

	cc1_warn_at(&e->expr->where, implicit_func,
			"implicit declaration of function \"%s\"", *psp);

	df = decl_new();
	memcpy_safe(&df->where, &e->where);
	df->ref = func_ty;
	df->spel = e->expr->bits.ident.bits.ident.spel;
	df->flags |= DECL_FLAGS_IMPLICIT;

	fold_decl(df, stab); /* update calling conv, for e.g. */

	df->sym->type = sym_global;

	e->expr->bits.ident.bits.ident.sym = df->sym;
	e->expr->tree_type = func_ty;

	owning_func = symtab_func(stab);
	if(owning_func)
		symtab_insert_before(symtab_root(stab), owning_func, df);
	else
		symtab_add_to_scope(symtab_root(stab), df); /* function call at global scope */
}
Beispiel #10
0
void fold_expr_compound_lit(expr *e, symtable *stab)
{
	decl *d = e->bits.complit.decl;
	int static_ctx = e->bits.complit.static_ctx; /* global or static */

	if(cc1_std < STD_C99)
		cc1_warn_at(&e->where, c89_compound_literal,
				"compound literals are a C99 feature");

	/* if(!stab->parent) assert(static_ctx);
	 *
	 * except things like sizeof() just pass 0 for static_ctx,
	 * as it doesn't matter, we're not code-gen'd
	 */
	if(!stab->parent)
		static_ctx = 1;

	if(COMP_LIT_INITIALISED(e))
		return; /* being called from fold_gen_init_assignment_base */

	/* must be set before the recursive fold_gen_init_assignment_base */
	e->tree_type = d->ref;

	if(static_ctx){
		assert(!d->spel_asm);
		d->spel_asm = out_label_data_store(STORE_COMP_LIT);
		d->store = store_static;
	}

	e->bits.complit.sym = sym_new_and_prepend_decl(
			stab, d, static_ctx ? sym_global : sym_local);

	/* fold the initialiser */
	UCC_ASSERT(d->bits.var.init.dinit, "no init for comp.literal");

	decl_init_brace_up_fold(d, stab);

	/*
	 * update the type, for example if an array type has been completed
	 * this is done before folds, for array bounds checks
	 */
	e->tree_type = d->ref;

	if(!static_ctx){
		/* create the code for assignemnts
		 *
		 * - we must create a nested scope,
		 *   otherwise any other decls in stab's scope will
		 *   be generated twice - once for the scope we're nested in (stab),
		 *   and again on our call to gen_stmt() in our gen function
		 */
		decl_init_create_assignments_base_and_fold(d, e, stab);
	}else{
		fold_decl_global_init(d, stab);
	}
}
Beispiel #11
0
static void handle_pragma_ucc(const char *pragma, where *loc)
{
	if(!strncmp(pragma, "namespace ", 10)){
		free(ucc_namespace);
		ucc_namespace = ustrdup(pragma + 10);
		fprintf(stderr, "namespace \"%s\"\n", ucc_namespace);
	}else{
		cc1_warn_at(loc, unknown_pragma, "unknown ucc pragma '%s'", pragma);
	}
}
Beispiel #12
0
static attribute *parse_attr_format(symtable *symtab, const char *ident)
{
    /* __attribute__((format (printf, fmtarg, firstvararg))) */
    attribute *da;
    char *func;
    enum fmt_type fmt;

    (void)symtab;
    (void)ident;

    EAT(token_open_paren);

    func = token_current_spel();
    EAT(token_identifier);

    /* TODO: token_current_spel()
     * and token_get_current_str(..,..)
     * checks everywhere */
    if(!func)
        return NULL;

#define CHECK(s) !strcmp(func, s) || !strcmp(func, "__" s "__")
    if(CHECK("printf")) {
        fmt = attr_fmt_printf;
    } else if(CHECK("scanf")) {
        fmt = attr_fmt_scanf;
    } else {
        cc1_warn_at(NULL, attr_format_unknown,
                    "unknown format func \"%s\"", func);
        parse_attr_bracket_chomp(1);
        return NULL;
    }

    da = attribute_new(attr_format);
    da->bits.format.fmt_func = fmt;

    EAT(token_comma);

    da->bits.format.fmt_idx = currentval.val.i - 1;
    EAT(token_integer);

    EAT(token_comma);

    da->bits.format.var_idx = currentval.val.i - 1;
    EAT(token_integer);

    EAT(token_close_paren);

    return da;
}
Beispiel #13
0
static attribute *parse_attr_nonnull(symtable *symtab, const char *ident)
{
    /* __attribute__((nonnull(1, 2, 3, 4...)))
     * or
     * __attribute__((nonnull)) - all args
     */
    attribute *da = attribute_new(attr_nonnull);
    unsigned long l = 0;
    int had_error = 0;

    (void)symtab;
    (void)ident;

    if(accept(token_open_paren)) {
        while(curtok != token_close_paren) {
            if(curtok == token_integer) {
                int n = currentval.val.i;
                if(n <= 0) {
                    /* shouldn't ever be negative */
                    cc1_warn_at(NULL,
                                attr_nonnull_bad,
                                "%s nonnull argument ignored", n < 0 ? "negative" : "zero");
                    had_error = 1;
                } else {
                    /* implicitly disallow functions with >32 args */
                    /* n-1, since we convert from 1-base to 0-base */
                    l |= 1 << (n - 1);
                }
            } else {
                EAT(token_integer); /* raise error */
            }
            EAT(curtok);

            if(accept(token_comma))
                continue;
            break;
        }
        EAT(token_close_paren);
    }

    /* if we had an error, go with what we've got, (even if it's nothing), to avoid spurious warnings */
    da->bits.nonnull_args = (l || had_error) ? l : ~0UL; /* all if 0 */

    return da;
}
Beispiel #14
0
static void warn_value_changed_at(
		where *w,
		const char *infmt,
		int signed_in, int signed_out,
		integral_t a, integral_t b)
{
	char *fmt = ustrdup(infmt);
	char *p = fmt;

	for(;;){
		p = strchr(p, '%');
		if(!p)
			break;
		p += 3;
		if(*p == 'A' || *p == 'B'){
			*p = (*p == 'A' ? signed_in : signed_out) ? 'd' : 'u';
		}
	}

	cc1_warn_at(w, overflow, fmt, a, b);
	free(fmt);
}
Beispiel #15
0
void pragma_handle(const char *pragma, where *loc)
{
	if(!strncmp(pragma, "STDC", 4)){
		handle_pragma_stdc(str_spc_skip(pragma + 4), loc);

	}else if(!strncmp(pragma, "ucc", 3)){
		handle_pragma_ucc(str_spc_skip(pragma + 3), loc);

	}else{
		/*
		 * #pragma GCC visibility <arg>
		 * #pragma align <arg>
		 * #pragma pack <arg>
		 * #pragma section <arg>
		 * #pragma unused <arg>
		 * #pragma weak <arg>
		 * #pragma attribute <arg>
		 */

		cc1_warn_at(loc, unknown_pragma, "unknown pragma '%s'", pragma);
	}
}
Beispiel #16
0
void bitfield_trunc_check(decl *mem, expr *from)
{
	consty k;

	if(expr_kind(from, cast)){
		/* we'll warn about bitfield truncation, prevent warnings
		 * about cast truncation
		 */
		from->expr_cast_implicit = 0;
	}

	const_fold(from, &k);

	if(k.type == CONST_NUM){
		const sintegral_t kexp = k.bits.num.val.i;
		/* highest may be -1 - kexp is zero */
		const int highest = integral_high_bit(k.bits.num.val.i, from->tree_type);
		const int is_signed = type_is_signed(mem->bits.var.field_width->tree_type);

		const_fold(mem->bits.var.field_width, &k);

		UCC_ASSERT(k.type == CONST_NUM, "bitfield size not val?");
		UCC_ASSERT(K_INTEGRAL(k.bits.num), "fp bitfield size?");

		if(highest > (sintegral_t)k.bits.num.val.i
		|| (is_signed && highest == (sintegral_t)k.bits.num.val.i))
		{
			sintegral_t kexp_to = kexp & ~(-1UL << k.bits.num.val.i);

			cc1_warn_at(&from->where,
					bitfield_trunc,
					"truncation in store to bitfield alters value: "
					"%" NUMERIC_FMT_D " -> %" NUMERIC_FMT_D,
					kexp, kexp_to);
		}
	}
}
Beispiel #17
0
static void try_pointer_propagate(
		expr *e, enum type_cmp cmp,
		type *const tt_l, type *const tt_r)
{
	/* 6.5.15 p6 */
	int l_ptr = !!type_is_ptr_or_block(tt_l);
	int r_ptr = !!type_is_ptr_or_block(tt_r);

	/* if both the second and third operands are pointers */
	if(l_ptr && r_ptr){
		int allowed = TYPE_EQUAL_ANY
				| TYPE_QUAL_ADD
				| TYPE_QUAL_SUB
				| TYPE_QUAL_POINTED_ADD
				| TYPE_QUAL_POINTED_SUB;

		if(cmp & allowed){
			e->tree_type = pointer_to_qualified(type_next(tt_l), tt_l, tt_r);
		}
	}

	if(!e->tree_type && (l_ptr || r_ptr)){
		/* or one is a null pointer constant and the other is a pointer */
		int l_ptr_null = expr_is_null_ptr(
				e->lhs ? e->lhs : e->expr, NULL_STRICT_INT);

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

		/* both may still be pointers here */
		if((l_ptr && r_ptr_null) || (r_ptr && l_ptr_null)){
			type *pointed_to;

			if(l_ptr_null != r_ptr_null){
				/* only one is an int - pick the other side */
				pointed_to = type_next(l_ptr_null ? tt_r : tt_l);
			}else{
				/* both are pointers, pick either side */
				pointed_to = type_next(l_ptr ? tt_l : tt_r);
			}

			e->tree_type = pointer_to_qualified(
					pointed_to,
					l_ptr ? tt_l : NULL,
					r_ptr ? tt_r : NULL);
		}
	}

	if(!e->tree_type && l_ptr && r_ptr){
		e->tree_type = pointer_to_qualified(
					type_nav_btype(cc1_type_nav, type_void),
					tt_l, tt_r);

		/* gcc/clang relax the rule here.
		 * 0 ? (A *)0 : (B *)0
		 * becomes a void pointer too */
		if(!type_is_void_ptr(tt_l) && !type_is_void_ptr(tt_r)){
			char buf[TYPE_STATIC_BUFSIZ];

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

	if(!e->tree_type){
		char buf[TYPE_STATIC_BUFSIZ];

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

		fold_had_error = 1;

		e->tree_type = type_nav_btype(cc1_type_nav, type_void);
	}
}
void fold_expr_funcall(expr *e, symtable *stab)
{
	type *func_ty;
	funcargs *args_from_decl;
	char *sp = NULL;
	unsigned count_decl;

	check_implicit_funcall(e, stab, &sp);

	FOLD_EXPR(e->expr, stab);
	func_ty = e->expr->tree_type;

	if(!type_is_callable(func_ty)){
		warn_at_print_error(&e->expr->where,
				"%s-expression (type '%s') not callable",
				expr_str_friendly(e->expr, 0),
				type_to_str(func_ty));

		fold_had_error = 1;

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

	e->tree_type = type_func_call(func_ty, &args_from_decl);

	/* func count comparison, only if the func has arg-decls, or the func is f(void) */
	UCC_ASSERT(args_from_decl, "no funcargs for decl %s", sp);

	count_decl = dynarray_count(args_from_decl->arglist);

	if(check_arg_counts(args_from_decl, count_decl, e->funcargs, e, sp))
		return;

	if(e->funcargs){
		check_arg_voidness_and_nonnulls(
				e, stab,
				args_from_decl, count_decl,
				e->funcargs, sp);
	}

	if(!FUNCARGS_EMPTY_NOVOID(args_from_decl))
		check_arg_types(args_from_decl, e->funcargs, stab, sp, &e->where);

	if(e->funcargs)
		default_promote_args(e->funcargs, count_decl, stab);

	if(type_is_s_or_u(e->tree_type)){
		/* handled transparently by the backend */
		e->f_islval = expr_is_lval_struct;

		cc1_warn_at(&e->expr->where,
				aggregate_return,
				"called function returns aggregate (%s)",
				type_to_str(e->tree_type));
	}

	/* attr */
	{
		type *fnty = e->expr->tree_type;

		/* look through decays */
		if(expr_kind(e->expr, cast) && expr_cast_is_lval2rval(e->expr))
			fnty = expr_cast_child(e->expr)->tree_type;

		format_check_call(fnty, e->funcargs, args_from_decl->variadic);

		sentinel_check(
				&e->where, e,
				e->funcargs, args_from_decl->variadic,
				count_decl, stab);
	}

	/* check the subexp tree type to get the funcall attributes */
	if(func_or_builtin_attr_present(e, attr_warn_unused))
		e->freestanding = 0; /* needs use */

	if(sp && !cc1_fopt.freestanding)
		check_standard_funcs(sp, e->funcargs);
}
Beispiel #19
0
void fold_expr_funcall(expr *e, symtable *stab)
{
	decl *df;
	funcargs *args_exp;

	if(expr_kind(e->expr, identifier) && e->expr->spel){
		char *const sp = e->expr->spel;

		e->sym = symtab_search(stab, sp);
		if(!e->sym){
			df = decl_new_where(&e->where);

			df->type->primitive = type_int;
			df->type->spec     |= spec_extern;

			cc1_warn_at(&e->where, 0, WARN_IMPLICIT_FUNC, "implicit declaration of function \"%s\"", sp);

			df->spel = sp;

			df->funcargs = funcargs_new();

			if(e->funcargs)
				/* set up the funcargs as if it's "x()" - i.e. any args */
				function_empty_args(df->funcargs);

			e->sym = symtab_add(symtab_root(stab), df, sym_global, SYMTAB_WITH_SYM, SYMTAB_PREPEND);
		}else{
			df = e->sym->decl;
		}

		fold_expr(e->expr, stab);
	}else{
		fold_expr(e->expr, stab);

		/*
		 * convert int (*)() to remove the deref
		 */
		if(decl_is_func_ptr(e->expr->tree_type)){
			/* XXX: memleak */
			e->expr = e->expr->lhs;
			fprintf(stderr, "FUNCPTR\n");
		}else{
			fprintf(stderr, "decl %s\n", decl_to_str(e->expr->tree_type));
		}

		df = e->expr->tree_type;

		if(!decl_is_callable(df)){
			die_at(&e->expr->where, "expression %s (%s) not callable",
					e->expr->f_str(),
					decl_to_str(df));
		}
	}

	e->tree_type = decl_copy(df);
	/*
	 * int (*x)();
	 * (*x)();
	 * evaluates to tree_type = int;
	 */
	decl_func_deref(e->tree_type);


	if(e->funcargs){
		expr **iter;
		for(iter = e->funcargs; *iter; iter++)
			fold_expr(*iter, stab);
	}

	/* func count comparison, only if the func has arg-decls, or the func is f(void) */
	args_exp = decl_funcargs(e->tree_type);

	UCC_ASSERT(args_exp, "no funcargs for decl %s", df->spel);

	if(args_exp->arglist || args_exp->args_void){
		expr **iter_arg;
		decl **iter_decl;
		int count_decl, count_arg;

		count_decl = count_arg = 0;

		for(iter_arg  = e->funcargs;       iter_arg  && *iter_arg;  iter_arg++,  count_arg++);
		for(iter_decl = args_exp->arglist; iter_decl && *iter_decl; iter_decl++, count_decl++);

		if(count_decl != count_arg && (args_exp->variadic ? count_arg < count_decl : 1)){
			die_at(&e->where, "too %s arguments to function %s (got %d, need %d)",
					count_arg > count_decl ? "many" : "few",
					df->spel, count_arg, count_decl);
		}

		if(e->funcargs){
			funcargs *argument_decls = funcargs_new();

			for(iter_arg = e->funcargs; *iter_arg; iter_arg++)
				dynarray_add((void ***)&argument_decls->arglist, (*iter_arg)->tree_type);

			fold_funcargs_equal(args_exp, argument_decls, 1, &e->where, "argument", df->spel);
			funcargs_free(argument_decls, 0);
		}
	}
}