Bug #10365
Mismatched number of parameters passed to routine for external function
100%
History
#1 Updated by Radu Apetrii 12 months ago
The 4GL testcase:
start.p:
DEFINE VARIABLE h AS HANDLE. FUNCTION f RETURNS LOGICAL (INPUT x AS CHARACTER) IN h. RUN start2.p PERSISTENT SET h. MESSAGE f(?).
start2.p:
FUNCTION f RETURNS LOGICAL (INPUT x AS CHARACTER) : RETURN yes. END FUNCTION.
The key line here is MESSAGE f(?) that gets converted to message(FUNC_CALL_SITE_1.clone().execute(logical.class));. When executing this testcase, the program throws a Mismatched number of parameters passed to routine f start2.p. (3234). The execute function should be execute(logical.class, new unknown()), or in other words, the current behavior is missing a second parameter, the unknown value that was passed as an argument in the 4GL code.
#2 Updated by Radu Apetrii 12 months ago
- Status changed from New to WIP
- Assignee set to Radu Apetrii
Just to have the issue highlighted. The current converted code is:
message(FUNC_CALL_SITE_1.clone().execute(logical.class));
and the correct code should be:
message(FUNC_CALL_SITE_1.clone().execute(logical.class, new unknown()));
#4 Updated by Radu Apetrii 12 months ago
May I get a hint as to where I should look (i.e. in which file) first? Thank you!
#5 Updated by Constantin Asofiei 12 months ago
Make sure you -Drules.tracing=true to see where/how this gets emitted. Also, add a normal function and check how the ? literal is being converted - that will allow you to backtrack and see why that rule doesn't include this function call.
#6 Updated by Radu Apetrii 12 months ago
- Assignee changed from Radu Apetrii to Florin Eugen Rotaru
Florin has very kindly accepted to look into this conversion error while I get my environment stable again.
#7 Updated by Florin Eugen Rotaru 12 months ago
- % Done changed from 0 to 60
- reviewer Greg Shah added
I have found a fix for the issue by making this change in literals.rules:
=== modified file 'rules/convert/literals.rules' --- old/rules/convert/literals.rules 2024-03-18 19:25:52 +0000 +++ new/rules/convert/literals.rules 2025-08-07 14:11:10 +0000 @@ -631,7 +631,7 @@ <rule>!suppress and type == prog.unknown_val <rule>!switchCaseLiteral and !evalLib("type_pair", parent, prog.func_char, prog.kw_string) - <rule>this.relativePath(prog.func_class, prog.unknown_val) + <rule>classname == null <action>createPeerAst(java.constructor, "unknown", parentid)</action> </rule>
The prog.func_class is too specific and it leaves out the other possible returned types, such as func_int, func_char, func_longchar, etc.
Also, the unknown check is needed because for other kinds of functions, e.g. a function defined in the same file, the ? argument will be added twice, for examplemessage g(?). -----> message(g(new unknown(), new character()));
I will re-launch the conversion with to test this change.
#8 Updated by Radu Apetrii 12 months ago
Florin Eugen Rotaru wrote:
I will re-launch the conversion with to test this change.
The original note is #10109-91, if you want to check the before and after code.
#9 Updated by Florin Eugen Rotaru 12 months ago
My approach is not good, Instead of relying on the null classname, we might want to make sure that the classname is of the specific datatype expected by the function (in our case character).
Thus I am investigating why the classname isn't set properly at line<action>classname = getNoteString("classname")</action> in literals.rules and if we can do that. This would mean that
message(FUNC_CALL_SITE_1.clone().execute(logical.class, new unknown()));
could become
message(FUNC_CALL_SITE_1.clone().execute(logical.class, new character())); or other types, depending of the function signature. This is because when calling f(?), FWD interprets initialized primitive datatypes as unknown values.
Radu, would this be OK for your testcase?
#10 Updated by Radu Apetrii 12 months ago
Florin Eugen Rotaru wrote:
Radu, would this be OK for your testcase?
Yes, I just checked and it's alright to have new character().
#11 Updated by Florin Eugen Rotaru 12 months ago
The issue has to be in this condition from base_structure.xml:
<!-- TODO: some "virtual" OO method calls (e.g. Progress.Lang.Enum:GetEnum) have no original method definition to reference, which means there is no refid; wrapping for these will not work right now; to avoid bad behavior we must not try to process the dictionary lookup since it will NOT have non-null values for this current call and may return an enclosing method or function call's typelist/modes instead; this is why we exclude the missing refid case --> <rule> parent != null and parent.isAnnotation("refid") and ((evalLib("function_call_type", parent.type) and (!parent.isAnnotation("param_modes") or copy.indexPos > 0)) or (evalLib("oo_call_type", parent, copy) and !evalLib("is_builtin_cls", parent) and !evalLib("is_java_cls", parent) and isNote("param_index"))) ...
In our case, this rule is not entered and some annotations are not added. Especially important is the one which should have been added at line
<rule>type == prog.string or type == prog.unknown_val
<action>putNote("classname", wrapper)</action>
</rule>
#12 Updated by Florin Eugen Rotaru 11 months ago
Florin Eugen Rotaru wrote:
The issue has to be in this
conditionfrombase_structure.xml:[...]
In our case, this rule is not entered and some annotations are not added. Especially important is the one which should have been added at line
[...]
Greg, do you remember what the use case for the part !parent.isAnnotation("param_modes") or copy.indexPos > 0) was? Why do we want to avoid here the first arguments of calls for functions that have param_modes annotations?
Removing it solves the bug, I am now testing with a conversion.
#13 Updated by Florin Eugen Rotaru 11 months ago
I have adopted a different strategy after noticing that replacing INPUT x AS CHARACTER with INPUT-OUTPUT x AS CHARACTER in Radu's testcase, the issue seems to be gone.
=== modified file 'rules/annotations/annotations.xml' --- old/rules/annotations/annotations.xml 2024-09-06 13:47:47 +0000 +++ new/rules/annotations/annotations.xml 2025-08-11 12:43:32 +0000 @@ -794,7 +794,7 @@ <action>deftype = fref.getAnnotation("typelist", parIdx)</action> <action>defmodes = fref.getAnnotation("param_modes")</action> <action>defmode = charAt(defmodes, parIdx)</action> - <rule>defmode != "I" and (deftype == "integer" or deftype == "int64" or deftype == "character") + <rule>deftype == "logical" or deftype == "integer" or deftype == "int64" or deftype == "character" <!-- required only for direct calls for which the argument type can't be narrowed via the BDT hierarchy - like 'longchar' vs 'character' at OUTPUT mode See ClassDefinition.annotateCallSignature for the equivalent done for OO calls. -->
Waiting for the conversion to finish.
#16 Updated by Radu Apetrii 11 months ago
Florin, will this fix work if the function from the first note returns a character instead of a logical? I've encountered such a case in the application I am testing, so I was wondering if I should redo the conversion (I can't remember if I did the conversion when your changes got into 6506a).
#17 Updated by Florin Eugen Rotaru 11 months ago
Radu Apetrii wrote:
Florin, will this fix work if the function from the first note returns a character instead of a logical? I've encountered such a case in the application I am testing, so I was wondering if I should redo the conversion (I can't remember if I did the conversion when your changes got into 6506a).
Radu, yes, it will fix those instances too. I have seen cases such cases myself when comparing the sources. Probably you need to re-convert.
#21 Updated by Constantin Asofiei 11 months ago
For rev 15775/15776 - the change is for unknown literal, right? If so, can you right a test for decimal, also?
Because I wonder if this is not needed only if the argument is unknown literal, and not always. Also, we need to check for OO calls which emit in a similar way, if it works OK.
#22 Updated by Florin Eugen Rotaru 11 months ago
Constantin Asofiei wrote:
For rev 15775/15776 - the change is for unknown literal, right?
More specific, the change adds the <classname> tag to the .ast for function parameters of the type "INPUT", as it was missing only for this type. In practice, this was only affecting the unknown literals, because unlike the other literals/values, it could not infer a classname, and because it was null, it ended up being skipped altogether.
I tested for decimal, int, logical literals and it works fine.
I've also tested with OO calls and it also looks OK:
def var obj1 as D. obj1 = new D(). obj1:m1(?). obj1:m2(0.2).
Converts to:
obj1.assign(ObjectOps.newInstance(com.hotel.D.class));
obj1.ref().m1(new character());
obj1.ref().m2(decimal.fromLiteral("0.2"));
#23 Updated by Constantin Asofiei 11 months ago
Please use a DYNAMIC-INVOKE call.
#24 Updated by Florin Eugen Rotaru 11 months ago
Constantin Asofiei wrote:
Please use a
DYNAMIC-INVOKEcall.
Sure, I tested this code:
def var obj1 as D. obj1 = new D(). DYNAMIC-INVOKE(obj1, "m1", ?). DYNAMIC-INVOKE(obj1, "m1", 0.2).
Which converts to
externalProcedure(Start1.this, new Block((Body) () ->
{
obj1.assign(ObjectOps.newInstance(com.apptechnologies.apppro.D.class));
ObjectOps.invokeStandalone(obj1, "m1", "I", new unknown());
ObjectOps.invokeStandalone(obj1, "m1", "I", decimal.fromLiteral("0.2"));
}));
#26 Updated by Constantin Asofiei 10 months ago
- Status changed from Review to Internal Test
Florin, please rebase and do conversion testing (compare src/ folder with trunk and 10365a).
#29 Updated by Radu Apetrii 10 months ago
The changes are already in 6506b, and from the testing that I did until now, things seemed really good.