Bug #11977
A literal left behind when a constant conditional collapses is not wrapped.
100%
History
#1 Updated by Octavian Adrian Gavril 22 days ago
- topics Conversion added
Here is a test case:
define temp-table ttcust no-undo
field code as character
field qty as integer.
create ttcust.
assign
ttcust.code = (if "abc" <> ? then "abc" else ttcust.code)
ttcust.qty = (if 1 <> ? then 1 else ttcust.qty).
ttcust.code = ("ghi").
display ttcust.code ttcust.qty.
Text and NumberType wrapper types are not generated properly. As a result, the converted code ends up looking like this:
batch(() -> { ttcust.setCode(("abc")); ttcust.setQty((1)); });
This results in the following compilation errors:
just-compile: [javac] Compiling 28 source files to /home/aog/gcd/dataset/build/classes [javac] Compiling 2 source files to /home/aog/gcd/dataset/build/classes [javac] /home/aog/gcd/dataset/src/com/goldencode/dataset/A.java:38: error: incompatible types: String cannot be converted to Text [javac] ttcust.setCode(("abc")); [javac] ^ [javac] /home/aog/gcd/dataset/src/com/goldencode/dataset/A.java:39: error: incompatible types: int cannot be converted to NumberType [javac] ttcust.setQty((1)); [javac] ^ [javac] Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output [javac] 2 errors BUILD FAILED
#3 Updated by Octavian Adrian Gavril 21 days ago
- Status changed from New to WIP
- Assignee set to Octavian Adrian Gavril
- reviewer Ovidiu Maxiniuc added
This is the original AST for ttcust.code = (if "abc" <> ? then "abc" else ttcust.code):
<ast col="16" id="12884901927" line="8" text="=" type="ASSIGN"> [...] <ast col="4" id="12884901930" line="8" text="ttcust.code" type="FIELD_CHAR"> [...] </ast> <ast col="0" id="12884901931" line="0" text="expression" type="EXPRESSION"> [...] <ast col="18" id="12884901932" line="8" text="(" type="LPARENS"> [...] <ast col="38" id="12884901943" line="8" text=""abc"" type="STRING"> [...] </ast> </ast> </ast> </ast>
In annotations/cleanup.rules, we reduce expressions to a single output when possible, but the LPARENS node remains. Later, when literal wrapping should be performed in convert/literals, the isAssign = upPath(this, prog.assign, prog.expression) check fails because of the extra layer added by LPARENS.
I created task branch 11977a and committed a fix for this issue in rev16800. Now we drop the parenthesis node whenever a single leaf node is all that remains.
This is how the AST looks after the fix:
<ast col="16" id="12884901927" line="8" text="=" type="ASSIGN"> [...] <ast col="4" id="12884901930" line="8" text="ttcust.code" type="FIELD_CHAR"> [...] </ast> <ast col="0" id="12884901931" line="0" text="expression" type="EXPRESSION"> [...] <ast col="38" id="12884901943" line="8" text=""abc"" type="STRING"> [...] </ast> </ast> </ast>
And here is the generated output:
batch(() -> { ttcust.setCode(new character("abc")); ttcust.setQty(new integer(1)); });
So, this testcase is resolved in 11977a/16800.
I noticed that Ovidiu worked in this area as well.
Ovidiu, please review these changes.
#4 Updated by Octavian Adrian Gavril 21 days ago
- % Done changed from 0 to 100
- Status changed from WIP to Review
#6 Updated by Octavian Adrian Gavril 21 days ago
Greg Shah wrote:
I don't have an issue with the concept. But I also don't understand why we are seeing this issue now. It seems like a regression.
I suspect that this is not a common way to write conditional assignments. None of our active customers use this syntax in their current codebase.
#7 Updated by Ovidiu Maxiniuc 21 days ago
- Status changed from Review to Internal Test
I understand that the trunk was expecting to reduce the assignment to something like:
ttcust.code = ("abc")How will these expressions convert? Your changes are limited to reduced ternary cases. I am just thinking if we should move this code in a more generic place.
The changes are good. It should work as described.
To note: ref.numChildren == 1 is always true (if lparens doesn't have a exactly one child then there is a problem) and can be dropped.
PS: maybe that strange condition was 'composed' using includes? Or it was simply written to comment out some code - I do sometime add the equivalent of if false in front of a statement to temporarily disable it.
#8 Updated by Octavian Adrian Gavril 20 days ago
- % Done changed from 100 to 90
- Status changed from Internal Test to Review
Ovidiu Maxiniuc wrote:
I understand that the trunk was expecting to reduce the assignment to something like:[...]
How will these expressions convert?
Yes, but those expressions written directly in the OE code are handled in fixups/normalize_expressions, where an LPARENS node is stripped if it wraps a single leaf, which is not the case for the original test case. Instead, the current reduction occurs in annotations/cleanup, which runs later during the Code Annotations phase. That's why those expressions convert fine today, whereas the test case does not.
Your changes are limited to reduced ternary cases. I am just thinking if we should move this code in a more generic place.
Indeed. I found another place in the same file (annotations/cleanup) that reduces EQUALS/NOT_EQ against an unknown literal to BOOL_TRUE/BOOL_FALSE. I'm moving this task back to WIP to find a more generic place for these changes. Here is a test case that currently fails with 11977a:
define temp-table ttcust no-undo field flag as logical.
create ttcust.
ttcust.flag = ("abc" <> ?).
Having this error:
[javac] /home/aog/gcd/dataset/src/com/goldencode/dataset/A.java:30: error: incompatible types: boolean cannot be converted to logical [javac] ttcust.setFlag((true)); [javac] ^ [javac] Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output [javac] 1 error
PS: maybe that strange condition was 'composed' using includes? Or it was simply written to comment out some code - I do sometime add the equivalent of
if falsein front of a statement to temporarily disable it.
That's right. The original code that reproduced this issue used includes for that conditional clause. However, the behavior remains the same whether it's written directly in OE or composed via includes.
#9 Updated by Octavian Adrian Gavril 20 days ago
- Status changed from Review to WIP
#10 Updated by Octavian Adrian Gavril 20 days ago
- % Done changed from 90 to 100
- Status changed from WIP to Review
I've moved the fix to a generic location after all possible reductions in rev16801. Here is a test case containing all the scenarios we discussed so far, which currently passes in 11977a:
define temp-table ttcust no-undo
field code as character
field qty as integer
field flag as logical.
create ttcust.
assign
ttcust.code = (if "abc" <> ? then "abc" else ttcust.code)
ttcust.qty = (if 1 <> ? then 1 else ttcust.qty).
ttcust.code = ("ghi").
ttcust.flag = ("abc" <> ?).
display ttcust.code ttcust.qty ttcust.flag.
Ovidiu, please review.
#11 Updated by Ovidiu Maxiniuc 20 days ago
Things may be more complicated. Think of following testcases:
ttcust.code = (("ghi")).
ttcust.flag = (("abc") <> ?).
ttcust.qty = (ttcust.qty + 1) * 2.Please these in your tests. A cast under a where predicate, where parenthesis occur. Most likely they are OK, but I would like to be sure the new code is not too aggressive in dropping the LPARENS nodes.
Related to latest changes (r16801): using analog may not be the correct node to handle. The new rule checks whether type prog.lparens and [..] analog.numChildren 1 but if the copy or at least one of its 'previous' siblings was moved, the analog and this/copy may not have the same type any more (that is, the analog being dropped is no longer a LPARENS node). I do not have yet a testcase to prove this.
OTOH, an extra LPARENS node is not a big deal, and isAssign = upPath(this, prog.assign, prog.expression) should not fail because of it. Is that code OK?
#12 Updated by Ovidiu Maxiniuc 20 days ago
Me back. I looked again back at original incorrectly generated code:
ttcust.setCode(("abc"));
ttcust.setQty((1));Removing the intermediary LPARENS is a local solution. Similar ASTs may be generated later (the cleanup.rules is pretty mush at the middle of the TRPL annotations process and a lot may happen afterward). I think part of the problem is downstream, when the wrappers are injected. Now it is unable to detect the necessity of the BDT wrapper because of the LPARENS. If that was the case, the generated code would look like this: ttcust.setCode((new character("abc")));
ttcust.setQty((new integer(1)));or rather like this: ttcust.setCode(new character(("abc")));
ttcust.setQty(new integer((1)));Both of these are valid in Java. So fixing this would make TRPL more flexible/resilient.#13 Updated by Octavian Adrian Gavril 20 days ago
Ovidiu Maxiniuc wrote:
Things may be more complicated. Think of following testcases:[...]Please these in your tests. A
castunder a where predicate, where parenthesis occur. Most likely they are OK, but I would like to be sure the new code is not too aggressive in dropping the LPARENS nodes.
Yes, these tests are fine.
Related to latest changes (r16801): using
analogmay not be the correct node to handle. The new rule checks whethertype prog.lparens and [..] analog.numChildren 1but if thecopyor at least one of its 'previous' siblings was moved, theanalogandthis/copymay not have the same type any more (that is, theanalogbeing dropped is no longer a LPARENS node). I do not have yet a testcase to prove this.
I fixed this in 11977a/16802.
OTOH, an extra LPARENS node is not a big deal, and
isAssign = upPath(this, prog.assign, prog.expression)should not fail because of it. Is that code OK?
Well, that was the original issue. Due to the fact that LPARENS was still present, the isAssign = upPath(this, prog.assign, prog.expression) check fails. upPath matches the parent and the grandparent types, so it expects EXPRESSION above the literal and ASSIGN above that.
With the parentheses left in place the parent is LPARENS, so the test returns false and the literal is never wrapped.
#14 Updated by Octavian Adrian Gavril 19 days ago
Ovidiu Maxiniuc wrote:
Me back. I looked again back at original incorrectly generated code:[...]Removing the intermediary LPARENS is a local solution. Similar ASTs may be generated later (the
cleanup.rulesis pretty mush at the middle of the TRPLannotationsprocess and a lot may happen afterward). I think part of the problem is downstream, when the wrappers are injected. Now it is unable to detect the necessity of the BDT wrapper because of the LPARENS. If that was the case, the generated code would look like this:[...]or rather like this:[...]Both of these are valid in Java. So fixing this would make TRPL more flexible/resilient.
Agreed. I reverted my changes from annotations/cleanup and used the correct node in convert/literals when there is an extra LPARENS level between the literal and the assignment. These changes have been committed in rev16803.
#15 Updated by Octavian Adrian Gavril 19 days ago
Ovidiu, please review the changes.
#16 Updated by Ovidiu Maxiniuc 18 days ago
- Status changed from Review to Internal Test
Very well! Let's test it.
#17 Updated by Octavian Adrian Gavril 15 days ago
Ovidiu Maxiniuc wrote:
Very well! Let's test it.
I've tested the Hotel ChUI, Hotel GUI, ChUI regression testing app, OO app, and ETF conversions so far, and there are no differences in the generated output.
I am continuing with a large GUI app. Let me know if you would like me to test anything else.
#18 Updated by Ovidiu Maxiniuc 15 days ago
I think the large GUI app should offer good coverage. If it passes I'm OK to merge the branch.
#19 Updated by Octavian Adrian Gavril 14 days ago
Conversion of large GUI app completed successfully with no differences in the generated output.
#20 Updated by Octavian Adrian Gavril 13 days ago
Is there anything left for this task or we can go ahead and merge it?
#21 Updated by Ovidiu Maxiniuc 12 days ago
I've already given the green light in note 18, but the branch needs to be queued by Constantin or Alexandru.
#22 Updated by Alexandru Lungu 12 days ago
- Status changed from Internal Test to Merge Pending
You can merge this to trunk next.
#23 Updated by Octavian Adrian Gavril 11 days ago
Rebased branch 11977a with trunk/16812. The new revision is 16816.
#24 Updated by Octavian Adrian Gavril 11 days ago
- Status changed from Merge Pending to Test
- version_resolved set to 16813
Branch 11977a was merged into trunk as rev. 16813 and archived.