Project

General

Profile

Bug #10365

Mismatched number of parameters passed to routine for external function

Added by Radu Apetrii 12 months ago. Updated 10 months ago.

Status:
Internal Test
Priority:
Normal
Assignee:
Florin Eugen Rotaru
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
production:
No
env_name:
topics:

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 example
message 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 condition from base_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.

#14 Updated by Florin Eugen Rotaru 11 months ago

  • Status changed from WIP to Review
  • % Done changed from 60 to 100

I have compared the generated sources with and without the above fix. The issue is resolved (it had been present in many files).

I have committed to 6506a/15775.

#15 Updated by Florin Eugen Rotaru 11 months ago

  • reviewer Constantin Asofiei added

+ Constantin as reviewer.

#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.

#18 Updated by Greg Shah 11 months ago

Code Review Task Branch 6506a Revision 15775

No objections. Please edit the comment at location so that it doesn't focus on output parameters.

#19 Updated by Florin Eugen Rotaru 11 months ago

Greg Shah wrote:

Code Review Task Branch 6506a Revision 15775

No objections. Please edit the comment at location so that it doesn't focus on output parameters.

Done. Committed rev 6506a/15776.

#20 Updated by Greg Shah 11 months ago

Any objections, Constantin?

#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-INVOKE call.

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"));
      }));

#25 Updated by Greg Shah 10 months ago

Constantin: Do you have any remaining concerns?

#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).

#27 Updated by Greg Shah 10 months ago

The changes should be in 6506b.

#28 Updated by Greg Shah 10 months ago

We will rely upon the testing of 6506b to test these changes.

#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.

Also available in: Atom PDF