pawn-lang / compiler

Pawn compiler for SA-MP with bug fixes and new features - runs on Windows, Linux, macOS
Other
300 stars 70 forks source link

Referencing a following parameter in a complex sizeof/tagof default parameter expression #689

Open IS4Code opened 2 years ago

IS4Code commented 2 years ago

A simple sizeof/tagof expression works when referencing a parameter at the same or greater position, i.e.:

native n1(tag = tagof(val), {_,Float}:val); // works
native n2(size = sizeof(val), val[]); // works

This is an interesting behaviour in the first place, but it is useful enough. However, the following does not work:

native n3(tag = (tagof(val)), {_,Float}:val); // doesn't work
native n4(size = (sizeof(val)), val[]); // doesn't work

That is, using a complex expression (parentheses, operators etc.) with a sizeof/tagof operator that references the following parameters (or the current parameter) does not compile ('undefined symbol "val"').

I think the code that handles symbol resolution for default values should take all parameters into account, for the sake of consistency (and in case some macro modifies the expression).

Workspace Information:

Daniel-Cortez commented 2 years ago

I think the code that handles symbol resolution for default values should take all parameters into account

Well, that's the problem: there's no way to take an expression operand into account if it's not defined yet.

The point is that the functionalities for sizeof and tagof are actually duplicated: arg2=tagof(arg1) is handled by the code in function doarg() (file sc1.c), where the compiler memoizes the name of the operand of tagof, to process it after the function is fully declared and all arguments are known, while arg2=(tagof(arg1)) is processed in hier2() (sc3.c), as a part of a "normal" constant expression, which is why this kind of sizeof/tagof doesn't work on following function arguments, as they're not defined at the moment when the expression is parsed.

I believe that duplicating the functionalities of sizeof and tagof was a deliberate choice of the original author of Pawn to allow them to be used on function arguments that are defined after being referenced by the said operators. There's a downside to it though, as the compiler expects a ) or , right after the sizeof or tagof, so we can't do something like

Func(str[], length = sizeof(str) - 1) // error 001: expected token: ")", but found "-"
{ /* ... */ }

while this would work:

Func(str[], length = (sizeof(str) - 1)) // OK
{ /* ... */ }

In order to "fix" this (I'm not sure if this can really be considered a bug, hence the quotes), we could do the following:

IS4Code commented 2 years ago

@Daniel-Cortez Yeah, I imagine it would not be easy... In any case, suppressing the error and then either reparsing or updating what was parsed seems like the best option. Then the normal constant expression parsing could be used in all cases to support the sized(x)-1 case.

Daniel-Cortez commented 2 years ago

Another thing worth mentioning is that the default values are constant expressions, which means it's possible to use named constants there, but the user might forget to use a named constant instead of a hardcoded value in one of the declarations and/or the definition of the function, e.g.:

const DEFAULT_VAL = 1;
Func(arg = DEFAULT_VAL);

/*...*/

Func(arg = 1) // should be "arg = DEFAULT_VAL"
{
    // ...
}

In this example the resulting default value for arg would still be 1, so the compiler won't detect this kind of mistake, it would only become apparent when the value of DEFAULT_VAL is changed. This is where recording the default expressions and comparing them at each function re-declaration/definition might come in handy, although a simple strcmp(oldstr,newstr) won't do - we'll also need to remove excess whitespaces, so, for example, DEFAULT_VAL + 1 won't be treated as different from DEFAULT_VAL+1. There are also operators sizeof, tagof, __nameof and __addressof that actually require a whitespace after them if the argument isn't enclosed into parentheses, as otherwise they would break after the whitespace removal (e.g. sizeof a1 would become sizeofa1), so we'll have to work around them somehow.

Daniel-Cortez commented 2 years ago

Also, the following code

Func(const a[], size = (sizeof(a)))
    return a[0] - a[0] + arg;

would make the compiler print warning 224: indeterminate array size in "sizeof" expression (symbol "a") and assume 0 in place of sizeof, although after removing the excess pair of parentheses around sizeof(a)

Func(const a[], size = sizeof(a))
    return a[0] - a[0] + arg;

the code would compile with no errors or warnings. For now I'm not sure how to resolve this; perhaps we should make the compiler compute recorded default expressions at each function call if they cause warning 224 when first parsing them at the function declaration/definition.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity.