haxe icon indicating copy to clipboard operation
haxe copied to clipboard

[hl] Cannot extend @:structInit class with constructor in subclass

Open MSGhero opened this issue 2 years ago • 6 comments

On HL, I am not able to extend a @:structInit class if the subclass has a constructor, receiving Invalid_argument("List.map2"). See below example, which works in try.haxe.org on JS but not HL.

package;

class Main {
	
	static public function main():Void {
		
		// Invalid_argument("List.map2")
		var b:B = new B();
		trace(b.a);
	}
}

@:structInit
class A {
	public var a:Int;
}

class B extends A {
	public function new() {
		a = 1;
	}
}

Occurs at least on Haxe 4.0.5 through 4.2.5 and 6bcae15, with HL 1.11

MSGhero avatar Apr 09 '22 02:04 MSGhero

I suspect that this is some general problem in the typer because the HL generator doesn't care about @:structInit at all.

Simn avatar Apr 19 '22 07:04 Simn

Yeah this generates a super() call without arguments, even though the parent constructor has one argument. Fails on some other targets too.

Simn avatar Apr 19 '22 07:04 Simn

Actually, I think this just has to fail with Missing super constructor call because we wouldn't know what argument to pass in.

Simn avatar Apr 19 '22 09:04 Simn

Yeah an error with "super" in it points me in a good direction. It's a bit confusing mixing supers with structInit, but ¯\_(ツ)_/¯

class Main {
	static public function main():Void {
                var b:B = { };
		trace(b.a); // 1
	}
}

@:structInit
class A {
	public var a:Int;
        public function new(i:Int) a = i;
}

@:structInit
class B extends A {
	public function new() {
		super(1);
	}
}

MSGhero avatar Apr 19 '22 23:04 MSGhero

Yeah our @:structInit handling is a bit shit... We also have problems in the other direction:

class Main {
	static public function main():Void {
		var a:A = {a: 0};
		trace(a);
	}
}

class A0 {
	public function new(x:String) {}
}

@:structInit
class A extends A0 {
	public var a:Int;
}

This can't possibly work because we cannot generate a super() call to A0.

The only situation in which we can generate a super(args) call is if we generate the entire constructor, because only then do we have an argument mapping. I'll look into cleaning this up.

Simn avatar Apr 20 '22 06:04 Simn

You don't even need to have a constructor in the chain to trigger this; just mixing structInit classes with unmarked ones can break it. In this example, we get the error because the middle class B is not @:structInit, even thought it has no explicit constructor.

@:structInit class A {
	public var foo: Int = 1;
}

class B extends A {}

@:structInit class C extends B {}

class Test {
  static function main() {
    var test: C = {};
    trace(test.foo);
  }
}

nspitko avatar Aug 28 '22 00:08 nspitko