Re: Unexpected aliasing

2019-11-12 Thread Bastiaan Veelo via Digitalmars-d-learn

On Tuesday, 12 November 2019 at 08:15:20 UTC, Basile B. wrote:


I'm curious to know what is the equivalent in Pascal that your 
transpiler fails to translate since Pascal records don't have 
constructors at all. Maybe you used an old school 'Object' ?


Note that Extended Pascal is not exactly Pascal. An example:

  TYPE Ints(upperBound) = ARRAY [1 .. upperBound] of Integer;
   MyRecord = RECORD
  integers : Ints(5);
  END;
   SchematicRecord(num) = RECORD
  integers : Ints(num);
  END;
   IntsPtr = ^Ints;

  PROCEDURE myProcedure(PROTECTED VAR someArr : Ints);
  BEGIN
  writeln(someArr.upperBound);
  END;

  PROCEDURE proc;
  VAR dynamicInts : IntsPtr;
  rec : MyRecord;
  BEGIN
  dynamicInts = new(10);
  myProcedure(dynamicInts^);
  myProcedure(rec.integers);
  END;

In this case, only the upper bound of Ints is parameterized, but 
the lower bound could be parameterized as well. Records can also 
be schematized. Procedure arguments can take schemas that are 
undiscriminated, they carry their schemaparameters as properties.


Bastiaan.


Re: Unexpected aliasing

2019-11-12 Thread Bastiaan Veelo via Digitalmars-d-learn
On Monday, 11 November 2019 at 21:52:12 UTC, Jonathan M Davis 
wrote:
On Monday, November 11, 2019 12:17:37 PM MST Bastiaan Veelo via 
Digitalmars- d-learn wrote:


[...]

I could use some help in rewriting the code below so that arr1 
and arr2 each have their own data; ideally with minimal 
changes so that I can make the transcompiler do the right 
thing.


Thanks!
Bastiaan.

void main()
{
  import std.stdio;

  WrapIntegerArray arr1;
  arr1[0] = 42;

  WrapIntegerArray arr2;

  writeln(arr2[0]); // 42, not 0.
  writeln("arr1.wrap.arr.ptr = ", arr1.wrap.arr.ptr);
  writeln("arr2.wrap.arr.ptr = ", arr2.wrap.arr.ptr); // 
identical

  assert(arr2[0] == 0); // fails
}

struct IntegerArray
{
  int[] arr;
  alias arr this;
  this(int l)
  {
  arr = new int[l];
  }
}

struct WrapIntegerArray
{
  auto wrap = IntegerArray(5); // This is CTFE! :-(
  alias wrap this;
}


All struct and class members which are directly initialized 
must have their values known at compile-time. For structs, 
that's what goes in the init value for the type. A side effect 
of this is that it's usually a bad idea to directly initialize 
dynamic arrays which are member variables. You need to do the 
initialization in a constructor. And for structs, if you need a 
no-arg constructor, then you'll need to use a factory function 
(since structs can't have no-arg constructors). e.g.


struct WrapIntegerArray
{
IntegerArray wrap;
alias wrap this;

this(int len)
{
wrap = IntegerArray(len);
}
}

or

struct WrapIntegerArray
{
IntegerArray wrap;
alias wrap this;

static make()
{
WrapIntegerArray retval;
retval.wrap = IntegerArray(5);
return retval;
}
}

So, you could then have something like

auto arr1 = WrapIntegerArray(5);
arr1[0] = 42;

or

auto arr1 = WrapIntegerArray.make();
arr1[0] = 42;

but if you use the init value (which is what you get if you let 
the type be default-initialized), then you'll have to first do 
something to allocate the dynamic array if you want to be able 
to index it, since if you don't give it a value at 
compile-time, it's null (and you don't want to give it a value 
at compile-time, because then every default-initialized struct 
of that type will refer to the same dynamic array). Of course, 
you could always just append values, and the dynamic array will 
be allocated and grow accordingly, but that's obviously not the 
same as allocating it up front to have a specific length.


- Jonathan M Davis


Thank you Jonathan. A factory function seems to be what I need, 
then. It'll be an interesting challenge to have my transpiler 
detect when a factory function is needed, and making sure that 
they are called at the right places. But this might escalate 
since structs can have members that are structs that have a 
factory function. So the enclosing struct also needs a factory 
function. And so forth.


