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

Generic classes inline setter in other file messes up something in the compilation server. #9358

Closed
muchitto opened this issue Apr 26, 2020 · 4 comments · Fixed by #11376
Closed

Comments

@muchitto
Copy link

muchitto commented Apr 26, 2020

So, I have this strange situation where I have this two source file example:

src/Main.hx:

enum StateEnum {
    State1;
    State2;
}

class Main {
    var stateHandler = new StateHandler<StateEnum>();

    public function new () {
        stateHandler.state = State1;
    }
    
    public static function main() {
        new Main();
        
    }
}

src/StateHandler.hx

@:generic
class StateHandler<S> {
    public var state (default, set) : S;

    public function new () {}

    inline function set_state (state) {
        trace("State changed in the handler!");

        return this.state = state;
    }
}

build.hxml

-cp src
-m Main

-hl out/hl.hl

The first time I compile it using the compilation server, it works great. When I modify the Main.hx-file (like just adding a line break in the end of the file). It gives me this error:

Main.hx on line 11: Don't know how to cast StateHandler_StateEnum to StateHandler

When I modify the StateHandler.hx-file (again, only adding a line break at the end or something) it seems to reset the situation and compilation works great again until I modify the Main.hx-file again. Also removing the inline from the set_state -setter, makes things run smoothly again.

Also, so far this seems to happen only when I compile to hashlink...

I'm using the latest Haxe nightly. 4.1.0-rc.1+5d88aa62d

@ncannasse
Copy link
Member

Oooh a reproducible compiler cache error :D

@RealyUniqueName RealyUniqueName added this to the Bugs milestone Apr 29, 2020
@Simn Simn modified the milestones: Bugs, Later Mar 24, 2023
@Simn
Copy link
Member

Simn commented Nov 15, 2023

This still reproduces, here's the pertinent dump diff:

Merge_6ktfVX6ooU

So somehow the type parameter becomes unapplied. I don't really understand the HL error though because it talks about StateHandler_StateEnum and StateHandler, while the dump diff has neither of those types.

@Simn Simn self-assigned this Nov 15, 2023
@Simn
Copy link
Member

Simn commented Nov 15, 2023

This is quite confusing... the result of the generic expansion is correct on the second run:

[GENERIC] {
  	cf_name = set_state;
	  cf_doc = None;
	  cf_type = TMono (None);
	  cf_pos = source/StateHandler.hx: 114-225;
	  cf_name_pos = source/StateHandler.hx: 130-139;
	  cf_meta = [];
	  cf_kind = inline method;
	  cf_params = [];
	  cf_expr = [Function:(state : StateEnum) -> StateEnum]
			[Arg state<11905>:StateEnum]
			[Block:Dynamic]
				[Call:Void]
					[Field:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void]
						[TypeExpr haxe.Log:Class<haxe.Log>]
						[FStatic:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void]
							haxe.Log
							trace:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void
					[Const:String] "State changed in the handler!"
					[ObjectDecl:{ methodName : String, lineNumber : Int, fileName : String, ?customParams : Null<Array<Dynamic>>, className : String }]
						fileName: [Const:String] "source/StateHandler.hx"
						lineNumber: [Const:Int] 8
						className: [Const:String] "StateHandler"
						methodName: [Const:String] "set_state"
				[Return:Dynamic]
					[Binop:StateEnum]
						[Field:StateEnum]
							[Const:StateHandler_StateEnum] this
							[FInstance:StateEnum]
								StateHandler_StateEnum
								state:StateEnum
						=
						[Local state(11905):StateEnum:StateEnum];
	  cf_flags = ;
  }

But the inliner receives a field with a different expression:

Inline set_state:
	Args: 
		state<11908> = [Field:StateEnum]
			[TypeExpr StateEnum:Enum<StateEnum>]
			[FEnum:StateEnum]
				StateEnum
				State1
		_this<11907> = [Field:StateHandler_StateEnum]
			[Const:Main] this
			[FInstance:StateHandler_StateEnum]
				Main
				stateHandler:StateHandler_StateEnum
	Expr: [Block:Dynamic]
		[Call:Void]
			[Field:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void]
				[TypeExpr haxe.Log:Class<haxe.Log>]
				[FStatic:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void]
					haxe.Log
					trace:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void
			[Const:String] "State changed in the handler!"
			[ObjectDecl:{ methodName : String, lineNumber : Int, fileName : String, ?customParams : Null<Array<Dynamic>>, className : String }]
				fileName: [Const:String] "source/StateHandler.hx"
				lineNumber: [Const:Int] 8
				className: [Const:String] "StateHandler"
				methodName: [Const:String] "set_state"
		[Return:Dynamic]
			[Binop:StateHandler.S]
				[Field:StateHandler.S]
					[Const:StateHandler<StateHandler.S>] this
					[FInstance:StateHandler.S]
						StateHandler<StateHandler.S>
						state:StateHandler.S
				=
				[Local state(3270):StateHandler.S:StateHandler.S]

Note how the [Binop:StateEnum] has become [Binop:StateHandler.S]. This seems to suggest that it somehow picks up the set_state from StateHandler itself, but I don't know yet how that's possible (and how it relates to the compilation server).

@Simn
Copy link
Member

Simn commented Nov 15, 2023

That took me forever, but I finally found it. It has a certain idiotic beauty to it: The server persists cf_expr_unoptimized, which is the original field expression that is used when inlining. On the first compilation, the generic instance creator copies the original field and maps cf_expr to make it expand generics.

When Main.hx gets invalidated, the generic instance StateHandler_StateEnum also gets invalidated because StateEnum is defined in that file. However, StateHandler itself remains in the cache, and so do the cf_expr_unoptimized of its fields. On the second compilation, the generic expansion then copies that value to the generated class field, and the inliner then picks it up, leading to the observed error.

@kLabz We should check if there are any other instances of { cf with cf_ ... } that could cause this, because this kind of stuff would cause us HXB-grief as well. IMO all these should be replaced by a function call, if for no other reason than making it easier to find them in the sources.

@Simn Simn closed this as completed in 128261a Nov 15, 2023
0b1kn00b pushed a commit to 0b1kn00b/haxe that referenced this issue Jan 25, 2024
* change return of build_instance to record

* also rename

* also seperate

* also bring back typedef deprecation check

* alsp keep special cases in the right place

* mess around

* avoid some work if we need no params anyway

* make @:generic inference work again

* remove Generic_Exception

* push expected type before loading instance

closes HaxeFoundation#3864

* expand generic types on field call candidate when expanding generic function

closes HaxeFoundation#5482

* build generic parents as well

closes HaxeFoundation#6761

* inherit `@:autoBuild` to generic instance

closes HaxeFoundation#5536

* also inherit @:keepSub while we're at it

closes HaxeFoundation#6500

* deal with KExpr in generic classes properly

closes HaxeFoundation#7574

* don't inherit cf_expr_unoptimized to generic instances

closes HaxeFoundation#9358

* reroute FClosure in map_expr_type

closes HaxeFoundation#9395

* inherit cl_using to generic instances

closes HaxeFoundation#10528

* set inherited flags on generic instance fields at the right time

closes HaxeFoundation#11010

* I really don't care

* wild guess

* avoid the follow change
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants