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

Switch with at least one field match accesses all the fields. #11062

Closed
back2dos opened this issue Mar 30, 2023 · 3 comments · Fixed by #11141
Closed

Switch with at least one field match accesses all the fields. #11062

back2dos opened this issue Mar 30, 2023 · 3 comments · Fixed by #11141
Assignees
Milestone

Comments

@back2dos
Copy link
Member

back2dos commented Mar 30, 2023

This piece of code traces called even though nothing access bar:

class Test {
    static function main() {
        var foo = new Foo();
        
        switch foo {
            case { foo: 123 }:
                trace('yes!');
            case { foo: 321 }:
                trace('yes!');
            default:
        }
    }
}

class Foo {
    public var foo:Int = 5;
    
    public var bar(get, never):Int;
    function get_bar() {
        trace('called');
        return 4;
    }
    
    public function new() {}
}

This broke between 3.4.7 and 4.0.5. So in any case, not a regression (within Haxe 4).

It is quite unfortunate for getters that cause actual side effects (like tracking access), or even just do relatively expensive computations (in the worst case synchronized via a lock/mutex). Also potentially sabotages DCE.

I would speculate that maybe this got introduced in Haxe 4, because as the analyzer got better at cleaning up things, the compiler got a bit more liberal about churning out temp stuff. Unfortunately, non-final methods are never pure, so the getter call remains (even if I remove the trace). In my head, the intermediary AST has something like var _tmp_bar = foo.get_bar(); (I can see the temp var for the foo field, so maybe I'm not too far off) and then the analyzer deletes the var, but not the init expr, because that's not pure.

If that is the problem, then one solution would be to support @:pure on expression level (could be a useful footgun for macros too) and then generate something like var _tmp_bar = @:pure foo.get_bar();.

@Simn Simn self-assigned this Mar 30, 2023
@Simn
Copy link
Member

Simn commented Mar 30, 2023

I consider this a pattern matcher bug, it should not generate unused temp vars like that.

@Simn Simn added this to the Later milestone Mar 30, 2023
@Simn
Copy link
Member

Simn commented Mar 30, 2023

I took a quick look but couldn't find an easy solution. We would either have to detect unused fields and filter them out or address this at pattern-level.

@Simn
Copy link
Member

Simn commented Apr 13, 2023

I have fixed this in 17b28d2, but I don't particularly like this solution. I think the general problem here is that we should defer initialization of subject variables until they are actually used. This problem is quite similar to #5274, so ideally we'd find a solution that addresses both cases.

It seems difficult to know what match subjects are used ahead of time because patterns can expand and whatnot, so ideally we would lazify all bind variables and mark them as used during processing. This should be relatively easy for generated locals because these won't appear in user code.

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.

2 participants