Extension Overview

Now that we have Polyglot up and running, we can start implementing language extensions. This section describes the example language we will use to explore Polyglot features through the remainder of this tutorial.

Problems with array covariance

The covariance of array types in Java leads to performance overhead and ArrayStoreException exceptions. Covariance of array types permits the following subtyping rule:
  A ≤ B  
A[] ≤ B[]
That is, if A is a subtype of B, an array of type A is also a subtype of an array of type B. This subtyping rule allows the following program to type-check:
While the above subtyping rule is convenient for historical reasons, run-time type safety cannot be guaranteed without extra run-time checks, because the underlying array can only be assigned elements whose type is a subtype of the constructed array type. As a result, this program type-checks but fails at run time:
On line 5, the assignment to an element of oa will compile, because elements of oa is of type Object, determined by its declaration Object[], and we are assigning an Object to oa[1]. At run time, the underlying array represented by oa is in fact a String array. Storing an object in a String array will result in a run-time error.
Let's develop a Java language extension called CArray that removes the troublesome subtyping rule above, permitting array covariance only if array elements cannot be modified. We describe the semantics of CArray below.

Introduction to CArray

The above example program shows that array covariance and array assignments can interact in an undesirable way. A separation of these two properties will eliminate the run-time error. We must enforce these rules: Our extension language, CArray embraces both rules by (1) disallowing subtyping in traditional arrays, and (2) introducing an array type whose elements are constant, permitting array covariance.
An array whose elements are constant can be declared using keyword const before the first square bracket, such as
int const[]
which denotes an integer array whose elements are not modifiable.
Multidimensional constant arrays are supported:
String const[][] csm = { { "Hello", "World" },
                         { "Kitty", "Cup" } };
This code declares a constant array of constant arrays of String, whose elements (i.e., arrays of String) are unmodifiable. That is, the keyword const applies to both the inner and outer arrays.

Example CArray programs

Constant arrays can be used as formal parameters. Elements in constant arrays can always be read, but can never be written:
class CArrayAccess {
    int const[] a = { 1, 2, 3 };
    int addNine(int const[] a) {
        a[2] = 6; // error, cannot change array elements
        return a[1] + 9; // okay
    }
}
Constant arrays are covariant, but nonconstant arrays are invariant:
class CArrayCovariance {
    String const[] csa;
    CharSequence const[] cca;
    Object const[] coa;

    String[] sa;
    CharSequence[] ca;
    Object[] oa;

    void foo() {
        cca = csa; // String const[] ≤ CharSequence const[]
        coa = cca; // CharSequence const[] ≤ Object const[]
        coa = csa; // String const[] ≤ Object const[]
        
        /*
        These assignments are illegal:
        ca = sa; // String[] ≤ CharSequence[]
        oa = ca; // CharSequence[] ≤ Object[]
        oa = sa; // String[] ≤ Object[]
        */
    }
}
We can assign a nonconstant array to a constant array. This prevents updates to the array elements via the constant array. We cannot assign a constant array to a nonconstant array, however, or we would make nonconstant arrays covariant through a series of assignments:
class CArrayAssign {
    int const[] cia;
    String const[] csa;
    CharSequence const[] cca;
    Object const[] coa;

    int[] ia;
    String[] sa;
    CharSequence[] ca;
    Object[] oa;

    void foo() {
        cia = ia;
        csa = sa;
        cca = sa; // via covariance
        cca = ca;
        coa = sa; // via covariance
        coa = ca; // via covariance
        coa = oa;
        
        /*
        These assignments are illegal:
        ia = cia;
        sa = csa;
        ca = cca;
        oa = coa;

        If oa = coa were legal, these three assignments would make nonconstant
        arrays covariant!
        csa = sa; // okay
        coa = csa; // okay
        oa = coa; // Were this legal, transitivity would yield oa = sa!
        */
    }

    void bar(int const[] a) {
    }

    void baz(int[] a) {
    }

    void quux() {
        bar(cia);
        bar(ia);
        baz(ia);

        /*
        This invocation is illegal:
        baz(cia);
        */
    }
}
The results of array initializers and array creation expressions are considered nonconstant, as they can be assigned to both traditional and constant arrays:
class CArrayInit {
    int const[][] cim1 = { { 1, 2, 3 } };
    String const[] csa = { "Hello", "World!" };
    int const[][] cim2 = {
            new int[] { 1, 2, 3 },
            new int[] { 4, 5, 6 },
            new int[] { 7, 8, 9 } };

    /*
    These initializations are illegal because of incorrect types:
    int const[][] cimx = { 1, 2, 3 };
    String const[] csax = { { "Hello", "World!" } };
    */

    void foo() {
        int const[][] cim = new int[][] {
                        new int[] { 1, 2, 3 },
                        new int[] { 4, 5, 6 },
                        new int[] { 7, 8, 9 } };
    }
}
Constant arrays can be cast to other constant array types as long as the corresponding non-constant array types have that subtyping relationship in Java. (See JLS 3rd Ed, 4.10.3. This does not, for example, permit int[] to be cast to long[].) Nonconstant arrays cannot be cast to nonconstant arrays of different types. Constant arrays cannot be cast to nonconstant arrays; otherwise, we could make nonconstant arrays covariant through a series of casts and assignments:
class CArrayCast {
    Object const[] coa;
    String const[] csa;
    Object[] oa;
    String[] sa;
    int const[] cia;

    void foo() {
        Object const[] coa1 = (Object const[]) csa;
        String const[] csa1 = (String const[]) coa;

        /*
        These casts are illegal:
        int const[] cia1 = (int const[]) coa;
        Object[] oa1 = (Object[]) sa;
        String[] sa1 = (String[]) oa;
        int[] ia1 = (int[]) cia;
        oa = (Object[]) coa; 

        If oa = (Object[]) coa were legal, these three assignments would make
        nonconstant arrays covariant!
        csa = sa; // okay
        coa = csa; // okay
        oa = (Object[]) coa; // Were this legal, transitivity would yield oa = sa!
        */
    }
}