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); } } }
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 */ }
static void sentinel_check(where *w, expr *e, expr **args, const int variadic, const int nstdargs, symtable *stab) { #define ATTR_WARN_RET(w, ...) \ do{ cc1_warn_at(w, attr_sentinel, __VA_ARGS__); return; }while(0) attribute *attr = func_or_builtin_attr_present(e, attr_sentinel); int i, nvs; expr *sentinel; if(!attr) return; if(!variadic) return; /* warning emitted elsewhere, on the decl */ if(attr->bits.sentinel){ consty k; FOLD_EXPR(attr->bits.sentinel, stab); const_fold(attr->bits.sentinel, &k); if(k.type != CONST_NUM || !K_INTEGRAL(k.bits.num)) die_at(&attr->where, "sentinel attribute not reducible to integer constant"); i = k.bits.num.val.i; }else{ i = 0; } nvs = dynarray_count(args) - nstdargs; if(nvs == 0) ATTR_WARN_RET(w, "not enough variadic arguments for a sentinel"); UCC_ASSERT(nvs >= 0, "too few args"); if(i >= nvs) ATTR_WARN_RET(w, "sentinel index is not a variadic argument"); sentinel = args[(nstdargs + nvs - 1) - i]; /* must be of a pointer type, printf("%p\n", 0) is undefined */ if(!expr_is_null_ptr(sentinel, NULL_STRICT_ANY_PTR)) ATTR_WARN_RET(&sentinel->where, "sentinel argument expected (got %s)", type_to_str(sentinel->tree_type)); #undef ATTR_WARN_RET }
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_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; }