Project

General

Profile

Bug #4082

strong buffer scope can resolve ambiguous names

Added by Greg Shah about 5 years ago. Updated about 5 years ago.

Status:
New
Priority:
Normal
Assignee:
-
Target version:
-
Start date:
Due date:
% Done:

0%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:

History

#1 Updated by Greg Shah about 5 years ago

The following program (see testcases/uast/strong_buffer_scope_resolves_ambiguous_names.p) works in the 4GL:

def temp-table tt1 field prefix-num as int field prefix-txt as char.
def temp-table tt2 field prefix-another as int.

def var confused like tt1.prefix-num.

do for tt2:
   create tt1.
   tt1.prefix-num = 14.
   tt1.prefix-txt = "whatevs".

   create tt2.
   tt2.prefix-another = 29.

   confused = prefix.
end.

/* this should print 29 */
message confused.

The DO FOR creates a strong buffer scope for tt2. In the 4GL, the tt2 table is given precedence over other ambiguous names in tables that are also in the same scope. Without the FOR tt2, there would be a compile error: ** prefix is ambiguous with tt2.prefix-another and tt1.prefix-num (72).

In FWD, we fail to parse:

./strong_buffer_scope_resolves_ambiguous_names.p:14:15: unexpected token: prefix
        at com.goldencode.p2j.uast.ProgressParser.lvalue(ProgressParser.java:15249)
        at com.goldencode.p2j.uast.ProgressParser.primary_expr(ProgressParser.java:56543)
        at com.goldencode.p2j.uast.ProgressParser.chained_object_members(ProgressParser.java:20892)
        at com.goldencode.p2j.uast.ProgressParser.un_type(ProgressParser.java:56173)
        at com.goldencode.p2j.uast.ProgressParser.prod_expr(ProgressParser.java:56040)
        at com.goldencode.p2j.uast.ProgressParser.sum_expr(ProgressParser.java:40169)
        at com.goldencode.p2j.uast.ProgressParser.compare_expr(ProgressParser.java:55626)
        at com.goldencode.p2j.uast.ProgressParser.log_not_expr(ProgressParser.java:55485)
        at com.goldencode.p2j.uast.ProgressParser.log_and_expr(ProgressParser.java:55422)
        at com.goldencode.p2j.uast.ProgressParser.expr(ProgressParser.java:10561)
        at com.goldencode.p2j.uast.ProgressParser.un_type(ProgressParser.java:56196)
        at com.goldencode.p2j.uast.ProgressParser.prod_expr(ProgressParser.java:56040)
        at com.goldencode.p2j.uast.ProgressParser.sum_expr(ProgressParser.java:40169)
        at com.goldencode.p2j.uast.ProgressParser.compare_expr(ProgressParser.java:55626)
        at com.goldencode.p2j.uast.ProgressParser.log_not_expr(ProgressParser.java:55485)
        at com.goldencode.p2j.uast.ProgressParser.log_and_expr(ProgressParser.java:55422)
        at com.goldencode.p2j.uast.ProgressParser.expr(ProgressParser.java:10561)
        at com.goldencode.p2j.uast.ProgressParser.assignment(ProgressParser.java:8075)
        at com.goldencode.p2j.uast.ProgressParser.single_block(ProgressParser.java:6816)
        at com.goldencode.p2j.uast.ProgressParser.block(ProgressParser.java:6505)
        at com.goldencode.p2j.uast.ProgressParser.inner_block(ProgressParser.java:7759)                                                                                                                                                            
        at com.goldencode.p2j.uast.ProgressParser.single_block(ProgressParser.java:6794)                                                                                                                                                           
        at com.goldencode.p2j.uast.ProgressParser.block(ProgressParser.java:6505)                                                                                                                                                                  
        at com.goldencode.p2j.uast.ProgressParser.external_proc(ProgressParser.java:6432)                                                                                                                                                          
        at com.goldencode.p2j.uast.AstGenerator.parse(AstGenerator.java:1500)                                                                                                                                                                      
        at com.goldencode.p2j.uast.AstGenerator.processFile(AstGenerator.java:966)                                                                                                                                                                 
        at com.goldencode.p2j.uast.ScanDriver.lambda$scan$0(ScanDriver.java:375)                                                                                                                                                                   
        at com.goldencode.p2j.uast.ScanDriver.scan(ScanDriver.java:410)
        at com.goldencode.p2j.uast.ScanDriver.scan(ScanDriver.java:248)
        at com.goldencode.p2j.convert.TransformDriver.runScanDriver(TransformDriver.java:349)
        at com.goldencode.p2j.convert.TransformDriver.front(TransformDriver.java:220)
        at com.goldencode.p2j.convert.TransformDriver.executeJob(TransformDriver.java:845)
        at com.goldencode.p2j.convert.ConversionDriver.main(ConversionDriver.java:983)
