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

[cc lang=”java”]public separable Code fun(Code c1, Code c2) {
return <| ( `(lfTest.eval(e,f).booleanCodeValue()) ? `c2 : `c1 ) |>;
}[/cc]

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

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

[cc lang=”java”]public separable Code fun(Code c1, Code c2,
final Class xc) {
return <| ( `(lfTest.eval(e,f).booleanCodeValue()) ? `c2 : `c1 ) |>;
}[/cc]

To implement this, we had to find all variables of type [cci lang=”java”]Class[/cci] 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 [cci lang=”java”]Resolver.findVar[/cci] and [cci lang=”java”]Resolver.findField[/cci] methods.

[cc lang=”java”] /** This method returns a list of all VarSymbols of type Class in scope where X is a type variable. */
List getClassVarsInScope() {
// System.out.println(“env = “+env);
List 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 processPotentialClassVarInScope(Symbol s, List 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 where X is a type variable
ret = ret.prepend(vs);
}
}
return ret;
}

List _getClassVarsInScope(Env env, List ret) {
Env 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 _getClassFieldsInScope(Env env,
Type site,
TypeSymbol c,
List 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;
}[/cc]

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