protected DecisionTreeBuilder( Symbol enclosingSymbol, Conversions conversions) { this._enclosingSymbol = enclosingSymbol; this._conversions = conversions; }
internal SubsumptionDiagnosticBuilder(Symbol enclosingSymbol, Conversions conversions, BoundExpression expression) : base(enclosingSymbol, conversions) { _subsumptionTree = DecisionTree.Create(expression, expression.Type, enclosingSymbol); }
/// <remarks> /// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification. /// NOTE: If some or all of the expressions have error types, we return error type as the inference result. /// </remarks> public static TypeSymbol InferBestType(ImmutableArray<BoundExpression> exprs, Conversions conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // SPEC: 7.5.2.14 Finding the best common type of a set of expressions // SPEC: In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and // SPEC: the return types of anonymous functions with block bodies are found in this way. // SPEC: Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method: // SPEC: T M<X>(X x1 … X xm) // SPEC: with the Ei as arguments. // SPEC: More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X. // SPEC: Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions. // SPEC: If no such S exists, the expressions have no best common type. // All non-null types are candidates for best type inference. HashSet<TypeSymbol> candidateTypes = new HashSet<TypeSymbol>(); foreach (BoundExpression expr in exprs) { TypeSymbol type = expr.Type; if ((object)type != null) { if (type.IsErrorType()) { hadMultipleCandidates = false; return type; } candidateTypes.Add(type); } } hadMultipleCandidates = candidateTypes.Count > 1; // Perform best type inference on candidate types. return InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, ref useSiteDiagnostics); }
/// <remarks> /// This method implements best type inference for the conditional operator ?:. /// NOTE: If either expression is an error type, we return error type as the inference result. /// </remarks> public static TypeSymbol InferBestTypeForConditionalOperator(BoundExpression expr1, BoundExpression expr2, Conversions conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // SPEC: The second and third operands, x and y, of the ?: operator control the type of the conditional expression. // SPEC: • If x has type X and y has type Y then // SPEC: o If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression. // SPEC: o If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression. // SPEC: o Otherwise, no expression type can be determined, and a compile-time error occurs. // SPEC: • If only one of x and y has a type, and both x and y, are implicitly convertible to that type, then that is the type of the conditional expression. // SPEC: • Otherwise, no expression type can be determined, and a compile-time error occurs. // A type is a candidate if all expressions are convertible to that type. ArrayBuilder<TypeSymbol> candidateTypes = ArrayBuilder<TypeSymbol>.GetInstance(); TypeSymbol type1 = expr1.Type; if ((object)type1 != null) { if (type1.IsErrorType()) { candidateTypes.Free(); hadMultipleCandidates = false; return type1; } if (conversions.ClassifyImplicitConversionFromExpression(expr2, type1, ref useSiteDiagnostics).Exists) { candidateTypes.Add(type1); } } TypeSymbol type2 = expr2.Type; if ((object)type2 != null && type2 != type1) { if (type2.IsErrorType()) { candidateTypes.Free(); hadMultipleCandidates = false; return type2; } if (conversions.ClassifyImplicitConversionFromExpression(expr1, type2, ref useSiteDiagnostics).Exists) { candidateTypes.Add(type2); } } hadMultipleCandidates = candidateTypes.Count > 1; return InferBestType(candidateTypes.ToImmutableAndFree(), conversions, ref useSiteDiagnostics); }
protected BoundExpression ConvertCaseExpression(CSharpSyntaxNode node, BoundExpression caseExpression, Binder sectionBinder, out ConstantValue constantValueOpt, DiagnosticBag diagnostics, bool isGotoCaseExpr = false) { bool hasErrors = false; if (isGotoCaseExpr) { // SPEC VIOLATION for Dev10 COMPATIBILITY: // Dev10 compiler violates the SPEC comment below: // "if the constant-expression is not implicitly convertible (§6.1) to // the governing type of the nearest enclosing switch statement, // a compile-time error occurs" // If there is no implicit conversion from gotoCaseExpression to switchGoverningType, // but there exists an explicit conversion, Dev10 compiler generates a warning "WRN_GotoCaseShouldConvert" // instead of an error. See test "CS0469_NoImplicitConversionWarning". HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = Conversions.ClassifyConversionFromExpression(caseExpression, SwitchGoverningType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); if (!conversion.IsValid) { GenerateImplicitConversionError(diagnostics, node, conversion, caseExpression, SwitchGoverningType); hasErrors = true; } else if (!conversion.IsImplicit) { diagnostics.Add(ErrorCode.WRN_GotoCaseShouldConvert, node.Location, SwitchGoverningType); hasErrors = true; } caseExpression = CreateConversion(caseExpression, conversion, SwitchGoverningType, diagnostics); } return(ConvertPatternExpression(SwitchGoverningType, node, caseExpression, out constantValueOpt, hasErrors, diagnostics)); }
internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, TypeSymbol rightType, bool rightIsNull, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // SPEC: The predefined reference type equality operators require one of the following: // SPEC: (1) Both operands are a value of a type known to be a reference-type or the literal null. // SPEC: Furthermore, an explicit reference conversion exists from the type of either // SPEC: operand to the type of the other operand. Or: // SPEC: (2) One operand is a value of type T where T is a type-parameter and the other operand is // SPEC: the literal null. Furthermore T does not have the value type constraint. // SPEC ERROR: Notice that the spec calls out that an explicit reference conversion must exist; // SPEC ERROR: in fact it should say that an explicit reference conversion, implicit reference // SPEC ERROR: conversion or identity conversion must exist. The conversion from object to object // SPEC ERROR: is not classified as a reference conversion at all; it is an identity conversion. // Dev10 does not follow the spec exactly for type parameters. Specifically, in Dev10, // if a type parameter argument is known to be a value type, or if a type parameter // argument is not known to be either a value type or reference type and the other // argument is not null, reference type equality cannot be applied. Otherwise, the // effective base class of the type parameter is used to determine the conversion // to the other argument type. (See ExpressionBinder::GetRefEqualSigs.) if (((object)leftType != null) && leftType.IsTypeParameter()) { if (leftType.IsValueType || (!leftType.IsReferenceType && !rightIsNull)) { return false; } leftType = ((TypeParameterSymbol)leftType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)leftType != null); } if (((object)rightType != null) && rightType.IsTypeParameter()) { if (rightType.IsValueType || (!rightType.IsReferenceType && !leftIsNull)) { return false; } rightType = ((TypeParameterSymbol)rightType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)rightType != null); } var leftIsReferenceType = ((object)leftType != null) && leftType.IsReferenceType; if (!leftIsReferenceType && !leftIsNull) { return false; } var rightIsReferenceType = ((object)rightType != null) && rightType.IsReferenceType; if (!rightIsReferenceType && !rightIsNull) { return false; } // If at least one side is null then clearly a conversion exists. if (leftIsNull || rightIsNull) { return true; } var leftConversion = Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics); if (leftConversion.IsIdentity || leftConversion.IsReference) { return true; } var rightConversion = Conversions.ClassifyConversion(rightType, leftType, ref useSiteDiagnostics); if (rightConversion.IsIdentity || rightConversion.IsReference) { return true; } return false; }
/// <remarks> /// This method implements best type inference for the conditional operator ?:. /// NOTE: If either expression is an error type, we return error type as the inference result. /// </remarks> public static TypeSymbol InferBestTypeForConditionalOperator(BoundExpression expr1, BoundExpression expr2, Conversions conversions, out bool hadMultipleCandidates, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: The second and third operands, x and y, of the ?: operator control the type of the conditional expression. // SPEC: • If x has type X and y has type Y then // SPEC: o If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression. // SPEC: o If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression. // SPEC: o Otherwise, no expression type can be determined, and a compile-time error occurs. // SPEC: • If only one of x and y has a type, and both x and y, are implicitly convertible to that type, then that is the type of the conditional expression. // SPEC: • Otherwise, no expression type can be determined, and a compile-time error occurs. // A type is a candidate if all expressions are convertible to that type. ArrayBuilder <TypeSymbol> candidateTypes = ArrayBuilder <TypeSymbol> .GetInstance(); TypeSymbol type1 = expr1.Type; if ((object)type1 != null) { if (type1.IsErrorType()) { candidateTypes.Free(); hadMultipleCandidates = false; return(type1); } if (conversions.ClassifyImplicitConversionFromExpression(expr2, type1, ref useSiteDiagnostics).Exists) { candidateTypes.Add(type1); } } TypeSymbol type2 = expr2.Type; if ((object)type2 != null && type2 != type1) { if (type2.IsErrorType()) { candidateTypes.Free(); hadMultipleCandidates = false; return(type2); } if (conversions.ClassifyImplicitConversionFromExpression(expr1, type2, ref useSiteDiagnostics).Exists) { candidateTypes.Add(type2); } } hadMultipleCandidates = candidateTypes.Count > 1; return(InferBestType(candidateTypes.ToImmutableAndFree(), conversions, ref useSiteDiagnostics)); }
/// <remarks> /// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification. /// NOTE: If some or all of the expressions have error types, we return error type as the inference result. /// </remarks> public static TypeSymbol InferBestType(ImmutableArray <BoundExpression> exprs, Conversions conversions, out bool hadMultipleCandidates, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: 7.5.2.14 Finding the best common type of a set of expressions // SPEC: In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and // SPEC: the return types of anonymous functions with block bodies are found in this way. // SPEC: Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method: // SPEC: T M<X>(X x1 … X xm) // SPEC: with the Ei as arguments. // SPEC: More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X. // SPEC: Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions. // SPEC: If no such S exists, the expressions have no best common type. // All non-null types are candidates for best type inference. HashSet <TypeSymbol> candidateTypes = new HashSet <TypeSymbol>(); foreach (BoundExpression expr in exprs) { TypeSymbol type = expr.Type; if ((object)type != null) { if (type.IsErrorType()) { hadMultipleCandidates = false; return(type); } candidateTypes.Add(type); } } hadMultipleCandidates = candidateTypes.Count > 1; // Perform best type inference on candidate types. return(InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, ref useSiteDiagnostics)); }
public static TypeSymbol InferBestType(ImmutableArray <TypeSymbol> types, Conversions conversions, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { var inferrer = new BestTypeInferrer(conversions); return(inferrer.GetBestType(types, ref useSiteDiagnostics)); }
private BestTypeInferrer(Conversions conversions) { _conversions = conversions; }
private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability, Conversions otherNullabilityOpt) : base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability, otherNullabilityOpt) { _binder = binder; }
public static TypeSymbol InferBestType(ImmutableArray<TypeSymbol> types, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { var inferrer = new BestTypeInferrer(conversions); return inferrer.GetBestType(types, ref useSiteDiagnostics); }
/// <remarks> /// NOTE: Keep this method in sync with <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>. /// </remarks> private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions( BoundExpression sourceExpression, TypeSymbol source, TypeSymbol target, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(sourceExpression != null || (object)source != null); Debug.Assert((object)target != null); // User-defined conversions that involve generics can be quite strange. There // are two basic problems: first, that generic user-defined conversions can be // "shadowed" by built-in conversions, and second, that generic user-defined // conversions can make conversions that would never have been legal user-defined // conversions if declared non-generically. I call this latter kind of conversion // a "suspicious" conversion. // // The shadowed conversions are easily dealt with: // // SPEC: If a predefined implicit conversion exists from a type S to type T, // SPEC: all user-defined conversions, implicit or explicit, are ignored. // SPEC: If a predefined explicit conversion exists from a type S to type T, // SPEC: any user-defined explicit conversion from S to T are ignored. // // The rule above can come into play in cases like: // // sealed class C<T> { public static implicit operator T(C<T> c) { ... } } // C<object> c = whatever; // object o = c; // // The built-in implicit conversion from C<object> to object must shadow // the user-defined implicit conversion. // // The caller of this method checks for user-defined conversions *after* // predefined implicit conversions, so we already know that if we got here, // there was no predefined implicit conversion. // // Note that a user-defined *implicit* conversion may win over a built-in // *explicit* conversion by the rule given above. That is, if we created // an implicit conversion from T to C<T>, then the user-defined implicit // conversion from object to C<object> could be valid, even though that // would be "replacing" a built-in explicit conversion with a user-defined // implicit conversion. This is one of the "suspicious" conversions, // as it would not be legal to declare a user-defined conversion from // object in a non-generic type. // // The way the native compiler handles suspicious conversions involving // interfaces is neither sensible nor in line with the rules in the // specification. It is not clear at this time whether we should be exactly // matching the native compiler, the specification, or neither, in Roslyn. // Spec (6.4.4 User-defined implicit conversions) // A user-defined implicit conversion from an expression E to type T is processed as follows: // SPEC: Find the set of types D from which user-defined conversion operators... var d = ArrayBuilder <NamedTypeSymbol> .GetInstance(); ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteDiagnostics); // SPEC: Find the set of applicable user-defined and lifted conversion operators, U... var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance(); ComputeApplicableUserDefinedImplicitConversionSet(sourceExpression, source, target, d, ubuild, ref useSiteDiagnostics); d.Free(); ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree(); // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs. if (u.Length == 0) { return(UserDefinedConversionResult.NoApplicableOperators(u)); } // SPEC: Find the most specific source type SX of the operators in U... TypeSymbol sx = MostSpecificSourceTypeForImplicitUserDefinedConversion(u, source, ref useSiteDiagnostics); #if XSHARP if ((object)sx == null) { // When converting to USUAL and no valid operator is found then choose the operator with an Object Parameter // Except when the source is a pointer type. if (this is Conversions) { Conversions conv = this as Conversions; bool usePointer = source.IsPointerType(); if (conv.Compilation.Options.HasRuntime && target == conv.Compilation.UsualType()) { for (int i = 0; i < u.Length; i++) { var x = u[i]; if (usePointer && x.ToType == target && x.FromType.IsVoidPointer()) { return(UserDefinedConversionResult.Valid(u, i)); } if (!usePointer && x.ToType == target && x.FromType == conv.Compilation.GetSpecialType(SpecialType.System_Object)) { return(UserDefinedConversionResult.Valid(u, i)); } } } } } #endif if ((object)sx == null) { return(UserDefinedConversionResult.NoBestSourceType(u)); } // SPEC: Find the most specific target type TX of the operators in U... TypeSymbol tx = MostSpecificTargetTypeForImplicitUserDefinedConversion(u, target, ref useSiteDiagnostics); if ((object)tx == null) { return(UserDefinedConversionResult.NoBestTargetType(u)); } int?best = MostSpecificConversionOperator(sx, tx, u); if (best == null) { return(UserDefinedConversionResult.Ambiguous(u)); } return(UserDefinedConversionResult.Valid(u, best.Value)); }
internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, TypeSymbol rightType, bool rightIsNull, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: The predefined reference type equality operators require one of the following: // SPEC: (1) Both operands are a value of a type known to be a reference-type or the literal null. // SPEC: Furthermore, an explicit reference conversion exists from the type of either // SPEC: operand to the type of the other operand. Or: // SPEC: (2) One operand is a value of type T where T is a type-parameter and the other operand is // SPEC: the literal null. Furthermore T does not have the value type constraint. // SPEC ERROR: Notice that the spec calls out that an explicit reference conversion must exist; // SPEC ERROR: in fact it should say that an explicit reference conversion, implicit reference // SPEC ERROR: conversion or identity conversion must exist. The conversion from object to object // SPEC ERROR: is not classified as a reference conversion at all; it is an identity conversion. // Dev10 does not follow the spec exactly for type parameters. Specifically, in Dev10, // if a type parameter argument is known to be a value type, or if a type parameter // argument is not known to be either a value type or reference type and the other // argument is not null, reference type equality cannot be applied. Otherwise, the // effective base class of the type parameter is used to determine the conversion // to the other argument type. (See ExpressionBinder::GetRefEqualSigs.) if (((object)leftType != null) && leftType.IsTypeParameter()) { if (leftType.IsValueType || (!leftType.IsReferenceType && !rightIsNull)) { return(false); } leftType = ((TypeParameterSymbol)leftType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)leftType != null); } if (((object)rightType != null) && rightType.IsTypeParameter()) { if (rightType.IsValueType || (!rightType.IsReferenceType && !leftIsNull)) { return(false); } rightType = ((TypeParameterSymbol)rightType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)rightType != null); } var leftIsReferenceType = ((object)leftType != null) && leftType.IsReferenceType; if (!leftIsReferenceType && !leftIsNull) { return(false); } var rightIsReferenceType = ((object)rightType != null) && rightType.IsReferenceType; if (!rightIsReferenceType && !rightIsNull) { return(false); } // If at least one side is null then clearly a conversion exists. if (leftIsNull || rightIsNull) { return(true); } var leftConversion = Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics); if (leftConversion.IsIdentity || leftConversion.IsReference) { return(true); } var rightConversion = Conversions.ClassifyConversion(rightType, leftType, ref useSiteDiagnostics); if (rightConversion.IsIdentity || rightConversion.IsReference) { return(true); } return(false); }
internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder) { ExpressionSyntax expressionSyntax = TargetExpressionSyntax; VariableDeclarationSyntax declarationSyntax = syntax.Declaration; Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither. bool hasErrors = false; BoundMultipleLocalDeclarations declarationsOpt = null; BoundExpression expressionOpt = null; Conversion iDisposableConversion = Conversion.NoConversion; TypeSymbol iDisposable = this.Compilation.GetSpecialType(SpecialType.System_IDisposable); // no need for diagnostics, so use the Compilation version Debug.Assert((object)iDisposable != null); if (expressionSyntax != null) { expressionOpt = this.BindTargetExpression(diagnostics); HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = this.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, iDisposable, ref useSiteDiagnostics); diagnostics.Add(expressionSyntax, useSiteDiagnostics); if (!iDisposableConversion.IsImplicit) { TypeSymbol expressionType = expressionOpt.Type; if ((object)expressionType == null || !expressionType.IsErrorType()) { Error(diagnostics, ErrorCode.ERR_NoConvToIDisp, expressionSyntax, expressionOpt.Display); } hasErrors = true; } } else { ImmutableArray <BoundLocalDeclaration> declarations; BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations); Debug.Assert(!declarations.IsEmpty); declarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarations); TypeSymbol declType = declarations[0].DeclaredType.Type; if (declType.IsDynamic()) { iDisposableConversion = Conversion.ImplicitDynamic; } else { HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = Conversions.ClassifyImplicitConversion(declType, iDisposable, ref useSiteDiagnostics); diagnostics.Add(declarationSyntax, useSiteDiagnostics); if (!iDisposableConversion.IsImplicit) { if (!declType.IsErrorType()) { Error(diagnostics, ErrorCode.ERR_NoConvToIDisp, declarationSyntax, declType); } hasErrors = true; } } } BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(syntax.Statement, diagnostics); return(new BoundUsingStatement( syntax, this.Locals, declarationsOpt, expressionOpt, iDisposableConversion, boundBody, hasErrors)); }
public Value(BoundExpression expr, DisposeCheckerPass pass) { Debug.Assert(expr.Kind != HijackedBoundKindForValueHolder); this.creations = new HashSet <BoundExpression>(); expr = SkipReferenceConversions(expr); switch (expr.Kind) { case HijackedBoundKindForValueHolder: { var holder = (BoundValueHolder)expr; var value = holder.value; creations = value.creations; } break; case BoundKind.ObjectCreationExpression: case BoundKind.NewT: HashSet <DiagnosticInfo> useSiteDiagnostics = null; if (Conversions.IsBaseInterface(pass.IDisposableType, expr.Type, ref useSiteDiagnostics)) { creations.Add(expr); } break; case BoundKind.Local: { var local = (BoundLocal)expr; Value value; pass.State.variables.TryGetValue(local.LocalSymbol, out value); if (value != null) { creations = value.creations; } } break; case BoundKind.DeclarationExpression: { var local = (BoundDeclarationExpression)expr; Value value; pass.State.variables.TryGetValue(local.LocalSymbol, out value); if (value != null) { creations = value.creations; } } break; case BoundKind.Parameter: { var parameter = (BoundParameter)expr; Value value; pass.State.variables.TryGetValue(parameter.ParameterSymbol, out value); if (value != null) { creations = value.creations; } } break; default: break; } }