Ejemplo n.º 1
1
  private List<TypeProjection> substituteTypeArguments(
      List<TypeParameterDescriptor> typeParameters,
      List<TypeProjection> typeArguments,
      int recursionDepth)
      throws SubstitutionException {
    List<TypeProjection> substitutedArguments =
        new ArrayList<TypeProjection>(typeParameters.size());
    for (int i = 0; i < typeParameters.size(); i++) {
      TypeParameterDescriptor typeParameter = typeParameters.get(i);
      TypeProjection typeArgument = typeArguments.get(i);

      TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, recursionDepth + 1);

      switch (conflictType(
          typeParameter.getVariance(), substitutedTypeArgument.getProjectionKind())) {
        case NO_CONFLICT:
          // if the corresponding type parameter is already co/contra-variant, there's not need for
          // an explicit projection
          if (typeParameter.getVariance() != Variance.INVARIANT
              && !substitutedTypeArgument.isStarProjection()) {
            substitutedTypeArgument =
                new TypeProjectionImpl(Variance.INVARIANT, substitutedTypeArgument.getType());
          }
          break;
        case OUT_IN_IN_POSITION:
        case IN_IN_OUT_POSITION:
          substitutedTypeArgument = TypeUtils.makeStarProjection(typeParameter);
          break;
      }

      substitutedArguments.add(substitutedTypeArgument);
    }
    return substitutedArguments;
  }
Ejemplo n.º 2
0
 private void appendTypeProjections(
     @NotNull List<TypeProjection> typeProjections, @NotNull StringBuilder builder) {
   for (Iterator<TypeProjection> iterator = typeProjections.iterator(); iterator.hasNext(); ) {
     TypeProjection typeProjection = iterator.next();
     if (typeProjection.getProjectionKind() != Variance.INVARIANT) {
       builder.append(typeProjection.getProjectionKind()).append(" ");
     }
     builder.append(renderType(typeProjection.getType()));
     if (iterator.hasNext()) {
       builder.append(", ");
     }
   }
 }
Ejemplo n.º 3
0
  private Variance calculateArgumentProjectionKindFromSuper(
      @NotNull TypeProjection argument,
      @NotNull List<TypeProjectionAndVariance> projectionsFromSuper) {
    Set<Variance> projectionKindsInSuper = Sets.newLinkedHashSet();
    for (TypeProjectionAndVariance projectionAndVariance : projectionsFromSuper) {
      projectionKindsInSuper.add(projectionAndVariance.typeProjection.getProjectionKind());
    }

    Variance defaultProjectionKind = argument.getProjectionKind();
    if (projectionKindsInSuper.size() == 0) {
      return defaultProjectionKind;
    } else if (projectionKindsInSuper.size() == 1) {
      Variance projectionKindInSuper = projectionKindsInSuper.iterator().next();
      if (defaultProjectionKind == INVARIANT || defaultProjectionKind == projectionKindInSuper) {
        return projectionKindInSuper;
      } else {
        reportError(
            "Incompatible projection kinds in type arguments of super methods' return types: "
                + projectionsFromSuper
                + ", defined in current: "
                + argument);
        return defaultProjectionKind;
      }
    } else {
      reportError(
          "Incompatible projection kinds in type arguments of super methods' return types: "
              + projectionsFromSuper);
      return defaultProjectionKind;
    }
  }
