bmx-ng / bcc

A next-generation bcc parser for BlitzMax
zlib License
33 stars 12 forks source link

Overloading and identifying types (numbers and Null) #467

Open GWRon opened 5 years ago

GWRon commented 5 years ago

The latest brl.mod/reflection.mod-change introduced some overloading - leading to issues in my code as the overloading ("of course"?) cannot identify what method to use.

SuperStrict
Framework Brl.StandardIO

Type TTest
    Method PrintOut(v:Int)
        Print "Integer: " + v
    End Method

    Method PrintOut(v:Byte)
        Print "Byte: " + v
    End Method

    Method PrintOut(v:Short)
        Print "Short: " + v
    End Method

    Method PrintOut(v:String)
        Print "String: " + v
    End Method

    Method PrintOut(v:Object)
        If v
            Print "Object: " + v.ToString()
        Else
            Print "Object: null"
        EndIf
    End Method
End Type

Local t:TTest = New TTest
t.PrintOut(Null) 'fails
t.PrintOut(Object(Null)) 'works

t.PrintOut(0) 'odd
t.PrintOut(0:Byte) 'works

First fail results in Compile Error: Unable to determine overload to use: Method TTest.PrintOut(v:Int) or Method TTest.PrintOut(v:Byte). The "odd" print is using the "int" overloaded method. When using the brl.reflection functionality field.Set(obj, 0) it results in Compile Error: Unable to determine overload to use: Method TField.Set:Int(obj:Object,value:Byte) or Method TField.Set:Int(obj:Object,value:Short).

I understand the "problem" behind it - how should the compiler know about the value given to the function. Is a "0" an integer or an "byte"? A "null" can be an empty string, a non existing object, or simply a "0".

During compilation BCC knows if there is a "", an 0 or a null written, so it should be able to change preference: empty strings prefer string-overloads, null prefers objects and a number ... hmm, it could be the lowest possible number (0-255 = byte - and so on) but this hmm... is tricky. Maybe BCC should output a warning for ambiguity then. Or it should print a hint next to the current error message. Something stating that a number can be "defined" by appending the type (0:byte).

What are your thoughts on this?

HurryStarfish commented 5 years ago

First off: Yes, t.PrintOut(0) failing looks like a bug. It should choose the Int overload without complaints. The error message you're getting would only be legitimate if you only had a Byte overload and Short overload, but no Int overload.


I understand the "problem" behind it - how should the compiler know about the value given to the function. Is a "0" an integer or an "byte"?

That's very simple, actually: 0 is always an Int.

A "null" can be an empty string, a non existing object, or simply a "0".

During compilation BCC knows if there is a "", an 0 or a null written, so it should be able to change preference: empty strings prefer string-overloads, null prefers objects and a number

bcc does do that, but it doesn't involve any special treatment in regard to overloading. The way it works (or should work, if it isn't currently bugged) is this:

Null has a unique type, the "null type". You can't really do anything with that type at all, except convert it to another type. It can be implicitly and explicitly converted to any other type*, and when doing so, it turns into the default value for that type. This is why t.PrintOut(Null) fails - Null can turn into any other type, so it fits every overload of PrintOut equally well and thus the call is ambiguous.

As for other literals, including the ones you brought up:

Note that all this is actually unrelated to overloading: 0 is always an Int, and "" is always the same as String(Null). But due to this, a t.PrintOut(0) is supposed to prefer an Int overload and t.PrintOut("") is supposed to prefer a String overload. (And by "prefer", I mean "choose it without a warning or error".) If it doesn't do that, then that is indeed a bug.

(* except intrinsics and structs currently... we should probably fix that sometime)

GWRon commented 5 years ago

That's actually very simple: 0 is always an Int.

Why is this? Why not the smallest possible numeric type...or the biggest? Because in nonstrict numbers would be int by default too?

Anyways and as always: thanks for your elaborative answer...albeit they are often too complex to chew in one piece.

HurryStarfish commented 5 years ago

Why is this? Why not the smallest possible numeric type...or the biggest? Because Int was chosen as being generally the most useful one. Why not smaller? Because Byte or Short would restrict you to a smaller set of numbers you can work with, and the lesser amount of RAM they take up doesn't normally matter. Why not larger? Because Int, being 32 bit large, is efficient for calculations on a 32 bit computer. Long, which is 64 bit, works fine on a 64 bit computer, but is slower on a 32 bit one.

Several other languages (for example, Java) also use an 32-bit-sized int type for this reason. There are however also languages like C, where int is whatever the compiler feels like (which means it can choose the most efficient size for the target system, at the expense of portability).

GWRon commented 5 years ago

So it does not choose by "what suits best" but by "what is the most common".

Aside of that question it seems the second point of the issue (field.set()) is the one being potentially borked then(?).

HurryStarfish commented 5 years ago

If there is a TField.Set:Int(obj:Object,value:Int) overload, then field.Set(obj, 0) should always choose that, no matter which other overloads exist for the second parameter. Since you get the same kind of error from your TTest example though, it apparently has nothing to do with TField.Set in particular, it's just the overload resolution currently not working right.