XSharpPublic icon indicating copy to clipboard operation
XSharpPublic copied to clipboard

Incompatibility with Object.ToString()

Open DenGhostYY opened this issue 2 years ago • 11 comments

Describe the bug I can't move a class with a method :ToString() from XBase++ to X#

To Reproduce .prg

procedure Main()
    local o
    
    o := Example():new(5)
    ? o:ToString()
    
    return
    
class Example
exported:
    inline method Init(b)
        ::b := b
    return self

    inline method ToString()
    return ntoc(::b)

    var b
endclass

Expected behavior (xBase++ exe) Output

5

Actual behavior (X# compiler)

error XS9042: Override of virtual method 'ToString' in child class has CLIPPER calling convention but overridden method in parent class is STRICT.
error XS0508: 'Example.ToString(params usual[])': return type must be 'string' to match overridden member 'object.ToString()'

Additional context X# Compiler version 2.16.0.5 (public) -dialect:xBase++ -xpp1 -lb -memvar -vo1 -vo3 -vo5 -vo10 -vo15 -vo16

DenGhostYY avatar Jun 27 '23 04:06 DenGhostYY

In .Net all classes do have a ToString() (and a couple more) method, as they are all derived from System.Objects, so this is something that can't be changed.

I think the solution is easy, specify the NEW modifier in the method declaration, to make your ToString() different than the standard ToString():

inline new method ToString()

An even better solution is to strongly type the method and give it the same signature with the standard method as in

METHOD ToString() AS STRING STRICT (need to also specify OVERRIDE METHOD , if /vo3 is not enabled)

but I think you will not prefer this one, as it involves more changes in the code.

cpyrgas avatar Jun 27 '23 06:06 cpyrgas

The modifier new only works if I add a type declaration to the variable o. This option does not suit me, since the type declaration will have to be added at all places of use.

procedure Main()
    local o as Example
    
    o := Example():new(5)
    ? o:ToString()
    
    wait
    return
    
    
class Example
exported:
    inline method Init(b)
        ::b := b
        return self
        
    inline new method ToString()
        return str(::b)
        
    var b
endclass

When I rewrote the method signature like this

inline method ToString() as string strict
        return str(::b)

I got a compiler error

error XS9002: Parser: unexpected input 'strict'

DenGhostYY avatar Jun 27 '23 07:06 DenGhostYY

That's strange, the syntax with "strict" seems to compile fine here. Maybe there was some other syntax error in the code and the parser got confused?

But about the version with NEW, you are right, I think that's a general (not related only to the XBase++ dialect) bug in the runtime, when there exist both a CLIPPER and a STRICT method in the class hierarchy, then a late bound call to that method invokes always the parent one (no matter if it's the CLIPPER or STRICT one), instead of the child one.

Robert, do you agree this is a problem? Following sample code always prints "foo parent" and "bar parent", instead of calling the child ones:

CLASS Parent
	METHOD Foo() AS VOID STRICT
		? "foo parent"
	METHOD Bar() CLIPPER
		? "bar parent"
	RETURN NIL
END CLASS

CLASS Child INHERIT Parent
	NEW METHOD Foo() CLIPPER
		? "foo child"
	RETURN NIL
	NEW METHOD Bar() AS VOID STRICT
		? "bar parent"
END CLASS

FUNCTION Start( ) AS VOID
	LOCAL u := Child{}
	u:Foo()
	u:Bar()

cpyrgas avatar Jun 27 '23 08:06 cpyrgas

Chris, The child method in your example also displays "bar parent". So yes, that makes sense...

Robert

RobertvanderHulst avatar Jun 27 '23 08:06 RobertvanderHulst

The description of the method syntax doesn't mention the identifier calling convention anywhere. Can I send you a sln-project? https://www.xsharp.eu/help/command_xpp_classmembers.html https://github.com/X-Sharp/XSharpPublic/issues/1286#issuecomment-1609047415

DenGhostYY avatar Jun 27 '23 08:06 DenGhostYY

The calling convention was recently added in this commit https://github.com/X-Sharp/XSharpDev/commit/fb755e1dce948dcf6a4653d17f94f8ef2e9af6c2 to the xpp inline method, so that explains why the STRICT clause works for Chris and not for you. This commit also adds support for Type parameters to XPP methods.

RobertvanderHulst avatar Jun 27 '23 09:06 RobertvanderHulst

Robert,

Oops, sorry. In the corrected code of the sample below, it prints

foo parent strict bar child strict

so it always calls the STRICT version of the method, no matter if it's in the child or parent class.

CLASS Parent
	METHOD Foo() AS VOID STRICT
		? "foo parent strict"
	METHOD Bar() CLIPPER
		? "bar parent clipper"
	RETURN NIL
END CLASS

CLASS Child INHERIT Parent
	NEW METHOD Foo() CLIPPER
		? "foo child clipper"
	RETURN NIL
	NEW METHOD Bar() AS VOID STRICT
		? "bar child strict"
END CLASS

FUNCTION Start( ) AS VOID
	LOCAL u := Child{}
	u:Foo()
	u:Bar()

cpyrgas avatar Jun 27 '23 09:06 cpyrgas

Did I understand correctly that the commit has already been made and I can update the compiler using this link? https://www.xsharp.eu/itm-downloads/download?path=installers%252FXSharpSetupPublic2.16.0.5.zip

The calling convention was recently added in this commit X-Sharp/XSharpDev@fb755e1 to the xpp inline method, so that explains why the STRICT clause works for Chris and not for you. This commit also adds support for Type parameters to XPP methods.

DenGhostYY avatar Jun 27 '23 09:06 DenGhostYY

I am sorry but that fix is not included in the 2.16.0.5 build. That build is from end of may. The fix was done on June 1.

RobertvanderHulst avatar Jun 27 '23 10:06 RobertvanderHulst

When is the next build coming out?

DenGhostYY avatar Jun 27 '23 10:06 DenGhostYY

We plan a subscribers build in the coming weeks. Probably an internal build this week and a beta next week. We have not scheduled a date for the public build yet.

RobertvanderHulst avatar Jun 27 '23 10:06 RobertvanderHulst