-
-
Notifications
You must be signed in to change notification settings - Fork 658
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
[js] Type mismatch when using Bool default values #4793
Comments
I cannot reproduce the problem with this: interface ISomeOtherInterface { }
interface IAnotherInterface { }
interface ITest extends IAnotherInterface {
function hello (world:String, cool:Bool = true):ISomeOtherInterface;
}
class TestImpl implements ITest {
public function hello (world:String, cool:Bool = true):ISomeOtherInterface {
return null;
}
} Any more hints? Does it only happen on a specific target? |
The issue title says "js". |
Oh right! But I can't reproduce it on JS either. |
It's part of a very complex codebase. It's something like: interface IModel {
public function foo (a:Int, b:String):Dynamic;
public function foo2 (a:Bool, b:Int):Void;
}
interface IListModel extends IModel {
public function foo3 (a:Float, b:Bool = true):IOtherModel;
}
class ListModelImpl implements IListModel {
...
}
class OtherListModelImpl extends ListModelImpl implements YetAnotherModel {
...
} I have a sinking feeling it may be hard to reproduce outside the original codebase, which I can't share (unfortunately). I had a feeling that the JS-specific nature and "question mark fixes the issue" characteristics might lend some clues to where or what in the code leads to this. On a dynamic target, I assume Null would most of the time fly invisibly under the radar, but since it needs to subscribe to an interface, that's where trouble comes. Even more curiously, on the "ListModelImpl" class, it even says that "notifyListModel" overrides the parent class with a bad type. It has no parent type, other than an interface, and the interface has no such function (though it has a few that start with "notify"), so it's all very curious. Layer upon layer upon layer |
Well, Maybe a bisect is necessary. |
Actually that's not true, I have made a change to this line recently: 4557c34 However, that should only affect things if |
The version of Haxe used was compiled from this source version: This appears to be two days before the I hoped this commit would make a difference: ...but I updated to the latest source from yesterday, same problem 😢 I believe the issue is still type mismatches (such as with an interface), not issues with passing parameters, though it is strange that errors occurred for at least one field that was not part of an interface or a parent class. Although the error occurs when using interfaces, its interesting that error occurs on the "override" code path, not the interface code path: https://github.com/HaxeFoundation/haxe/blob/development/typeload.ml#L889-L891 |
I just got the same error again, let me share the error output: path/to/Container.hx:120: lines 120-140 : Field setPrincipalComponentAndWalk overloads parent class with different or incomplete type
path/to/ActionOverlayView.hx:29: lines 29-117 : Defined in this class
path/to/Container.hx:120: lines 120-140 : child : com.framework.IFocusableComponent -> ?stopAtLayer : Null<Bool> -> Void should be child : com.framework.IFocusableComponent -> ?stopAtLayer : Bool -> Void
path/to/Container.hx:120: lines 120-140 : Null<Bool> should be Bool
path/to/ActionOverlayView.hx:29: lines 29-117 : Defined in this class The relevant function looks like this: private function setPrincipalComponentAndWalk(child : IFocusableComponent, stopAtLayer : Bool = false) : Void { ... } This occurs a few times, for basically any |
Are you sure you don't have |
I just hit an area of code that got upset about This original code works outside the project: interface IEventDispatcher {
public function addEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void;
public function removeEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false):Void;
...
}
class EventDispatcher implements IEventDispatcher {
public function addEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void { ... }
public function removeEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false):Void { ... }
...
} This modified code fails to compile... when I changed the interface to try and fix the other project, it broke EventDispatcher. The following does not unify: interface IEventDispatcher {
public function addEventListener (type:String, listener:Dynamic->Void, ?useCapture:Bool = false, ?priority:Int = 0, ?useWeakReference:Bool = false):Void;
public function removeEventListener (type:String, listener:Dynamic->Void, ?useCapture:Bool = false):Void;
...
}
class EventDispatcher implements IEventDispatcher {
public function addEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void { ... }
public function removeEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false):Void { ... }
...
} I always think of the question mark as being superfluous syntax when assigning a default value. It actually, in the current compiler and the JS target, results in Do you think that we should treat all If both are treated consistently, it may solve our problem. C++ (among other targets) definitely work with the code, so it seems related either to being a dynamic target, or maybe the JS code compilation process specifically. Thank you! |
As I said Let's try to get an overview:
|
Hmm, I don't think there is an It is still curious to me that other platforms (notably C++) are not having compile errors, I wonder if the type is inferred to The way it reacts about functions on a class with no parent type, unrelated to interfaces used, responding that it overrides with the wrong type, almost suggests to me that one pass (macro pass?) is typing something as I assumed that |
The real issue of grief comes from "It already is Bool, not Null, what am I supposed to do?!" (╯°□°)╯︵ ┻━┻) If you would like to tighten things up so the error occurs elsewhere (not allowing If
I've always thought of HOWEVER -- that won't fix the problem I'm having, it would remove my workaround to get the code to compile on the JavaScript target, and instead of adding |
I have improved error reporting, it now reports the position of the base/interface field as well like so:
This tells you exactly which two field the compiler "compares". Please paste the signatures of both fields so we can be sure they are indeed equal. |
I agree we should forbid value:Bool = null when no ? is set. This could be checked post compilation the way we already have "null can't be Bool on static platform" |
I also suggest I'm trying to test with the latest source, thanks @Simn |
The only nice thing about |
The new changes you made revealed one error like this: interface TestInterface {
function test (a:Dynamic):Void;
}
class Test implements TestInterface {
function test (a:CustomType):Void;
} This used to compile, but looks fishy. I'll keep running the tests to see what else comes up |
Yes we no longer allow this, it violates variance. See #4378. Reminds me to update the changelog. |
I am still working through it, but I wanted to mention that I got this output in an error I was working on: type : String -> listener : (Dynamic -> Void) -> ?useCapture : Null<Bool> -> Void should be type : String -> listener : (Dynamic -> Void) -> ?useCapture : Bool -> Void It says Anyway, I'll report more findings soon |
This is probably related to type printing only. Internally our function type arguments is represented as That's a discrepancy to our syntax though, so I suppose we should address it. I guess we can check if the argument type is nullable and only print the |
Related: #2561 |
Actually this is in favor of Joshua's idea because |
Alright, here's what I get: path/to/Component.hx:502: lines 502-505 : Field addEventListener overloads parent class with different or incomplete type
path/to/ScrollableContainer.hx:22: lines 22-293 : Defined in this class
path/to/Component.hx:502: lines 502-505 : Base field is defined here
path/to/ScrollableContainer.hx:22: lines 22-293 : Defined in this class
path/to/Component.hx:502: lines 502-505 : type : String -> listener : (Dynamic -> Void) -> ?useCapture : Null<Bool> -> ?priority : Null<Int> -> ?useWeakReference : Null<Bool> -> Void should be type : String -> listener : (Dynamic -> Void) -> ?useCapture : Bool -> ?priority : Int -> ?useWeakReference : Bool -> Void
path/to/Component.hx:502: lines 502-505 : Null<Bool> should be Bool
path/to/ScrollableContainer.hx:22: lines 22-293 : Defined in this class Here's an overview of what the code looks like, relevant to the error: interface IEventDispatcher {
public function addEventListener (type:String, listener:Dynamic->Void, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void;
...
}
interface IInvalidating {
...
}
interface IValidating {
...
}
interface IComponent extends IEventDispatcher extends IInvalidating extends IValidating {
...
}
class Component implements IComponent {
public function addEventListener(type : String, listener : Dynamic -> Void, useCapture : Bool = false, priority : Int = 0, useWeakReference : Bool = false) : Void { ... }
...
}
interface IScrollableContainer extends IContainer {
...
}
class ScrollableContainer extends Container implements IScrollableContainer {
...
} (the |
You keep mentioning interfaces, but this error clearly comes from an override. It looks very strange because the error positions suggest that the field is checking overrides against itself. At this point I wonder if there are any build macros involved? The only conceivable situation where something like this could happen is if a build macro makes a "copy" of a field and uses that fields' position. |
You are right, it is funny how it behaves as if it has overridden itself. I had looked for signs of munit doing something, or if mcover was enabled, but it appears to be caused by a library called mockatoo. Removing this haxelib appears to resolve these errors. https://github.com/misprintt/mockatoo/blob/master/src/mockatoo/macro/MockMaker.hx I have been looking at the code to see if there is something that would convert EDIT: Actually, I'm not sure yet if that solves the issue, or just creates different compile errors that mask it. Trying to figure it out 😓 |
I found the culprit, see linked issue. I'm still not sure why this behavior changed though. The Either way, this seems to be working as (currently) intended on our end. I'll keep it open to deal with |
Mockatoo has the exact same problem we have: When translating back to syntax they can't print |
I don't think we can make such change in 3.x, we can forbid some invalid cases if we decide to do so, but not silently change the semantics of ? Just a recap : the difference between ? and = was introduced because some platforms had their own native way to have not nullable optional parameters, and it was necessary to express this behavior to correctly declare such externs and be able to override native classes. |
I think I agree that we shouldn't change this at the moment. I'm struggling to see why we should disallow |
I never knew that I always believed it was shorthand, or an extra way of being verbose, like: class MyClass {
...
function privateFunction () {}
private function privateFunction2 ():Void {}
} Both I always thought When did |
As far as I'm aware Regarding unification on Dynamic targets, maybe we could indeed skip the check, but I'm not sure. It's probably not good for self-documentation if an overriding field has a different signature, so I'm not quite sure what we would gain by allowing this. |
@Simn I agree both are equivalent, but I think there's a lot of ?a:Bool occurrences out there, much more than a:Bool = null. Also, I would indeed like to keep ?a:Bool in 4.x |
I really think we should do something about the fact that we can't print our own function type properly for Haxe 4. |
Mockatoo would iterate over Static platforms received This forced type |
Does Would it be |
|
I will leave it to you all to decide if any changes are made to the compile behavior. IMHO, This is invalid syntax: var value = null;
if (value) {} If you cannot use On a different subject, as Nicolas mentions, I do believe there a lot of function test (?a:Bool) {
return a ? true : false;
} There is no I do see exceptions to this, but 100% they seem to be related to Neko only, as Neko uses #if neko
if (a == null) a = 0;
#end Changing the behavior of |
I have the same (or similar) problem with default Bools for Haxe/JS.
The Output is:
|
That's how it works on dynamic targets. |
Reading through this issue again I think we can close it because the original issue is resolved. Changing the way |
Using recent sources of the Haxe compiler, there appear to be mismatches when using
Bool
default values.For example, there is an interface that defines a function with an optional
Bool
parameter. There is a class that implements the interface, which also has the optionalBool
parameter.Something like this:
When compiled, the compiler says that the class' default
Bool
value is reallyNull<Bool>
, while the interface remainsBool
. The types are not the same, and it throws a compile error.If this were the real code, it would throw an error that
String->Null<Bool>->ISomeOtherInterface
should beString->Bool->ISomeOtherInterface
and that it overrides the parent class (yes it uses the words "parent class") with the wrong type.Interestingly (and lucky for me!) this does not occur if all parameters have a question mark.
?myValue:Bool = true
is consistentlyBool
in all cases. It's using onlymyValue:Bool = true
(which the code all originally uses) that results in theNull<Bool>
issues.Magically, this works:
I don't know if this would consistently happen all the time, or it's some combination of interfaces extending interfaces being implemented on classes extended by other classes. I don't know, but I wonder if the question mark exception here is intentional, or is how this issue was undetected (as I think all the Haxe test code uses question marks for optional values?)
The question marks should be optional, I am thankful for a workaround, but maybe you guys have an idea of the root cause? I believe it only affects 3.3 dev builds
The text was updated successfully, but these errors were encountered: