use of com.redhat.ceylon.model.typechecker.model.TypedDeclaration in project ceylon-compiler by ceylon.
the class AbstractTransformer method makeTypedDeclarationTypeDescriptorResolved.
private JCExpression makeTypedDeclarationTypeDescriptorResolved(TypedDeclaration declaration) {
// figure out the method name
String methodName = declaration.getPrefixedName();
List<JCExpression> arguments;
if (declaration instanceof Function)
arguments = makeReifiedTypeArgumentsResolved(getTypeArguments((Function) declaration), true);
else
arguments = List.nil();
if (declaration.isToplevel()) {
JCExpression getterClassNameExpr;
if (declaration instanceof Function) {
getterClassNameExpr = naming.makeName(declaration, Naming.NA_FQ | Naming.NA_WRAPPER);
} else {
String getterClassName = Naming.getAttrClassName(declaration, 0);
getterClassNameExpr = naming.makeUnquotedIdent(getterClassName);
}
arguments = arguments.prepend(makeSelect(getterClassNameExpr, "class"));
} else
arguments = arguments.prepend(make().Literal(methodName));
JCMethodInvocation typedDeclarationDescriptor = make().Apply(null, makeSelect(makeTypeDescriptorType(), "functionOrValue"), arguments);
// see if the declaration has a container too
Declaration enclosingDeclaration = getDeclarationContainer(declaration);
JCExpression containerType = null;
if (enclosingDeclaration instanceof TypedDeclaration)
containerType = makeTypedDeclarationTypeDescriptorResolved((TypedDeclaration) enclosingDeclaration);
else if (enclosingDeclaration instanceof TypeDeclaration) {
Type qualifyingType = ((TypeDeclaration) enclosingDeclaration).getType();
containerType = makeReifiedTypeArgumentResolved(qualifyingType, true);
}
if (containerType == null) {
return typedDeclarationDescriptor;
} else {
return make().Apply(null, makeSelect(makeTypeDescriptorType(), "member"), List.of(containerType, typedDeclarationDescriptor));
}
}
use of com.redhat.ceylon.model.typechecker.model.TypedDeclaration in project ceylon-compiler by ceylon.
the class AbstractTransformer method getRefinedDeclaration.
/*
* We have several special cases here to find the best non-widening refinement in case of multiple inheritace:
*
* - The first special case is for some decls like None.first, which inherits from ContainerWithFirstElement
* twice: once with Nothing (erased to j.l.Object) and once with Element (a type param). Now, in order to not widen the
* return type it can't be Nothing (j.l.Object), it must be Element (a type param that is not instantiated), because in Java
* a type param refines j.l.Object but not the other way around.
* - The second special case is when implementing an interface first with a non-erased type, then with an erased type. In this
* case we want the refined decl to be the one with the non-erased type.
* - The third special case is when we implement a declaration via multiple super types, without having any refining
* declarations in those supertypes, simply by instantiating a common super type with different type parameters
*/
private TypedReference getRefinedDeclaration(TypedReference typedReference, Type currentType) {
TypedDeclaration decl = typedReference.getDeclaration();
TypedDeclaration modelRefinedDecl = (TypedDeclaration) decl.getRefinedDeclaration();
Type referenceQualifyingType = typedReference.getQualifyingType();
boolean forMixinMethod = currentType != null && decl.getContainer() instanceof ClassOrInterface && referenceQualifyingType != null && !Decl.equal(referenceQualifyingType.getDeclaration(), currentType.getDeclaration());
// quick exit
if (Decl.equal(decl, modelRefinedDecl) && !forMixinMethod)
return null;
// modelRefinedDecl exists, but perhaps it's the toplevel refinement and not the one Java will look at
if (!forMixinMethod)
modelRefinedDecl = getFirstRefinedDeclaration(decl);
TypeDeclaration qualifyingDeclaration = currentType.getDeclaration();
if (qualifyingDeclaration instanceof ClassOrInterface) {
// if both are returning unboxed void we're good
if (Decl.isUnboxedVoid(decl) && Decl.isUnboxedVoid(modelRefinedDecl))
return null;
// only try to find better if we're erasing to Object and we're not returning a type param
if (willEraseToObject(typedReference.getType()) || isWideningTypeArguments(decl.getType(), modelRefinedDecl.getType(), true) && !isTypeParameter(typedReference.getType())) {
ClassOrInterface declaringType = (ClassOrInterface) qualifyingDeclaration;
Set<TypedDeclaration> refinedMembers = getRefinedMembers(declaringType, decl.getName(), com.redhat.ceylon.model.typechecker.model.ModelUtil.getSignature(decl), false);
// 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
TypedReference 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
TypedReference 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
Type extendedType = declaringType.getExtendedType();
if (extendedType != null) {
TypedReference refinedTypedReference = getRefinedTypedReference(extendedType, modelRefinedDecl);
Type refinedType = refinedTypedReference.getType();
if (!isTypeParameter(refinedType) && !willEraseToObject(refinedType))
return refinedTypedReference;
}
// then satisfied interfaces
for (Type satisfiedType : declaringType.getSatisfiedTypes()) {
TypedReference refinedTypedReference = getRefinedTypedReference(satisfiedType, modelRefinedDecl);
Type 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
*/
Type firstInstantiation = isInheritedWithDifferentTypeArguments(modelRefinedDecl.getContainer(), currentType);
if (firstInstantiation != null) {
TypedReference firstInstantiationTypedReference = getRefinedTypedReference(firstInstantiation, modelRefinedDecl);
Type firstInstantiationType = firstInstantiationTypedReference.getType();
if (isWidening(decl.getType(), firstInstantiationType) || isWideningTypeArguments(decl.getType(), firstInstantiationType, true))
return firstInstantiationTypedReference;
}
}
return getRefinedTypedReference(typedReference, modelRefinedDecl);
}
use of com.redhat.ceylon.model.typechecker.model.TypedDeclaration in project ceylon-compiler by ceylon.
the class AbstractTransformer method getFirstRefinedDeclaration.
private TypedDeclaration getFirstRefinedDeclaration(TypedDeclaration decl) {
if (decl.getContainer() instanceof ClassOrInterface == false)
return decl;
java.util.List<Type> signature = com.redhat.ceylon.model.typechecker.model.ModelUtil.getSignature(decl);
boolean overloaded = decl.isOverloaded() || decl.isAbstraction();
Declaration refinedDeclaration = decl.getRefinedDeclaration();
if (refinedDeclaration != null) {
overloaded |= refinedDeclaration.isOverloaded() || refinedDeclaration.isAbstraction();
}
ClassOrInterface container = (ClassOrInterface) decl.getContainer();
HashSet<TypeDeclaration> visited = new HashSet<TypeDeclaration>();
// start looking for it, but skip this type, only lookup upwards of it
TypedDeclaration firstRefinedDeclaration = getFirstRefinedDeclaration(container, decl, signature, visited, true, overloaded);
// only keep the first refined decl if its type can be trusted: if it is not itself widening
if (firstRefinedDeclaration != null) {
if (CodegenUtil.hasUntrustedType(firstRefinedDeclaration))
firstRefinedDeclaration = getFirstRefinedDeclaration(firstRefinedDeclaration);
}
return firstRefinedDeclaration != null ? firstRefinedDeclaration : decl;
}
use of com.redhat.ceylon.model.typechecker.model.TypedDeclaration in project ceylon-compiler by ceylon.
the class BoxingVisitor method visit.
@Override
public void visit(IndexExpression that) {
super.visit(that);
// we need to propagate from the underlying method call (item/span)
if (that.getPrimary() == null || that.getPrimary().getTypeModel() == null)
return;
Type lhsModel = that.getPrimary().getTypeModel();
if (lhsModel.getDeclaration() == null)
return;
String methodName = that.getElementOrRange() instanceof Tree.Element ? "get" : "span";
// find the method from its declaration
TypedDeclaration member = (TypedDeclaration) lhsModel.getDeclaration().getMember(methodName, null, false);
if (member == null)
return;
propagateFromDeclaration(that, member);
}
use of com.redhat.ceylon.model.typechecker.model.TypedDeclaration in project ceylon-compiler by ceylon.
the class BoxingDeclarationVisitor method boxAttribute.
private void boxAttribute(TypedDeclaration declaration, Node that) {
// deal with invalid input
if (declaration == null)
return;
TypedDeclaration refinedDeclaration = null;
refinedDeclaration = (TypedDeclaration) CodegenUtil.getTopmostRefinedDeclaration(declaration, optimisedMethodSpecifiersToMethods);
// deal with invalid input
if (refinedDeclaration == null)
return;
setBoxingState(declaration, refinedDeclaration, that);
}
Aggregations