ceylon/ceylon-compiler

JVM compiler really doesn't react well to higher order generics

lukedegruchy opened this issue · 0 comments

The JVM compiler outputs some rather cryptic compile errors in an IDE project where higher order generics were successfully compiled as JavaScript only.

This issue requests the implementation of clearer compile errors along the lines of "JVM compiler does not currently support Higher Order Generics feature" as opposed to the compile errors below.

Please see #2358 for the actual implementation of the feature on the JVM.

Apologies for the lengthly sample, but I figured this is good to capture the broad range of possible compile errors. Please note that this occurred on a production 1.2.0 Eclipse IDE, not one recently built from github sources.

Look for "JVM compile errors comments in the code below:

import ceylon.collection {
    LinkedList
}

shared void genericFunctionDefinitions() {
    alias ValueToValueValue => <Value> => [Value,Value];

    alias Pair<Value> => [Value,Value];

// JVM compile error:
// package SecondOrder does not exist
    interface SecondOrder<Box> given Box<Value> {
        shared formal Box<Float> createBox(Float float);
    }

    //apply to a generic type alias
    SecondOrder<Pair> something;

    //apply to a generic interface
    // List *is* a Type Function even though it already exists as something else
    SecondOrder<List> somethingElse;

    //apply to an anonymous type function
    SecondOrder<<Value> => [Value,Value]> somethingScaryLookin;

    Any pipe<Any>(Any anything) => anything;

    Number add<Number>(Number x, Number y)
            given Number satisfies Summable<Number>
            => x+y;

    // Are these both the same thing?
    alias AnyAnyAny => <Any> => Any(Any);
    alias AnyAny<Any> => Any(Any);

    alias AdditionLikeOperation
            => <Number> given Number satisfies Summable<Number>
            => Number(Number,Number);

    alias AdditionLikeOperationSimple<Number>
            given Number satisfies Summable<Number>
            => Number(Number,Number);
}

void genericFunctionReferences() {
    // generic function definition
    Any pipe<Any>(Any anything) => anything;

    // generic function reference
    <Any> => Any(Any) pipeFun = pipe;

    // generic function definition
    Number add<Number>(Number x, Number y)
            given Number satisfies Summable<Number>
            => x+y;

    // generic function alias
    alias AdditionLikeOperation
            => <Number> given Number satisfies Summable<Number>
            => Number(Number,Number);

    // generic function reference with raw function type
    <Number> given Number
             satisfies Summable<Number>
            => Number(Number,Number) addFun = add;

    // generic function reference with alias
    AdditionLikeOperation addFunWithAlias = add;

    // Simply pass the type to the generic function to create a function with the types set
    String(String) pipeString = pipeFun<String>;
    Object(Object) pipeObject = pipeFun<Object>;

    // Call the functions using type inference to obtain the resulting value
    String hi = pipeFun("hello");
    Integer zero = pipeFun(0);

    // generic function general form:
    // <TypeParameters> => ReturnType(ParameterTypes)

    // Take a Stream of elements and produce another Stream of elements
    // Define the Stream of elements returned in terms on the type constructor of the
    // Stream<Elem> param
    Stream<Elem> scan<Elem,Stream>
            (inputStream, newStream)
    //Note: Stream is a reference to a type function!
            given Stream<Elem> satisfies {Elem*} {

        // This is the original Stream
        Stream<Elem> inputStream;

        // This it the function type that will return a stream for a Stream of elements
        Stream<Elem> newStream<Elem>(Stream<Elem> elements);
        return nothing;
    }

    // Note:  I need Stream as well as Elem as a type parameter or this won't compile
    Stream<Elem> newStream<Stream,Elem>({Elem*} elements)
            given Stream<Elem> satisfies {Elem*};

    // 1) Iterable case:
    {Integer*} iterableInput = {1,2,3};
    scan<Integer,Iterable>(iterableInput, <Elem>({Elem*} elems) => elems);

    // 2) LinkedList case:
    LinkedList<Integer> linkedListInput = LinkedList{1,2,3};

    // JVM compile errors:
    // compiler bug: unhandled declaration unknown with type UnknownType at com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.makeReifiedTypeArgumentResolved(AbstractTransformer.java:5245)
    // compiler bug: visitor didn't yield any result at com.redhat.ceylon.compiler.java.codegen.ExpressionTransformer.transformExpression(ExpressionTransformer.java:340)
    // compiler bug: visitor didn't yield any result at unknown
    scan<Integer,LinkedList>(linkedListInput, LinkedList);

    Stream<Elem> newScan<Elem,Stream>
            (inputStream, newStream)
    //Note: Stream is a reference to a type function!
            given Stream<Elem> satisfies {Elem*} {

        // This is the original Stream
        Stream<Elem> inputStream;

        // This it the function type that will return a stream for a Stream of elements
        Stream<Elem> newStream<Elem>({Elem*} elements);
        return nothing;
    }

    // 1) Iterable case:
    {String*} iterableInput2 = {"1","2","3"};
    newScan<String,Iterable>(iterableInput2, <Elem>({Elem*} elems) => elems);

    // 2) LinkedList case:
    LinkedList<String> linkedListInput2 = LinkedList{"1","2","3"};

    // JVM compile errors:
    // compiler bug: unhandled declaration unknown with type UnknownType at com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.makeReifiedTypeArgumentResolved(AbstractTransformer.java:5245)
    // compiler bug: visitor didn't yield any result at com.redhat.ceylon.compiler.java.codegen.ExpressionTransformer.transformExpression(ExpressionTransformer.java:340)
    // compiler bug: visitor didn't yield any result at unknown
    newScan<String,LinkedList>(linkedListInput2, LinkedList);
}