private static ConversionKind ClassifyNullLiteralConversion(BoundExpression source, TypeSymbol destination) { Debug.Assert((object)source != null); Debug.Assert((object)destination != null); if (!source.IsLiteralNull()) { return ConversionKind.NoConversion; } // SPEC: An implicit conversion exists from the null literal to any nullable type. if (destination.IsNullableType()) { // The spec defines a "null literal conversion" specifically as a conversion from // null to nullable type. return ConversionKind.NullLiteral; } // SPEC: An implicit conversion exists from the null literal to any reference type. // SPEC: An implicit conversion exists from the null literal to type parameter T, // SPEC: provided T is known to be a reference type. [...] The conversion [is] classified // SPEC: as implicit reference conversion. if (destination.IsReferenceType) { if (destination.GetEffectiveNullability() != EffectiveNullability.Nullable) return ConversionKind.NoConversion; return ConversionKind.ImplicitReference; } // SPEC: The set of implicit conversions is extended to include... // SPEC: ... from the null literal to any pointer type. if (destination is PointerTypeSymbol) { return ConversionKind.NullToPointer; } return ConversionKind.NoConversion; }
protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharpSyntaxNode syntax, Conversion conversion, BoundExpression expression, TypeSymbol targetType) { Debug.Assert(expression != null); Debug.Assert((object)targetType != null); if (targetType.TypeKind == TypeKind.Error) { return; } if (expression.Kind == BoundKind.BadExpression) { return; } if (expression.Kind == BoundKind.UnboundLambda) { GenerateAnonymousFunctionConversionError(diagnostics, syntax, (UnboundLambda)expression, targetType); return; } var sourceType = expression.Type; if ((object)sourceType != null) { GenerateImplicitConversionError(diagnostics, this.Compilation, syntax, conversion, sourceType, targetType, expression.ConstantValue); return; } if (expression.IsLiteralNull()) { if (targetType.GetEffectiveNullability() != EffectiveNullability.Nullable) { Error(diagnostics, ErrorCode.ERR_NullAssignedToNonNullableType, syntax, targetType); return; } if (targetType.TypeKind == TypeKind.TypeParameter) { Error(diagnostics, ErrorCode.ERR_TypeVarCantBeNull, syntax, targetType); return; } if (targetType.IsValueType) { Error(diagnostics, ErrorCode.ERR_ValueCantBeNull, syntax, targetType); return; } } if (expression.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)expression; if (!Conversions.ReportDelegateMethodGroupDiagnostics(this, methodGroup, targetType, diagnostics)) { var nodeForSquiggle = syntax; while (nodeForSquiggle.Kind() == SyntaxKind.ParenthesizedExpression) { nodeForSquiggle = ((ParenthesizedExpressionSyntax)nodeForSquiggle).Expression; } if (nodeForSquiggle.Kind() == SyntaxKind.SimpleMemberAccessExpression || nodeForSquiggle.Kind() == SyntaxKind.PointerMemberAccessExpression) { nodeForSquiggle = ((MemberAccessExpressionSyntax)nodeForSquiggle).Name; } var location = nodeForSquiggle.Location; if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, targetType, location)) { return; } Error(diagnostics, targetType.IsDelegateType() ? ErrorCode.ERR_MethDelegateMismatch : ErrorCode.ERR_MethGrpToNonDel, location, methodGroup.Name, targetType); } return; } Debug.Assert(expression.HasAnyErrors && expression.Kind != BoundKind.UnboundLambda, "Missing a case in implicit conversion error reporting"); }
/// <summary> /// Determines if the source expression is convertible to the destination type via /// any built-in or user-defined implicit conversion. /// </summary> private Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(sourceExpression != null || (object)source != null); Debug.Assert(sourceExpression == null || (object)sourceExpression.Type == (object)source); Debug.Assert((object)destination != null); //PERF: identity conversion is by far the most common implicit conversion, check for that first if ((object)source != null && HasIdentityConversion(source, destination)) { // Will this too negatively impact performance? If so, should consider actually making T!? a separate TypeSymbol and default(T!?) returns type T not T!? if (sourceExpression == null || sourceExpression.Kind != BoundKind.DefaultOperator || !destination.IsReferenceType || destination.GetEffectiveNullability() == EffectiveNullability.Nullable) return Conversion.Identity; else return Conversion.NoConversion; } Conversion conversion = ClassifyImplicitBuiltInConversionFromExpression(sourceExpression, source, destination, ref useSiteDiagnostics); if (conversion.Exists) { return conversion; } if ((object)source != null) { // Try using the short-circuit "fast-conversion" path. Conversion fastConversion = FastClassifyConversion(source, destination); if (fastConversion.Exists) { return fastConversion.IsImplicit ? fastConversion : Conversion.NoConversion; } else { conversion = ClassifyImplicitBuiltInConversionSlow(source, destination, ref useSiteDiagnostics); if (conversion.Exists) { return conversion; } } } return GetImplicitUserDefinedConversion(sourceExpression, source, destination, ref useSiteDiagnostics); }