Passing a Class<T> for Every Type Variable T

When working on our GPCE Mint tutorial, Eddy and I realized that there is a problem when programmers use type variables inside brackets. A method like

1
2
3
4
public separable <X> Code<X> fun(Code<X> c1, Code<X> c2) {
return <| ( `(lfTest.eval(e,f).booleanCodeValue()) ?
              `c2 : `c1 )  |>;
}

causes Mint to generate code containing the free type variable X. There is no way to get any type information from a type variable at runtime, e.g. by doing X.class, so instead we decided to require the user to have a final instance of Class<X> in scope somewhere.

This was a pragmatic solution that seems to work pretty well. The method above can be rewritten as

1
2
3
4
5
public separable <X> Code<X> fun(Code<X> c1, Code<X> c2,
                                 final Class<X> xc) {
return <| ( `(lfTest.eval(e,f).booleanCodeValue()) ?
              `c2 : `c1 )  |>;
}

To implement this, we had to find all variables of type Class<X> that were in scope at the time a bracket was generated. This was surprisingly confusing to do with the javac environment and scope classes. In the end, I decided to adapt the Resolver.findVar and Resolver.findField methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    /** This method returns a list of all VarSymbols of type Class<X> in scope where X is a type variable. */
    List<VarSymbol> getClassVarsInScope() {
        // System.out.println("env = "+env);
        List<VarSymbol> ret = List.nil();
        Scope current = env.info.scope;
        // System.out.println("Scope: "+current);
        while(current != null) { // also go into the outer (enclosing) scopes
            // for(Symbol s: current.elems) {
            for (Scope.Entry e = current.elems; e != null; e = e.sibling) {
              ret = processPotentialClassVarInScope(e.sym, ret);
            }
            current = current.next;
        }
        // System.out.println("Asking for vars in "+env.enclClass.sym);
        ret = _getClassVarsInScope(env, ret);
        return ret.reverse();
    }
   
    List<VarSymbol> processPotentialClassVarInScope(Symbol s, List<VarSymbol> ret) {
        // System.out.println("in scope: "+s+" : "+s.getClass().getSimpleName());
        if (s instanceof VarSymbol) {
            VarSymbol vs = (VarSymbol)s;
            if ((vs.type.tsym == syms.classType.tsym) &&
                ((vs.flags() & FINAL) != 0) &&
                (vs.type.getTypeArguments().nonEmpty()) &&
                (vs.type.getTypeArguments().head instanceof TypeVar)) {
                // vs is of type Class<X> where X is a type variable
                ret = ret.prepend(vs);
            }
        }
        return ret;
    }
   
    List<VarSymbol> _getClassVarsInScope(Env<AttrContext> env, List<VarSymbol> ret) {
        Env<AttrContext> env1 = env;
        boolean staticOnly = false;
        while (env1.outer != null) {
            if (rs.isStatic(env1)) staticOnly = true;            
            Scope.Entry e = env1.info.scope.elems;
            while (e != null) {
                Symbol sym = e.sym;
                if (!(staticOnly &&
                      sym.kind == VAR &&
                      sym.owner.kind == TYP &&
                      (sym.flags() & STATIC) == 0)) {
                    ret = processPotentialClassVarInScope(sym, ret);
                }
                ret = _getClassFieldsInScope(env1, env1.enclClass.sym.type, env1.enclClass.sym, ret);
               
                e = e.sibling;
            }
            ret = _getClassFieldsInScope(env1, env1.enclClass.sym.type, env1.enclClass.sym, ret);
           
            if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true;
            env1 = env1.outer;
        }

        ret = _getClassFieldsInScope(env, syms.predefClass.type, syms.predefClass, ret);
       
        return ret;
    }
   
    List<VarSymbol> _getClassFieldsInScope(Env<AttrContext> env,
                                           Type site,
                                           TypeSymbol c,
                                           List<VarSymbol> ret) {
        while (c.type.tag == TYPEVAR)
            c = c.type.getUpperBound().tsym;
        Scope.Entry e = c.members().elems;
        while (e != null) {
            if (e.sym.kind == VAR && (e.sym.flags_field & SYNTHETIC) == 0) {
                if (rs.isAccessible(env, site, e.sym)) {
                    ret = processPotentialClassVarInScope(e.sym, ret);
                }
            }
            e = e.sibling;
        }
        Type st = types.supertype(c.type);
        if (st != null && (st.tag == CLASS || st.tag == TYPEVAR)) {
            ret = _getClassFieldsInScope(env, site, st.tsym, ret);
        }
        for (Type it: types.interfaces(c.type)) {
            ret = _getClassFieldsInScope(env, site, it.tsym, ret);
        }
        return ret;
    }

A new Mint release can be expected shortly.

Share

About Mathias

Software development engineer. Principal developer of DrJava. Recent Ph.D. graduate from the Department of Computer Science at Rice University.
This entry was posted in Mint. Bookmark the permalink.

Leave a Reply