My problem is the translation of so called schematic arrays, a 
type that I have only seen in Extended Pascal. A schematic array 
is a type in which the length of the array is a parameter of the 
type. Extended Pascal does not have dynamic arrays, so schematic 
arrays are its attempt at improving a bit on just plain static 
arrays. The length parameter can be constant, in which it behaves 
akin to templates (which it doesn't have either), but it can also 
be set at run time during initialization (so I can't translate it 
to a template).


Maybe I can omit the translation of schematic arrays 
(IntegerArray in this case) altogether and instead detect where 
and how they are instantiated. Then replace them with a static 
array whenever the length is known at compile time, and a dynamic 
array otherwise. A static array is not nice, but at least they 
can be members of structs without a factory function. Either way, 
my transpiler needs to smarten up...


Bastiaan.


Re: Unexpected aliasing

2019-11-12 Thread Basile B. via Digitalmars-d-learn
On Tuesday, 12 November 2019 at 07:59:39 UTC, Bastiaan Veelo 
wrote:
On Monday, 11 November 2019 at 20:05:11 UTC, Antonio Corbi 
wrote:

[...]


Thanks, Antonio. My problem is that the length of the array 
should be a built-in property of WrapIntegerArray (immutable in 
this case); what I'd actually want is a constructor without 
arguments. Jonathan's suggestion of using a factory function 
comes closest to that.


Bastiaan.


I'm curious to know what is the equivalent in Pascal that your 
transpiler fails to translate since Pascal records don't have 
constructors at all. Maybe you used an old school 'Object' ?


Re: Unexpected aliasing

2019-11-12 Thread Bastiaan Veelo via Digitalmars-d-learn

On Monday, 11 November 2019 at 20:05:11 UTC, Antonio Corbi wrote:
Defining and using a constructor for WrapIntegerArray seems to 
work:


void main()
{
import std.stdio;

WrapIntegerArray arr1 = WrapIntegerArray(5);
arr1[0] = 42;

WrapIntegerArray arr2 = WrapIntegerArray(5);

writeln(arr2[0]); // 42, not 0.
writeln("arr1.wrap.arr.ptr = ", arr1.wrap.arr.ptr);
	writeln("arr2.wrap.arr.ptr = ", arr2.wrap.arr.ptr); // 
identical

assert(arr2[0] == 0); // fails
}

struct IntegerArray
{
int[] arr;
alias arr this;
this(int l)
{
arr = new int[l];
}
}

struct WrapIntegerArray
{
this (int v) {
  wrap = IntegerArray(5);
}

IntegerArray wrap;
alias wrap this;
}

Hope this helps.
Antonio


Thanks, Antonio. My problem is that the length of the array 
should be a built-in property of WrapIntegerArray (immutable in 
this case); what I'd actually want is a constructor without 
arguments. Jonathan's suggestion of using a factory function 
comes closest to that.


Bastiaan.


Re: Unexpected aliasing

2019-11-11 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, November 11, 2019 12:17:37 PM MST Bastiaan Veelo via Digitalmars-
d-learn wrote:
> Recently I got my first surprise with our use of D. The symptom
> was that two local variables in two different functions appeared
> to be sharing data.
>
> A simplified example is shown below (the original was machine
> translated from Pascal and involved templates and various levels
> of indirection). What I did not know is that the initial value of
> struct members is a compile time feature, apparently. What I
> suspect is happening is that the array lives in the static data
> segment (or is created in the module constructor?) and that the
> slices inside arr1 and arr2 get initialised to point to that same
> array.
>
> I could use some help in rewriting the code below so that arr1
> and arr2 each have their own data; ideally with minimal changes
> so that I can make the transcompiler do the right thing.
>
> Thanks!
> Bastiaan.
>
> void main()
> {
>   import std.stdio;
>
>   WrapIntegerArray arr1;
>   arr1[0] = 42;
>
>   WrapIntegerArray arr2;
>
>   writeln(arr2[0]); // 42, not 0.
>   writeln("arr1.wrap.arr.ptr = ", arr1.wrap.arr.ptr);
>   writeln("arr2.wrap.arr.ptr = ", arr2.wrap.arr.ptr); // identical
>   assert(arr2[0] == 0); // fails
> }
>
> struct IntegerArray
> {
>   int[] arr;
>   alias arr this;
>   this(int l)
>   {
>   arr = new int[l];
>   }
> }
>
> struct WrapIntegerArray
> {
>   auto wrap = IntegerArray(5); // This is CTFE! :-(
>   alias wrap this;
> }

All struct and class members which are directly initialized must have their
values known at compile-time. For structs, that's what goes in the init
value for the type. A side effect of this is that it's usually a bad idea to
directly initialize dynamic arrays which are member variables. You need to
do the initialization in a constructor. And for structs, if you need a
no-arg constructor, then you'll need to use a factory function (since
structs can't have no-arg constructors). e.g.

struct WrapIntegerArray
{
IntegerArray wrap;
alias wrap this;

this(int len)
{
wrap = IntegerArray(len);
}
}

or

struct WrapIntegerArray
{
IntegerArray wrap;
alias wrap this;

static make()
{
WrapIntegerArray retval;
retval.wrap = IntegerArray(5);
return retval;
}
}

So, you could then have something like

auto arr1 = WrapIntegerArray(5);
arr1[0] = 42;

or

auto arr1 = WrapIntegerArray.make();
arr1[0] = 42;

but if you use the init value (which is what you get if you let the type be
default-initialized), then you'll have to first do something to allocate the
dynamic array if you want to be able to index it, since if you don't give it
a value at compile-time, it's null (and you don't want to give it a value at
compile-time, because then every default-initialized struct of that type
will refer to the same dynamic array). Of course, you could always just
append values, and the dynamic array will be allocated and grow accordingly,
but that's obviously not the same as allocating it up front to have a
specific length.

