Skip to content
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

Assignment of a string constant to a dynamic array fails #69

Open
svorkoetter opened this issue Dec 15, 2018 · 15 comments
Open

Assignment of a string constant to a dynamic array fails #69

svorkoetter opened this issue Dec 15, 2018 · 15 comments

Comments

@svorkoetter
Copy link

In the following, voc allows the assignment to ptr, but not the assignment to ptr2. I believe this to be a bug:

MODULE Test;

VAR
   pStr: POINTER TO ARRAY 128 OF CHAR;
   pStr2: POINTER TO ARRAY OF CHAR;

BEGIN
   NEW(pStr);
   pStr^ := "This is a test";

   NEW(pStr2,128);
   pStr2^ := "Another string";
END Test.
@norayr
Copy link
Member

norayr commented Dec 15, 2018

I agree, that it looks strange.
I just checked with OP2 compiler in Oberon S3 system, and it gives me

pos 199 err 113 Incompatible assignment

I believe this is in accordance with the report, but right now won't explain. Let us read and find the explanation.

@norayr
Copy link
Member

norayr commented Dec 15, 2018

Regarding the assignment, I believe, you can use COPY and Strings.Assign.

@norayr
Copy link
Member

norayr commented Dec 15, 2018

checked: COPY works.
Default module Strings has no Assign, but oocStrings has.
I am used to it because I usually import it as Strings := oocStrings. Still need to read the report.

@svorkoetter
Copy link
Author

svorkoetter commented Dec 15, 2018

Yes, I can use COPY, but the version of the Oberon report that introduced string assignment no longer has a COPY function, so using COPY is using a deprecated feature to work around a bug. Wirth's 2013 Oberon report states:

  1. Strings can be assigned to any array of characters, provided the number of characters in the
    string is less than that of the array. (A null character is appended).

Notice that it does not say that the left hand side must be a variable of type ARRAY OF CHAR, just that it must be an ARRAY OF CHAR. In my example above, pStr2^ is an ARRAY OF CHAR.

@norayr
Copy link
Member

norayr commented Dec 15, 2018

voc is Oberon-2 compiler. 2013's report is not applicable.
Oberon-07 is a different beast. Has no virtual functions.
And, btw, has no dynamic arrays!
So you cannot do what you want in Oberon-07 either.

@norayr
Copy link
Member

norayr commented Dec 16, 2018

so far what i found:

6.4.

If p is a variable of type P = POINTER TO T
...
if T is an n-dimensional open array type the allocation has to be done with NEW(p, e0, ..., en-1) where T is allocated with lengths given by the expressions e0, ..., en-1. In either case a pointer to the allocated variable is assigned to p. 
p is of type P.
The referenced variable p^ (pronounced as p-referenced) is of type T.

means that pstr2^is not an 'array n of char'.
but pstr^ is.
that is why assignment to pstr^ is possible, and to pstr2^ is not.

9.1

2. if Tv and Te are pointer types, the dynamic type of v becomes the dynamic type of e;
3. if Tv is ARRAY n OF CHAR and e is a string of length m <n, v[i] becomes ei for i = 0..m -1 and v[m] becomes 0X. 

and

Appendix A.

An expression e of type Te is assignment compatible with a variable v of type Tv if one of the following conditions hold: 
...
if Tv is ARRAY n OF CHAR, e is a string constant with m characters, and m <n; 

in our case it is not clear that pstr2^ can be treated as an array n of char.

so I guess we have two questions

  • clarify, if this behaviour is correct by the report. i believe it is.
  • decide if it's better to follow the report, or not in this particular case. in latter case, decide how to 'not follow', is it a compiler option for experimental extension, etc.

@norayr
Copy link
Member

norayr commented Dec 16, 2018

also checked how oo2c behaves.
does not allow the assignment:

