haxe icon indicating copy to clipboard operation
haxe copied to clipboard

Definite assignment analysis for if-cases and while-loops fail to conclude that value is initialized before usage

Open oscarcederberg opened this issue 1 year ago • 7 comments

The compiler is missing or is not precise enough in its definite assignment analysis of variables that are definitely assigned before a point of usage of the variable. This leads to the compiler giving an error for programs that are otherwise correct.

In both below failing cases, there is no way of the program to reach the trace()-call without the variable value being initialized beforehand:

class Test {
  static function main() {
    var value: Int;
    
    if (true) {
      value = 3;
    }

    trace(value); // ERROR: Local variable value used without being initialized
  }
}
class Test {
  static function main() {
    var value: Int;
    
    while (true) {
      var random = Math.round(Math.random() * 10);
      if (random != 3 {
        value = random;
        break;
      }
    }

    trace(value); // ERROR: Local variable value used without being initialized
  }
}

This is something that is handled in Clang (link), but not in Rust (link).

oscarcederberg avatar Jan 20 '24 21:01 oscarcederberg

The first case is a matter of detecting if (true) in particular, which should be easy.

The second one requires merging all loop exits, i.e. all breaks. This will also require some stack handling for nested loops, but shouldn't be too tricky either.

Simn avatar Jan 20 '24 22:01 Simn

Interestingly, the analyzer doesn't const-propagate the == case either:

class Main {
	@:analyzer(full_debug)
	static function main() {
		var value:Int = 0;

		while (true) {
			var random = Math.round(Math.random() * 10);
			if (random == 3) {
				value = random;
				break;
			}
		}

		trace(value);
	}
}

It should be able to generate this as trace(3). But that might be somewhat advanced and basically require treating == like =.

Simn avatar Jan 20 '24 22:01 Simn

Related/duplicate: https://github.com/HaxeFoundation/haxe/issues/8054

Antriel avatar Jan 21 '24 07:01 Antriel

That one seems much harder because it requires some sort of post-condition handling, and real code-flow analysis. The analyzer could probably figure it out but the var init checker won't be able to.

Simn avatar Jan 21 '24 08:01 Simn

I think I would be satisfied with some way to disable the uninit var check for the variable. IIRC my issue was using it with inlined constructor call, and initializing it to null prevented inlining. That can be solved by making the inlined constructor some empty dummy, and adding some init inlined function to actually initialize it, so it's not a big issue.

Antriel avatar Jan 21 '24 08:01 Antriel

The first case is a matter of detecting if (true) in particular, which should be easy.

if (...) {value = ...;} else {value = ...;} is another case as well, similar to the while (true) one. :slightly_smiling_face:

oscarcederberg avatar Jan 22 '24 12:01 oscarcederberg

The first case is a matter of detecting if (true) in particular, which should be easy.

if (...) {value = ...;} else {value = ...;} is another case as well, similar to the while (true) one. 🙂

That one is already handled though.

Simn avatar Jan 22 '24 13:01 Simn