HaxeFoundation / HaxeFoundation/haxe
[cpp] Lambda bug with Const type parameter
- Dominant language
- Haxe
- Stars
- 6.9k
- Forks
- 715
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 11
Description
## INFO
Haxe: `4.3.4`
Target: `cpp`
Platform: `windows`
## TEST CODE
Main.hx
```haxe
package;
@:generic
class BaseCustomType {
var value:String;
public function new() {
value = "BaseCustomType";
}
public function print():Void {
trace(value); // works
var lol = function() {
trace("before");
trace("lol", value);
trace("after");
}
lol(); // does not work (null object reference)
}
}
@:genericBuild(CustomMacro.build())
class CustomType {}
class Main {
public static function main() {
var hello = new CustomType<"Hello, Haxe!">();
hello.print();
}
}
```
CustomMacro.hx
```haxe
package;
#if !macro
class CustomMacro {}
#else
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.Expr;
using haxe.macro.Tools;
class CustomMacro {
static var counter:Int = 0;
public static function build():ComplexType {
var param = switch (Context.getLocalType()) {
case TInst(_, [param]):
param;
default:
throw "CustomMacro can only be used on a type parameter";
}
var s = switch (param) {
case TInst(_.get() => {kind: KExpr(macro $v{(s : String)})}, _):
s;
default:
throw "CustomMacro can only be used on a type parameter with a string literal";
}
var e = switch (param) {
case TInst(_.get() => {kind: KExpr(e)}, _):
e;
default:
throw "CustomMacro can only be used on a type parameter with a string literal";
}
var cls = Context.getLocalClass().get();
var name = cls.name + "_" + counter++;
var baseType:TypePath = {
pack: [],
name: "Main",
sub: "BaseCustomType",
params: [TPExpr(e)]
};
var genCls = macro class $name extends $baseType {
public function new() {
super();
value = $v{s};
}
};
genCls.pos = Context.currentPos();
Context.defineType(genCls);
return Context.getType(name).toComplexType();
}
}
#end
```
## ISSUE
We have `CustomType` which uses `@:genericBuild` to do stuff with the `Const` type parameter.
We also have `BaseCustomType`, with the `@:generic` metadata. Its purpose is to hold the base implementation of the class.
When we have a class with the `@:generic` metadata, it creates two cpp files:
`BaseCustomType__34_Hello_44__32_Haxe_33__34_`
`BaseCustomType_String`
This isn't necessarily an issue, however i noticed that lambdas don't work anymore.
I've tracked it down in the generated cpp code to be an issue with casting `this` to the wrong type.
The cpp code, creates a `_gthis` variable and parses it to the lambda as an argument.
The issue is that inside `BaseCustomType__34_Hello_44__32_Haxe_33__34_obj` it does this:
```cpp
::BaseCustomType_String _gthis = ::hx::ObjectPtr(this);
```
However, it needs to be:
```cpp
::BaseCustomType__34_Hello_44__32_Haxe_33__34_ _gthis = ::hx::ObjectPtr(this);
```
Since it casts it to the wrong type, it leads to a `Null Object Reference` error, when trying to access a member variable inside the lambda
Contributor guide
Research direction
Reproduce the issue using the Main.hx and CustomMacro.hx examples with Haxe 4.3.4 targeting C++ on Windows, then inspect the generated BaseCustomType C++ files. Trace the lambda's _gthis type and verify that accessing value inside the lambda no longer causes a null object reference.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100