- A Concurrent Affair - https://www.concurrentaffair.org -

Passing a Class<T> for Every Type Variable T

When working on our GPCE Mint tutorial [1], 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 [2] release can be expected shortly.

[3] [4]Share [5]