-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Undefined behavior due to nested procedure #37
Comments
I simplified the module even further, removing the MODULE Issue37;
IMPORT Out;
PROCEDURE Length(str: ARRAY OF CHAR) : INTEGER;
VAR i: INTEGER;
BEGIN
i := 0;
WHILE (i < LEN(str)) & (str[i] # 00X) DO INC(i) END;
RETURN i
END Length;
PROCEDURE FillString(VAR s : ARRAY OF CHAR);
VAR
i : INTEGER;
BEGIN
i := 0;
WHILE i < LEN(s) DO
s[i] := CHR(SHORT(i MOD 20) + 65);
INC(i)
END
END FillString;
PROCEDURE Run1();
VAR
str : ARRAY 257 OF CHAR;
BEGIN
str := "test";
Out.String("1.1 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
FillString(str);
Out.String("1.2 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
END Run1;
PROCEDURE Run2();
VAR
str : ARRAY 257 OF CHAR;
PROCEDURE Assert(b: BOOLEAN);
BEGIN
ASSERT(b)
END Assert;
BEGIN
str := "test";
Out.String("2.1 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
FillString(str);
Out.String("2.2 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
END Run2;
PROCEDURE Run3();
VAR
str : ARRAY 257 OF CHAR;
BEGIN
str := "test";
Out.String("3.1 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
FillString(str);
Out.String("3.2 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
END Run3;
BEGIN
Run1();
Run2();
Run2()
END Issue37. The erroneous behavior is still present, but actually not linked to |
Excellent. The work around is simple, so not a pressing issue. |
Fix to procedure unnesting (lambda lifter).
Closed by #39 |
The following code gives different result depending on if the functions is nested or not: MODULE Test;
IMPORT Out;
PROCEDURE IsLower (ch: CHAR) : BOOLEAN;
BEGIN RETURN (ch >= "a") & (ch <= "z")
END IsLower;
PROCEDURE Upper(ch: CHAR) : CHAR;
BEGIN
IF IsLower(ch) THEN RETURN CHR(ORD(ch) + (ORD("A") - ORD("a"))) END;
RETURN ch
END Upper;
PROCEDURE Length(str: ARRAY OF CHAR) : INTEGER;
VAR i: INTEGER;
BEGIN
i := 0;
WHILE (i < LEN(str)) & (str[i] # 00X) DO INC(i) END;
RETURN i
END Length;
PROCEDURE IMatch1(str, pattern : ARRAY OF CHAR; IgnoreCase : BOOLEAN; is, ip: LONGINT): BOOLEAN;
VAR
lens, lenp : LONGINT;
BEGIN
lens := Length(str);
lenp := Length(pattern);
WHILE ip < lenp DO
IF pattern[ip] = "*" THEN
WHILE is <= lens DO (* check to end of string *)
IF IMatch1(str, pattern, IgnoreCase, is, ip + 1) THEN RETURN TRUE END;
INC(is)
END;
RETURN FALSE
ELSIF is = lens THEN (* pattern not exhausted *)
RETURN FALSE
ELSIF pattern[ip] = "?" THEN
ELSIF ~IgnoreCase & (str[is] # pattern[ip]) THEN
RETURN FALSE
ELSIF IgnoreCase & (Upper(str[is]) # Upper(pattern[ip])) THEN
RETURN FALSE
END;
INC(is); INC(ip)
END;
RETURN is = lens
END IMatch1;
PROCEDURE Match1 (str, pattern : ARRAY OF CHAR; IgnoreCase : BOOLEAN): BOOLEAN;
BEGIN
IF Length(pattern) = 0 THEN RETURN Length(str) = 0 END;
RETURN IMatch1(str, pattern, IgnoreCase, 0, 0)
END Match1;
PROCEDURE Match2 (str, pattern : ARRAY OF CHAR; IgnoreCase : BOOLEAN): BOOLEAN;
PROCEDURE IMatch(str, pattern : ARRAY OF CHAR; IgnoreCase : BOOLEAN; is, ip: LONGINT): BOOLEAN;
VAR
lens, lenp : LONGINT;
BEGIN
lens := Length(str);
lenp := Length(pattern);
WHILE ip < lenp DO
IF pattern[ip] = "*" THEN
WHILE is <= lens DO (* check to end of string *)
IF IMatch(str, pattern, IgnoreCase, is, ip + 1) THEN RETURN TRUE END;
INC(is)
END;
RETURN FALSE
ELSIF is = lens THEN (* pattern not exhausted *)
RETURN FALSE
ELSIF pattern[ip] = "?" THEN
ELSIF ~IgnoreCase & (str[is] # pattern[ip]) THEN
RETURN FALSE
ELSIF IgnoreCase & (Upper(str[is]) # Upper(pattern[ip])) THEN
RETURN FALSE
END;
INC(is); INC(ip)
END;
RETURN is = lens
END IMatch;
BEGIN
IF Length(pattern) = 0 THEN RETURN Length(str) = 0 END;
RETURN IMatch(str, pattern, IgnoreCase, 0, 0)
END Match2;
BEGIN
Out.String("Match1('Filename.exe', 'f?l*.exe', FALSE) = "); Out.Int(ORD(Match1("Filename.exe", "f?l*.exe", FALSE)), 0); Out.Ln;
Out.String("Match2('Filename.exe', 'f?l*.exe', FALSE) = "); Out.Int(ORD(Match2("Filename.exe", "f?l*.exe", FALSE)), 0); Out.Ln
END Test. This outputs:
|
Those nested procedures should be outlawed! 😎 But fine, I'll reopen the issue and have a look in what corner of |
I take it back! tl;dr When unnesting procedures, TYPE _TMatch2(*Scope:1*) = RECORD str: ARRAY OF CHAR; pattern: ARRAY OF CHAR; IgnoreCase: BOOLEAN END(*Size:1*); While this type is correct, it relies on functionality that I have not yet introduced in the backend. Since open array types cannot be used for variables and record fields, a programmer could not explicitly create this type. Already the parser would not accept it. Obviously (and thanks for finding that loophole), I overlooked that open arrays will be used in this way internally by To fix the problem, I will add support for variables and record fields with an open array type by allocating them as pointers and checking that they are stored and loaded correctly in the LLVM-IR. However, I think it makes sense to defer upgrading the Parser and Sema to support this functionality as I consider it to be extended functionality that goes beyond the standard Oberon. At the moment my priorities are fixing bugs and closing the remaining gaps to support all of standard Oberon (e.g., record extensions, function pointers, |
I agree. Here we have a known reproducible "weird" case which can be deferred until later when more important issues are solved. I found this when porting code from my XDS library and have now about 170 tests passed. |
Consider the following code (simplified larger module):
This outputs unexpected result:
The text was updated successfully, but these errors were encountered: