Mapped class to wrapped value types can't work as expected

public class TestClass {
    public static void test() {

        // ok
        #if ECHOES
        Nullable<int> a = 5;
        #elif COOPER
        java.lang.Integer a = 5;
        #elif NOUGAT
        Foundation.NSInteger a = 5;
        #endif

        gint ga = 5;    // (E62) Type mismatch, cannot assign "Integer"/"Int32" to "gint"
        a = ga;     // ok

        doSth(a);   // ok
        doSth(ga);  // COOPER: (E62) Type mismatch, cannot assign "gint" to "Integer"
                    // ECHOES/NOUGAT: (E486) Parameter 1 is "gint", should be "Int32", in call to static void TestClass.doSth(Int32 v)

    }

    public static void doSth(int v) {
    }
}

#if ECHOES
public __mapped struct gint => Nullable<int> {
#elif COOPER
public __mapped class gint => java.lang.Integer {
#elif NOUGAT
public __mapped struct gint => Foundation.NSInteger {
#endif
}

Elements 8.3.92.1895

  1. This can be reproduced on all platforms Android/.NET/iOS
  2. This can be reproduced for other wrapped value types: Boolean, Double, etc.

you can only map to a base type, such as Int. “Nullable” or “not nullable” is a modification of a type, yo cant map to that.

Actually I’ve tried things like:

public __mapped class gint => java.lang.Integer{
}// for Android

public __mapped struct gint => Foundation.NSInteger{
} // for iOS

public __mapped struct gint => Nullable<int>{
} // for .NET

and get the same compile errors…But the target classes themself can work under the same context, for example, the implicit conversion of java.lang.Integer and raw int is OK.

I think int? is only an alias name for a specified type on different platform, so I just try to use it to reduce duplicated code.

This issue is found because I try to make things like:

#if ECHOES
public __mapped struct gint => int {
}
#elif COOPER || NOUGAT
public __mapped class gint => int? {
}
#endif

So I can use gint to implement generic interface/delegate with a clean code:

public interface ITestInterface<T>{
    T get();
}

public class TestClass : ITestInterface<gint>{
    public gint get() => 5;
}

Otherwise, cross-platform code will be terrible:

public class TestClass : ITestInterface<int>{
    #if ECHOES
    public int get() => 5;
    #else
    public int? get() => 5;
    #endif
}

Another example:

public class List<T> {
    public delegate int Comparer<T>(T a, T b);

    public void sort(Comparer<T> comparer) {
        // ...
    }
}

public static class ListComparer {

    // Without gint
    #if ECHOES
    public static int intDescent(int a, int b) {
    #else
    public static int intDescent(int? a, int? b) {
    #endif
        return b - a;
    }


    // With gint
    public static int intDescent(gint a, gint b) {
        return b - a;
    }
}

public class TestCode {
    public void test() {
        var list = new List<int>();
        list.sort(ListComparer.intDescent);
    }
}

Thanks, logged as bugs://73821 << to disable mapping to nullable

Thanks, logged as bugs://73822 << for the errors

Hrmm, if mapping to nullable is disabled, I have to change codes which works now:

public mapped class gint => int?{
}

to

#if COOPER
public __mapped class gint => java.lang.Integer{
#elif NOUGAT
public __mapped class gint => Foundation.NSInteger{
#elif ECHOES
public __mapped struct gint => Nullable<int>{
#endif
}

This seems make no sense??

bugs://73821 got closed with status fixed.

Why? just remove the ?

its mapping to a nullable type that makes no sense. a nullable type is not an actual distinct type. the type is, say, int. a variable of type int? can be still points to an int, (or to null). there’s no such thing as a “nullable int” type the can live at runtime (platform specific implementation details aside). you can not allocate a “nullable int”. you cannot have an instance off a “nullable int”. you can have an int instance or not have an int instance.

  1. I think int? is only an alias name for a specific type, which means for example: int? will become java.lang.Integer for Java when compile.
    So the only reason I will use int? in C# is I want to use this alias name to replace duplicated codes, just like we use object to replace different specific object types (java.lang.Object, System.Object, NSObject etc.)
    Correct me if I’m wrong.

  2. Making a mapping type to int? is my attempt to solve a generic problem for raw types:

Is there a better way to solve this?

That’s an implementation detail for how nullable types are implemented on Java. Conceptually, on a language level, what i write above applies.

Still don’t see any benefit from simply disabling mapping to nullable types in RO C#…
If a nullable type (such as nullable raw types on all platforms as far as we know) is only an alias name of a specific real type, the mapping should be legal.
If a nullable type whose “nullable” is only a concept for compiler, it can be simply ignored in mapping because there’s no side-effect.

On the other side, the generic type problem for raw types in cross-platform code as I described exists, proving mapping types to nullable raw types are useful.

Actually this reported bug has nothing to do with mapping to nullable types. The cause is the mapping type mechanism can’t work well for some specific real types.
(Here refer to my another unresolved report months ago: Mapped class to Class can’t work on iOS)

Maybe it’s my fault to drag in things like int? in my bug report…
If mapping to nullable type is disabled in the next beta, I should change my report title and description by replacing int? with java.lang.Integer, NSInteger and Nullable<int>.

ok, lets assume:

type MyInt mapped to nullable Integer

What do these three vars do? are z and z nullable? not nullable? is y double nullable? what?

var x: MyInt;
var y: nullable MyInt;
var z: not nullable MyInt;

sure it could be ignored. but that’s one imprecise and confusing, there’s all sort son stuff thats technically wrong that we could just “ignore”. but different than human languages, programming languages tent to want to be precise, and consider “wrong” stuff as errors.

right, which is why i logged a separate issue for the actual problem.

ok…I don’t get used to using “nullable” and “non-nullable” concepts much in my dev yet, since official C# and Java are not fond of these concepts stressed by Swift. (Official C# thinks only non-nullable value types can be attached with nullable modification)

Well, I will modify my origin bug report in order to meet new limitations.

Can you tell me what your current issue with this is, as you referred to from the other thread?

I think this reply is detailed enough to explain why we need things like gint if there’s no other better solution:
http://talk.remobjects.com/t/mapped-class-to-wrapped-value-types-cant-work-as-expected/7635/4

Then on 8.3.92.1901, I can define the gint but it can’t work as a mapped type without explicitly defined gint<->int? operators, so it’s the bug 73822 I reported in this thread.

After 8.3.92.1901, I have no way to define the gint, because of the bug I reported here:
http://talk.remobjects.com/t/compile-error-when-map-to-normal-type/7803

Also I have no way to define implicit gint<->int? conversions as I reported here:
http://talk.remobjects.com/t/cant-define-implicit-conversion/7804

Is this still an issue you need solved?

It seems be fixed on COOPER and ECHOES, but still need to define implicit operators to make it work on NOUGAT.

Here’s the test code:

public __mapped struct gint => Foundation.NSInteger {
    // Uncomment below operators to eliminate compile errors
    // public static implicit operator gint(int r) => r;
    // public static implicit operator int(gint g) => g;
}

public class TestClass {
    public TestClass() {
        gint a = 5; // error: (E62) Type mismatch, cannot assign "Int32" to "gint"
        int aa = a; // error: (E62) Type mismatch, cannot assign "gint" to "Int32"
        doSth(a);   // error: (E486) Parameter 1 is "gint", should be "Int32", in call to void TestClass.doSth(Int32 v)
    }

    public void doSth(int v) {
    }
}

oh! but that’s not a bug. The big difference here is that you use NSInteger, which is an int64, if you use int32 it works. Otherwise you need conversion methods.

Gotcha! Then there’s no problem now.