Bug 16282 - inconsistent behavior when using ctype with boxing object
Summary: inconsistent behavior when using ctype with boxing object
Status: RESOLVED UPSTREAM
Alias: None
Product: Compilers
Classification: Mono
Component: VisualBasic ()
Version: 2.10.x
Hardware: PC Linux
: --- normal
Target Milestone: ---
Assignee: Rolf Bjarne Kvinge [MSFT]
URL:
Depends on:
Blocks:
 
Reported: 2013-11-15 16:11 UTC by Hzj_jie
Modified: 2018-05-22 12:17 UTC (History)
5 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
source code w/ binary (3.96 KB, application/octet-stream)
2013-11-16 05:01 UTC, Hzj_jie
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED UPSTREAM

Description Hzj_jie 2013-11-15 16:11:28 UTC
when moving my code to mono, I found an inconsistent behavior when ctype <vb.net, similar as (TYPE)obj in c#> between Microsoft .net framework and mono. the basic case is following,

Public Module main
    Public Function cast(Of t)(ByVal i As Object, ByRef o As t) As Boolean
        If i Is Nothing Then
            Console.WriteLine("cast as Nothing")
            o = Nothing
            Return True
        Else
            Try
                o = DirectCast(i, t)
                Console.WriteLine("cast as DirectCast")
                Return True
            Catch
                Try
                    o = CType(i, t)
                    Console.WriteLine("cast as CType")
                    Return True
                Catch
                    o = Nothing
                    Console.WriteLine("cast failed")
                    Return False
                End Try
            End Try
            '1. may not be able to CType(i, t)
            '2. may not be able to GetType(t).IsValueType?
            'On Error Resume Next
            'o = CType(i, t)
            'Return Not o Is Nothing OrElse GetType(t).IsValueType
        End If
    End Function

    Public Function cast(Of t)(ByVal i As Object) As t
        Dim o As t
        If cast(Of t)(i, o) Then
            Return o
        Else
            Return Nothing
        End If
    End Function

    Public Sub Main()
        Dim i As Int64 = 100
        Console.WriteLine(CType(i, Byte))
        Console.WriteLine(CType(i, SByte))
        Console.WriteLine(CType(i, Int32))
        Console.WriteLine(CType(i, UInt32))
        Console.WriteLine(CType(i, Int64))
        Console.WriteLine(CType(i, UInt64))

        Console.WriteLine("----")

        Console.WriteLine(cast(Of Byte)(i))
        Console.WriteLine(cast(Of SByte)(i))
        Console.WriteLine(cast(Of Int32)(i))
        Console.WriteLine(cast(Of UInt32)(i))
        Console.WriteLine(cast(Of Int64)(i))
        Console.WriteLine(cast(Of UInt64)(i))

        Console.WriteLine("----")

        Console.WriteLine("CType(i, Object) Is Nothing ? " + Convert.ToString(CType(i, Object) Is Nothing))
    End Sub
End Module

when calling the cast function <surely it's not necessary to do the casting in this way, but it's a part of generic classes, while I do not know the exactly type when it is been used.>, the Int64 i should be boxed as an object, and in this situation, the ctype behavior is inconsistent.
following the output in .net and mono
.net
100
100
100
100
100
100
----
cast as CType
100
cast as CType
100
cast as CType
100
cast as CType
100
cast as DirectCast
100
cast as CType
100
----
CType(i, Object) Is Nothing ? False

mono
100
100
100
100
100
100
----
cast failed
0
cast failed
0
cast failed
0
cast failed
0
cast as DirectCast
100
cast failed
0
----
CType(i, Object) Is Nothing ? False

it looks like CType function failed to detect the real object type after boxing.
the compile command is pretty simple as vbc ctype.vb.
the .net version i am running is 3.5, but i also tested it on 4.0, with same output.
the mono version i am running is
Mono JIT compiler version 2.10.8.1 (Debian 2.10.8.1-5ubuntu1)
Copyright (C) 2002-2011 Novell, Inc, Xamarin, Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  x86
        Disabled:      none
        Misc:          softdebug
        LLVM:          supported, not enabled.
        GC:            Included Boehm (with typed GC and Parallel Mark)

please kindly let me know if it's a known issue, thank you in advanced.
Comment 1 Zoltan Varga 2013-11-16 02:49:28 UTC
Could you attach the compiled executable ?
Comment 2 Hzj_jie 2013-11-16 05:01:33 UTC
Created attachment 5456 [details]
source code w/ binary
Comment 3 Zoltan Varga 2013-11-16 18:10:10 UTC
-> vb
Comment 4 Hzj_jie 2013-11-18 07:11:14 UTC
as i have tried boxing + ctype, it's more likely the bug is coming from generics instead of runtime.
Comment 5 Zoltan Varga 2013-11-18 09:52:06 UTC
The problem is probably in the Microsoft.VisualBasic.CompilerServices.Conversions::ToGenericParameter<!!0>(object) method which is called by the compiled executable to do the casting.
Comment 6 Hzj_jie 2013-11-25 09:05:11 UTC
is there anyway i can help to resolve the issue?
thank you in advanced.
Comment 7 Zoltan Varga 2013-11-25 10:01:13 UTC
The vb compiler/class libs are not heavily maintained nowdays, so it might take some time before this gets fixed.
Comment 8 Hzj_jie 2013-11-25 23:23:31 UTC
yes, as a dev myself, it would be great if i can help from source level directly, any information about how to start?
Comment 9 Zoltan Varga 2013-11-26 02:07:43 UTC
https://github.com/mono/mono-basic
Comment 10 Hzj_jie 2013-11-27 10:16:42 UTC
thank you Zoltan, I will have a look soon.
Comment 11 Hzj_jie 2013-12-14 12:23:43 UTC
as far as my investigation, this bug is not from the Conversions class, but as i have moved the code from vb.net to c#, it works well,

