public CodeGenerationTypeParameterSymbol( INamedTypeSymbol containingType, ImmutableArray <AttributeData> attributes, VarianceKind varianceKind, string name, NullableAnnotation nullableAnnotation, ImmutableArray <ITypeSymbol> constraintTypes, bool hasConstructorConstraint, bool hasReferenceConstraint, bool hasValueConstraint, bool hasUnmanagedConstraint, bool hasNotNullConstraint, int ordinal ) : base( containingType?.ContainingAssembly, containingType, attributes, Accessibility.NotApplicable, default, name, SpecialType.None, nullableAnnotation ) { this.Variance = varianceKind; this.ConstraintTypes = constraintTypes; this.Ordinal = ordinal; this.HasConstructorConstraint = hasConstructorConstraint; this.HasReferenceTypeConstraint = hasReferenceConstraint; this.HasValueTypeConstraint = hasValueConstraint; this.HasUnmanagedTypeConstraint = hasUnmanagedConstraint; this.HasNotNullConstraint = hasNotNullConstraint; }
internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance) { Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); TypeWithAnnotations elementType = ElementTypeWithAnnotations.MergeNullability(((ArrayTypeSymbol)other).ElementTypeWithAnnotations, variance); return(WithElementType(elementType)); }
internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance, out bool hadNullabilityMismatch) { Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); TypeSymbolWithAnnotations pointedAtType = PointedAtType.MergeNullability(((PointerTypeSymbol)other).PointedAtType, VarianceKind.None, out hadNullabilityMismatch); return(WithPointedAtType(pointedAtType)); }
private static bool CanTypeParameterBeVariant( ITypeParameterSymbol parameter, VarianceKind variance, ITypeSymbol type, bool requireOutputSafety, bool requireInputSafety, ISymbol context) { switch (type.Kind) { case SymbolKind.TypeParameter: var typeParam = (ITypeParameterSymbol)type; if (!typeParam.Equals(parameter)) { return(true); } return(!((requireInputSafety && requireOutputSafety && variance != VarianceKind.None) || (requireOutputSafety && variance == VarianceKind.In) || (requireInputSafety && variance == VarianceKind.Out))); case SymbolKind.ArrayType: return(CanTypeParameterBeVariant(parameter, variance, ((IArrayTypeSymbol)type).ElementType, requireOutputSafety, requireInputSafety, context)); case SymbolKind.ErrorType: case SymbolKind.NamedType: return(CanTypeParameterBeVariant(parameter, variance, (INamedTypeSymbol)type, requireOutputSafety, requireInputSafety, context)); default: return(true); } }
internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance) { Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); TypeWithAnnotations pointedAtType = PointedAtTypeWithAnnotations.MergeEquivalentTypes(((PointerTypeSymbol)other).PointedAtTypeWithAnnotations, VarianceKind.None); return(WithPointedAtType(pointedAtType)); }
/// <summary> /// Creates a type parameter symbol that can be used to describe a type parameter declaration. /// </summary> public static ITypeParameterSymbol CreateTypeParameter( ImmutableArray <AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray <ITypeSymbol> constraintTypes, bool hasConstructorConstraint = false, bool hasReferenceConstraint = false, bool hasValueConstraint = false, int ordinal = 0) { return(new CodeGenerationTypeParameterSymbol(null, attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal)); }
internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance) { Debug.Assert( this.Equals( other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes ) ); return(this); }
internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance) { Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); var otherType = (FunctionTypeSymbol)other; var delegateType = (NamedTypeSymbol)_delegateType.MergeEquivalentTypes(otherType._delegateType, variance); return((object)_delegateType == delegateType ? this : otherType.WithDelegateType(delegateType)); }
public SourceTypeParameterSymbol( SourceNamedTypeSymbol owner, string name, int ordinal, VarianceKind varianceKind, ImmutableArray <Location> locations, ImmutableArray <SyntaxReference> syntaxRefs ) : base(name, ordinal, locations, syntaxRefs) { _owner = owner; _varianceKind = varianceKind; }
internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance) { Debug.Assert(this.Equals(other, TypeCompareKind.AllIgnoreOptions)); var mergedSignature = Signature.MergeEquivalentTypes(((FunctionPointerTypeSymbol)other).Signature, variance); if ((object)mergedSignature != Signature) { return(new FunctionPointerTypeSymbol(mergedSignature)); } return(this); }
private static bool CheckTypeParameter(ITypeParameterSymbol typeParameter, VarianceKind variance, INamedTypeSymbol interfaceType) { if (typeParameter.Variance != VarianceKind.None) { return(false); } foreach (INamedTypeSymbol baseInterface in interfaceType.AllInterfaces) { var canBeVariant = CanTypeParameterBeVariant( typeParameter, variance, baseInterface, true, false, baseInterface); if (!canBeVariant) { return(false); } } foreach (ISymbol member in interfaceType.GetMembers()) { bool canBeVariant; switch (member.Kind) { case SymbolKind.Method: canBeVariant = CheckTypeParameterInMethod(typeParameter, variance, (IMethodSymbol)member); if (!canBeVariant) { return(false); } break; case SymbolKind.Event: canBeVariant = CheckTypeParameterInEvent(typeParameter, variance, (IEventSymbol)member); if (!canBeVariant) { return(false); } break; default: break; } } return(true); }
public static RefKindInfo ConvertToStructure(this VarianceKind variance) { switch (variance) { case VarianceKind.Out: return(RefKindInfo.Out); case VarianceKind.In: return(RefKindInfo.In); default: return(RefKindInfo.None); } }
public CodeGenerationTypeParameterSymbol( INamedTypeSymbol containingType, IList <AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray <ITypeSymbol> constraintTypes, bool hasConstructorConstraint, bool hasReferenceConstraint, bool hasValueConstraint, int ordinal) : base(containingType, attributes, Accessibility.NotApplicable, default(DeclarationModifiers), name, SpecialType.None) { this.Variance = varianceKind; this.ConstraintTypes = constraintTypes; this.Ordinal = ordinal; this.HasConstructorConstraint = hasConstructorConstraint; this.HasReferenceTypeConstraint = hasReferenceConstraint; this.HasValueTypeConstraint = hasValueConstraint; }
private static void ReportIssue(ITypeParameterSymbol typeParameter, VarianceKind variance, SyntaxNodeAnalysisContext context) { if (!typeParameter.DeclaringSyntaxReferences.Any()) { return; } var location = typeParameter.DeclaringSyntaxReferences.First().GetSyntax().GetLocation(); if (variance == VarianceKind.In) { context.ReportDiagnosticWhenActive(Diagnostic.Create(rule, location, "in", typeParameter.Name, "contravariant")); return; } if (variance == VarianceKind.Out) { context.ReportDiagnosticWhenActive(Diagnostic.Create(rule, location, "out", typeParameter.Name, "covariant")); } }
private static bool CheckTypeParameter(ITypeParameterSymbol typeParameter, VarianceKind variance, ITypeSymbol returnType, ImmutableArray <IParameterSymbol> parameters) { var canBe = CheckTypeParameterContraintsInSymbol(typeParameter, variance); if (!canBe) { return(false); } canBe = CanTypeParameterBeVariant(typeParameter, variance, returnType, true, false); if (!canBe) { return(false); } canBe = CheckTypeParameterInParameters(typeParameter, variance, parameters); return(canBe); }
public CodeGenerationTypeParameterSymbol( INamedTypeSymbol containingType, IList<AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray<ITypeSymbol> constraintTypes, bool hasConstructorConstraint, bool hasReferenceConstraint, bool hasValueConstraint, int ordinal) { instance = Activator.CreateInstance (typeInfo, new object[] { containingType, attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal }); }
public CodeGenerationTypeParameterSymbol( INamedTypeSymbol containingType, IList <AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray <ITypeSymbol> constraintTypes, bool hasConstructorConstraint, bool hasReferenceConstraint, bool hasValueConstraint, int ordinal) { instance = Activator.CreateInstance(typeInfo, new object[] { containingType, attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal }); }
/// <summary> /// Merges nullability. /// </summary> public static NullableAnnotation MergeNullableAnnotation(this NullableAnnotation a, NullableAnnotation b, VarianceKind variance) { return(variance switch { VarianceKind.In => a.Meet(b), VarianceKind.Out => a.Join(b), VarianceKind.None => a.EnsureCompatible(b), _ => throw ExceptionUtilities.UnexpectedValue(variance) });
private static bool CanTypeParameterBeVariant( ITypeParameterSymbol parameter, VarianceKind variance, INamedTypeSymbol namedType, bool requireOutputSafety, bool requireInputSafety, ISymbol context) { switch (namedType.TypeKind) { case TypeKind.Class: case TypeKind.Struct: case TypeKind.Enum: case TypeKind.Interface: case TypeKind.Delegate: case TypeKind.Error: break; default: return true; } var currentNamedType = namedType; while (currentNamedType != null) { for (int i = 0; i < currentNamedType.Arity; i++) { var typeParam = currentNamedType.TypeParameters[i]; var typeArg = currentNamedType.TypeArguments[i]; if (!typeArg.Equals(parameter)) { return false; } var requireOut = false; var requireIn = false; switch (typeParam.Variance) { case VarianceKind.Out: requireOut = requireOutputSafety; requireIn = requireInputSafety; break; case VarianceKind.In: requireOut = requireInputSafety; requireIn = requireOutputSafety; break; case VarianceKind.None: requireIn = true; requireOut = true; break; default: throw new NotSupportedException(); } if (!CanTypeParameterBeVariant(parameter, variance, typeArg, requireOut, requireIn, context)) { return false; } } currentNamedType = currentNamedType.ContainingType; } return true; }
private static bool CanTypeParameterBeVariant( ITypeParameterSymbol parameter, VarianceKind variance, ITypeSymbol type, bool requireOutputSafety, bool requireInputSafety, ISymbol context) { switch (type.Kind) { case SymbolKind.TypeParameter: var typeParam = (ITypeParameterSymbol)type; if (!typeParam.Equals(parameter)) { return true; } return !((requireInputSafety && requireOutputSafety && variance != VarianceKind.None) || (requireOutputSafety && variance == VarianceKind.In) || (requireInputSafety && variance == VarianceKind.Out)); case SymbolKind.ArrayType: return CanTypeParameterBeVariant(parameter, variance, ((IArrayTypeSymbol)type).ElementType, requireOutputSafety, requireInputSafety, context); case SymbolKind.ErrorType: case SymbolKind.NamedType: return CanTypeParameterBeVariant(parameter, variance, (INamedTypeSymbol)type, requireOutputSafety, requireInputSafety, context); default: return true; } }
private static bool CheckTypeParameterContraintsInSymbol(ITypeParameterSymbol typeParameter, VarianceKind variance, ISymbol context) { foreach (ITypeSymbol constraintType in typeParameter.ConstraintTypes) { var canBe = CanTypeParameterBeVariant( typeParameter, variance, constraintType, false, true, context); if (!canBe) { return false; } } return true; }
internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance, out bool hadNullabilityMismatch) { throw new NotSupportedException(); }
public SourceTypeParameterSymbol(SourceNamedTypeSymbol owner, string name, int ordinal, VarianceKind varianceKind, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs) : base(name, ordinal, locations, syntaxRefs) { this.owner = owner; this.varianceKind = varianceKind; }
/// <summary> /// Creates a type parameter symbol that can be used to describe a type parameter declaration. /// </summary> public static ITypeParameterSymbol CreateTypeParameter(IList<AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray<ITypeSymbol> constraintTypes, bool hasConstructorConstraint = false, bool hasReferenceConstraint = false, bool hasValueConstraint = false, int ordinal = 0) { return new CodeGenerationTypeParameterSymbol(null, attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal); }
private static bool CheckTypeParameterInEvent(ITypeParameterSymbol typeParameter, VarianceKind variance, IEventSymbol @event) { return(CanTypeParameterBeVariant( typeParameter, variance, @event.Type, false, true)); }
public static ITypeParameterSymbol CreateTypeParameter(IList<AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray<ITypeSymbol> constraintTypes, bool hasConstructorConstraint = false, bool hasReferenceConstraint = false, bool hasValueConstraint = false, int ordinal = 0) { try { return (ITypeParameterSymbol)createTypeParameterMethod.Invoke (null, new object[] { attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal}); } catch (TargetInvocationException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return null; } }
void AppendVariance (StringBuilder sb, VarianceKind variance) { if (variance == VarianceKind.In) { sb.Append (Highlight ("in ", colorStyle.KeywordParameter)); } else if (variance == VarianceKind.Out) { sb.Append (Highlight ("out ", colorStyle.KeywordParameter)); } }
public static ITypeParameterSymbol CreateTypeParameter(IList <AttributeData> attributes, VarianceKind varianceKind, string name, ImmutableArray <ITypeSymbol> constraintTypes, bool hasConstructorConstraint = false, bool hasReferenceConstraint = false, bool hasValueConstraint = false, int ordinal = 0) { try { return((ITypeParameterSymbol)createTypeParameterMethod.Invoke(null, new object[] { attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal })); } catch (TargetInvocationException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return(null); } }
internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance, out bool hadNullabilityMismatch) { Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); hadNullabilityMismatch = false; return(this); }
/// <summary> /// Merges nullability. /// </summary> public static NullableAnnotation MergeNullableAnnotation(this NullableAnnotation a, NullableAnnotation b, VarianceKind variance) => variance switch {
public SourceTypeParameterSymbol(SourceNamedTypeSymbol owner, string name, int ordinal, VarianceKind varianceKind, NullabilityPreservationKind preservationKind, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs) : base(name, ordinal, preservationKind, locations, syntaxRefs) { _owner = owner; _varianceKind = varianceKind; }
private static bool CheckTypeParameterContraintsInSymbol(ITypeParameterSymbol typeParameter, VarianceKind variance) { foreach (var constraintType in typeParameter.ConstraintTypes) { var canBe = CanTypeParameterBeVariant( typeParameter, variance, constraintType, false, true); if (!canBe) { return(false); } } return(true); }
internal void CreateTypeEdge(TypeWithNode source, TypeWithNode target, TypeSubstitution?targetSubstitution, VarianceKind variance, EdgeLabel label) { #if DEBUG if (source.FlowLabel != null) { label = new EdgeLabel($"{label}\n{source.FlowLabel}"); } #endif if (targetSubstitution != null && target.Type is ITypeParameterSymbol tp) { // If calling `void SomeCall<T>(T x);` as `SomeCall<string>(null)`, then // we need either `x: T?` or `T = string?`: // (source is nullable) implies (target is nullable || substitutedTarget is nullable) // We can't represent such a choice in the graph, so we always substitute and prefer `string?`. // However, if the variance causes us to create edges the other way around // (e.g. an override-edge for `override void SomeCall(string x)`), we have: // (target is nullable || substitutedTarget is nullable) implies (source is nullable) // This can be represented by using two edges. if (variance == VarianceKind.In || variance == VarianceKind.None) { CreateEdge(target.Node, source.Node, label); } // Perform the substitution: target = targetSubstitution.Value[tp.TypeParameterKind, tp.FullOrdinal()]; targetSubstitution = null; } Debug.Assert(source.Type?.TypeKind == target.Type?.TypeKind, "Type kinds do not match"); if (source.Type is INamedTypeSymbol namedType) { if (!SymbolEqualityComparer.Default.Equals(source.Type?.OriginalDefinition, target.Type?.OriginalDefinition)) { throw new InvalidOperationException($"Types don't match: {source.Type} vs. {target.Type}"); } var namedTypeTypeParameters = namedType.FullTypeParameters().ToList(); Debug.Assert(source.TypeArguments.Count == namedTypeTypeParameters.Count); Debug.Assert(target.TypeArguments.Count == namedTypeTypeParameters.Count); for (int i = 0; i < namedTypeTypeParameters.Count; i++) { var sourceArg = source.TypeArguments[i]; var targetArg = target.TypeArguments[i]; var combinedVariance = (variance, namedTypeTypeParameters[i].Variance).Combine();
private static bool CheckTypeParameter(ITypeParameterSymbol typeParameter, VarianceKind variance, INamedTypeSymbol interfaceType) { if (typeParameter.Variance != VarianceKind.None) { return false; } foreach (INamedTypeSymbol baseInterface in interfaceType.AllInterfaces) { var canBeVariant = CanTypeParameterBeVariant( typeParameter, variance, baseInterface, true, false, baseInterface); if (!canBeVariant) { return false; } } foreach (ISymbol member in interfaceType.GetMembers()) { var canBeVariant = false; switch (member.Kind) { case SymbolKind.Method: canBeVariant = CheckTypeParameterInMethod(typeParameter, variance, (IMethodSymbol)member); if (!canBeVariant) { return false; } break; case SymbolKind.Event: canBeVariant = CheckTypeParameterInEvent(typeParameter, variance, (IEventSymbol)member); if (!canBeVariant) { return false; } break; default: break; } } return true; }
private static void ReportIssue(ITypeParameterSymbol typeParameter, VarianceKind variance, SyntaxNodeAnalysisContext context) { if (!typeParameter.DeclaringSyntaxReferences.Any()) { return; } var location = typeParameter.DeclaringSyntaxReferences.First().GetSyntax().GetLocation(); if (variance == VarianceKind.In) { context.ReportDiagnostic(Diagnostic.Create(Rule, location, "in", typeParameter.Name, "contravariant")); return; } if (variance == VarianceKind.Out) { context.ReportDiagnostic(Diagnostic.Create(Rule, location, "out", typeParameter.Name, "covariant")); return; } }
private static bool CheckTypeParameterInMethod(ITypeParameterSymbol typeParameter, VarianceKind variance, IMethodSymbol method) { var canBe = CheckTypeParameterContraintsInSymbol(typeParameter, variance); if (!canBe) { return(false); } canBe = CanTypeParameterBeVariant( typeParameter, variance, method.ReturnType, true, false); if (!canBe) { return(false); } return(CheckTypeParameterInParameters(typeParameter, variance, method.Parameters)); }
private static bool CheckTypeParameterInMethod(ITypeParameterSymbol typeParameter, VarianceKind variance, IMethodSymbol method) { var canBe = CheckTypeParameterContraintsInSymbol(typeParameter, variance, method); if (!canBe) { return false; } canBe = CanTypeParameterBeVariant( typeParameter, variance, method.ReturnType, true, false, method); if (!canBe) { return false; } return CheckTypeParameterInParameters(typeParameter, variance, method.Parameters, method); }
private static bool CheckTypeParameterInParameters(ITypeParameterSymbol typeParameter, VarianceKind variance, ImmutableArray <IParameterSymbol> parameters) { foreach (var param in parameters) { var canBe = CanTypeParameterBeVariant( typeParameter, variance, param.Type, param.RefKind != RefKind.None, true); if (!canBe) { return(false); } } return(true); }
private static bool CheckTypeParameterInEvent(ITypeParameterSymbol typeParameter, VarianceKind variance, IEventSymbol @event) { return CanTypeParameterBeVariant( typeParameter, variance, @event.Type, false, true, @event); }
private static bool CanTypeParameterBeVariant( ITypeParameterSymbol parameter, VarianceKind variance, INamedTypeSymbol namedType, bool requireOutputSafety, bool requireInputSafety) { switch (namedType.TypeKind) { case TypeKind.Class: case TypeKind.Struct: case TypeKind.Enum: case TypeKind.Interface: case TypeKind.Delegate: case TypeKind.Error: break; default: return(true); } if (namedType.IsTupleType()) { return(false); } var currentNamedType = namedType; while (currentNamedType != null) { for (var i = 0; i < currentNamedType.Arity; i++) { var typeParam = currentNamedType.TypeParameters[i]; var typeArg = currentNamedType.TypeArguments[i]; if (!typeArg.Equals(parameter)) { return(false); } bool requireOut; bool requireIn; switch (typeParam.Variance) { case VarianceKind.Out: requireOut = requireOutputSafety; requireIn = requireInputSafety; break; case VarianceKind.In: requireOut = requireInputSafety; requireIn = requireOutputSafety; break; case VarianceKind.None: requireIn = true; requireOut = true; break; default: throw new NotSupportedException(); } if (!CanTypeParameterBeVariant(parameter, variance, typeArg, requireOut, requireIn)) { return(false); } } currentNamedType = currentNamedType.ContainingType; } return(true); }
private static bool CheckTypeParameterInParameters(ITypeParameterSymbol typeParameter, VarianceKind variance, ImmutableArray<IParameterSymbol> parameters, ISymbol context) { foreach (IParameterSymbol param in parameters) { var canBe = CanTypeParameterBeVariant( typeParameter, variance, param.Type, param.RefKind != RefKind.None, true, context); if (!canBe) { return false; } } return true; }
private static bool CheckTypeParameter(ITypeParameterSymbol typeParameter, VarianceKind variance, INamedTypeSymbol delegateType, ITypeSymbol returnType, ImmutableArray<IParameterSymbol> parameters) { var canBe = CheckTypeParameterContraintsInSymbol(typeParameter, variance, delegateType); if (!canBe) { return false; } canBe = CanTypeParameterBeVariant(typeParameter, variance, returnType, true, false, delegateType); if (!canBe) { return false; } canBe = CheckTypeParameterInParameters(typeParameter, variance, parameters, delegateType); return canBe; }
internal NullabilityEdge?CreateTypeEdge(TypeWithNode source, TypeWithNode target, TypeSubstitution?targetSubstitution, VarianceKind variance) { if (targetSubstitution != null && target.Type is ITypeParameterSymbol tp) { // Perform the substitution: target = targetSubstitution.Value[tp.TypeParameterKind, tp.FullOrdinal()]; targetSubstitution = null; } Debug.Assert(source.Type?.TypeKind == target.Type?.TypeKind); if (source.Type is INamedTypeSymbol namedType) { if (!SymbolEqualityComparer.Default.Equals(source.Type?.OriginalDefinition, target.Type?.OriginalDefinition)) { throw new InvalidOperationException($"Types don't match: {source.Type} vs. {target.Type}"); } var namedTypeTypeParameters = namedType.FullTypeParameters().ToList(); Debug.Assert(source.TypeArguments.Count == namedTypeTypeParameters.Count); Debug.Assert(target.TypeArguments.Count == namedTypeTypeParameters.Count); for (int i = 0; i < namedTypeTypeParameters.Count; i++) { tp = namedTypeTypeParameters[i]; var sourceArg = source.TypeArguments[i]; var targetArg = target.TypeArguments[i]; var combinedVariance = (variance, tp.Variance).Combine(); CreateTypeEdge(sourceArg, targetArg, targetSubstitution, combinedVariance); } } else if (source.Type is IArrayTypeSymbol || source.Type is IPointerTypeSymbol) { CreateTypeEdge(source.TypeArguments.Single(), target.TypeArguments.Single(), targetSubstitution, variance); } NullabilityEdge?edge = null; if (variance == VarianceKind.In || variance == VarianceKind.None) { edge = CreateEdge(target.Node, source.Node); } if (variance == VarianceKind.Out || variance == VarianceKind.None) { edge = CreateEdge(source.Node, target.Node); } return(edge); }