private static bool IsVarianceUnsafe <T>( TypeSymbol type, bool requireOutputSafety, bool requireInputSafety, Symbol context, LocationProvider <T> locationProvider, T locationArg, BindingDiagnosticBag diagnostics) where T : Symbol { Debug.Assert(requireOutputSafety || requireInputSafety); // A type T is "output-unsafe" ["input-unsafe"] if one of the following holds: switch (type.Kind) { case SymbolKind.TypeParameter: // 1) T is a contravariant [covariant] type parameter TypeParameterSymbol typeParam = (TypeParameterSymbol)type; if (requireInputSafety && requireOutputSafety && typeParam.Variance != VarianceKind.None) { // This sub-case isn't mentioned in the spec, because it's not required for // the definition. It just allows us to give a better error message for // type parameters that are both output-unsafe and input-unsafe. diagnostics.AddVarianceError(typeParam, context, locationProvider, locationArg, MessageID.IDS_Invariantly); return(true); } else if (requireOutputSafety && typeParam.Variance == VarianceKind.In) { // The is output-unsafe case (1) from the spec. diagnostics.AddVarianceError(typeParam, context, locationProvider, locationArg, MessageID.IDS_Covariantly); return(true); } else if (requireInputSafety && typeParam.Variance == VarianceKind.Out) { // The is input-unsafe case (1) from the spec. diagnostics.AddVarianceError(typeParam, context, locationProvider, locationArg, MessageID.IDS_Contravariantly); return(true); } else { return(false); } case SymbolKind.ArrayType: // 2) T is an array type with an output-unsafe [input-unsafe] element type return(IsVarianceUnsafe(((ArrayTypeSymbol)type).ElementType, requireOutputSafety, requireInputSafety, context, locationProvider, locationArg, diagnostics)); case SymbolKind.ErrorType: case SymbolKind.NamedType: var namedType = (NamedTypeSymbol)type; // 3) (see IsVarianceUnsafe(NamedTypeSymbol)) return(IsVarianceUnsafe(namedType, requireOutputSafety, requireInputSafety, context, locationProvider, locationArg, diagnostics)); default: return(false); } }