diff mbox series

[v2,2/8] builtin: define a symbol_op for a generic op acting on integer

Message ID 20210412212111.29186-3-luc.vanoostenryck@gmail.com (mailing list archive)
State Mainlined, archived
Headers show
Series scheck: add a symbolic checker | expand

Commit Message

Luc Van Oostenryck April 12, 2021, 9:21 p.m. UTC
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(+)

Comments

Ramsay Jones April 13, 2021, 12:23 a.m. UTC | #1
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
>
Luc Van Oostenryck April 13, 2021, 4:42 p.m. UTC | #2
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 mbox series

Patch

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