use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class AbstractTransformer method makeTypeTest.
private <R> R makeTypeTest(TypeTestTransformation<R> typeTester, JCExpression firstTimeExpr, Naming.CName varName, Type testedType, Type expressionType) {
R result = null;
// make sure aliases are resolved
testedType = testedType.resolveAliases();
// optimisation when all we're doing is making sure it is not null
if (expressionType != null && testedType.getSupertype(typeFact().getObjectDeclaration()) != null && expressionType.isExactly(typeFact().getOptionalType(testedType))) {
JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
return typeTester.nullTest(varExpr, JCTree.Tag.NE);
}
TypeDeclaration declaration = testedType.getDeclaration();
if (declaration instanceof ClassOrInterface) {
JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
if (isAnything(testedType)) {
// everything is Void, it's the root of the hierarchy
return typeTester.eval(varExpr, true);
} else if (isNull(testedType)) {
// is Null => is null
return typeTester.nullTest(varExpr, JCTree.Tag.EQ);
} else if (testedType.isExactly(typeFact().getObjectType())) {
// is Object => is not null
return typeTester.nullTest(varExpr, JCTree.Tag.NE);
} else if (testedType.isExactly(typeFact().getIdentifiableType())) {
// it's erased
return typeTester.isIdentifiable(varExpr);
} else if (testedType.getDeclaration().equals(typeFact().getTrueValueDeclaration().getTypeDeclaration())) {
return typeTester.isTrue(varExpr);
} else if (testedType.getDeclaration().equals(typeFact().getFalseValueDeclaration().getTypeDeclaration())) {
return typeTester.isFalse(varExpr);
} else if (testedType.isExactly(typeFact().getBasicType())) {
// it's erased
return typeTester.isBasic(varExpr);
} else if (testedType.getDeclaration().getQualifiedNameString().equals("java.lang::Error")) {
// need to exclude AssertionError
return typeTester.andOr(typeTester.isInstanceof(varExpr, testedType, expressionType), typeTester.not(typeTester.isInstanceof(varName.makeIdent(), typeFact().getAssertionErrorDeclaration().getType(), expressionType)), JCTree.Tag.AND);
} else if (!hasTypeArguments(testedType)) {
// non-generic Class or interface, use instanceof
return typeTester.isInstanceof(varExpr, testedType, expressionType);
} else {
// generic class or interface...
if (declaration.getSelfType() != null && // of TypeArg
declaration.getSelfType().getDeclaration() instanceof TypeParameter && // given TypeArg satisfies SelfType<TypeArg>
declaration.getSelfType().isSubtypeOf(declaration.getType())) {
Type selfTypeArg = testedType.getTypeArguments().get(declaration.getSelfType().getDeclaration());
if (selfTypeArg.getDeclaration() instanceof ClassOrInterface) {
// first check if the type is inhabited or not
if (selfTypeArg.getDeclaration().inherits(declaration)) {
// "is SelfType<ClassOrInterface>" can be written "is ClassOrInterface"
return makeTypeTest(typeTester, firstTimeExpr, varName, selfTypeArg, expressionType);
} else {
// always false, for example Comparable<Anything> is uninhabited because Anything does not inherit from Comparable
return typeTester.eval(varExpr, false);
}
}
// if not, keep trying
}
if (canOptimiseReifiedTypeTest(testedType)) {
// Use an instanceof
return typeTester.isInstanceof(varExpr, testedType, expressionType);
} else {
// Have to use a reified test
if (!Decl.equal(declaration, expressionType.getDeclaration()) && canUseFastFailTypeTest(testedType)) {
// instanceof shortcircuit doesn't achieve anything
return typeTester.andOr(typeTester.isInstanceof(varExpr, testedType, expressionType), typeTester.isReified(varName.makeIdent(), testedType), JCTree.Tag.AND);
} else {
return typeTester.isReified(varExpr, testedType);
}
}
}
} else if (typeFact().isUnion(testedType)) {
for (Type pt : testedType.getCaseTypes()) {
R partExpr = makeTypeTest(typeTester, firstTimeExpr, varName, pt, expressionType);
firstTimeExpr = null;
if (result == null) {
result = partExpr;
} else {
result = typeTester.andOr(result, partExpr, JCTree.Tag.OR);
}
}
return result;
} else if (typeFact().isIntersection(testedType)) {
for (Type pt : testedType.getSatisfiedTypes()) {
R partExpr = makeTypeTest(typeTester, firstTimeExpr, varName, pt, expressionType);
firstTimeExpr = null;
if (result == null) {
result = partExpr;
} else {
result = typeTester.andOr(result, partExpr, JCTree.Tag.AND);
}
}
return result;
} else if (testedType.isNothing()) {
// nothing is Bottom
JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
return typeTester.eval(varExpr, false);
} else if (declaration instanceof TypeParameter) {
JCExpression varExpr = firstTimeExpr != null ? firstTimeExpr : varName.makeIdent();
if (!reifiableUpperBounds((TypeParameter) declaration, expressionType).isEmpty()) {
// If we're testing against a type parameter with
// class or interface upper bounds we can again shortcircuit the
// Util.isReified() using instanceof against the bounds
result = typeTester.isReified(varName.makeIdent(), testedType);
Iterator<Type> iterator = reifiableUpperBounds((TypeParameter) declaration, expressionType).iterator();
while (iterator.hasNext()) {
Type type = iterator.next();
ClassOrInterface c = ((ClassOrInterface) type.resolveAliases().getDeclaration());
result = typeTester.andOr(typeTester.isInstanceof(iterator.hasNext() ? varName.makeIdent() : varExpr, c.getType(), expressionType), result, JCTree.Tag.AND);
}
return result;
} else {
return typeTester.isReified(varExpr, testedType);
}
} else {
throw BugException.unhandledDeclarationCase(declaration);
}
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class AbstractTransformer method substituteTypeArgumentsForTypeParameterBound.
protected Type substituteTypeArgumentsForTypeParameterBound(Reference target, Type bound) {
Declaration declaration = target.getDeclaration();
if (declaration.getContainer() instanceof ClassOrInterface) {
Type targetType = target.getQualifyingType();
// static methods have a container but do not capture type parameters
if (targetType != null && !declaration.isStatic()) {
ClassOrInterface methodContainer = (ClassOrInterface) declaration.getContainer();
Type supertype = targetType.getSupertype(methodContainer);
// we need type arguments that may come from the method container
bound = bound.substitute(supertype);
}
}
// and those that may come from the method call itself
return bound.substitute(target.getTypeArguments(), null);
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class AbstractTransformer method getSimpleNumParametersOfCallable.
private int getSimpleNumParametersOfCallable(Type args) {
// can be a defaulted tuple of Empty|Tuple
if (args.isUnion()) {
java.util.List<Type> caseTypes = args.getCaseTypes();
if (caseTypes == null || caseTypes.size() != 2)
return -1;
Type caseA = caseTypes.get(0);
TypeDeclaration caseADecl = caseA.getDeclaration();
Type caseB = caseTypes.get(1);
TypeDeclaration caseBDecl = caseB.getDeclaration();
if (caseADecl instanceof ClassOrInterface == false || caseBDecl instanceof ClassOrInterface == false)
return -1;
if (caseADecl.isEmpty() && caseBDecl.isTuple())
return getSimpleNumParametersOfCallable(caseB);
if (caseBDecl.isEmpty() && caseADecl.isTuple())
return getSimpleNumParametersOfCallable(caseA);
return -1;
}
// can be Tuple, Empty, Sequence or Sequential
if (!args.isClassOrInterface())
return -1;
TypeDeclaration declaration = args.getDeclaration();
if (declaration.isTuple()) {
Type rest = args.getTypeArgumentList().get(2);
int ret = getSimpleNumParametersOfCallable(rest);
if (ret == -1)
return -1;
return ret + 1;
}
if (declaration.isEmpty()) {
return 0;
}
if (declaration.isSequential() || declaration.isSequence()) {
return 1;
}
return -1;
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface in project ceylon by eclipse.
the class AbstractTransformer method makeTupleTypeDescriptor.
private JCExpression makeTupleTypeDescriptor(Type pt, boolean firstElementOptional) {
java.util.List<Type> tupleElementTypes = typeFact().getTupleElementTypes(pt);
boolean isVariadic = typeFact().isTupleLengthUnbounded(pt);
boolean atLeastOne = false;
boolean needsRestSplit = false;
Type restType = null;
if (isVariadic) {
// unwrap the last element
restType = tupleElementTypes.get(tupleElementTypes.size() - 1);
// to optimise
if (restType.isUnknown())
return null;
tupleElementTypes.set(tupleElementTypes.size() - 1, typeFact.getSequentialElementType(restType));
atLeastOne = restType.getDeclaration().inherits(typeFact().getSequenceDeclaration());
// the last rest element may be a type param, in which case we resolve it at runtime
needsRestSplit = restType.getDeclaration() instanceof ClassOrInterface == false || (!restType.getDeclaration().equals(typeFact.getSequenceDeclaration()) && !restType.getDeclaration().equals(typeFact.getSequentialDeclaration()));
}
int firstDefaulted;
if (!firstElementOptional) {
// only do this crazy computation if the first element is not optional (case of []|[A] which is a union type really)
int minimumLength = typeFact().getTupleMinimumLength(pt);
// [B+] -> 1
if (atLeastOne)
minimumLength--;
// [A,B=] -> 1
// [A=,B*] -> 0
// [A,B+] -> 1
// [B*] -> 0
// [B+] -> 0
// [A,B=] -> 2
// [A=,B*] -> 1
// [A,B+] -> 1
// [B*] -> 0
// [B+] -> 0
int nonVariadicParams = tupleElementTypes.size();
if (isVariadic)
nonVariadicParams--;
// [A,B=] -> 2!=1 -> 1
// [A=,B*] -> 1!=0 -> 0
// [A,B+] -> 1==1 -> -1
// [B*] -> 0==0 -> -1
// [B+] -> 0==0 -> -1
firstDefaulted = nonVariadicParams != minimumLength ? minimumLength : -1;
} else {
firstDefaulted = 0;
}
JCExpression restTypeDescriptor = null;
JCExpression restElementTypeDescriptor = null;
if (needsRestSplit) {
Type restElementType = tupleElementTypes.get(tupleElementTypes.size() - 1);
tupleElementTypes.remove(tupleElementTypes.size() - 1);
restTypeDescriptor = makeReifiedTypeArgumentResolved(restType, false);
restElementTypeDescriptor = makeReifiedTypeArgumentResolved(restElementType, false);
}
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
if (needsRestSplit) {
args.append(restTypeDescriptor);
args.append(restElementTypeDescriptor);
} else {
args.append(makeBoolean(isVariadic));
args.append(makeBoolean(atLeastOne));
}
args.append(makeInteger(firstDefaulted));
for (Type element : tupleElementTypes) {
args.append(makeReifiedTypeArgumentResolved(element, false));
}
JCExpression tupleDescriptor = make().Apply(null, makeSelect(makeTypeDescriptorType(), needsRestSplit ? "tupleWithRest" : "tuple"), args.toList());
return tupleDescriptor;
}
use of org.eclipse.ceylon.model.typechecker.model.ClassOrInterface 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