Ejemplo n.º 4
0
  /**
   * Determines what constraint (supertype, subtype or equal) should be generated for type parameter
   * {@code T} in a constraint like (in this example subtype one): <br>
   * {@code MyClass<in/out/- A> <: MyClass<in/out/- B>}, where {@code MyClass<in/out/- T>} is
   * declared. <br>
   * The parameters description is given according to the example above.
   *
   * @param typeParameterVariance declared variance of T
   * @param subjectTypeProjection {@code in/out/- A}
   * @param constrainingTypeProjection {@code in/out/- B}
   * @param upperConstraintKind kind of the constraint {@code MyClass<...A> <: MyClass<...B>}
   *     (subtype in this example).
   * @return kind of constraint to be generated: {@code A <: B} (subtype), {@code A >: B}
   *     (supertype) or {@code A = B} (equal).
   */
  @NotNull
  private static ConstraintKind getTypeParameterConstraintKind(
      @NotNull Variance typeParameterVariance,
      @NotNull TypeProjection subjectTypeProjection,
      @NotNull TypeProjection constrainingTypeProjection,
      @NotNull ConstraintKind upperConstraintKind) {
    // If variance of type parameter is non-trivial, it should be taken into consideration to infer
    // result constraint type.
    // Otherwise when type parameter declared as INVARIANT, there might be non-trivial use-site
    // variance of a supertype.
    //
    // Example: Let class MyClass<T> is declared.
    //
    // If super type has 'out' projection:
    // MyClass<A> <: MyClass<out B>,
    // then constraint A <: B can be generated.
    //
    // If super type has 'in' projection:
    // MyClass<A> <: MyClass<in B>,
    // then constraint A >: B can be generated.
    //
    // Otherwise constraint A = B should be generated.

    Variance varianceForTypeParameter;
    if (typeParameterVariance != INVARIANT) {
      varianceForTypeParameter = typeParameterVariance;
    } else if (upperConstraintKind == SUPER_TYPE) {
      varianceForTypeParameter = constrainingTypeProjection.getProjectionKind();
    } else if (upperConstraintKind == SUB_TYPE) {
      varianceForTypeParameter = subjectTypeProjection.getProjectionKind();
    } else {
      varianceForTypeParameter = INVARIANT;
    }

    return getTypeParameterConstraintKind(varianceForTypeParameter, upperConstraintKind);
  }
Ejemplo n.º 5
0
  private TypeProjection substituteCompoundType(
      TypeProjection originalProjection, int recursionDepth) throws SubstitutionException {
    final JetType type = originalProjection.getType();
    Variance projectionKind = originalProjection.getProjectionKind();
    if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
      // substitution can't change type parameter
      // todo substitute bounds
      return originalProjection;
    }

    List<TypeProjection> substitutedArguments =
        substituteTypeArguments(
            type.getConstructor().getParameters(), type.getArguments(), recursionDepth);

    // Only type parameters of the corresponding class (or captured type parameters of outer
    // declaration) are substituted
    // e.g. for return type Foo of 'add(..)' in 'class Foo { fun <R> add(bar: Bar<R>): Foo }' R
    // shouldn't be substituted in the scope
    TypeSubstitution substitutionFilteringTypeParameters =
        new TypeSubstitution() {
          private final Collection<TypeConstructor> containedOrCapturedTypeParameters =
              TypeUtilsKt.getContainedAndCapturedTypeParameterConstructors(type);

          @Nullable
          @Override
          public TypeProjection get(@NotNull JetType key) {
            return containedOrCapturedTypeParameters.contains(key.getConstructor())
                ? substitution.get(key)
                : null;
          }

          @Override
          public boolean isEmpty() {
            return substitution.isEmpty();
          }
        };
    JetType substitutedType =
        JetTypeImpl.create(
            type.getAnnotations(), // Old annotations. This is questionable
            type.getConstructor(), // The same constructor
            type.isMarkedNullable(), // Same nullability
            substitutedArguments,
            substitutionFilteringTypeParameters,
            new SubstitutingScope(
                type.getMemberScope(), create(substitutionFilteringTypeParameters)),
            type.getCapabilities());
    return new TypeProjectionImpl(projectionKind, substitutedType);
  }
