use of com.redhat.ceylon.model.typechecker.model.ClassOrInterface in project ceylon-compiler by ceylon.
the class AbstractTransformer method collectQualifyingTypeArguments.
/**
* Collects all the type parameters and arguments required for an interface that's been pulled up to the
* toplevel, including its containing type and method type parameters.
*/
private void collectQualifyingTypeArguments(java.util.List<TypeParameter> qualifyingTypeParameters, Map<TypeParameter, Type> qualifyingTypeArguments, java.util.List<Reference> qualifyingTypes) {
// make sure we only add type parameters with the same name once, as duplicates are erased from the target interface
// since they cannot be accessed
Set<String> names = new HashSet<String>();
// walk the qualifying types backwards to make sure we only add a TP with the same name once and the outer one wins
for (int i = qualifyingTypes.size() - 1; i >= 0; i--) {
Reference qualifiedType = qualifyingTypes.get(i);
Map<TypeParameter, Type> tas = qualifiedType.getTypeArguments();
java.util.List<TypeParameter> tps = ((Generic) qualifiedType.getDeclaration()).getTypeParameters();
// add any type params for this type
if (tps != null) {
int index = 0;
for (TypeParameter tp : tps) {
// add it only once
if (names.add(tp.getName())) {
// start putting all these type parameters at 0 and then in order
// so that outer type params end up before inner type params but
// order is preserved within each type
qualifyingTypeParameters.add(index++, tp);
qualifyingTypeArguments.put(tp, tas.get(tp));
}
}
}
// add any container method TP
Declaration declaration = qualifiedType.getDeclaration();
if (Decl.isLocal(declaration)) {
Scope scope = declaration.getContainer();
// collect every container method until the next type or package
java.util.List<Function> methods = new LinkedList<Function>();
while (scope != null && scope instanceof ClassOrInterface == false && scope instanceof Package == false) {
if (scope instanceof Function) {
methods.add((Function) scope);
}
scope = scope.getContainer();
}
// methods are sorted inner to outer, which is the order we're following here for types
for (Function method : methods) {
java.util.List<TypeParameter> methodTypeParameters = method.getTypeParameters();
if (methodTypeParameters != null) {
int index = 0;
for (TypeParameter tp : methodTypeParameters) {
// add it only once
if (names.add(tp.getName())) {
// start putting all these type parameters at 0 and then in order
// so that outer type params end up before inner type params but
// order is preserved within each type
qualifyingTypeParameters.add(index++, tp);
qualifyingTypeArguments.put(tp, tp.getType());
}
}
}
}
}
}
}
use of com.redhat.ceylon.model.typechecker.model.ClassOrInterface in project ceylon-compiler by ceylon.
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.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.EQ);
} else if (testedType.isExactly(typeFact().getObjectType())) {
// is Object => is not null
return typeTester.nullTest(varExpr, JCTree.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.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.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.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.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.AND);
}
return result;
} else {
return typeTester.isReified(varExpr, testedType);
}
} else {
throw BugException.unhandledDeclarationCase(declaration);
}
}
use of com.redhat.ceylon.model.typechecker.model.ClassOrInterface 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.ClassOrInterface in project ceylon-compiler by ceylon.
the class AbstractTransformer method supportsReified.
public static boolean supportsReified(Declaration declaration) {
if (declaration instanceof ClassOrInterface) {
// Java constructors don't support reified type arguments
return Decl.isCeylon((TypeDeclaration) declaration);
} else if (Decl.isConstructor(declaration)) {
// Java constructors don't support reified type arguments
return Decl.isCeylon(Decl.getConstructor(declaration));
} else if (declaration instanceof Function) {
if (((Function) declaration).isParameter()) {
// those can never be parameterised
return false;
}
if (Decl.isToplevel(declaration))
return true;
// Java methods don't support reified type arguments
Function m = (Function) CodegenUtil.getTopmostRefinedDeclaration(declaration);
// See what its container is
ClassOrInterface container = Decl.getClassOrInterfaceContainer(m);
// that must be Ceylon so it supports it
if (container == null)
return true;
return supportsReified(container);
} else {
throw BugException.unhandledDeclarationCase(declaration);
}
}
use of com.redhat.ceylon.model.typechecker.model.ClassOrInterface in project ceylon-compiler by ceylon.
the class ClassTransformer method makeCompanionInstanceAssignment.
/**
* Returns the companion instances assignment expression used in the constructor,
* e.g.
* <pre>
* this.$ceylon$language$Enumerable$this$ = new .ceylon.language.Enumerable$impl<.com.redhat.ceylon.compiler.java.test.structure.klass.SerializableEnumerable>(.com.redhat.ceylon.compiler.java.test.structure.klass.SerializableEnumerable.$TypeDescriptor$, this);
* </pre>
* @param classBuilder
* @return
*/
private JCExpressionStatement makeCompanionInstanceAssignment(final Class model, final Interface iface, final Type satisfiedType) {
final Type bestSatisfiedType = getBestSatisfiedType(model.getType(), iface);
JCExpression containerInstance = null;
if (!Decl.isToplevel(iface) && !Decl.isLocal(iface)) {
// if it's a member type we need to qualify the new instance with its $impl container
ClassOrInterface interfaceContainer = Decl.getClassOrInterfaceContainer(iface, false);
if (interfaceContainer instanceof Interface) {
ClassOrInterface modelContainer = model;
// first try to find exactly the interface we are looking for
while ((modelContainer = Decl.getClassOrInterfaceContainer(modelContainer, false)) != null && !modelContainer.equals(interfaceContainer)) {
// keep searching
}
// then find one that inherits it
if (modelContainer == null) {
modelContainer = model;
while ((modelContainer = Decl.getClassOrInterfaceContainer(modelContainer, false)) != null && modelContainer.getType().getSupertype(interfaceContainer) == null) {
// keep searching
}
}
if (modelContainer == null) {
throw new BugException("Could not find container that satisfies interface " + iface.getQualifiedNameString() + " to find qualifying instance for companion instance for " + model.getQualifiedNameString());
}
// if it's an interface we just qualify it properly
if (modelContainer instanceof Interface) {
JCExpression containerType = makeJavaType(modelContainer.getType(), JT_COMPANION | JT_SATISFIES | JT_RAW);
containerInstance = makeSelect(containerType, "this");
} else {
// it's a class: find the right field used for the interface container impl
String containerFieldName = getCompanionFieldName((Interface) interfaceContainer);
JCExpression containerType = makeJavaType(modelContainer.getType(), JT_SATISFIES);
containerInstance = makeSelect(makeSelect(containerType, "this"), containerFieldName);
}
}
}
List<JCExpression> state = List.nil();
// pass all reified type info to the constructor
for (JCExpression t : makeReifiedTypeArguments(satisfiedType)) {
state = state.append(t);
}
// pass the instance of this
state = state.append(expressionGen().applyErasureAndBoxing(naming.makeThis(), model.getType(), false, true, BoxingStrategy.BOXED, bestSatisfiedType, ExpressionTransformer.EXPR_FOR_COMPANION));
final JCExpression ifaceImplType;
if (!Decl.isToplevel(iface) && !Decl.isLocal(iface) && Decl.getClassOrInterfaceContainer(iface, false) instanceof Interface) {
ifaceImplType = makeJavaType(bestSatisfiedType, JT_COMPANION | JT_CLASS_NEW | JT_NON_QUALIFIED);
} else {
ifaceImplType = makeJavaType(bestSatisfiedType, JT_COMPANION | JT_CLASS_NEW);
}
JCExpression newInstance = make().NewClass(containerInstance, null, ifaceImplType, state, null);
JCExpressionStatement companionInstanceAssign = make().Exec(make().Assign(// TODO Use qualified name for quoting?
makeSelect("this", getCompanionFieldName(iface)), newInstance));
return companionInstanceAssign;
}
Aggregations