// now we must select a different refined declaration if we refine it more than once
if(refinedMembers.size() > (forMixinMethod ? 0 : 1)){
// first case
for(TypedDeclaration refinedDecl : refinedMembers){
// get the type reference to see if any eventual type param is instantiated in our inheritance of this type/method
ProducedTypedReference refinedTypedReference = getRefinedTypedReference(typedReference, refinedDecl);
// if it is not instantiated, that's the one we're looking for
if(isTypeParameter(refinedTypedReference.getType()))
return refinedTypedReference;
}
// second case
for(TypedDeclaration refinedDecl : refinedMembers){
// get the type reference to see if any eventual type param is instantiated in our inheritance of this type/method
ProducedTypedReference refinedTypedReference = getRefinedTypedReference(typedReference, refinedDecl);
// if we're not erasing this one to Object let's select it
if(!willEraseToObject(refinedTypedReference.getType()) && !isWideningTypeArguments(refinedDecl.getType(), modelRefinedDecl.getType(), true))
return refinedTypedReference;
}
// third case
if(isTypeParameter(modelRefinedDecl.getType())){
// it can happen that we have inherited a method twice from a single refined declaration
// via different supertype instantiations, without having ever refined them in supertypes
// so we try each super type to see if we already have a matching typed reference
// first super type
ProducedType extendedType = declaringType.getExtendedType();
if(extendedType != null){
ProducedTypedReference refinedTypedReference = getRefinedTypedReference(extendedType, modelRefinedDecl);
ProducedType refinedType = refinedTypedReference.getType();
if(!isTypeParameter(refinedType)
&& !willEraseToObject(refinedType))
return refinedTypedReference;
}
// then satisfied interfaces
for(ProducedType satisfiedType : declaringType.getSatisfiedTypes()){
ProducedTypedReference refinedTypedReference = getRefinedTypedReference(satisfiedType, modelRefinedDecl);
ProducedType refinedType = refinedTypedReference.getType();
if(!isTypeParameter(refinedType)
&& !willEraseToObject(refinedType))
return refinedTypedReference;
}
}
}
}
/*
* Now there's another crazy case:
*
* interface Top<out Element> {
* Top<Element> ret => nothing;
* }
* interface Left satisfies Top<Integer> {}
* interface Right satisfies Top<String> {}
* class Bottom() satisfies Left&Right {}
*
* Where Bottom.ret does not exist and is typed as returning Integer&String which is Nothing, erased to Object,
* and we look at what it refines and find only a single definition Top.ret typed as returning Integer&String (Nothing),
* so we think there's no widening, but Java will only see Top<Integer>.ret from Left, and that's the one we want
* to use for determining widening.
* See https://github.com/ceylon/ceylon-compiler/issues/1765
*/
ProducedType firstInstantiation = isInheritedWithDifferentTypeArguments(modelRefinedDecl.getContainer(), currentType);
if(firstInstantiation != null){
ProducedTypedReference firstInstantiationTypedReference = getRefinedTypedReference(firstInstantiation, modelRefinedDecl);
ProducedType firstInstantiationType = firstInstantiationTypedReference.getType();
if(isWidening(decl.getType(), firstInstantiationType)
|| isWideningTypeArguments(decl.getType(), firstInstantiationType, true))
return firstInstantiationTypedReference;
}