Ejemplo n.º 6
0
  private static void doUnify(
      TypeProjection knownProjection,
      TypeProjection projectWithVariables,
      Predicate<TypeConstructor> isVariable,
      UnificationResultImpl result) {
    JetType known = knownProjection.getType();
    JetType withVariables = projectWithVariables.getType();

    // in Foo ~ in X  =>  Foo ~ X
    Variance knownProjectionKind = knownProjection.getProjectionKind();
    Variance withVariablesProjectionKind = projectWithVariables.getProjectionKind();
    if (knownProjectionKind == withVariablesProjectionKind
        && knownProjectionKind != Variance.INVARIANT) {
      doUnify(new TypeProjection(known), new TypeProjection(withVariables), isVariable, result);
      return;
    }

    // Foo? ~ X?  =>  Foo ~ X
    if (known.isNullable() && withVariables.isNullable()) {
      doUnify(
          new TypeProjection(knownProjectionKind, TypeUtils.makeNotNullable(known)),
          new TypeProjection(withVariablesProjectionKind, TypeUtils.makeNotNullable(withVariables)),
          isVariable,
          result);
      return;
    }

    // in Foo ~ out X  => fail
    // in Foo ~ X  =>  may be OK
    if (knownProjectionKind != withVariablesProjectionKind
        && withVariablesProjectionKind != Variance.INVARIANT) {
      result.fail();
      return;
    }

    // Foo ~ X? => fail
    if (!known.isNullable() && withVariables.isNullable()) {
      result.fail();
      return;
    }

    // Foo ~ X  =>  x |-> Foo
    TypeConstructor maybeVariable = withVariables.getConstructor();
    if (isVariable.apply(maybeVariable)) {
      result.put(maybeVariable, new TypeProjection(knownProjectionKind, known));
      return;
    }

    // Foo? ~ Foo || in Foo ~ Foo || Foo ~ Bar
    boolean structuralMismatch =
        known.isNullable() != withVariables.isNullable()
            || knownProjectionKind != withVariablesProjectionKind
            || !known.getConstructor().equals(withVariables.getConstructor());
    if (structuralMismatch) {
      result.fail();
      return;
    }

    // Foo<A> ~ Foo<B, C>
    if (known.getArguments().size() != withVariables.getArguments().size()) {
      result.fail();
      return;
    }

    // Foo ~ Foo
    if (known.getArguments().isEmpty()) {
      return;
    }

    // Foo<...> ~ Foo<...>
    List<TypeProjection> knownArguments = known.getArguments();
    List<TypeProjection> withVariablesArguments = withVariables.getArguments();
    for (int i = 0; i < knownArguments.size(); i++) {
      TypeProjection knownArg = knownArguments.get(i);
      TypeProjection withVariablesArg = withVariablesArguments.get(i);

      doUnify(knownArg, withVariablesArg, isVariable, result);
    }
  }
