Extension method compiler confusion

Given this Method in a Module:

Public Function ToBytesVariant(extends v as Variant) As Uint8()
[...]
End Function

Should I be able to call it using a normal (non-extension) syntax?

var v as variant = "foobar"
var bytes() as uint8 
bytes = v.ToBytesVariant()   // this compiles
bytes = ToBytesVariant(v)    // this does not compile

The second version does not compile, and the error message is weird:

Extension method Module1.ToBytesVariant requires a conversion from class Window1.Window1 to type Variant; use CType to explicitly convert first

Is this

  • a bug in the compiler, where calling an Extension method without using Extension syntax should be allowed, but is failing?
  • a bug in the compiler, where calling an Extension method without using Extension syntax is not allowed, and the error message is wrong?

No, extends means that you have to prefix the class before the method, ie your first syntax. That’s the point of it.

So I think you are picking option two?

I notice that if I try a third way:

bytes = Module1.ToBytesVariant(v)

I get another error message

Static reference to extension method: call this on a value of type Variant.

the error message is more clear.

Agreed, the first error is not clear at all. That one is much better. I think I have a bug report for that. Let me see.

No it was a different but similar issue, which is now closed:
Delegate defined with an Assigns parameter results in a baffling error message
https://tracker.xojo.com/xojoinc/xojo/-/issues/64376

I’m not getting an error here. This is my toBytes function definition in a module:

Public Function toBytes(extends v as Variant) As UInt8()
  ...
End Function

Then using as:

var v as Variant = "Test"
var result() as UInt8 = v.toBytes

I do note that your function is named VariantToBytes but you’re trying to call a function named ToBytesVariant.

1 Like

The problem is when you try and do:

var v as Variant = "Test"
var result() as UInt8 = toBytes( v )

But why would you? It’s an extends method.

1 Like

Sorry, that was a typo. Fixed.

I’ve submitted this as Extension method - confusing compiler error message

To get around that, you need a separate definition:

Public Function toBytes(extends v as Variant) As UInt8()
  ...
End Function
Public Function toBytes(v as Variant) As UInt8()
  return v.toBytes
End Function
3 Likes

Right, if it’s not allowed, the compiler message should be sensible.

I think it’s just giving the best it can because you’re doing something you shouldn’t be and it probably hasn’t come up a lot before without people realizing and correcting their issue. A better message would be a great idea, though, and I’ll toss a thumbs up on your case. Be sure you do the same.

Agreed, always thumbs up your own bugs if they are affecting you. It helps to boost the priority of a fix. +1 from me.

The error message is worded like it is because, in the context of Window1, these two lines of code are equivalent.

bytes = ToBytesVariant(v)
bytes = Self.ToBytesVariant(v)    

Self is simply implied due to the context, just like you can do either of these to change the window width.

Width = 500
Self.Width = 500

I do agree that the wording is confusing, but I’m not sure how they could fix that since it’s technically correct.

3 Likes

Interesting point about “Self” - in fact, this code:

bytes = Self.ToBytesVariant

gives the exact same error message.

And this code:

bytes = ToBytesVariant(v)

in the context of a Module method (not a Window method), does not give the weird error message.

So I think you are right, it’s the implied “Self” in all window methods which is confusing the compiler here. Fun!

1 Like

I stumbled upon this because I had a vague sense that you could always call extension methods with or without the dot notation, e.g.

dim s as string
dim n as integer
n = s.LenB()
n = LenB(s)

but in hindsight, I think this was just a feature of the API, where some functions such as LenB are defined with both method signatures.

That’s what led me to think this was a general property of Extension methods.

The n = LenB(s) requires the method to be defined with an Assigns parameter, where the s.LenB requires an Extends parameter.

I don’t remember if you can do an Assigns signature in a module method, but I would think so.

It’s not an Assigns method. An Assigns method would allow you to say:

Method LenB( s as String, assigns x as Integer ) // Allows:
LenB(s) = 10
(s as a standard parameter, 10 in x as an assigns)

Method LenB( Extends s as String, assigns x as Integer )  // Allows:
s.LenB = 10
(s via extends, 10 in x via assigns)

Method LenB( Extends s as String ) as Integer  // Allows:
variable = LenB( s )

Method LenB( Extends s as String  ) as Integer  // Allows:
variable s.LenB

Yup, you’re right. Shows you how much I use Assigns… :grimacing:

They can be very nice for code readability.

I notice that in API2, this “dual use” doesn’t exist:

// API2
var s as string
var n as integer
n = s.Length()  // OK
n = Length(s)   // Error: There is more than one method with this name but this does not match any of the available signatures.