using System;

public static class Program
{
    public static bool cast<T>(object i, out T o)
    {
        if(i == null)
        {
            Console.WriteLine("cast as null");
            o = default(T);
            return true;
        }
        else
        {
            try
            {
                o = (T)i;
                Console.WriteLine("cast as (T)");
                return true;
            }
            catch
            {
                try
                {
                    o = (T)(dynamic)i;
                    Console.WriteLine("cast as (T)(dynamic)");
                    return true;
                }
                catch
                {
                    o = default(T);
                    Console.WriteLine("cast failed");
                    return false;
                }
            }
        }
    }

    public static T cast<T>(object i)
    {
        T o;
        if(cast<T>(i, out o)) return o;
        else return default(T);
    }

    public static void Main()
    {
        long i = 100;
        Console.WriteLine((byte)i);
        Console.WriteLine((sbyte)i);
        Console.WriteLine((int)i);
        Console.WriteLine((uint)i);
        Console.WriteLine((long)i);
        Console.WriteLine((ulong)i);

        Console.WriteLine("----");

        Console.WriteLine(cast<byte>(i));
        Console.WriteLine(cast<sbyte>(i));
        Console.WriteLine(cast<int>(i));
        Console.WriteLine(cast<uint>(i));
        Console.WriteLine(cast<long>(i));
        Console.WriteLine(cast<ulong>(i));

        Console.WriteLine("----");
    }
}

so it is a VBRuntime specific issue, but i am still working on finding out the implementation of CType function
Comment 12 Rolf Bjarne Kvinge [MSFT] 2013-12-16 08:27:21 UTC
CType is not one function in particular, it's treated specially by the vb compiler depending no the types you're trying to convert.

In this particular case it's converted into a call to Microsoft.VisualBasic.CompilerServices.Conversions::ToGenericParameter, which you can find here: https://github.com/mono/mono-basic/blob/master/vbruntime/Microsoft.VisualBasic/Microsoft.VisualBasic.CompilerServices/Conversions.vb#L127
Comment 13 Hzj_jie 2013-12-16 10:07:41 UTC
Thank you for the information Rolf,
to be clear, the binary is built in Windows with vbc, instead of mono compiler. so it should have no dependencies to mono VB compiler.

but since the ToGenericParameter function is in runtime, it should be called during runtime.
i have had a look at the ToGenericParameter function, it directly calls DirectCast, which is AFAIK, not correct, since boxed object can not be direct cast to other types as the following logic shows.

Imports System

Public Module _Main
    Public Sub Main()
        Dim a As Integer = 100
        Dim b As Object = a
        Console.WriteLine(DirectCast(b, Integer))
        Console.WriteLine(DirectCast(b, Long))    'will fail w/ exception
    End Sub
End Module

so in this case, is it correct we should let ToGenericParameter to unbox the object if it's indeed a value type?
Comment 14 Hzj_jie 2013-12-23 07:13:11 UTC
so in this case, we could change the implementation of ToGenericParameter function from DirectCast to Convert.ChangeType. i have changed the test case a little bit, while since the ctype in mono is not working well, using Convert.ChangeType works. is there anyway i can push the change to mono?


Public Module main
    Public Function cast(Of t)(ByVal i As Object, ByRef o As t) As Boolean
        If i Is Nothing Then
            Console.WriteLine("cast as Nothing")
            o = Nothing
            Return True
        Else
            Console.WriteLine(i.GetType().FullName())
            Try
                o = DirectCast(i, t)
                Console.WriteLine("cast as DirectCast")
                Return True
            Catch
                Try
                    o = Convert.ChangeType(i, GetType(t))
                    Console.WriteLine("cast as Convert.ChangeType")
                    Return True
                Catch
                    Try
                        o = CType(i, t)
                        Console.WriteLine("cast as CType")
                        Return True
                    Catch
                        o = Nothing
                        Console.WriteLine("cast failed")
                        Return False
                    End Try
                End Try
            End Try
            '1. may not be able to CType(i, t)
            '2. may not be able to GetType(t).IsValueType?
            'On Error Resume Next
            'o = CType(i, t)
            'Return Not o Is Nothing OrElse GetType(t).IsValueType
        End If
    End Function

    Public Function cast(Of t)(ByVal i As Object) As t
        Dim o As t
        If cast(Of t)(i, o) Then
            Return o
        Else
            Return Nothing
        End If
    End Function

    Public Sub Main()
        Dim i As Int64 = 100
        Console.WriteLine(CType(i, Byte))
        Console.WriteLine(CType(i, SByte))
        Console.WriteLine(CType(i, Int32))
        Console.WriteLine(CType(i, UInt32))
        Console.WriteLine(CType(i, Int64))
        Console.WriteLine(CType(i, UInt64))

        Console.WriteLine("----")

        Console.WriteLine(cast(Of Byte)(i))
        Console.WriteLine(cast(Of SByte)(i))
        Console.WriteLine(cast(Of Int32)(i))
        Console.WriteLine(cast(Of UInt32)(i))
        Console.WriteLine(cast(Of Int64)(i))
        Console.WriteLine(cast(Of UInt64)(i))

        Console.WriteLine("----")

        Console.WriteLine("CType(i, Object) Is Nothing ? " +
Convert.ToString(CType(i, Object) Is Nothing))
    End Sub
End Module
Comment 15 Rolf Bjarne Kvinge [MSFT] 2018-05-22 12:17:25 UTC
Moved to: https://github.com/mono/mono-basic/issues/34