Ejemplo n.º 7
0
  @NotNull
  private TypeProjection unsafeSubstitute(
      @NotNull TypeProjection originalProjection, int recursionDepth) throws SubstitutionException {
    assertRecursionDepth(recursionDepth, originalProjection, substitution);

    if (originalProjection.isStarProjection()) return originalProjection;

    // The type is within the substitution range, i.e. T or T?
    JetType type = originalProjection.getType();
    TypeProjection replacement = substitution.get(type);
    Variance originalProjectionKind = originalProjection.getProjectionKind();
    if (replacement == null
        && FlexibleTypesKt.isFlexible(type)
        && !TypeCapabilitiesKt.isCustomTypeVariable(type)) {
      Flexibility flexibility = FlexibleTypesKt.flexibility(type);
      TypeProjection substitutedLower =
          unsafeSubstitute(
              new TypeProjectionImpl(originalProjectionKind, flexibility.getLowerBound()),
              recursionDepth + 1);
      TypeProjection substitutedUpper =
          unsafeSubstitute(
              new TypeProjectionImpl(originalProjectionKind, flexibility.getUpperBound()),
              recursionDepth + 1);

      Variance substitutedProjectionKind = substitutedLower.getProjectionKind();
      assert (substitutedProjectionKind == substitutedUpper.getProjectionKind())
                  && originalProjectionKind == Variance.INVARIANT
              || originalProjectionKind == substitutedProjectionKind
          : "Unexpected substituted projection kind: "
              + substitutedProjectionKind
              + "; original: "
              + originalProjectionKind;

      JetType substitutedFlexibleType =
          DelegatingFlexibleType.create(
              substitutedLower.getType(),
              substitutedUpper.getType(),
              flexibility.getExtraCapabilities());
      return new TypeProjectionImpl(substitutedProjectionKind, substitutedFlexibleType);
    }

    if (KotlinBuiltIns.isNothing(type) || type.isError()) return originalProjection;

    if (replacement != null) {
      VarianceConflictType varianceConflict =
          conflictType(originalProjectionKind, replacement.getProjectionKind());

      // Captured type might be substituted in an opposite projection:
      // out 'Captured (in Int)' = out Int
      // in 'Captured (out Int)' = in Int
      boolean allowVarianceConflict = CapturedTypeConstructorKt.isCaptured(type);
      if (!allowVarianceConflict) {
        //noinspection EnumSwitchStatementWhichMissesCases
        switch (varianceConflict) {
          case OUT_IN_IN_POSITION:
            throw new SubstitutionException("Out-projection in in-position");
          case IN_IN_OUT_POSITION:
            // todo use the right type parameter variance and upper bound
            return new TypeProjectionImpl(
                Variance.OUT_VARIANCE, type.getConstructor().getBuiltIns().getNullableAnyType());
        }
      }
      JetType substitutedType;
      CustomTypeVariable typeVariable = TypeCapabilitiesKt.getCustomTypeVariable(type);
      if (replacement.isStarProjection()) {
        return replacement;
      } else if (typeVariable != null) {
        substitutedType = typeVariable.substitutionResult(replacement.getType());
      } else {
        // this is a simple type T or T?: if it's T, we should just take replacement, if T? - we
        // make replacement nullable
        substitutedType =
            TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
      }

      // substitutionType.annotations = replacement.annotations ++ type.annotations
      if (!type.getAnnotations().isEmpty()) {
        Annotations typeAnnotations = filterOutUnsafeVariance(type.getAnnotations());
        substitutedType =
            TypeUtilsKt.replaceAnnotations(
                substitutedType,
                new CompositeAnnotations(substitutedType.getAnnotations(), typeAnnotations));
      }

      Variance resultingProjectionKind =
          varianceConflict == VarianceConflictType.NO_CONFLICT
              ? combine(originalProjectionKind, replacement.getProjectionKind())
              : originalProjectionKind;
      return new TypeProjectionImpl(resultingProjectionKind, substitutedType);
    }
    // The type is not within the substitution range, i.e. Foo, Bar<T> etc.
    return substituteCompoundType(originalProjection, recursionDepth);
  }
Ejemplo n.º 8
0
  public static boolean canHaveSubtypes(KotlinTypeChecker typeChecker, @NotNull KotlinType type) {
    if (type.isMarkedNullable()) {
      return true;
    }
    if (!type.getConstructor().isFinal()) {
      return true;
    }

    List<TypeParameterDescriptor> parameters = type.getConstructor().getParameters();
    List<TypeProjection> arguments = type.getArguments();
    for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
      TypeParameterDescriptor parameterDescriptor = parameters.get(i);
      TypeProjection typeProjection = arguments.get(i);
      if (typeProjection.isStarProjection()) return true;

      Variance projectionKind = typeProjection.getProjectionKind();
      KotlinType argument = typeProjection.getType();

      switch (parameterDescriptor.getVariance()) {
        case INVARIANT:
          switch (projectionKind) {
            case INVARIANT:
              if (lowerThanBound(typeChecker, argument, parameterDescriptor)
                  || canHaveSubtypes(typeChecker, argument)) {
                return true;
              }
              break;
            case IN_VARIANCE:
              if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
                return true;
              }
              break;
            case OUT_VARIANCE:
              if (canHaveSubtypes(typeChecker, argument)) {
                return true;
              }
              break;
          }
          break;
        case IN_VARIANCE:
          if (projectionKind != Variance.OUT_VARIANCE) {
            if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
              return true;
            }
          } else {
            if (canHaveSubtypes(typeChecker, argument)) {
              return true;
            }
          }
          break;
        case OUT_VARIANCE:
          if (projectionKind != Variance.IN_VARIANCE) {
            if (canHaveSubtypes(typeChecker, argument)) {
              return true;
            }
          } else {
            if (lowerThanBound(typeChecker, argument, parameterDescriptor)) {
              return true;
            }
          }
          break;
      }
    }
    return false;
  }