Failure in file 'strong_buffer_scope_resolves_ambiguous_names.p':
com.goldencode.ast.AstException: Error processing ./strong_buffer_scope_resolves_ambiguous_names.p
        at com.goldencode.p2j.uast.AstGenerator.processFile(AstGenerator.java:971)
        at com.goldencode.p2j.uast.ScanDriver.lambda$scan$0(ScanDriver.java:375)
        at com.goldencode.p2j.uast.ScanDriver.scan(ScanDriver.java:410)
        at com.goldencode.p2j.uast.ScanDriver.scan(ScanDriver.java:248)
        at com.goldencode.p2j.convert.TransformDriver.runScanDriver(TransformDriver.java:349)
        at com.goldencode.p2j.convert.TransformDriver.front(TransformDriver.java:220)
        at com.goldencode.p2j.convert.TransformDriver.executeJob(TransformDriver.java:845)
        at com.goldencode.p2j.convert.ConversionDriver.main(ConversionDriver.java:983)
Caused by: java.lang.RuntimeException: Parser encountered 1 errors
        at com.goldencode.p2j.uast.AstGenerator.parse(AstGenerator.java:1548)
        at com.goldencode.p2j.uast.AstGenerator.processFile(AstGenerator.java:966)
        ... 7 more

The parser does call sym.addSchemaScope(false); when it detects that it will be encountering a DO FOR next. Clearly, this code does not do anything to tell the schema dictionary that a specific buffer is strongly scoped. From do_repeat_stmt, the for_record_spec [boolean flds, boolean force] is called with force set to true. This means the strongly scoped buffer will be forced to be promoted. But that doesn't tell the schema dictionary anything about the strong scope either.

Then inside the block, both CREATE statements will cause the respective buffers to be promoted. This means they have both been promoted inside the same schema scope so we will find prefix to be ambiguous.

It won't be a big deal to rework things so that the parser gives a special notification to the schema dictionary that a given schema scope should be treated as a strong scope (and which buffer is associated with it).

But it is not clear to me how to best modify the schema dictionary to honor this.

Eric: Do you have any thoughts on the best approach?

#2 Updated by Greg Shah about 5 years ago

I've enhanced the testcase (testcases/uast/strong_buffer_scope_resolves_ambiguous_names.p) to explore this behavior more thoroughly.

Findings:

  • A buffer specified as a strong scope will take priority over conflicts within that same schema scope. If this means that a normally ambiguous set of possible matches is cut down to a single possible match when considering the strongly scoped buffer, then this will silently disambiguate the conflicts. See tests 1 , 2 and 3.
  • If there are remaining conflicts within the same strongly scoped buffer (prefix matches multiple fields in tt13. (3509), see test 8) OR between two buffers that are both strongly scoped to the same schema scope (** prefix is ambiguous with tt4.prefix-num and tt3.prefix-num (72), see test 7), then the result will still be ambiguous. These are compile failures in the 4GL.
  • This behavior only disambiguates field conflicts. It cannot be used for table name ambiguity (Unknown or ambiguous table tt. (725), see test 9). Trying this with a table name ambiguity results in a compile failure.
  • Interestingly, this disambiguation effect is ONLY seen in the current schema scope. This means:
    • Directly nested blocks which do not open a new schema scope (e.g. most DO blocks) are considered to be in the same schema scope and their field references ARE subject to this disambiguation. See tests 2 and 3.
    • Any nested block that does open a new schema scope, will shift back to following the normal ambiguity rules. The containing strongly scoped buffers will have no effect. See tests 4, 5 and 6.
    • Nested strong scopes operate as one would expect. The nested scope will honor its own buffers but will ignore the containing scope's buffers.
def temp-table tt1 field prefix-num as int.
def temp-table tt2 field prefix-num as int.
def temp-table tt3 field prefix-num as int.
def temp-table tt4 field prefix-num as int.
def temp-table tt5 field prefix-num as int.
def temp-table tt6 field prefix-num as int.
def temp-table tt7 field prefix-num as int.
def temp-table tt8 field prefix-num as int.
def temp-table tt9 field prefix-num as int.
def temp-table tt10 field prefix-num as int.
def temp-table tt11 field prefix-num as int.
def temp-table tt12 field prefix-num as int.

def temp-table tt13 field prefix-num as int
                    field prefix-txt as char.

/* the LIKE does not affect the choice later to make to tt1 instead of tt2 */
def var confused like tt2.prefix-num.

/* test 1 */
/* a strong scope will disambiguate field references that would otherwise fail */
do for tt1:
   create tt1.
   tt1.prefix-num = 14.

   create tt2.
   tt2.prefix-num = 29.

   confused = prefix.
end.

if confused ne 14 then message "ERROR 1: confused should have been 14 but was actually " + string(confused).

confused = 0.

