|  | // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import 'package:analyzer/dart/element/element.dart'; | 
|  | import 'package:analyzer/dart/element/type.dart'; | 
|  | import 'package:analyzer/src/dart/element/element.dart'; | 
|  |  | 
|  | /// The variance of a type parameter `X` in a type `T`. | 
|  | class Variance { | 
|  | /// Used when `X` does not occur free in `T`. | 
|  | static const Variance unrelated = Variance._(0); | 
|  |  | 
|  | /// Used when `X` occurs free in `T`, and `U <: V` implies `[U/X]T <: [V/X]T`. | 
|  | static const Variance covariant = Variance._(1); | 
|  |  | 
|  | /// Used when `X` occurs free in `T`, and `U <: V` implies `[V/X]T <: [U/X]T`. | 
|  | static const Variance contravariant = Variance._(2); | 
|  |  | 
|  | /// Used when there exists a pair `U` and `V` such that `U <: V`, but | 
|  | /// `[U/X]T` and `[V/X]T` are incomparable. | 
|  | static const Variance invariant = Variance._(3); | 
|  |  | 
|  | /// The encoding associated with the variance. | 
|  | final int _encoding; | 
|  |  | 
|  | /// Computes the variance of the [typeParameter] in the [type]. | 
|  | factory Variance(TypeParameterElement typeParameter, DartType type) { | 
|  | if (type is TypeParameterType) { | 
|  | if (type.element == typeParameter) { | 
|  | return covariant; | 
|  | } else { | 
|  | return unrelated; | 
|  | } | 
|  | } else if (type is InterfaceType) { | 
|  | var result = unrelated; | 
|  | for (int i = 0; i < type.typeArguments.length; ++i) { | 
|  | var argument = type.typeArguments[i]; | 
|  | var parameter = type.element.typeParameters[i]; | 
|  |  | 
|  | // TODO (kallentu) : Clean up TypeParameterElementImpl casting once | 
|  | // variance is added to the interface. | 
|  | var parameterVariance = | 
|  | (parameter as TypeParameterElementImpl).variance; | 
|  | result = result | 
|  | .meet(parameterVariance.combine(Variance(typeParameter, argument))); | 
|  | } | 
|  | return result; | 
|  | } else if (type is FunctionType) { | 
|  | var result = Variance(typeParameter, type.returnType); | 
|  |  | 
|  | for (var parameter in type.typeFormals) { | 
|  | // If [parameter] is referenced in the bound at all, it makes the | 
|  | // variance of [parameter] in the entire type invariant.  The invocation | 
|  | // of [computeVariance] below is made to simply figure out if [variable] | 
|  | // occurs in the bound. | 
|  | var bound = parameter.bound; | 
|  | if (bound != null && !Variance(typeParameter, bound).isUnrelated) { | 
|  | result = invariant; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (var parameter in type.parameters) { | 
|  | result = result.meet( | 
|  | contravariant.combine( | 
|  | Variance(typeParameter, parameter.type), | 
|  | ), | 
|  | ); | 
|  | } | 
|  | return result; | 
|  | } | 
|  | return unrelated; | 
|  | } | 
|  |  | 
|  | /// Return the variance associated with the string representation of variance. | 
|  | factory Variance.fromKeywordString(String varianceString) { | 
|  | if (varianceString == "in") { | 
|  | return contravariant; | 
|  | } else if (varianceString == "inout") { | 
|  | return invariant; | 
|  | } else if (varianceString == "out") { | 
|  | return covariant; | 
|  | } else if (varianceString == "unrelated") { | 
|  | return unrelated; | 
|  | } | 
|  | throw ArgumentError('Invalid keyword string for variance: $varianceString'); | 
|  | } | 
|  |  | 
|  | /// Initialize a newly created variance to have the given [encoding]. | 
|  | const Variance._(this._encoding); | 
|  |  | 
|  | /// Return the variance with the given [encoding]. | 
|  | factory Variance._fromEncoding(int encoding) { | 
|  | switch (encoding) { | 
|  | case 0: | 
|  | return unrelated; | 
|  | case 1: | 
|  | return covariant; | 
|  | case 2: | 
|  | return contravariant; | 
|  | case 3: | 
|  | return invariant; | 
|  | } | 
|  | throw ArgumentError('Invalid encoding for variance: $encoding'); | 
|  | } | 
|  |  | 
|  | /// Return `true` if this represents the case when `X` occurs free in `T`, and | 
|  | /// `U <: V` implies `[V/X]T <: [U/X]T`. | 
|  | bool get isContravariant => this == contravariant; | 
|  |  | 
|  | /// Return `true` if this represents the case when `X` occurs free in `T`, and | 
|  | /// `U <: V` implies `[U/X]T <: [V/X]T`. | 
|  | bool get isCovariant => this == covariant; | 
|  |  | 
|  | /// Return `true` if this represents the case when there exists a pair `U` and | 
|  | /// `V` such that `U <: V`, but `[U/X]T` and `[V/X]T` are incomparable. | 
|  | bool get isInvariant => this == invariant; | 
|  |  | 
|  | /// Return `true` if this represents the case when `X` does not occur free in | 
|  | /// `T`. | 
|  | bool get isUnrelated => this == unrelated; | 
|  |  | 
|  | /// Combines variances of `X` in `T` and `Y` in `S` into variance of `X` in | 
|  | /// `[Y/T]S`. | 
|  | /// | 
|  | /// Consider the following examples: | 
|  | /// | 
|  | /// * variance of `X` in `Function(X)` is contravariant, variance of `Y` | 
|  | /// in `List<Y>` is covariant, so variance of `X` in `List<Function(X)>` is | 
|  | /// contravariant; | 
|  | /// | 
|  | /// * variance of `X` in `List<X>` is covariant, variance of `Y` in | 
|  | /// `Function(Y)` is contravariant, so variance of `X` in | 
|  | /// `Function(List<X>)` is contravariant; | 
|  | /// | 
|  | /// * variance of `X` in `Function(X)` is contravariant, variance of `Y` in | 
|  | /// `Function(Y)` is contravariant, so variance of `X` in | 
|  | /// `Function(Function(X))` is covariant; | 
|  | /// | 
|  | /// * let the following be declared: | 
|  | /// | 
|  | ///     typedef F<Z> = Function(); | 
|  | /// | 
|  | /// then variance of `X` in `F<X>` is unrelated, variance of `Y` in | 
|  | /// `List<Y>` is covariant, so variance of `X` in `List<F<X>>` is | 
|  | /// unrelated; | 
|  | /// | 
|  | /// * let the following be declared: | 
|  | /// | 
|  | ///     typedef G<Z> = Z Function(Z); | 
|  | /// | 
|  | /// then variance of `X` in `List<X>` is covariant, variance of `Y` in | 
|  | /// `G<Y>` is invariant, so variance of `X` in `G<List<X>>` is invariant. | 
|  | Variance combine(Variance other) { | 
|  | if (isUnrelated || other.isUnrelated) return unrelated; | 
|  | if (isInvariant || other.isInvariant) return invariant; | 
|  | return this == other ? covariant : contravariant; | 
|  | } | 
|  |  | 
|  | /// Returns true if this variance is greater than (above) or equal to the | 
|  | /// [other] variance in the partial order induced by the variance lattice. | 
|  | /// | 
|  | ///       unrelated | 
|  | /// covariant   contravariant | 
|  | ///       invariant | 
|  | bool greaterThanOrEqual(Variance other) { | 
|  | if (isUnrelated) { | 
|  | return true; | 
|  | } else if (isCovariant) { | 
|  | return other.isCovariant || other.isInvariant; | 
|  | } else if (isContravariant) { | 
|  | return other.isContravariant || other.isInvariant; | 
|  | } else { | 
|  | assert(isInvariant); | 
|  | return other.isInvariant; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Variance values form a lattice where unrelated is the top, invariant | 
|  | /// is the bottom, and covariant and contravariant are incomparable. | 
|  | /// [meet] calculates the meet of two elements of such lattice.  It can be | 
|  | /// used, for example, to calculate the variance of a typedef type parameter | 
|  | /// if it's encountered on the RHS of the typedef multiple times. | 
|  | /// | 
|  | ///       unrelated | 
|  | /// covariant   contravariant | 
|  | ///       invariant | 
|  | Variance meet(Variance other) => | 
|  | Variance._fromEncoding(_encoding | other._encoding); | 
|  |  | 
|  | /// Returns the associated keyword lexeme. | 
|  | String toKeywordString() { | 
|  | switch (this) { | 
|  | case contravariant: | 
|  | return 'in'; | 
|  | case invariant: | 
|  | return 'inout'; | 
|  | case covariant: | 
|  | return 'out'; | 
|  | case unrelated: | 
|  | return ''; | 
|  | default: | 
|  | throw ArgumentError( | 
|  | 'Missing keyword lexeme representation for variance: $this'); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | String toString() { | 
|  | switch (this) { | 
|  | case contravariant: | 
|  | return 'contravariant'; | 
|  | case invariant: | 
|  | return 'invariant'; | 
|  | case covariant: | 
|  | return 'covariant'; | 
|  | case unrelated: | 
|  | return 'unrelated'; | 
|  | default: | 
|  | throw UnimplementedError('encoding: $_encoding'); | 
|  | } | 
|  | } | 
|  | } |