E111 Variable expected, redux

OK, I’m trying to use mapped types to wrap some array handling for cross platform use. We’re getting some odd results. Sample Java project (which is FAR from complete), which generates more errors than the .NET version. I’ve implemented both the mapped type (for ArrayList) and as plain arrays. The comments explain the errors and their locations.

namespace TextConApp;

interface

uses
  java.util;

type
  ConsoleApp = class
  public
    class method Main(args: array of String);
		fResIDs_0Base: StrResourceArrayList0;
		fNumItems: LongInt;
  end;

  AccArrayList<E> = public class mapped to ArrayList<E>
  public
    method add(element: E); mapped to add(element); //  Boolean;
		property Item[i: Integer]: E read mapped[i] write mapped[i]; default;
  end;

	AccArray<E> = public class mapped to array of E
	public
		property Item[i: Integer]: E read mapped[i] write mapped[i]; default;
	end;

	StrResInfo = public record
	public
		resStringIndex: LongInt;
	END;
	ResInfoArrayList0 = AccArrayList<StrResInfo>;
	ResInfoArray0 = array of StrResInfo;

	StrResource = public RECORD
	public
		numItems: LongInt;
		theItems_0Base: ResInfoArrayList0;
	END;
	StrResourceArrayList0 = AccArrayList<StrResource>;

	StrResource2 = public RECORD
	public
		numItems: LongInt;
		theItems_0Base: ResInfoArray0;
	END;
	StrResourceArray0 = array of StrResource2;

	TStrResourceArrayListMapped = public class
	public
		fResIDs_0Base: StrResourceArrayList0;
		fNumItems, fNumPointers: LongInt;
		procedure DoSomething;
	end;

	TStrResourceArray = public class
	public
		fResIDs_0Base: StrResourceArray0;
		fNumItems, fNumPointers: LongInt;
		procedure DoSomething;
	end;

implementation

class method ConsoleApp.Main(args: array of String);
begin
  System.out.println('Hello World.');
end;

	procedure TStrResourceArrayListMapped.DoSomething;
		var
			myStrResInfo: StrResInfo;
			myStrResource: StrResource;
	begin
		fNumItems := 0;
		fNumPointers := 0;

		// NOTE: Project has 'Allow Legacy "with"' checked.

		// What we want to work:
		with fResIDs_0Base[pred(fNumItems)] do 
			// This generates (E111) Variable expected in both Java and .NET
			theItems_0Base[pred(numItems)].resStringIndex := fNumPointers;

		// Which is essentially:
		// This generates (E111) Variable expected in both Java and .NET
		fResIDs_0Base[pred(fNumItems)].theItems_0Base[pred(fResIDs_0Base[pred(fNumItems)].numItems)].resStringIndex := fNumPointers;

		// What I thought might work via casting:
		// This generates (E111) Variable expected in both Java and .NET
		StrResInfo(StrResource(fResIDs_0Base[pred(fNumItems)]).theItems_0Base[pred(fResIDs_0Base[pred(fNumItems)].numItems)]).resStringIndex := fNumPointers;

		// What compiles:
		// This compiles in both Java and .NET
		myStrResource := fResIDs_0Base[pred(fNumItems)];
		myStrResInfo := myStrResource.theItems_0Base[pred(myStrResource.numItems)];
		myStrResInfo.resStringIndex := fNumPointers;
		
	end;

	procedure TStrResourceArray.DoSomething;
		var
			myStrResInfo: StrResInfo;
			myStrResource2: StrResource2;
	begin
		fNumItems := 0;
		fNumPointers := 0;

		// What we want to work:
		with fResIDs_0Base[pred(fNumItems)] do
		// This compiles in both Java and .NET
			theItems_0Base[pred(numItems)].resStringIndex := fNumPointers;

		// Which is essentially:
		// This compiles in both Java and .NET
		fResIDs_0Base[pred(fNumItems)].theItems_0Base[pred(fResIDs_0Base[pred(fNumItems)].numItems)].resStringIndex := fNumPointers;

		// What works:
		// This compiles in both Java and .NET
		myStrResource2 := fResIDs_0Base[pred(fNumItems)];
		myStrResInfo := myStrResource2.theItems_0Base[pred(myStrResource2.numItems)];
		myStrResInfo.resStringIndex := fNumPointers;
		
	end;

end.

Thots?

From what i see, for each “Variable expected” you get, it’s becuase you try to change a record field value returned by a property getter? That’s not allowed because they wouldn’t actually take effect, any change you do on a record only applies to where it it is stored, since a property getter returns a copy of a record, it would change the result of that, then throw it away.

Well, after further fiddling, it seems the ‘record’ is causing the problem. If I change the references to ‘record’ to ‘class’, as in:

StrResInfo = public class
	public
		resStringIndex: LongInt;
	END;
	ResInfoArrayList0 = AccArrayList<StrResInfo>;
	ResInfoArray0 = array of StrResInfo;

	StrResource = public class
	public
		numItems: LongInt;
		theItems_0Base: ResInfoArrayList0;
	END;
	StrResourceArrayList0 = AccArrayList<StrResource>;

	StrResource2 = public class
	public
		numItems: LongInt;
		theItems_0Base: ResInfoArray0;
	END;
	StrResourceArray0 = array of StrResource2;

then it compiles, and works as expected.

I was under the impression that a ‘record’ was a class, except without functions. Must we change all our code implementing records to call them classes?

A record is a value type, it has different semantics than a class type. the general idea is that records are allocated on the stack/inline and classes are allocated on the heap. Now the way they’re passed is quite different too, records are copied when passed, while classes are passed as a reference to it. (Side note, on Java we make records work by building them on top of classes, but that’s an implementation detail).

Changing from records to classes has some side effects, but if you originally used records and pointers to them, classes would be what you should use now

I’m still trying to understand this. Is the implementation of ArrayList fundamentally different than Array of MyRecord?

Because ArrayList does not permit assignment to fields within MyRecord, but Array of MyRecord permits assignment to MyRecord fields.

I guess it would follow that [] index operators on ‘Array of’ style arrays are not index properties by they are for ArrayList?

We like the ‘stack-yness’ of the records, but we need arrays that can be resized–otherwise we’d stick with ‘Array of’ arrays.