teststring.Mod:13:11: Expression not compatible with variable type `ARRAY'

@norayr
Copy link
Member

norayr commented Dec 16, 2018

to sum up: both versions of OP2 - from V4 and S3 systems prohibit this.
oo2c which has a completely different codebase also prohibits this.
I believe all those compilers follow the report regarding this case.

@svorkoetter
Copy link
Author

svorkoetter commented Dec 16, 2018

It would be interesting to determine if this behaviour is intended. In my mind, the specification is ambiguous in this regard. Which version of the report are you using?

In my day job, I'm actively involved in the standardization of another (very different) language, and I can tell you that just because N implementations with completely different codebases all agree, doesn't mean they are right. :-)

@diegosardina
Copy link

@svorkoetter There is no ambiguity here, the term arrays doesn't include open arrays.
In Oberon only parameters may be open array (i.e. they are restricted to formal parameters) and as pointer base type in Oberon-2.

Since the days of Modula-2 open arrays were meant to be accessed only element-wise. No assignment was permitted and this has been retained in Oberon. In the type system they are anonymous and incompatible with the rest of the world.

Strings are a special case of structured literals that have been made compatible with arrays of characters (we are talking here about arrays, not open arrays) and open arrays only in case of formal parameters. Once the assignment is done, conventional rules apply.

COPY() has been created to make life easier, but not specifically for open arrays. Once a literal string is assigned to an array v2, the v1 := v2 operation copies the whole source array v2 to the destination array v1 (assuming they have the same type), while COPY() stops at the null character (and it allows for string truncation).

Oberon-07 and Component Pascal allow a bit more flexibility but be aware that this flexibility is susceptible of runtime errors.

@Oleg-N-Cher
Copy link

Stefan,
In the project Ofront+ I modified the translator for more flexible work with strings. In particular, I implemented the string operation str1 := str2$ - assignment in the style of Component Pascal, as well as the built-in calculation of the length (to 0X character) - LEN(str$). So if you need something like voc, try it. Your code from the first post works if Ofront+.

https://github.com/Oleg-N-Cher/OfrontPlus

@Oleg-N-Cher
Copy link

Diego,
I would like to comment on your sentence: "Oberon-07 and Component Pascal allow a bit more flexibility but be aware that this flexibility is susceptible of runtime errors."

COPY() implemented in Ofront/voc silently truncates the string literal to the size of the target string without any warning. In terms of reliability, this is very bad, because it distorts the data in a string. Which would you prefer? Distort data or issue a warning about the insufficient size of the target string as a runtime error? My choice is quite obvious.

I remembered how I recently worked on a project being developed simultaneously in Oberon-2 and Component Pascal. The Oberon-2 version, of course, used COPY. There was a pointer to a small string that I, not knowing its actual size, tried to assign a literal that was larger than the size. And Oberon-2 with its COPY did not help me to find this problem - the string literal was truncated quietly, whereas Component Pascal showed me what's really going on. So the problem was detected instantly. This moment, in the end, determined my choice.

@svorkoetter
Copy link
Author

svorkoetter commented Dec 16, 2018

@diegosardina Thanks, that makes sense. The last time I did much programming in Oberon was in 1990 or so, and Oberon at that time didn't have open arrays at all, not even as procedure formal parameters (I just checked my September 1989 paper copy of the report). And I see what you mean about run time errors. Unlike arrNPtr^ := "someString" whose validity can be determined at compile time, arrPtr^ := "someString" requires a run time check (which could fail).

@svorkoetter
Copy link
Author

@Oleg-N-Cher I agree that a silent truncation is not the best idea. It's still better than C's strcpy silently writing beyond the end of the target though. :-)

@diegosardina
Copy link

@Oleg-N-Cher

My intention wasn't to say that something is better than another one, but to warn that an assignment to an open array is susceptible of runtime errors and it requires proper checks in the code (the same, of course, holds for COPY).

However COPY causes truncation when two different (-> different length) arrays of characters are used, of course you should expected something like this, it's an utility to break the rules of the type system. Open arrays instead may be used without care.

I agree that in general runtime checks are better than unexpected results due to a silent truncation.
But while the former will halt your program later because you forgot (or were lazy) to write unit testing, the latter will only surprise you with, indeed, something unexpected (that is related to something not really severe and common as copying strings between two different length arrays).

For this reason I gave much more relevance to the first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants