Message ID | 20210412212111.29186-3-luc.vanoostenryck@gmail.com (mailing list archive) |
---|---|
State | Mainlined, archived |
Headers | show |
Series | scheck: add a symbolic checker | expand |
On 12/04/2021 22:21, Luc Van Oostenryck wrote: > This can be used to define some generic (polymorphic) builtin > with a signature like: > <name>(int) > <name>(T, T) > <name>(int, T) > <name>(int, T, long, T, ... T) > where T is some integer type which will be instantiated at each call. > > Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com> > --- > builtin.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > builtin.h | 2 ++ > 2 files changed, 63 insertions(+) > > diff --git a/builtin.c b/builtin.c > index ff03dbab9a06..928e03050375 100644 > --- a/builtin.c > +++ b/builtin.c > @@ -390,6 +390,67 @@ static struct symbol_op overflow_p_op = { > }; > > > +/// > +// Evaluate the arguments of 'generic' integer operators. > +// > +// Parameters with a complete type are used like in a normal prototype. > +// The first parameter with a 'dynamic' type will be consider > +// as polymorphic and for each calls will be instancied with the type > +// of its effective argument. > +// The next dynamic parameters will the use this polymorphic type. > +// This allows to declare functions with some parameters having > +// a type variably defined at call time: > +// int foo(int, T, T); > +static int evaluate_generic_int_op(struct expression *expr) > +{ > + struct symbol *fntype = expr->fn->ctype->ctype.base_type; > + struct symbol_list *types = NULL; > + struct symbol *ctype = NULL; > + struct expression *arg; > + struct symbol *t; > + int n = 0; > + > + PREPARE_PTR_LIST(fntype->arguments, t); > + FOR_EACH_PTR(expr->args, arg) { Hmm, now n is always 0 in the error message, so: n++; here? > + if (!is_dynamic_type(t)) { > + ; > + } else if (!ctype) { > + // fist 'dynamic' type, chat that it is an integer s/chat/check/ > + t = arg->ctype; > + if (!t) > + return 0; > + if (t->type == SYM_NODE) > + t = t->ctype.base_type; > + if (!t) > + return 0; > + if (t->ctype.base_type != &int_type) > + goto err; > + > + // next 'dynamic' arguments will use this type > + ctype = t; > + } else { > + // use the previous 'dynamic' type > + t = ctype; > + } > + add_ptr_list(&types, t); > + NEXT_PTR_LIST(t); > + } END_FOR_EACH_PTR(arg); > + FINISH_PTR_LIST(t); > + return evaluate_arguments(types, expr->args); Hmm, does this do the usual argument promotions, so e.g. an 'generic' 'short' gets promoted to 'int' in the prototype? I guess not, that would have to be done above, while adding to the types list, right? Hmm, I would have to study evaluate_arguments(), but it may be worth a comment here? > + > +err: > + sparse_error(arg->pos, "non-integer type for argument %d:", n); > + info(arg->pos, " %s", show_typename(arg->ctype)); > + expr->ctype = &bad_ctype; > + return 0; > +} So, this certainly looks better. Thanks! ;-) ATB, Ramsay Jones > + > +struct symbol_op generic_int_op = { > + .args = args_prototype, > + .evaluate = evaluate_generic_int_op, > +}; > + > + > static int eval_atomic_common(struct expression *expr) > { > struct symbol *fntype = expr->fn->ctype->ctype.base_type; > diff --git a/builtin.h b/builtin.h > index 9cb6728444fe..5fe77c926244 100644 > --- a/builtin.h > +++ b/builtin.h > @@ -14,4 +14,6 @@ struct builtin_fn { > > void declare_builtins(int stream, const struct builtin_fn tbl[]); > > +extern struct symbol_op generic_int_op; > + > #endif >
On Tue, Apr 13, 2021 at 01:23:00AM +0100, Ramsay Jones wrote: > On 12/04/2021 22:21, Luc Van Oostenryck wrote: > > + int n = 0; > > + > > + PREPARE_PTR_LIST(fntype->arguments, t); > > + FOR_EACH_PTR(expr->args, arg) { > > Hmm, now n is always 0 in the error message, so: > n++; > here? Hehe, yes indeed. > > + if (!is_dynamic_type(t)) { > > + ; > > + } else if (!ctype) { > > + // fist 'dynamic' type, chat that it is an integer > > s/chat/check/ fixed. > > + return evaluate_arguments(types, expr->args); > > Hmm, does this do the usual argument promotions, so e.g. an 'generic' > 'short' gets promoted to 'int' in the prototype? I guess not, that > would have to be done above, while adding to the types list, right? Well, evaluate_arguments() is the normal function used to evaluate the arguments of all function calls, so, yes, it does arguments promotion but only when the type is not specified (so either the '...' of varadic function or an argument of a variadic builtin's which is declared as NULL in the corresponding struct builtin_fn (non-variadic builtins can't have such NULL arguments because the first NULL is used to determine its arity and this is then normaly checked in the .args method before the evaluation)). > Hmm, I would have to study evaluate_arguments(), but it may be worth > a comment here? Not here, because there is nothing special but sure, evaluate_arguments() should be documented and even more so how struct builtin_fn should be used (because it's much more complex since a lot of builtins doesn't follow the way normal C declarations rules). > > So, this certainly looks better. Thanks! ;-) Thanks to you! -- Luc
diff --git a/builtin.c b/builtin.c index ff03dbab9a06..928e03050375 100644 --- a/builtin.c +++ b/builtin.c @@ -390,6 +390,67 @@ static struct symbol_op overflow_p_op = { }; +/// +// Evaluate the arguments of 'generic' integer operators. +// +// Parameters with a complete type are used like in a normal prototype. +// The first parameter with a 'dynamic' type will be consider +// as polymorphic and for each calls will be instancied with the type +// of its effective argument. +// The next dynamic parameters will the use this polymorphic type. +// This allows to declare functions with some parameters having +// a type variably defined at call time: +// int foo(int, T, T); +static int evaluate_generic_int_op(struct expression *expr) +{ + struct symbol *fntype = expr->fn->ctype->ctype.base_type; + struct symbol_list *types = NULL; + struct symbol *ctype = NULL; + struct expression *arg; + struct symbol *t; + int n = 0; + + PREPARE_PTR_LIST(fntype->arguments, t); + FOR_EACH_PTR(expr->args, arg) { + if (!is_dynamic_type(t)) { + ; + } else if (!ctype) { + // fist 'dynamic' type, chat that it is an integer + t = arg->ctype; + if (!t) + return 0; + if (t->type == SYM_NODE) + t = t->ctype.base_type; + if (!t) + return 0; + if (t->ctype.base_type != &int_type) + goto err; + + // next 'dynamic' arguments will use this type + ctype = t; + } else { + // use the previous 'dynamic' type + t = ctype; + } + add_ptr_list(&types, t); + NEXT_PTR_LIST(t); + } END_FOR_EACH_PTR(arg); + FINISH_PTR_LIST(t); + return evaluate_arguments(types, expr->args); + +err: + sparse_error(arg->pos, "non-integer type for argument %d:", n); + info(arg->pos, " %s", show_typename(arg->ctype)); + expr->ctype = &bad_ctype; + return 0; +} + +struct symbol_op generic_int_op = { + .args = args_prototype, + .evaluate = evaluate_generic_int_op, +}; + + static int eval_atomic_common(struct expression *expr) { struct symbol *fntype = expr->fn->ctype->ctype.base_type; diff --git a/builtin.h b/builtin.h index 9cb6728444fe..5fe77c926244 100644 --- a/builtin.h +++ b/builtin.h @@ -14,4 +14,6 @@ struct builtin_fn { void declare_builtins(int stream, const struct builtin_fn tbl[]); +extern struct symbol_op generic_int_op; + #endif
This can be used to define some generic (polymorphic) builtin with a signature like: <name>(int) <name>(T, T) <name>(int, T) <name>(int, T, long, T, ... T) where T is some integer type which will be instantiated at each call. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com> --- builtin.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 2 ++ 2 files changed, 63 insertions(+)