Resolve generic field type in a typed class


I'm importing my java library into MPS project as "javaclasses", and my language's purpose is to work with those imported classes when user specifies a reference to one. Any classes with type parameters are treated as "abstract" and not included in the reference scope, so user cannot specify them.

But there's a usecase when a specific class extends another class with some type parameters. For example:

class BasicNode<E extends NodeElement> {
    public final NodeParameter<Collection<N>> elements = new NodeParameter<>("Node elements");

class StringNode extends BasicNode<StringElement> {

This is an actual example that caused troubles for me.
Class BasicNode<E> has type parameters so it's not included into reference scope, and my MPS users cannot specify it. But StringNode is in the scope and totally valid reference.

When class reference is specified, I need to get all its public fields of type NodeParameter<T> and to read their types (T). So I was doing something like:

this.ref.getAllExtendedClassifiers().where({~it => 
  it.isInstanceOf(ClassConcept) && isValidNodeReference(it : ClassConcept); 
}).selectMany({~it => 
  it : ClassConcept.fields(); 

}).where({~it => 
  !it.isStatic() && isSubtype(it.type :< <NodeParameter>); 
}).forEach({~it => 

  node<Type> type = it.type : ClassifierType.parameter[0]; 
  parameters.add(concept/NodeParameterDescriptor/.create(, type)); 

But in the case when parameter type has reference to generic class type I get type Collection<@BaseNode.N extends NodeElement>. And I wanted to get Collection<StringElement>.

I know how to coerce classifier type to parent type with resolved parameters, so I was trying to use it somehow:

this.ref.getAllExtendedClassifiers().where({~it => 
  it.isInstanceOf(ClassConcept) && isValidNodeReference(it : ClassConcept); 
}).select({~it => 
  node<ClassifierType> coercedType = coerce(this.ref.getThisType() :< > ^(  it )^<* l # foo> < as ignored); 
  info "Coerced type: " + coercedType; 
  return coercedType; 

This gives me parent classifier type: BaseNode<StringElement>, but I cannot find any way to propagate this type unto the classifier fields. Since I still need the classifier itself, to call #fields(), so I end up having a link to valid coerced classifier type (BaseNode<StringElement>) and a link to it's field type with type reference (Collection<@BaseNode.N extends NodeElement>), and I really don't get how to connect these two together.

Is there a way to call coercion for field type with type variable references, without actually knowing how many parameters it takes?

Greatest thanks in advance!


I'm actively trying to find any solution (like maybe try to resolve reference manually, where I get "type variable declaration" from "type variable reference", and then I find out the index of the declaration in the classifier parameters, and then I get actual type by the found index from the coerced type), but it seems like there might be some more elegant solution, since there is for classifier coercion.
1 comment
Ok. I got this now )


1. I have a variable coercedType of type node<ClassifierType>
(Acquired thru coercion of original class type to it's generic parent type. See original post.)

2. I have a variable fieldType of type node<>
(Acquired from field declaration)

if (fieldType.isInstanceOf(IGenericType)) { 
  map<node<>, node<>> subs = new hashmap<node<>, node<>>; 
  fieldType : IGenericType.collectGenericSubstitutions(subs); 
  fieldType = fieldType : IGenericType.expandGenerics(subs); 

It works as wanted, but I'm yet to find out if coercion of original class type is required prior to this operation.

Solution found in baseLanguage's: typeOf_VarRef

Please sign in to leave a comment.