use of org.eclipse.ceylon.model.typechecker.model.TypedReference in project ceylon by eclipse.
the class ClassTransformer method generateInstantiatorDelegate.
private void generateInstantiatorDelegate(ClassDefinitionBuilder classBuilder, Type satisfiedType, Interface iface, Class klass, Constructor ctor, Type currentType, boolean includeBody) {
Type typeMember = satisfiedType.getTypeMember(klass, klass.getType().getTypeArgumentList());
if (ctor != null) {
typeMember = ctor.appliedType(typeMember, Collections.<Type>emptyList());
}
java.util.List<TypeParameter> typeParameters = klass.getTypeParameters();
java.util.List<Parameter> parameters = (ctor != null ? ctor.getParameterLists() : klass.getParameterLists()).get(0).getParameters();
long flags = modifierTransformation().instantiatorBridgeFlags(includeBody);
String instantiatorMethodName = naming.getInstantiatorMethodName(klass);
for (Parameter param : parameters) {
if (Strategy.hasDefaultParameterValueMethod(param) && !klass.isActual()) {
final TypedReference typedParameter = typeMember.getTypedParameter(param);
// If that method has a defaulted parameter,
// we need to generate a default value method
// which also delegates to the $impl
final MethodDefinitionBuilder defaultValueDelegate = makeDelegateToCompanion(iface, typeMember, currentType, flags, typeParameters, producedTypeParameterBounds(typeMember, klass), typedParameter.getFullType(), Naming.getDefaultedParamMethodName(ctor != null ? ctor : klass, param), parameters.subList(0, parameters.indexOf(param)), param.getModel().getTypeErased(), null, param, includeBody);
classBuilder.method(defaultValueDelegate);
}
if (Strategy.hasDefaultParameterOverload(param)) {
final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface, typeMember, currentType, flags, typeParameters, producedTypeParameterBounds(typeMember, klass), typeMember.getType(), instantiatorMethodName, parameters.subList(0, parameters.indexOf(param)), false, null, null, includeBody);
classBuilder.method(overload);
}
}
final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface, typeMember, currentType, flags, typeParameters, producedTypeParameterBounds(typeMember, klass), typeMember.getType(), instantiatorMethodName, parameters, false, null, null, includeBody);
classBuilder.method(overload);
}
use of org.eclipse.ceylon.model.typechecker.model.TypedReference in project ceylon by eclipse.
the class CallableBuilder method makeFunctionalInterfaceMethod.
public MethodDefinitionBuilder makeFunctionalInterfaceMethod() {
FunctionOrValue methodOrValue = (FunctionOrValue) functionalInterfaceMethod.getDeclaration();
MethodDefinitionBuilder callMethod = MethodDefinitionBuilder.method(gen, methodOrValue, 0);
callMethod.isOverride(true);
callMethod.modifiers(Flags.PUBLIC);
Type returnType = functionalInterfaceMethod.getType();
if (methodOrValue instanceof Value || !((Function) methodOrValue).isDeclaredVoid()) {
int flags = CodegenUtil.isUnBoxed(methodOrValue) ? 0 : JT_NO_PRIMITIVES;
callMethod.resultType(gen.makeJavaType(returnType, flags), null);
}
ListBuffer<JCExpression> args = new ListBuffer<>();
// this depends on how we're declared to Java
boolean turnedToRaw = gen.rawSupertype(functionalInterface, JT_EXTENDS | JT_CLASS_NEW);
boolean variadic = false;
if (methodOrValue instanceof Function) {
int index = 0;
java.util.List<Parameter> parameters = ((Function) methodOrValue).getFirstParameterList().getParameters();
for (int i = 0; i < parameters.size(); i++) {
Parameter param = parameters.get(i);
Parameter targetParam = paramLists.getParameters().get(i);
TypedReference typedParameter = functionalInterfaceMethod.getTypedParameter(param);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(gen, param.getName());
Type paramType = typedParameter.getType();
if (param.isSequenced()) {
paramType = gen.typeFact().getSequentialElementType(paramType);
variadic = true;
}
// This is very special-casey, but is required and I haven't found more subtle
if (turnedToRaw && gen.simplifyType(param.getType()).isTypeParameter()) {
paramType = gen.typeFact().getObjectType();
}
long flags = Flags.FINAL;
// that looks fishy, perhaps we need to call non-widening rules instead
JCExpression javaType = gen.makeJavaType(paramType, paramType.isRaw() ? AbstractTransformer.JT_RAW : 0);
if (param.isSequenced()) {
flags |= Flags.VARARGS;
javaType = gen.make().TypeArray(javaType);
}
pdb.type(new TransformedType(javaType));
pdb.modifiers(flags);
callMethod.parameter(pdb);
JCExpression arg;
if (param.isSequenced()) {
arg = gen.javaVariadicToSequential(paramType, param);
} else {
arg = gen.makeUnquotedIdent(param.getName());
Type argumentType = parameterTypes.get(index);
if (gen.isOptional(paramType) && argumentType.isSubtypeOf(gen.typeFact().getObjectType()) && !targetParam.getModel().hasUncheckedNullType()) {
arg = gen.utilInvocation().checkNull(arg);
}
Type simpleParamType = gen.simplifyType(paramType);
// make unboxed java strings pass for ceylon strings so we can box them
boolean unboxedString = gen.isJavaStringExactly(simpleParamType);
if (unboxedString)
simpleParamType = gen.typeFact().getStringType();
BoxingStrategy boxingStrategy = BoxingStrategy.BOXED;
// in rare cases we don't want boxes
if (unboxedString && gen.isJavaStringExactly(argumentType))
boxingStrategy = BoxingStrategy.UNBOXED;
arg = gen.expressionGen().applyErasureAndBoxing(arg, simpleParamType, !(CodegenUtil.isUnBoxed(param.getModel()) || unboxedString), boxingStrategy, argumentType);
}
args.append(arg);
index++;
}
} else {
// no-arg getter
}
String callMethodName;
if (variadic)
callMethodName = Naming.getCallableVariadicMethodName();
else
callMethodName = Naming.getCallableMethodName();
JCExpression call = gen.make().Apply(null, gen.makeUnquotedIdent(callMethodName), args.toList());
JCStatement body;
if (methodOrValue instanceof Function && ((Function) methodOrValue).isDeclaredVoid())
body = gen.make().Exec(call);
else {
Type callableReturnType = gen.getReturnTypeOfCallable(typeModel);
Type simpleReturnType = gen.simplifyType(returnType);
boolean unboxed = CodegenUtil.isUnBoxed(methodOrValue) || gen.isJavaStringExactly(simpleReturnType);
if (unboxed) {
call = gen.expressionGen().applyErasureAndBoxing(call, callableReturnType, true, BoxingStrategy.UNBOXED, returnType);
} else if (callableReturnType.isNull()) {
// if the callable returns null and the SAM does not, we need a cast
call = gen.expressionGen().applyErasureAndBoxing(call, callableReturnType, true, BoxingStrategy.INDIFFERENT, returnType);
}
body = gen.make().Return(call);
}
callMethod.body(body);
return callMethod;
}
use of org.eclipse.ceylon.model.typechecker.model.TypedReference in project ceylon by eclipse.
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 or Tree.IndexExpression?
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);
}
int flags = decl.hasUncheckedNullType() ? EXPR_TARGET_ACCEPTS_NULL : 0;
flags |= leftTerm.getSmall() && !rightTerm.getSmall() ? EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK : 0;
if (decl.isMember() && useFieldInAssignment(op, null, decl) && !ModelUtil.isLocalToInitializer(decl) && useJavaBox(decl, ((TypedDeclaration) decl.getRefinedDeclaration()).getType())) {
boxing = BoxingStrategy.JAVA;
flags |= EXPR_HAS_NULL_CHECK_FENCE;
}
rhs = transformExpression(rightTerm, boxing, targetType, flags);
} else if (leftTerm instanceof Tree.IndexExpression) {
Tree.IndexExpression idx = (Tree.IndexExpression) leftTerm;
Unit unit = op.getUnit();
Type pt = idx.getPrimary().getTypeModel();
boxing = (unit.isJavaPrimitiveArrayType(pt)) ? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED;
rhs = transformExpression(rightTerm, boxing, idx.getTypeModel(), 0);
} else if (leftTerm 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();
} else {
return makeErroneous(leftTerm, "compiler bug: left term of type '" + leftTerm.getClass().getSimpleName() + "' is not yet supported");
}
if (tmpInStatement) {
return makeAssignment(op, leftTerm, transformAssignmentLhs(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 org.eclipse.ceylon.model.typechecker.model.TypedReference in project ceylon by eclipse.
the class NamedArgumentInvocation method transformPrimary.
protected TransformedInvocationPrimary transformPrimary(JCExpression primaryExpr, String selector) {
JCExpression transformedPrimary = instanceFieldName != null ? instanceFieldName.makeIdent() : primaryExpr;
String transformedSelector = selector;
if (getPrimaryDeclaration() instanceof Value) {
if (transformedPrimary == null)
transformedPrimary = gen.makeUnquotedIdent(selector);
else
transformedPrimary = gen.makeQualIdent(primaryExpr, selector);
Type primaryType = ((Value) getPrimaryDeclaration()).getType();
TypedReference samRef = gen.checkForFunctionalInterface(primaryType);
if (samRef != null)
transformedSelector = samRef.getDeclaration().getName();
else
transformedSelector = Naming.getCallableMethodName();
} else if (getPrimaryDeclaration() instanceof Function && getPrimaryDeclaration().isParameter() && // This is fishy at best
transformedPrimary == null) {
// FIXME: there's a good chance this should be merged with the previous block
transformedPrimary = gen.makeUnquotedIdent(selector);
transformedSelector = Naming.getCallableMethodName();
} else if (isCallableToFunctionalInterfaceBridge) {
transformedSelector = Naming.getCallableMethodName();
}
return new TransformedInvocationPrimary(transformedPrimary, transformedSelector);
}
use of org.eclipse.ceylon.model.typechecker.model.TypedReference in project ceylon by eclipse.
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
*/
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(), org.eclipse.ceylon.model.typechecker.model.ModelUtil.getSignature(decl), org.eclipse.ceylon.model.typechecker.model.ModelUtil.isVariadic(decl));
// 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