use of com.redhat.ceylon.model.typechecker.model.TypeParameter in project ceylon-compiler by ceylon.
the class ExpressionTransformer method getVarianceCastResult.
private VarianceCastResult getVarianceCastResult(Type expectedType, Type exprType) {
// exactly the same type, doesn't need casting
if (expectedType == null || exprType.isExactly(expectedType))
return null;
// if we're not trying to put it into an interface, there's no need
if (!(expectedType.getDeclaration() instanceof Interface))
return null;
// the interface must have type arguments, otherwise we can't use raw types
if (expectedType.getTypeArguments().isEmpty())
return null;
// see if any of those type arguments has variance
boolean hasVariance = false;
for (TypeParameter t : expectedType.getTypeArguments().keySet()) {
if (expectedType.isContravariant(t) || expectedType.isCovariant(t)) {
hasVariance = true;
break;
}
}
if (!hasVariance)
return null;
// see if we're inheriting the interface twice with different type parameters
java.util.List<Type> satisfiedTypes = new LinkedList<Type>();
for (Type superType : simplifyType(exprType).getSupertypes()) {
if (Decl.equal(superType.getDeclaration(), expectedType.getDeclaration()))
satisfiedTypes.add(superType);
}
// discard the supertypes that have the same erasure
for (int i = 0; i < satisfiedTypes.size(); i++) {
Type pt = satisfiedTypes.get(i);
for (int j = i + 1; j < satisfiedTypes.size(); j++) {
Type other = satisfiedTypes.get(j);
if (pt.isExactly(other) || haveSameErasure(pt, other)) {
satisfiedTypes.remove(j);
break;
}
}
}
// we need at least two instantiations
if (satisfiedTypes.size() <= 1)
return null;
boolean needsCast = false;
// we need at least one that differs
for (Type superType : satisfiedTypes) {
if (!exprType.isExactly(superType)) {
needsCast = true;
break;
}
}
// no cast needed if they are all the same type
if (!needsCast)
return null;
// find the better cast match
for (Type superType : satisfiedTypes) {
if (expectedType.isExactly(superType))
return new VarianceCastResult(superType);
}
// nothing better than a raw cast (Stef: not sure that can happen)
return RawCastVarianceResult;
}
use of com.redhat.ceylon.model.typechecker.model.TypeParameter in project ceylon-compiler by ceylon.
the class ExpressionTransformer method needsCast.
boolean needsCast(Type exprType, Type expectedType, boolean expectedTypeNotRaw, boolean expectedTypeHasConstrainedTypeParameters, boolean downCast) {
// error handling
if (exprType == null)
return false;
// make sure we work on definite types
exprType = simplifyType(exprType);
expectedType = simplifyType(expectedType);
// abort if both types are the same
if (exprType.isExactly(expectedType)) {
// really trust the expected type
if (!expectedTypeHasConstrainedTypeParameters)
return false;
}
// now see about erasure
boolean eraseExprType = willEraseToObject(exprType);
boolean eraseExpectedType = willEraseToObject(expectedType);
// if we erase expected type we need no cast
if (eraseExpectedType) {
// unless the expected type is parameterised with bounds that erasure to Object can't possibly satisfy
if (!expectedTypeHasConstrainedTypeParameters)
return false;
}
// if we erase the expr type we need a cast
if (eraseExprType)
return true;
// find their common type
Type commonType = exprType.getSupertype(expectedType.getDeclaration());
if (commonType == null || !(commonType.getDeclaration() instanceof ClassOrInterface)) {
// we did not find any common type, but we may be downcasting, in which case we need a cast
return downCast;
}
// some times we can lose info due to an erased type parameter somewhere in the inheritance graph
if (lostTypeParameterInInheritance(exprType, commonType))
return true;
if (!expectedTypeNotRaw) {
// if we know for sure that the expected type is NOT raw. if it's false we've no idea but we can check:
if (isTurnedToRaw(expectedType)) {
return false;
}
// the common type could be erased
if (commonType.isExactly(expectedType))
return false;
}
//special case for Callable because only the first type param exists in Java, the rest is completely suppressed
boolean isCallable = isCeylonCallable(commonType);
// now see if the type parameters match
java.util.List<Type> commonTypeArgs = commonType.getTypeArgumentList();
java.util.List<TypeParameter> commonTps = commonType.getDeclaration().getTypeParameters();
java.util.List<Type> expectedTypeArgs = expectedType.getTypeArgumentList();
java.util.List<TypeParameter> expectedTps = expectedType.getDeclaration().getTypeParameters();
// check that we got them all otherwise we just don't know
if (commonTypeArgs.size() != expectedTypeArgs.size())
return false;
for (int i = 0, n = commonTypeArgs.size(); i < n; i++) {
// apply the same logic to each type param: see if they would require a raw cast
Type commonTypeArg = commonTypeArgs.get(i);
Type expectedTypeArg = expectedTypeArgs.get(i);
if (hasDependentTypeParameters(commonTps, commonTps.get(i)) || hasDependentTypeParameters(expectedTps, expectedTps.get(i))) {
// if the type parameters are not identical:
if (!simplifyType(commonTypeArg).isExactly(simplifyType(expectedTypeArg))) {
return true;
}
}
if (needsCast(commonTypeArg, expectedTypeArg, expectedTypeNotRaw, expectedTypeHasConstrainedTypeParameters, downCast))
return true;
// stop after the first one for Callable
if (isCallable)
break;
}
return false;
}
use of com.redhat.ceylon.model.typechecker.model.TypeParameter in project ceylon-compiler by ceylon.
the class TypeParserTests method testParamsVariance2.
@Test
public void testParamsVariance2() {
Type type = new TypeParser(MockLoader.instance).decodeType("t2<b,out c>", null, mockDefaultModule, mockPkgUnit);
assertTypeWithParameters(type);
Map<TypeParameter, SiteVariance> varianceOverrides = type.getVarianceOverrides();
Assert.assertNotNull(varianceOverrides);
Assert.assertEquals(1, varianceOverrides.size());
List<TypeParameter> tps = type.getDeclaration().getTypeParameters();
Assert.assertEquals(null, varianceOverrides.get(tps.get(0)));
Assert.assertEquals(SiteVariance.OUT, varianceOverrides.get(tps.get(1)));
}
use of com.redhat.ceylon.model.typechecker.model.TypeParameter in project ceylon-compiler by ceylon.
the class AbstractTransformer method makeJavaType.
JCExpression makeJavaType(final Type ceylonType, final int flags) {
Type type = ceylonType;
if (type == null || type.isUnknown())
return make().Erroneous();
if (type.getDeclaration() instanceof Constructor) {
type = type.getExtendedType();
}
// resolve aliases
if ((flags & JT_CLASS_LITERAL) == 0)
type = type.resolveAliases();
if ((flags & __JT_RAW_TP_BOUND) != 0 && type.isTypeParameter()) {
type = type.getExtendedType();
}
if (type.isUnion()) {
for (Type pt : type.getCaseTypes()) {
if (pt.getDeclaration().isAnonymous()) {
// found one, let's try to make it simpler
Type simplerType = typeFact().denotableType(type);
if (!simplerType.isNothing() && !simplerType.isUnion()) {
type = simplerType;
} else if (isCeylonBoolean(simplifyType(simplerType))) {
type = simplerType;
}
break;
}
}
}
if (type.getDeclaration().isJavaEnum()) {
type = type.getExtendedType();
}
if (type.isTypeConstructor()) {
return make().QualIdent(syms().ceylonAbstractTypeConstructorType.tsym);
}
// ERASURE
if ((flags & JT_CLASS_LITERAL) == 0 && // literals to the alias class
willEraseToObject(type)) {
// - The Ceylon type U|V results in the Java type Object
if ((flags & JT_SATISFIES) != 0) {
return null;
} else {
return make().Type(syms().objectType);
}
} else if (willEraseToAnnotation(type)) {
return make().Type(syms().annotationType);
} else if (willEraseToException(type)) {
if ((flags & JT_CLASS_NEW) != 0 || (flags & JT_EXTENDS) != 0) {
return makeIdent(syms().ceylonExceptionType);
} else {
return make().Type(syms().exceptionType);
}
} else if (willEraseToThrowable(type)) {
if ((flags & JT_CLASS_NEW) != 0 || (flags & JT_EXTENDS) != 0) {
return makeIdent(syms().throwableType);
} else {
return make().Type(syms().throwableType);
}
} else if (willEraseToSequence(type)) {
if ((flags & (JT_CLASS_NEW | JT_EXTENDS | JT_IS)) == 0) {
Type typeArg = simplifyType(type).getTypeArgumentList().get(0);
Type seqType = typeFact.getSequenceType(typeArg);
if (typeFact.isOptionalType(type)) {
type = typeFact.getOptionalType(seqType);
} else {
type = seqType;
}
}
} else if ((flags & (JT_SATISFIES | JT_EXTENDS | JT_NO_PRIMITIVES | JT_CLASS_NEW)) == 0 && ((isCeylonBasicType(type) && !isOptional(type)) || isJavaString(type))) {
if (isCeylonString(type) || isJavaString(type)) {
return make().Type(syms().stringType);
} else if (isCeylonBoolean(type)) {
return make().TypeIdent(TypeTags.BOOLEAN);
} else if (isCeylonInteger(type)) {
if ("short".equals(type.getUnderlyingType())) {
return make().TypeIdent(TypeTags.SHORT);
} else if ((flags & JT_SMALL) != 0 || "int".equals(type.getUnderlyingType())) {
return make().TypeIdent(TypeTags.INT);
} else {
return make().TypeIdent(TypeTags.LONG);
}
} else if (isCeylonFloat(type)) {
if ((flags & JT_SMALL) != 0 || "float".equals(type.getUnderlyingType())) {
return make().TypeIdent(TypeTags.FLOAT);
} else {
return make().TypeIdent(TypeTags.DOUBLE);
}
} else if (isCeylonCharacter(type)) {
if ("char".equals(type.getUnderlyingType())) {
return make().TypeIdent(TypeTags.CHAR);
} else {
return make().TypeIdent(TypeTags.INT);
}
} else if (isCeylonByte(type)) {
return make().TypeIdent(TypeTags.BYTE);
}
} else if (isCeylonBoolean(type) && !isTypeParameter(type)) {
//&& (flags & TYPE_ARGUMENT) == 0){
// special case to get rid of $true and $false types
type = typeFact.getBooleanType();
} else if ((flags & JT_VALUE_TYPE) == 0 && isJavaArray(type)) {
return getJavaArrayElementType(type, flags);
}
JCExpression jt = null;
Type simpleType;
if ((flags & JT_CLASS_LITERAL) == 0)
simpleType = simplifyType(type);
else
simpleType = type;
// see if we need to cross methods when looking up container types
// this is required to properly collect all the type parameters for local interfaces
// which we pull up to the toplevel and capture all the container type parameters
boolean needsQualifyingTypeArgumentsFromLocalContainers = Decl.isCeylon(simpleType.getDeclaration()) && simpleType.getDeclaration() instanceof Interface && // this is only valid for interfaces, not for their companion which stay where they are
(flags & JT_COMPANION) == 0;
java.util.List<Reference> qualifyingTypes = null;
Reference qType = simpleType;
boolean hasTypeParameters = false;
while (qType != null) {
hasTypeParameters |= !qType.getTypeArguments().isEmpty();
if (qualifyingTypes != null)
qualifyingTypes.add(qType);
Declaration typeDeclaration = qType.getDeclaration();
// all the containing type parameters that it captures
if (// local or anonymous
(Decl.isLocal(typeDeclaration) || !typeDeclaration.isNamed()) && needsQualifyingTypeArgumentsFromLocalContainers && typeDeclaration instanceof ClassOrInterface) {
Declaration container = Decl.getDeclarationScope(typeDeclaration.getContainer());
while (container instanceof Function) {
qType = ((Function) container).getReference();
if (qualifyingTypes == null) {
qualifyingTypes = new java.util.ArrayList<Reference>();
qualifyingTypes.add(simpleType);
}
hasTypeParameters = true;
qualifyingTypes.add(qType);
container = Decl.getDeclarationScope(container.getContainer());
}
if (container instanceof TypeDeclaration) {
qType = ((TypeDeclaration) container).getType();
} else {
qType = null;
}
} else if (typeDeclaration.isNamed()) {
// avoid anonymous types which may pretend that they have a qualifying type
qType = qType.getQualifyingType();
if (qType != null && qType.getDeclaration() instanceof ClassOrInterface == false) {
// sometimes the typechecker throws qualifying intersections at us and
// we can't make anything of them, since some members may be unrelated to
// the qualified declaration. This happens with "extends super.Foo()"
// for example. See https://github.com/ceylon/ceylon-compiler/issues/1478
qType = ((Type) qType).getSupertype((TypeDeclaration) typeDeclaration.getContainer());
}
} else {
// skip local declaration containers
qType = null;
}
// delayed allocation if we have a qualifying type
if (qualifyingTypes == null && qType != null) {
qualifyingTypes = new java.util.ArrayList<Reference>();
qualifyingTypes.add(simpleType);
}
}
int firstQualifyingTypeWithTypeParameters = qualifyingTypes != null ? qualifyingTypes.size() - 1 : 0;
// find the first static one, from the right to the left
if (qualifyingTypes != null) {
for (Reference pt : qualifyingTypes) {
Declaration declaration = pt.getDeclaration();
if (declaration instanceof TypeDeclaration && Decl.isStatic((TypeDeclaration) declaration)) {
break;
}
firstQualifyingTypeWithTypeParameters--;
}
if (firstQualifyingTypeWithTypeParameters < 0)
firstQualifyingTypeWithTypeParameters = 0;
// put them in outer->inner order
Collections.reverse(qualifyingTypes);
}
if (((flags & JT_RAW) == 0) && hasTypeParameters && !rawSupertype(ceylonType, flags)) {
// special case for interfaces because we pull them into toplevel types
if (Decl.isCeylon(simpleType.getDeclaration()) && qualifyingTypes != null && qualifyingTypes.size() > 1 && simpleType.getDeclaration() instanceof Interface && // this is only valid for interfaces, not for their companion which stay where they are
(flags & JT_COMPANION) == 0) {
JCExpression baseType;
TypeDeclaration tdecl = simpleType.getDeclaration();
// collect all the qualifying type args we'd normally have
java.util.List<TypeParameter> qualifyingTypeParameters = new java.util.ArrayList<TypeParameter>();
java.util.Map<TypeParameter, Type> qualifyingTypeArguments = new java.util.HashMap<TypeParameter, Type>();
collectQualifyingTypeArguments(qualifyingTypeParameters, qualifyingTypeArguments, qualifyingTypes);
ListBuffer<JCExpression> typeArgs = makeTypeArgs(isCeylonCallable(simpleType), flags, qualifyingTypeArguments, qualifyingTypeParameters, simpleType);
if (isCeylonCallable(type) && (flags & JT_CLASS_NEW) != 0) {
baseType = makeIdent(syms().ceylonAbstractCallableType);
} else {
baseType = naming.makeDeclarationName(tdecl, DeclNameFlag.QUALIFIED);
}
if (typeArgs != null && typeArgs.size() > 0) {
jt = make().TypeApply(baseType, typeArgs.toList());
} else {
jt = baseType;
}
} else if ((flags & JT_NON_QUALIFIED) == 0) {
int index = 0;
if (qualifyingTypes != null) {
for (Reference qualifyingType : qualifyingTypes) {
jt = makeParameterisedType(qualifyingType.getType(), type, flags, jt, qualifyingTypes, firstQualifyingTypeWithTypeParameters, index);
index++;
}
} else {
jt = makeParameterisedType(simpleType, type, flags, jt, qualifyingTypes, firstQualifyingTypeWithTypeParameters, index);
}
} else {
jt = makeParameterisedType(type, type, flags, jt, qualifyingTypes, 0, 0);
}
} else {
TypeDeclaration tdecl = simpleType.getDeclaration();
// - The Ceylon type T results in the Java type T
if (isCeylonCallable(type) && (flags & JT_CLASS_NEW) != 0) {
jt = makeIdent(syms().ceylonAbstractCallableType);
} else if (tdecl instanceof TypeParameter)
jt = makeQuotedIdent(tdecl.getName());
else // don't use underlying type if we want no primitives
if ((flags & (JT_SATISFIES | JT_NO_PRIMITIVES)) != 0 || simpleType.getUnderlyingType() == null) {
jt = naming.makeDeclarationName(tdecl, jtFlagsToDeclNameOpts(flags));
} else
jt = makeQuotedFQIdent(simpleType.getUnderlyingType());
}
return (jt != null) ? jt : makeErroneous(null, "compiler bug: the java type corresponding to " + ceylonType + " could not be computed");
}
use of com.redhat.ceylon.model.typechecker.model.TypeParameter in project ceylon-compiler by ceylon.
the class AbstractTransformer method nonWideningType.
Type nonWideningType(TypedReference declaration, TypedReference refinedDeclaration) {
final Reference pr;
if (declaration.equals(refinedDeclaration)) {
pr = declaration;
} else {
Type refinedType = refinedDeclaration.getType();
// since it may have changed name
if (refinedType.getDeclaration() instanceof TypeParameter && refinedType.getDeclaration().getContainer() instanceof Function) {
// find its index in the refined declaration
TypeParameter refinedTypeParameter = (TypeParameter) refinedType.getDeclaration();
Function refinedMethod = (Function) refinedTypeParameter.getContainer();
int i = 0;
for (TypeParameter tp : refinedMethod.getTypeParameters()) {
if (tp.getName().equals(refinedTypeParameter.getName()))
break;
i++;
}
if (i >= refinedMethod.getTypeParameters().size()) {
throw new BugException("can't find type parameter " + refinedTypeParameter.getName() + " in its container " + refinedMethod.getName());
}
// the refining method type parameter should be at the same index
if (declaration.getDeclaration() instanceof Function == false)
throw new BugException("refining declaration is not a method: " + declaration);
Function refiningMethod = (Function) declaration.getDeclaration();
if (i >= refiningMethod.getTypeParameters().size()) {
throw new BugException("refining method does not have enough type parameters to refine " + refinedMethod.getName());
}
pr = refiningMethod.getTypeParameters().get(i).getType();
} else {
pr = refinedType;
}
}
if (pr.getDeclaration() instanceof Functional && Decl.isMpl((Functional) pr.getDeclaration())) {
// the innermost Callable.
return getReturnTypeOfCallable(pr.getFullType());
}
return pr.getType();
}
Aggregations