From 0c21d0e7f0f192db2997c72f9c3e1082635d6ac4 Mon Sep 17 00:00:00 2001 From: Bulby Date: Fri, 29 Oct 2021 21:52:13 -0400 Subject: [PATCH 1/5] full overload support --- proposals/0000-full-overload-support.md | 63 +++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 proposals/0000-full-overload-support.md diff --git a/proposals/0000-full-overload-support.md b/proposals/0000-full-overload-support.md new file mode 100644 index 0000000..919454f --- /dev/null +++ b/proposals/0000-full-overload-support.md @@ -0,0 +1,63 @@ +# Full Overload Support + +* Proposal: [HXP-NNNN](NNNN-filename.md) +* Author: [BulbyVR](https://github.com/TheDrawingCoder-Gamer) + +## Introduction + +Makes the overload keyword fully work between all languages, by creating aliases on the compiled language end. + +## Motivation + +Right now, the overload keyword isn't fully cross compatible, which defeats the purpose of "Haxe, the cross-platform language." +This proposal reworks compiling overloading on languages that don't fully support it, like hashlink, by editing the names of +functions and function calls for different function signatures. +## Detailed design + +If the target already supports overloading, that implementation is used. +If not, then any overloaded function will be renamed. +Normal functions are renamed by their type signature. +For example: +```hx +overload static function fromArray(arr:Array):Color +overload static function fromArray(arr:Array):Color +``` +would become +```hx +static function fromArray_array_float_color(arr:Array):Color +static function fromArray_array_int_color(arr:Array):Color +``` +This applies to function calls as well. + +For constructors, one must be selected as the "true" constructor, and others are transformed into static functions. +The true constructor is the one that otherwise would end up with the longest function name. For example: +```hx +overload function new(r:Int, g:Int, b:Int, a:Int) +overload function new(r:Int, g:Int, b:Int) +``` +would become +```hx +function new (r:Int, g:Int, b:Int) +static function new_int_int_int(r:Int, g:Int, b:Int):Color +``` + +For all type signatures, it's calculated by lowercasing the types for each in the args and the return type, then placing them in order with underscores, with return type at the end. +The signature stringification can be changed a bit (especially considering type parameters) but it doesn't matter that much as long as it's unique. Generic Classes also apply like this, but using the given name. + +## Impact on existing code + +This shouldn't affect any existing code except for making it more cross platform. + +## Drawbacks + +On the compiled end the function names could be a bit verbose, however that doesn't matter much for most languages. + + +## Alternatives + +You can name the functions this way yourself, but this would make it more convenient to make overloads. + + +## Unresolved questions + +The naming scheme of the type signatures still needs to be fully worked out. It must be unique for any unique signature. From 79c335c273233de96dace274036ebabae8e38f53 Mon Sep 17 00:00:00 2001 From: Bulby Date: Sat, 30 Oct 2021 09:17:31 -0400 Subject: [PATCH 2/5] Clarify how overloads works --- proposals/0000-full-overload-support.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/proposals/0000-full-overload-support.md b/proposals/0000-full-overload-support.md index 919454f..d3d363f 100644 --- a/proposals/0000-full-overload-support.md +++ b/proposals/0000-full-overload-support.md @@ -37,13 +37,22 @@ overload function new(r:Int, g:Int, b:Int) ``` would become ```hx -function new (r:Int, g:Int, b:Int) +function new (r:Int, g:Int, b:Int, a:Int) static function new_int_int_int(r:Int, g:Int, b:Int):Color ``` For all type signatures, it's calculated by lowercasing the types for each in the args and the return type, then placing them in order with underscores, with return type at the end. The signature stringification can be changed a bit (especially considering type parameters) but it doesn't matter that much as long as it's unique. Generic Classes also apply like this, but using the given name. - + +Interfaces must not be allowed to have overloads, and this precludes the class implementing the function from overloading the function. When using reflection, getProperty must be used. Return types being changed does not count as a different type signature, and will emit an error. + +How do function calls know what to call? + +Haxe already knows how to look through overloads when it comes to monomorphs; But for clarity, Haxe will look through the overloads in order. +It first removes any that can't be the same length as the amount of args given, taking optional arguments into consideration. +Then, it tries to find one where the first arg unifies with the overload's first type, and then second arg, and so on. +Monomorphs are calculated exactly like they are for overloads already. + ## Impact on existing code This shouldn't affect any existing code except for making it more cross platform. From 1f61192e5631f0df0d635b2b1ea5f7ac7ba3e307 Mon Sep 17 00:00:00 2001 From: Bulby Date: Mon, 1 Nov 2021 08:40:58 -0400 Subject: [PATCH 3/5] Overriding, Overloading clarification --- proposals/0000-full-overload-support.md | 97 +++++++++++++++++++++---- 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/proposals/0000-full-overload-support.md b/proposals/0000-full-overload-support.md index d3d363f..84a621e 100644 --- a/proposals/0000-full-overload-support.md +++ b/proposals/0000-full-overload-support.md @@ -15,34 +15,105 @@ functions and function calls for different function signatures. ## Detailed design If the target already supports overloading, that implementation is used. -If not, then any overloaded function will be renamed. -Normal functions are renamed by their type signature. +If not, then any overloaded function will be renamed, and an adapter function will be created to support dynamic dispatch. +Normal functions are renamed by their type signature, and prefixed with "hx__" For example: ```hx -overload static function fromArray(arr:Array):Color -overload static function fromArray(arr:Array):Color +overload function fromArray(arr:Array):Color +overload function fromArray(arr:Array):Color ``` would become ```hx -static function fromArray_array_float_color(arr:Array):Color -static function fromArray_array_int_color(arr:Array):Color +function fromArray(arr:Dynamic) { + switch (Type.typeof(arr)) { + case TClass(Array): + hx__fromArray_Array_Float(arr); + case TClass(Array): + hx__fromArray_Array_Int(arr); + } +} +function hx__fromArray_Array_Float(arr:Array):Color +function hx__fromArray_Array_Int(arr:Array):Color ``` This applies to function calls as well. -For constructors, one must be selected as the "true" constructor, and others are transformed into static functions. -The true constructor is the one that otherwise would end up with the longest function name. For example: +For constructors, all are transformed into static functions and the original constructor is transformed into an adapter function. In the overloads, any super calls are redirected to their proper overload. Parent constructors that are not covered by their child are not exposed (can't be called from child). In the overloads, the first argument becomes "inst", meaning instance. It replaces any usage of self and is used for any assignment of member vars. ```hx -overload function new(r:Int, g:Int, b:Int, a:Int) -overload function new(r:Int, g:Int, b:Int) +class Parent { + overload function new(r:Int, g:Int, b:Int) + overload function new(arr:Array) +} +class Child extends Parent { + overload function new(r:Int, g:Int, b:Int, a:Int) { + // unrelated code + super([r, g, b, a]); + // blah blah + } + overload function new(r:Int, g:Int, b:Int) +} + ``` would become ```hx -function new (r:Int, g:Int, b:Int, a:Int) -static function new_int_int_int(r:Int, g:Int, b:Int):Color +class Parent { + + static function hx__new_Int_Int_Int(inst:Parent, r:Int, g:Int, b:Int):Void + static function hx__new_Array_Float(inst:Parent, arr:Array):Void + // Types aren't compatible thus dynamic + function new(...rest:Dynamic) { + switch (rest.length) { + case 1: + hx__new_Array_Float(this, rest[0]); + case 3: + hx__new_Int_Int_Int(this, rest[0], rest[1], rest[2]); + } + } + +} + +class Child extends Parent{ + static function hx__new_Int_Int_Int_Int(inst:Child, r:Int, g:Int, b:Int, a:Int):Void { + // dynamic dispatch isn't required here as we know our parent + Parent.new_Array_Float(inst, [r, g, b, a]); + } + static function hx__new_Int_Int_Int(inst:Child, r:Int, g:Int, b:Int):Void + // all args are int thus we don't need to make it dynamic + function new(...rest:Int) { + switch (rest.length) { + case 3: + hx__new_Int_Int_Int(this, rest[0], rest[1], rest[2]); + case 4: + hx__new_Int_Int_Int_Int(this, rest[0], rest[1], rest[2], rest[3]); + } + } + +} ``` -For all type signatures, it's calculated by lowercasing the types for each in the args and the return type, then placing them in order with underscores, with return type at the end. +When overriding a function that has overloads, the child function must be overloaded. Any parent functions not covered by the child is exposed. + +For all type signatures, it's calculated by stringing the argument types together with underscores. The signature stringification can be changed a bit (especially considering type parameters) but it doesn't matter that much as long as it's unique. Generic Classes also apply like this, but using the given name. +If two classes have the same name but are in different packages, they become fully qualified if the function names would overlap. + +``` +overload function fromVector3(v3:bulby.Vector3) +overload function fromVector3(v3:peote.Vector3) +``` +becomes +``` +function hx__fromVector3_bulby_Vector3(v3:bulby.Vector3) +function hx__fromVector3_peote_Vector3(v3:peote.Vector3) +function fromVector3(u0:Dynamic) { + switch (Type.typeof(u0)) { + case TClass(bulby.Vector3): + hx__fromVector3_bulby_Vector3(u0); + case TClass(peote.Vector3): + hx__fromVector3_peote_Vector3(u0); + } +} +``` + Interfaces must not be allowed to have overloads, and this precludes the class implementing the function from overloading the function. When using reflection, getProperty must be used. Return types being changed does not count as a different type signature, and will emit an error. From fd0d17b1facb51973101dbe0f5e9d027729a84d8 Mon Sep 17 00:00:00 2001 From: Bulby Date: Mon, 1 Nov 2021 08:41:45 -0400 Subject: [PATCH 4/5] Forgot the `hx` code marker --- proposals/0000-full-overload-support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-full-overload-support.md b/proposals/0000-full-overload-support.md index 84a621e..bff05f2 100644 --- a/proposals/0000-full-overload-support.md +++ b/proposals/0000-full-overload-support.md @@ -96,12 +96,12 @@ For all type signatures, it's calculated by stringing the argument types togethe The signature stringification can be changed a bit (especially considering type parameters) but it doesn't matter that much as long as it's unique. Generic Classes also apply like this, but using the given name. If two classes have the same name but are in different packages, they become fully qualified if the function names would overlap. -``` +```hx overload function fromVector3(v3:bulby.Vector3) overload function fromVector3(v3:peote.Vector3) ``` becomes -``` +```hx function hx__fromVector3_bulby_Vector3(v3:bulby.Vector3) function hx__fromVector3_peote_Vector3(v3:peote.Vector3) function fromVector3(u0:Dynamic) { From ef14fd80f836f2f3a26d6e3adf138906b30816ed Mon Sep 17 00:00:00 2001 From: Bulby Date: Mon, 6 Dec 2021 07:05:09 -0500 Subject: [PATCH 5/5] `@:generic` reference --- proposals/0000-full-overload-support.md | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/proposals/0000-full-overload-support.md b/proposals/0000-full-overload-support.md index bff05f2..736511d 100644 --- a/proposals/0000-full-overload-support.md +++ b/proposals/0000-full-overload-support.md @@ -16,7 +16,7 @@ functions and function calls for different function signatures. If the target already supports overloading, that implementation is used. If not, then any overloaded function will be renamed, and an adapter function will be created to support dynamic dispatch. -Normal functions are renamed by their type signature, and prefixed with "hx__" +Normal functions are renamed by their type signature (which is usually identical to if it were generated by `@:generic`). For example: ```hx overload function fromArray(arr:Array):Color @@ -27,13 +27,13 @@ would become function fromArray(arr:Dynamic) { switch (Type.typeof(arr)) { case TClass(Array): - hx__fromArray_Array_Float(arr); + fromArray_Array_Float(arr); case TClass(Array): - hx__fromArray_Array_Int(arr); + fromArray_Array_Int(arr); } } -function hx__fromArray_Array_Float(arr:Array):Color -function hx__fromArray_Array_Int(arr:Array):Color +function fromArray_Array_Float(arr:Array):Color +function fromArray_Array_Int(arr:Array):Color ``` This applies to function calls as well. @@ -57,33 +57,33 @@ would become ```hx class Parent { - static function hx__new_Int_Int_Int(inst:Parent, r:Int, g:Int, b:Int):Void - static function hx__new_Array_Float(inst:Parent, arr:Array):Void + static function new_Int_Int_Int(inst:Parent, r:Int, g:Int, b:Int):Void + static function new_Array_Float(inst:Parent, arr:Array):Void // Types aren't compatible thus dynamic function new(...rest:Dynamic) { switch (rest.length) { case 1: - hx__new_Array_Float(this, rest[0]); + new_Array_Float(this, rest[0]); case 3: - hx__new_Int_Int_Int(this, rest[0], rest[1], rest[2]); + new_Int_Int_Int(this, rest[0], rest[1], rest[2]); } } } class Child extends Parent{ - static function hx__new_Int_Int_Int_Int(inst:Child, r:Int, g:Int, b:Int, a:Int):Void { + static function new_Int_Int_Int_Int(inst:Child, r:Int, g:Int, b:Int, a:Int):Void { // dynamic dispatch isn't required here as we know our parent Parent.new_Array_Float(inst, [r, g, b, a]); } - static function hx__new_Int_Int_Int(inst:Child, r:Int, g:Int, b:Int):Void + static function new_Int_Int_Int(inst:Child, r:Int, g:Int, b:Int):Void // all args are int thus we don't need to make it dynamic function new(...rest:Int) { switch (rest.length) { case 3: - hx__new_Int_Int_Int(this, rest[0], rest[1], rest[2]); + new_Int_Int_Int(this, rest[0], rest[1], rest[2]); case 4: - hx__new_Int_Int_Int_Int(this, rest[0], rest[1], rest[2], rest[3]); + new_Int_Int_Int_Int(this, rest[0], rest[1], rest[2], rest[3]); } } @@ -102,14 +102,14 @@ overload function fromVector3(v3:peote.Vector3) ``` becomes ```hx -function hx__fromVector3_bulby_Vector3(v3:bulby.Vector3) -function hx__fromVector3_peote_Vector3(v3:peote.Vector3) +function fromVector3_bulby_Vector3(v3:bulby.Vector3) +function fromVector3_peote_Vector3(v3:peote.Vector3) function fromVector3(u0:Dynamic) { switch (Type.typeof(u0)) { case TClass(bulby.Vector3): - hx__fromVector3_bulby_Vector3(u0); + fromVector3_bulby_Vector3(u0); case TClass(peote.Vector3): - hx__fromVector3_peote_Vector3(u0); + fromVector3_peote_Vector3(u0); } } ```