use of com.redhat.ceylon.model.typechecker.model.TypedReference in project ceylon-compiler by ceylon.
the class ExpressionTransformer method transformAssignment.
private JCExpression transformAssignment(Node op, Tree.Term leftTerm, Tree.Term rightTerm) {
// Remember and disable inStatement for RHS
boolean tmpInStatement = inStatement;
inStatement = false;
// FIXME: can this be anything else than a Tree.MemberOrTypeExpression or Tree.ParameterizedExpression?
final JCExpression rhs;
BoxingStrategy boxing;
if (leftTerm instanceof Tree.MemberOrTypeExpression) {
TypedDeclaration decl = (TypedDeclaration) ((Tree.MemberOrTypeExpression) leftTerm).getDeclaration();
boxing = CodegenUtil.getBoxingStrategy(decl);
if (decl instanceof Value) {
Value val = (Value) decl;
if (val.getSetter() != null && val.getSetter().getUnboxed() != null) {
boxing = CodegenUtil.getBoxingStrategy(val.getSetter());
}
}
Type targetType = tmpInStatement ? leftTerm.getTypeModel() : rightTerm.getTypeModel();
// if we're dealing with widening do not trust the type of the declaration and get the real type
if (CodegenUtil.hasUntrustedType(decl)) {
TypedReference typedRef = (TypedReference) ((Tree.MemberOrTypeExpression) leftTerm).getTarget();
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
targetType = nonWideningType(typedRef, nonWideningTypedRef);
}
rhs = transformExpression(rightTerm, boxing, targetType, decl.hasUncheckedNullType() ? EXPR_TARGET_ACCEPTS_NULL : 0);
} else {
// instanceof Tree.ParameterizedExpression
boxing = CodegenUtil.getBoxingStrategy(leftTerm);
Tree.ParameterizedExpression paramExpr = (Tree.ParameterizedExpression) leftTerm;
FunctionOrValue decl = (FunctionOrValue) ((Tree.MemberOrTypeExpression) paramExpr.getPrimary()).getDeclaration();
CallableBuilder callableBuilder = CallableBuilder.anonymous(gen(), paramExpr, decl, (Tree.Expression) rightTerm, paramExpr.getParameterLists(), paramExpr.getPrimary().getTypeModel(), decl instanceof Function ? !((Function) decl).isDeferred() : false);
rhs = callableBuilder.build();
}
if (tmpInStatement) {
return transformAssignment(op, leftTerm, rhs);
} else {
Type valueType = rightTerm.getTypeModel();
if (isNull(valueType))
valueType = leftTerm.getTypeModel();
return transformAssignAndReturnOperation(op, leftTerm, boxing == BoxingStrategy.BOXED, leftTerm.getTypeModel(), valueType, new AssignAndReturnOperationFactory() {
@Override
public JCExpression getNewValue(JCExpression previousValue) {
return rhs;
}
});
}
}
use of com.redhat.ceylon.model.typechecker.model.TypedReference in project ceylon-compiler by ceylon.
the class AbstractTransformer method getGetterInterfaceType.
Type getGetterInterfaceType(TypedDeclaration attrTypedDecl) {
TypedReference typedRef = getTypedReference(attrTypedDecl);
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
Type nonWideningType = nonWideningType(typedRef, nonWideningTypedRef);
Type type;
boolean unboxed = CodegenUtil.isUnBoxed(attrTypedDecl);
if (unboxed && isCeylonBoolean(nonWideningType)) {
type = javacCeylonTypeToProducedType(syms().ceylonGetterBooleanType);
} else if (unboxed && isCeylonInteger(nonWideningType)) {
type = javacCeylonTypeToProducedType(syms().ceylonGetterLongType);
} else if (unboxed && isCeylonFloat(nonWideningType)) {
type = javacCeylonTypeToProducedType(syms().ceylonGetterDoubleType);
} else if (unboxed && isCeylonCharacter(nonWideningType)) {
type = javacCeylonTypeToProducedType(syms().ceylonGetterIntType);
} else if (unboxed && isCeylonByte(nonWideningType)) {
type = javacCeylonTypeToProducedType(syms().ceylonGetterByteType);
} else {
type = javacCeylonTypeToProducedType(syms().ceylonGetterType);
Type typeArg = nonWideningType;
if (unboxed && isCeylonString(typeArg)) {
typeArg = javacJavaTypeToProducedType(syms().stringType);
}
type = appliedType(type.getDeclaration(), typeArg);
}
return type;
}
use of com.redhat.ceylon.model.typechecker.model.TypedReference in project ceylon-compiler by ceylon.
the class AbstractTransformer method getTypeForParameter.
Type getTypeForParameter(Parameter parameter, Reference producedReference, int flags) {
/* this method is bogus: It's really trying to answer
* "what's the type of the java declaration of the given parameter",
* but using the ceylon type system to do so.
*/
boolean functional = parameter.getModel() instanceof Function;
if (producedReference == null) {
return parameter.getType();
}
final TypedReference producedTypedReference = producedReference.getTypedParameter(parameter);
final Type type = functional ? producedTypedReference.getFullType() : producedTypedReference.getType();
final TypedDeclaration producedParameterDecl = producedTypedReference.getDeclaration();
final Type declType = producedParameterDecl.getType();
// be more resilient to upstream errors
if (declType == null)
return typeFact.getUnknownType();
if (isJavaVariadic(parameter) && (flags & TP_SEQUENCED_TYPE) == 0) {
// type of param must be Iterable<T>
Type elementType = typeFact.getIteratedType(type);
if (elementType == null) {
log.error("ceylon", "Invalid type for Java variadic parameter: " + type.asQualifiedString());
return type;
}
return elementType;
}
if (declType.isClassOrInterface()) {
return type;
} else if ((declType.isTypeParameter()) && (flags & TP_TO_BOUND) != 0) {
if (!declType.getSatisfiedTypes().isEmpty()) {
// use upper bound
Type upperBound = declType.getSatisfiedTypes().get(0);
// make sure we apply the type arguments
upperBound = substituteTypeArgumentsForTypeParameterBound(producedReference, upperBound);
Type self = upperBound.getDeclaration().getSelfType();
if (self != null) {
// make sure we apply the type arguments
Type selfUpperBound = self.substitute(upperBound);
if (!willEraseToObject(selfUpperBound) && (willEraseToObject(type) || expressionGen().needsCast(type, selfUpperBound, false, false, false))) {
return selfUpperBound;
}
}
if (!willEraseToObject(upperBound) && (willEraseToObject(type) || expressionGen().needsCast(type, upperBound, false, false, false))) {
return upperBound;
}
}
}
return type;
}
use of com.redhat.ceylon.model.typechecker.model.TypedReference in project ceylon-compiler by ceylon.
the class NamedArgumentInvocation method bindAttributeArgument.
private void bindAttributeArgument(Tree.AttributeArgument attrArg, Parameter declaredParam, Naming.SyntheticName argName) {
ListBuffer<JCStatement> statements;
final Value model = attrArg.getDeclarationModel();
final String name = model.getName();
String className = Naming.getAttrClassName(model, 0);
final List<JCTree> attrClass = gen.gen().transformAttribute(model, name, className, null, attrArg.getBlock(), attrArg.getSpecifierExpression(), null, null);
TypedReference typedRef = gen.getTypedReference(model);
TypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef);
Type nonWideningType = gen.nonWideningType(typedRef, nonWideningTypedRef);
Type type = parameterType(declaredParam, model.getType(), 0);
final BoxingStrategy boxType = getNamedParameterBoxingStrategy(declaredParam);
JCExpression initValue = gen.make().Apply(null, gen.makeSelect(gen.makeUnquotedIdent(className), Naming.getGetterName(model)), List.<JCExpression>nil());
initValue = gen.expressionGen().applyErasureAndBoxing(initValue, nonWideningType, !CodegenUtil.isUnBoxed(nonWideningTypedRef.getDeclaration()), boxType, type);
JCTree.JCVariableDecl var = gen.make().VarDef(gen.make().Modifiers(FINAL, List.<JCAnnotation>nil()), argName.asName(), gen.makeJavaType(type, boxType == BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0), initValue);
statements = toStmts(attrArg, attrClass).append(var);
bind(declaredParam, argName, gen.makeJavaType(type, boxType == BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0), statements.toList());
}
use of com.redhat.ceylon.model.typechecker.model.TypedReference 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);
}
Aggregations