001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.lang.types.checker; 018 019import org.jetbrains.annotations.NotNull; 020import org.jetbrains.annotations.Nullable; 021import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 022import org.jetbrains.jet.lang.types.*; 023import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 024 025import java.util.List; 026 027import static org.jetbrains.jet.lang.types.Variance.*; 028 029public class TypeCheckingProcedure { 030 031 // This method returns the supertype of the first parameter that has the same constructor 032 // as the second parameter, applying the substitution of type arguments to it 033 @Nullable 034 public static JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { 035 TypeConstructor constructor = subtype.getConstructor(); 036 if (constructor.equals(supertype.getConstructor())) { 037 return subtype; 038 } 039 for (JetType immediateSupertype : constructor.getSupertypes()) { 040 JetType correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype); 041 if (correspondingSupertype != null) { 042 return TypeSubstitutor.create(subtype).safeSubstitute(correspondingSupertype, Variance.INVARIANT); 043 } 044 } 045 return null; 046 } 047 048 public static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) { 049 boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; 050 return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType(); 051 } 052 053 public static JetType getInType(TypeParameterDescriptor parameter, TypeProjection argument) { 054 boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE; 055 return isOutProjected ? KotlinBuiltIns.getInstance().getNothingType() : argument.getType(); 056 } 057 058 private final TypingConstraints constraints; 059 060 public TypeCheckingProcedure(TypingConstraints constraints) { 061 this.constraints = constraints; 062 } 063 064 public boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2) { 065 if (type1.isNullable() != type2.isNullable()) { 066 return false; 067 } 068 069 if (type1.isNullable()) { 070 // Then type2 is nullable, too (see the previous condition 071 return constraints.assertEqualTypes(TypeUtils.makeNotNullable(type1), TypeUtils.makeNotNullable(type2), this); 072 } 073 074 TypeConstructor constructor1 = type1.getConstructor(); 075 TypeConstructor constructor2 = type2.getConstructor(); 076 077 if (!constraints.assertEqualTypeConstructors(constructor1, constructor2)) { 078 return false; 079 } 080 081 List<TypeProjection> type1Arguments = type1.getArguments(); 082 List<TypeProjection> type2Arguments = type2.getArguments(); 083 if (type1Arguments.size() != type2Arguments.size()) { 084 return false; 085 } 086 087 for (int i = 0; i < type1Arguments.size(); i++) { 088 TypeParameterDescriptor typeParameter1 = constructor1.getParameters().get(i); 089 TypeProjection typeProjection1 = type1Arguments.get(i); 090 TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i); 091 TypeProjection typeProjection2 = type2Arguments.get(i); 092 if (getEffectiveProjectionKind(typeParameter1, typeProjection1) != getEffectiveProjectionKind(typeParameter2, typeProjection2)) { 093 return false; 094 } 095 096 if (!constraints.assertEqualTypes(typeProjection1.getType(), typeProjection2.getType(), this)) { 097 return false; 098 } 099 } 100 return true; 101 } 102 103 public enum EnrichedProjectionKind { 104 IN, OUT, INV, STAR; 105 106 @NotNull 107 public static EnrichedProjectionKind fromVariance(@NotNull Variance variance) { 108 switch (variance) { 109 case INVARIANT: 110 return INV; 111 case IN_VARIANCE: 112 return IN; 113 case OUT_VARIANCE: 114 return OUT; 115 } 116 throw new IllegalStateException("Unknown variance"); 117 } 118 } 119 120 // If class C<out T> then C<T> and C<out T> mean the same 121 // out * out = out 122 // out * in = * 123 // out * inv = out 124 // 125 // in * out = * 126 // in * in = in 127 // in * inv = in 128 // 129 // inv * out = out 130 // inv * in = out 131 // inv * inv = inv 132 public static EnrichedProjectionKind getEffectiveProjectionKind( 133 @NotNull TypeParameterDescriptor typeParameter, 134 @NotNull TypeProjection typeArgument 135 ) { 136 Variance a = typeParameter.getVariance(); 137 Variance b = typeArgument.getProjectionKind(); 138 139 // If they are not both invariant, let's make b not invariant for sure 140 if (b == INVARIANT) { 141 Variance t = a; 142 a = b; 143 b = t; 144 } 145 146 // Opposites yield STAR 147 if (a == IN_VARIANCE && b == OUT_VARIANCE) { 148 return EnrichedProjectionKind.STAR; 149 } 150 if (a == OUT_VARIANCE && b == IN_VARIANCE) { 151 return EnrichedProjectionKind.STAR; 152 } 153 154 // If they are not opposite, return b, because b is either equal to a or b is in/out and a is inv 155 return EnrichedProjectionKind.fromVariance(b); 156 } 157 158 public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { 159 if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) { 160 return true; 161 } 162 if (!supertype.isNullable() && subtype.isNullable()) { 163 return false; 164 } 165 subtype = TypeUtils.makeNotNullable(subtype); 166 supertype = TypeUtils.makeNotNullable(supertype); 167 if (KotlinBuiltIns.getInstance().isNothingOrNullableNothing(subtype)) { 168 return true; 169 } 170 @Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); 171 if (closestSupertype == null) { 172 return constraints.noCorrespondingSupertype(subtype, supertype); // if this returns true, there still isn't any supertype to continue with 173 } 174 175 return checkSubtypeForTheSameConstructor(closestSupertype, supertype); 176 } 177 178 private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) { 179 TypeConstructor constructor = subtype.getConstructor(); 180 assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor(); 181 182 List<TypeProjection> subArguments = subtype.getArguments(); 183 List<TypeProjection> superArguments = supertype.getArguments(); 184 List<TypeParameterDescriptor> parameters = constructor.getParameters(); 185 for (int i = 0; i < parameters.size(); i++) { 186 TypeParameterDescriptor parameter = parameters.get(i); 187 188 189 TypeProjection subArgument = subArguments.get(i); 190 JetType subIn = getInType(parameter, subArgument); 191 JetType subOut = getOutType(parameter, subArgument); 192 193 TypeProjection superArgument = superArguments.get(i); 194 JetType superIn = getInType(parameter, superArgument); 195 JetType superOut = getOutType(parameter, superArgument); 196 197 boolean argumentIsErrorType = ErrorUtils.isErrorType(subArgument.getType()) || ErrorUtils.isErrorType(superArgument.getType()); 198 if (!argumentIsErrorType && parameter.getVariance() == INVARIANT 199 && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) { 200 if (!constraints.assertEqualTypes(subArgument.getType(), superArgument.getType(), this)) return false; 201 } 202 else { 203 if (!constraints.assertSubtype(subOut, superOut, this)) return false; 204 if (!constraints.assertSubtype(superIn, subIn, this)) return false; 205 } 206 } 207 return true; 208 } 209}