haxe icon indicating copy to clipboard operation
haxe copied to clipboard

Cannot use structure inititialisation (from `@:structInit`) to declare a new entry in a map

Open Nales0 opened this issue 4 years ago • 3 comments

Using Haxe version 4.0.5. Here is the content of the file Main.hx.

package;

@:structInit
class Student
{
    public final name:String;
    public final score:Int;
}

class Main
{
    public static function main()
    {
        var map = new Map<String, Student>();

        var student1:Student = { name: "Boris", score: 20 };
        map["the best"] = student1;

        map["the worst"] = { name: "Vianney", score: -1 };  // Compilation error here
    }
}

When trying to launch this code using haxe --run Main, the compiler gives this error:

Main.hx:19: characters 9-58 : No @:arrayAccess function for haxe.ds.Map<String, Student> accepts arguments of String and { score : Int, name : String }

We must use a temporary variable to insert a new value in the map when using the way to construct an instance given by @:structInit.

Nales0 avatar Mar 30 '22 13:03 Nales0

Interestingly, var map:Map<String, Student> = ["the worst" => { name: "Vianney", score: -1 }]; does work

Edit: type hint makes it work too (map["the worst"] = ({ name: "Vianney", score: -1 } :Student);) but I suppose it should be handled without it

kLabz avatar Mar 30 '22 13:03 kLabz

This is a top-down inference problem which we can reduce to this:

abstract A(String) {
	public function new() {
		this = "";
	}

	@:arrayAccess public function arrayWrite(k:String, v:Array<Dynamic>) {}
}

class Main {
	public static function main() {
		var a = new A();
		a["foo"] = [1, "foo"];
	}
}

This fails with Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>, which means that there is no expected type when typing the rhs of the assignment.

I suppose we can do the same we do for from-casts here: If there's only a single @:arrayAccess on the abstract, we can use its types for top-down inference.

Simn avatar Aug 03 '22 06:08 Simn

Come to think of it, for consistency we would want this to work for the keys as well:

abstract A(String) {
	public function new() {
		this = "";
	}

	@:arrayAccess public function arrayRead(k:Array<Dynamic>) {}
}

class Main {
	public static function main() {
		var a = new A();
		a[[1, "foo"]];
	}
}

This gets pretty tricky due to some internal architecture concerns.

Simn avatar Aug 03 '22 12:08 Simn