/* test 2 */
/* only nested blocks without a schema scope are affected by this strong scope */
do for tt3:
   /* the inner DO block has no properties and does not create a new schema scope */
   /* this allows the strong scope to still disambiguate the naming conflict */
   do:
      /* the order doesn't matter here, these create/assign sections can be swapped without */
      /* changing the behavior, these can also be moved above this block without changing */
      /* the result */
      create tt4.
      tt4.prefix-num = 2000.

      create tt3.
      tt3.prefix-num = 100.

      confused = prefix.
   end.
end.

if confused ne 100 then message "ERROR 2: confused should have been 100 but was actually " + string(confused).

confused = 0.

/* test 3 */
/* only nested blocks without a schema scope are affected by this strong scope */
do for tt5:
   /* the order doesn't matter here, these create/assign sections can be swapped without */
   /* changing the behavior, these can also be moved into the inner block without changing */
   /* the result */
   create tt5.
   tt5.prefix-num = 20000.

   create tt6.
   tt6.prefix-num = 1000.

   /* the inner DO block has no properties and does not create a new schema scope */
   /* this allows the strong scope to still disambiguate the naming conflict */
   do:
      confused = prefix.
   end.
end.

if confused ne 20000 then message "ERROR 3: confused should have been 20000 but was actually " + string(confused).

confused = 0.

/* test 4 */
/* nested blocks with a schema scope hide the effect of the outer strong scope */
do for tt7:
   /* the inner DO FOR block allows the strong scope of tt8 to */
   /* disambiguate the naming conflict; tt7 is not given preference */
   /* in the nested block */
   do for tt8:
      /* the order doesn't matter here, these create/assign sections can be swapped without */
      /* changing the behavior */
      create tt7.
      tt7.prefix-num = 999.

      create tt8.
      tt8.prefix-num = -1.

      confused = prefix.
   end.
end.

if confused ne -1 then message "ERROR 4: confused should have been -1 but was actually " + string(confused).

confused = 0.

/* test 5 */
/* nested blocks with a schema scope hide the effect of the outer strong scope */
/* a better way to say this is that the strong scope only disambiguates */
/* within the same schema scope; buffers binding to nested scopes have precedence */
/* in those nested scopes that makes disambiguation unnecessary (see test 6 below) */
do for tt9:
   /* the inner REPEAT block has a schema scope, this disables the effect  */
   /* disambiguate the naming conflict; tt5 is not given preference */
   /* in the nested block */
   repeat:
      /* the order doesn't matter here, these create/assign sections can be swapped without */
      /* changing the behavior */

      /* this is scoped to the outer block, which is a different schema scope */
      create tt9.
      tt9.prefix-num = 9.

      /* this is scoped here and thus binds more tightly, no ambiguity is found */
      /* so there is nothing to disambiguate */
      create tt10.
      tt10.prefix-num = -10.

      confused = prefix.
      leave.
   end.
end.

if confused ne -10 then message "ERROR 5: confused should have been -10 but was actually " + string(confused).

confused = 0.

/* test 6 */
/* this example is really the same as test 5 above, but shows that the strong scope */
/* is not really part of the disambiguation */
/* nested blocks with a schema scope bind more tightly to the references scoped to */
/* the current block; this means there is no ambiguous name */
repeat:
   /* this is scoped to the outer block, which is a different schema scope */
   /* than the inner block */
   create tt11.
   tt11.prefix-num = 240.

   /* the inner REPEAT block has a schema scope, to which tt12 is scoped  */
   repeat:
      /* this is scoped here and thus binds more tightly, no ambiguity is found */
      /* so there is nothing to disambiguate */
      create tt12.
      tt12.prefix-num = -17.

      confused = prefix.
      leave.
   end.

   leave.
end.

if confused ne -17 then message "ERROR 6: confused should have been -17 but was actually " + string(confused).

/* test 7 (does not compile) */
/* multiple conflicting strong scopes cause a compile failure */
/* ** prefix is ambiguous with tt4.prefix-num and tt3.prefix-num (72) */
/*
do for tt3, tt4:
   repeat:
      create tt3.
      tt3.prefix-num = 100.

      create tt4.
      tt4.prefix-num = 2000.

      confused = prefix.
      leave.
   end.
end.
*/

/* test 8 (does not compile) */
/* multiple conflicting strong scopes cause a compile failure */
/* prefix matches multiple fields in tt13. (3509) */
/*
do for tt13:
   create tt13.
   tt13.prefix-num = 99.
   tt13.prefix-txt = "whatevs".

   confused = prefix.
end.
*/

/* test 9 (does not compile) */
/* strong scopes only disambiguate fields, not tables */
/* the following code generates the compile failure "Unknown or ambiguous table tt. (725)" */
/*
do for tt2:
   find first tt.
end.
*/

message "Finished successfully.".

Also available in: Atom PDF