- Jonathan M Davis





Re: Unexpected aliasing

2019-11-11 Thread Antonio Corbi via Digitalmars-d-learn

On Monday, 11 November 2019 at 19:17:37 UTC, Bastiaan Veelo wrote:
Recently I got my first surprise with our use of D. The symptom 
was that two local variables in two different functions 
appeared to be sharing data.


A simplified example is shown below (the original was machine 
translated from Pascal and involved templates and various 
levels of indirection). What I did not know is that the initial 
value of struct members is a compile time feature, apparently. 
What I suspect is happening is that the array lives in the 
static data segment (or is created in the module constructor?) 
and that the slices inside arr1 and arr2 get initialised to 
point to that same array.


I could use some help in rewriting the code below so that arr1 
and arr2 each have their own data; ideally with minimal changes 
so that I can make the transcompiler do the right thing.


Thanks!
Bastiaan.

void main()
{
import std.stdio;

WrapIntegerArray arr1;
arr1[0] = 42;

WrapIntegerArray arr2;

writeln(arr2[0]); // 42, not 0.
writeln("arr1.wrap.arr.ptr = ", arr1.wrap.arr.ptr);
	writeln("arr2.wrap.arr.ptr = ", arr2.wrap.arr.ptr); // 
identical

assert(arr2[0] == 0); // fails
}

struct IntegerArray
{
int[] arr;
alias arr this;
this(int l)
{
arr = new int[l];
}
}

struct WrapIntegerArray
{
auto wrap = IntegerArray(5); // This is CTFE! :-(
alias wrap this;
}



Defining and using a constructor for WrapIntegerArray seems to 
work:


void main()
{
import std.stdio;

WrapIntegerArray arr1 = WrapIntegerArray(5);
arr1[0] = 42;

WrapIntegerArray arr2 = WrapIntegerArray(5);

writeln(arr2[0]); // 42, not 0.
writeln("arr1.wrap.arr.ptr = ", arr1.wrap.arr.ptr);
writeln("arr2.wrap.arr.ptr = ", arr2.wrap.arr.ptr); // identical
assert(arr2[0] == 0); // fails
}

struct IntegerArray
{
int[] arr;
alias arr this;
this(int l)
{
arr = new int[l];
}
}

struct WrapIntegerArray
{
this (int v) {
  wrap = IntegerArray(5);
}

IntegerArray wrap;
alias wrap this;
}

Hope this helps.
Antonio



Unexpected aliasing

2019-11-11 Thread Bastiaan Veelo via Digitalmars-d-learn
Recently I got my first surprise with our use of D. The symptom 
was that two local variables in two different functions appeared 
to be sharing data.


A simplified example is shown below (the original was machine 
translated from Pascal and involved templates and various levels 
of indirection). What I did not know is that the initial value of 
struct members is a compile time feature, apparently. What I 
suspect is happening is that the array lives in the static data 
segment (or is created in the module constructor?) and that the 
slices inside arr1 and arr2 get initialised to point to that same 
array.


I could use some help in rewriting the code below so that arr1 
and arr2 each have their own data; ideally with minimal changes 
so that I can make the transcompiler do the right thing.


Thanks!
Bastiaan.

void main()
{
import std.stdio;

WrapIntegerArray arr1;
arr1[0] = 42;

WrapIntegerArray arr2;

writeln(arr2[0]); // 42, not 0.
writeln("arr1.wrap.arr.ptr = ", arr1.wrap.arr.ptr);
writeln("arr2.wrap.arr.ptr = ", arr2.wrap.arr.ptr); // identical
assert(arr2[0] == 0); // fails
}

struct IntegerArray
{
int[] arr;
alias arr this;
this(int l)
{
arr = new int[l];
}
}

struct WrapIntegerArray
{
auto wrap = IntegerArray(5); // This is CTFE! :-(
alias wrap this;
}