private static bool ReportQueryInferenceFailedSelectMany(FromClauseSyntax fromClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray <Symbol> symbols, DiagnosticBag diagnostics) { Debug.Assert(methodName == "SelectMany"); // Estimate the return type of Select's lambda argument BoundExpression arg = arguments.Argument(arguments.IsExtensionMethodInvocation ? 1 : 0); TypeSymbol type = null; if (arg.Kind == BoundKind.UnboundLambda) { var unbound = (UnboundLambda)arg; foreach (var t in unbound.Data.InferredReturnTypes()) { if (!t.IsErrorType()) { type = t; break; } } } if ((object)type == null || type.IsErrorType()) { return(false); } TypeSymbol receiverType = receiver != null ? receiver.Type : null; diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryTypeInferenceFailedSelectMany, new object[] { type, receiverType, methodName }, symbols), fromClause.Expression.Location); return(true); }
private void CheckUndefinedMethodCall(BoundRoutineCall x, TypeSymbol type, BoundRoutineName name) { if (name.IsDirect && x.TargetMethod.IsErrorMethod() && type != null && !type.IsErrorType()) { _diagnostics.Add(_routine, ((FunctionCall)x.PhpSyntax).NameSpan.ToTextSpan(), ErrorCode.WRN_UndefinedMethodCall, name.NameValue.ToString(), type.Name); } }
private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, BoundExpression operand, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { Debug.Assert((object)operandType != null); Debug.Assert((object)patternType != null); if (operandType.IsErrorType() || patternType.IsErrorType()) { return(false); } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return(true); } else if (patternType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType); return(true); } else if (!isVar) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = operand != null ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast : true) : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true); diagnostics.Add(typeSyntax, useSiteDiagnostics); switch (conversion.Kind) { case ConversionKind.Boxing: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.Unboxing: case ConversionKind.NullLiteral: case ConversionKind.ImplicitNullable: // these are the conversions allowed by a pattern match break; //case ConversionKind.ExplicitNumeric: // we do not perform numeric conversions of the operand //case ConversionKind.ImplicitConstant: //case ConversionKind.ImplicitNumeric: default: Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); return(true); } } return(false); }
private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, BoundExpression operand, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { if (operandType?.IsErrorType() == true || patternType?.IsErrorType() == true) { return(false); } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return(true); } else if (operand != null && operandType == (object)null && !operand.HasAnyErrors) { // It is an error to use pattern-matching with a null, method group, or lambda Error(diagnostics, ErrorCode.ERR_BadIsPatternExpression, operand.Syntax); return(true); } else if (!isVar) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = operand != null ? this.Conversions.ClassifyConversionForCast(operand, patternType, ref useSiteDiagnostics) : this.Conversions.ClassifyConversionForCast(operandType, patternType, ref useSiteDiagnostics); diagnostics.Add(typeSyntax, useSiteDiagnostics); switch (conversion.Kind) { case ConversionKind.Boxing: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.Unboxing: case ConversionKind.NullLiteral: case ConversionKind.ImplicitNullable: // these are the conversions allowed by a pattern match break; //case ConversionKind.ExplicitNumeric: // we do not perform numeric conversions of the operand //case ConversionKind.ImplicitConstant: //case ConversionKind.ImplicitNumeric: default: Error(diagnostics, ErrorCode.ERR_NoExplicitConv, typeSyntax, operandType, patternType); return(true); } } return(false); }
/// <summary> /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// </summary> private static TypeSymbol Better( TypeSymbol type1, TypeSymbol type2, ConversionsBase conversions, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return(type2); } if ((object)type2 == null || type2.IsErrorType()) { return(type1); } var conversionsWithoutNullability = conversions.WithNullability(false); var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return(type1); } if (type2.IsDynamic()) { return(type2); } if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)) { return(MethodTypeInferrer.Merge( TypeWithAnnotations.Create(type1), TypeWithAnnotations.Create(type2), VarianceKind.Out, conversions).Type); } return(null); } if (t1tot2) { return(type2); } if (t2tot1) { return(type1); } return(null); }
internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, DiagnosticBag diagnostics) { RefKind refKind = _methodSymbol.RefKind; TypeSymbol returnType = _methodSymbol.ReturnType.TypeSymbol; if (!this.IsDirectlyInIterator) { // This should only happen when speculating, but we don't have a good way to assert that since the // original binder isn't available here. // If we're speculating about a yield statement inside a non-iterator method, we'll try to be nice // and deduce an iterator element type from the return type. If we didn't do this, the // TypeInfo.ConvertedType of the yield statement would always be an error type. However, we will // not mutate any state (i.e. we won't store the result). return(GetIteratorElementTypeFromReturnType(refKind, returnType, node, diagnostics).elementType ?? CreateErrorType()); } if (_iteratorInfo == IteratorInfo.Empty) { DiagnosticBag elementTypeDiagnostics = DiagnosticBag.GetInstance(); (TypeSymbol elementType, bool asyncInterface) = GetIteratorElementTypeFromReturnType(refKind, returnType, node, elementTypeDiagnostics); Location errorLocation = _methodSymbol.Locations[0]; if ((object)elementType == null) { if (refKind != RefKind.None) { Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturnRef, errorLocation, _methodSymbol); } else if (!returnType.IsErrorType()) { Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, errorLocation, _methodSymbol, returnType); } elementType = CreateErrorType(); } else if (asyncInterface && !_methodSymbol.IsAsync) { Error(elementTypeDiagnostics, ErrorCode.ERR_IteratorMustBeAsync, errorLocation, _methodSymbol, returnType); } var info = new IteratorInfo(elementType, elementTypeDiagnostics.ToReadOnlyAndFree()); Interlocked.CompareExchange(ref _iteratorInfo, info, IteratorInfo.Empty); } if (node == null) { // node==null indicates this we are being called from the top-level of processing of a method. We report // the diagnostic, if any, at that time to ensure it is reported exactly once. diagnostics.AddRange(_iteratorInfo.ElementTypeDiagnostics); } return(_iteratorInfo.ElementType); }
private static void CheckEffectiveAndDeducedBaseTypes(ConversionsBase conversions, TypeSymbol effectiveBase, TypeSymbol deducedBase) { Debug.Assert((object)deducedBase != null); Debug.Assert((object)effectiveBase != null); HashSet <DiagnosticInfo> useSiteDiagnostics = null; Debug.Assert(deducedBase.IsErrorType() || effectiveBase.IsErrorType() || conversions.HasIdentityOrImplicitReferenceConversion(deducedBase, effectiveBase, ref useSiteDiagnostics) || conversions.HasBoxingConversion(deducedBase, effectiveBase, ref useSiteDiagnostics)); }
private static bool SatisfiesConstraintType( ConversionsBase conversions, TypeSymbol typeArgument, TypeSymbol constraintType, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { if (constraintType.IsErrorType()) { return(false); } // Spec 4.4.4 describes the valid conversions from // type argument A to constraint type C: // "An identity conversion (6.1.1). // An implicit reference conversion (6.1.6). ..." if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument, constraintType, ref useSiteDiagnostics)) { return(true); } // "... A boxing conversion (6.1.7), provided that type A is a non-nullable value type. ..." // NOTE: we extend this to allow, for example, a conversion from Nullable<T> to object. if (typeArgument.IsValueType && conversions.HasBoxingConversion(typeArgument.IsNullableType() ? ((NamedTypeSymbol)typeArgument).ConstructedFrom : typeArgument, constraintType, ref useSiteDiagnostics)) { return(true); } if (typeArgument.TypeKind == TypeKind.TypeParameter) { var typeParameter = (TypeParameterSymbol)typeArgument; // "... An implicit reference, boxing, or type parameter conversion // from type parameter A to C." if (conversions.HasImplicitTypeParameterConversion(typeParameter, constraintType, ref useSiteDiagnostics)) { return(true); } // TypeBind::SatisfiesBound allows cases where one of the // type parameter constraints satisfies the constraint. foreach (var typeArgumentConstraint in typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { if (SatisfiesConstraintType(conversions, typeArgumentConstraint, constraintType, ref useSiteDiagnostics)) { return(true); } } } return(false); }
private static bool DoSignaturesMatch( PEModuleSymbol moduleSymbol, TypeSymbol eventType, PEMethodSymbol addMethod, PEMethodSymbol removeMethod) { return ((eventType.IsDelegateType() || eventType.IsErrorType()) && DoesSignatureMatch(moduleSymbol, eventType, addMethod) && DoesSignatureMatch(moduleSymbol, eventType, removeMethod) && DoModifiersMatch(addMethod, removeMethod)); }
/// <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, ConversionsBase conversions, out bool hadNullabilityMismatch, 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. IEqualityComparer <TypeSymbol> comparer = conversions.IncludeNullability ? TypeSymbol.EqualsConsiderEverything : TypeSymbol.EqualsIgnoringNullableComparer; HashSet <TypeSymbol> candidateTypes = new HashSet <TypeSymbol>(comparer); foreach (BoundExpression expr in exprs) { TypeSymbol type = expr.Type; if ((object)type != null) { if (type.IsErrorType()) { hadNullabilityMismatch = false; return(type); } if (conversions.IncludeNullability) { type = type.SetSpeakableNullabilityForReferenceTypes(); } candidateTypes.Add(type); } } // Perform best type inference on candidate types. var builder = ArrayBuilder <TypeSymbol> .GetInstance(candidateTypes.Count); builder.AddRange(candidateTypes); var result = GetBestType(builder, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics); builder.Free(); return(result); }
/// <summary> /// Return the default value constant for the given type, /// or null if the default value is not a constant. /// </summary> public static ConstantValue GetDefaultValue(this TypeSymbol type) { // SPEC: A default-value-expression is a constant expression (§7.19) if the type // SPEC: is a reference type or a type parameter that is known to be a reference type (§10.1.5). // SPEC: In addition, a default-value-expression is a constant expression if the type is // SPEC: one of the following value types: // SPEC: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, or any enumeration type. Debug.Assert((object)type != null); if (type.IsErrorType()) { return(null); } if (type.IsReferenceType) { return(ConstantValue.Null); } if (type.IsValueType) { if (type.IsEnumType()) { type = type.GetEnumUnderlyingType(); } switch (type.SpecialType) { case SpecialType.System_SByte: case SpecialType.System_Byte: case SpecialType.System_Int16: case SpecialType.System_UInt16: case SpecialType.System_Int32: case SpecialType.System_UInt32: case SpecialType.System_Int64: case SpecialType.System_UInt64: case SpecialType.System_Char: case SpecialType.System_Boolean: case SpecialType.System_Single: case SpecialType.System_Double: case SpecialType.System_Decimal: return(ConstantValue.Default(type.SpecialType)); } } return(null); }
/// <summary> /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// </summary> private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return(type2); } if ((object)type2 == null || type2.IsErrorType()) { return(type1); } var t1tot2 = _conversions.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = _conversions.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return(type1); } if (type2.IsDynamic()) { return(type2); } if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames)) { return(MethodTypeInferrer.MergeTupleNames(type1, type2, MethodTypeInferrer.MergeDynamic(type1, type2, type1, _conversions.CorLibrary), _conversions.CorLibrary)); } return(null); } if (t1tot2) { return(type2); } if (t2tot1) { return(type1); } return(null); }
internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpressionArmSyntax node, TypeSymbol switchGoverningType, uint switchGoverningValEscape, BindingDiagnosticBag diagnostics) { Debug.Assert(node == _arm); Binder armBinder = this.GetRequiredBinder(node); bool hasErrors = switchGoverningType.IsErrorType(); ImmutableArray <LocalSymbol> locals = _armScopeBinder.Locals; BoundPattern pattern = armBinder.BindPattern(node.Pattern, switchGoverningType, switchGoverningValEscape, permitDesignations: true, hasErrors, diagnostics); BoundExpression?whenClause = node.WhenClause != null ? armBinder.BindBooleanExpression(node.WhenClause.Condition, diagnostics) : null; BoundExpression armResult = armBinder.BindValue(node.Expression, diagnostics, BindValueKind.RValue); var label = new GeneratedLabelSymbol("arm"); return(new BoundSwitchExpressionArm(node, locals, pattern, whenClause, armResult, label, hasErrors | pattern.HasErrors)); }
private static bool IsPossiblyByRefTypeParameter(TypeSymbol type) { if (type.IsTypeParameter()) { return(true); } if (type.IsErrorType()) { var byRefReturnType = type as ByRefReturnErrorTypeSymbol; return(((object)byRefReturnType != null) && byRefReturnType.ReferencedType.IsTypeParameter()); } return(false); }
internal void SetType(TypeSymbol newType) { TypeSymbol originalType = _type; // In the event that we race to set the type of a local, we should // always deduce the same type, or deduce that the type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() && newType.IsErrorType() || originalType == newType); if ((object)originalType == null) { Interlocked.CompareExchange(ref _type, newType, null); } }
internal void SetType(TypeSymbolWithAnnotations newType) { TypeSymbol originalType = _type.DefaultType; // In the event that we race to set the type of a local, we should // always deduce the same type, or deduce that the type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() && newType.IsErrorType() || originalType == newType.TypeSymbol); if ((object)originalType == null) { _type.InterlockedInitialize(newType); } }
internal void SetTypeWithAnnotations(TypeWithAnnotations newType) { Debug.Assert(!(newType.Type is null)); TypeSymbol originalType = _type?.Value.DefaultType; // In the event that we race to set the type of a local, we should // always deduce the same type, or deduce that the type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() && newType.Type.IsErrorType() || TypeSymbol.Equals(originalType, newType.Type, TypeCompareKind.ConsiderEverything2)); if ((object)originalType == null) { Interlocked.CompareExchange(ref _type, new TypeWithAnnotations.Boxed(newType), null); } }
/// <summary> /// Can add some diagnostics into <paramref name="diagnostics"/>. /// </summary> private void SetType(CSharpCompilation compilation, DiagnosticBag diagnostics, TypeSymbol type) { TypeSymbol originalType = _lazyType; // In the event that we race to set the type of a field, we should // always deduce the same type, unless the cached type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() || originalType == type); if ((object)Interlocked.CompareExchange(ref _lazyType, type, null) == null) { TypeChecks(type, diagnostics); compilation.DeclarationDiagnostics.AddRange(diagnostics); state.NotePartComplete(CompletionPart.Type); } }
private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return(type2); } if ((object)type2 == null || type2.IsErrorType()) { return(type1); } var t1tot2 = _conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = _conversions.ClassifyImplicitConversion(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return(type1); } if (type2.IsDynamic()) { return(type2); } return(null); } if (t1tot2) { return(type2); } if (t2tot1) { return(type1); } return(null); }
/// <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)); }
internal void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, CSharpSyntaxNode syntax, UnboundLambda anonymousFunction, TypeSymbol targetType) { Debug.Assert((object)targetType != null); Debug.Assert(anonymousFunction != null); // Is the target type simply bad? // If the target type is an error then we've already reported a diagnostic. Don't bother // reporting the conversion error. if (targetType.IsErrorType() || syntax.HasErrors) { return; } // CONSIDER: Instead of computing this again, cache the reason why the conversion failed in // CONSIDER: the Conversion result, and simply report that. var reason = Conversions.IsAnonymousFunctionCompatibleWithType(anonymousFunction, targetType); // It is possible that the conversion from lambda to delegate is just fine, and // that we ended up here because the target type, though itself is not an error // type, contains a type argument which is an error type. For example, converting // (Foo foo)=>{} to Action<Foo> is a perfectly legal conversion even if Foo is undefined! // In that case we have already reported an error that Foo is undefined, so just bail out. if (reason == LambdaConversionResult.Success) { return; } var id = anonymousFunction.MessageID.Localize(); if (reason == LambdaConversionResult.BadTargetType) { if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, targetType, node: syntax)) { return; } // Cannot convert {0} to type '{1}' because it is not a delegate type Error(diagnostics, ErrorCode.ERR_AnonMethToNonDel, syntax, id, targetType); return; } if (reason == LambdaConversionResult.ExpressionTreeMustHaveDelegateTypeArgument) { Debug.Assert(targetType.IsExpressionTree()); Error(diagnostics, ErrorCode.ERR_ExpressionTreeMustHaveDelegate, syntax, ((NamedTypeSymbol)targetType).TypeArgumentsNoUseSiteDiagnostics[0]); return; } if (reason == LambdaConversionResult.ExpressionTreeFromAnonymousMethod) { Debug.Assert(targetType.IsExpressionTree()); Error(diagnostics, ErrorCode.ERR_AnonymousMethodToExpressionTree, syntax); return; } // At this point we know that we have either a delegate type or an expression type for the target. var delegateType = targetType.GetDelegateType(); // The target type is a vaid delegate or expression tree type. Is there something wrong with the // parameter list? // First off, is there a parameter list at all? if (reason == LambdaConversionResult.MissingSignatureWithOutParameter) { // COMPATIBILITY: The C# 4 compiler produces two errors for: // // delegate void D (out int x); // ... // D d = delegate {}; // // error CS1676: Parameter 1 must be declared with the 'out' keyword // error CS1688: Cannot convert anonymous method block without a parameter list // to delegate type 'D' because it has one or more out parameters // // This seems redundant, (because there is no "parameter 1" in the source code) // and unnecessary. I propose that we eliminate the first error. Error(diagnostics, ErrorCode.ERR_CantConvAnonMethNoParams, syntax, targetType); return; } // There is a parameter list. Does it have the right number of elements? if (reason == LambdaConversionResult.BadParameterCount) { // Delegate '{0}' does not take {1} arguments Error(diagnostics, ErrorCode.ERR_BadDelArgCount, syntax, targetType, anonymousFunction.ParameterCount); return; } // The parameter list exists and had the right number of parameters. Were any of its types bad? // If any parameter type of the lambda is an error type then suppress // further errors. We've already reported errors on the bad type. if (anonymousFunction.HasExplicitlyTypedParameterList) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { if (anonymousFunction.ParameterType(i).IsErrorType()) { return; } } } // The parameter list exists and had the right number of parameters. Were any of its types // mismatched with the delegate parameter types? // The simplest possible case is (x, y, z)=>whatever where the target type has a ref or out parameter. var delegateParameters = delegateType.DelegateParameters(); if (reason == LambdaConversionResult.RefInImplicitlyTypedLambda) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { var delegateRefKind = delegateParameters[i].RefKind; if (delegateRefKind != RefKind.None) { // Parameter {0} must be declared with the '{1}' keyword Error(diagnostics, ErrorCode.ERR_BadParamRef, anonymousFunction.ParameterLocation(i), i + 1, delegateRefKind.ToDisplayString()); } } return; } // See the comments in IsAnonymousFunctionCompatibleWithDelegate for an explanation of this one. if (reason == LambdaConversionResult.StaticTypeInImplicitlyTypedLambda) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { if (delegateParameters[i].Type.IsStatic) { // {0}: Static types cannot be used as parameter Error(diagnostics, ErrorCode.ERR_ParameterIsStaticClass, anonymousFunction.ParameterLocation(i), delegateParameters[i].Type); } } return; } // Otherwise, there might be a more complex reason why the parameter types are mismatched. if (reason == LambdaConversionResult.MismatchedParameterType) { // Cannot convert {0} to delegate type '{1}' because the parameter types do not match the delegate parameter types Error(diagnostics, ErrorCode.ERR_CantConvAnonMethParams, syntax, id, targetType); Debug.Assert(anonymousFunction.ParameterCount == delegateParameters.Length); for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { var lambdaParameterType = anonymousFunction.ParameterType(i); if (lambdaParameterType.IsErrorType()) { continue; } var lambdaParameterLocation = anonymousFunction.ParameterLocation(i); var lambdaRefKind = anonymousFunction.RefKind(i); var delegateParameterType = delegateParameters[i].Type; var delegateRefKind = delegateParameters[i].RefKind; if (!lambdaParameterType.Equals(delegateParameterType, ignoreCustomModifiers: true, ignoreDynamic: true)) { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType); // Parameter {0} is declared as type '{1}{2}' but should be '{3}{4}' Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation, i + 1, lambdaRefKind.ToPrefix(), distinguisher.First, delegateRefKind.ToPrefix(), distinguisher.Second); } else if (lambdaRefKind != delegateRefKind) { if (delegateRefKind == RefKind.None) { // Parameter {0} should not be declared with the '{1}' keyword Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToDisplayString()); } else { // Parameter {0} must be declared with the '{1}' keyword Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToDisplayString()); } } } return; } if (reason == LambdaConversionResult.BindingFailed) { var bindingResult = anonymousFunction.Bind(delegateType); Debug.Assert(ErrorFacts.PreventsSuccessfulDelegateConversion(bindingResult.Diagnostics)); diagnostics.AddRange(bindingResult.Diagnostics); return; } // UNDONE: LambdaConversionResult.VoidExpressionLambdaMustBeStatementExpression: Debug.Assert(false, "Missing case in lambda conversion error reporting"); }
internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder) { ExpressionSyntax expressionSyntax = TargetExpressionSyntax; VariableDeclarationSyntax declarationSyntax = _syntax.Declaration; bool hasAwait = _syntax.AwaitKeyword.Kind() != default; Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither. TypeSymbol iDisposable = hasAwait ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : this.Compilation.GetSpecialType(SpecialType.System_IDisposable); Debug.Assert((object)iDisposable != null); bool hasErrors = ReportUseSiteDiagnostics(iDisposable, diagnostics, hasAwait ? _syntax.AwaitKeyword : _syntax.UsingKeyword); Conversion iDisposableConversion = Conversion.NoConversion; BoundMultipleLocalDeclarations declarationsOpt = null; BoundExpression expressionOpt = null; AwaitableInfo awaitOpt = null; if (expressionSyntax != null) { expressionOpt = this.BindTargetExpression(diagnostics, originalBinder); HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = originalBinder.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, hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp, expressionSyntax, expressionOpt.Display); } hasErrors = true; } } else { ImmutableArray <BoundLocalDeclaration> declarations; originalBinder.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 = originalBinder.Conversions.ClassifyImplicitConversionFromType(declType, iDisposable, ref useSiteDiagnostics); diagnostics.Add(declarationSyntax, useSiteDiagnostics); if (!iDisposableConversion.IsImplicit) { if (!declType.IsErrorType()) { Error(diagnostics, hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp, declarationSyntax, declType); } hasErrors = true; } } } if (hasAwait) { TypeSymbol taskType = this.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask); hasErrors |= ReportUseSiteDiagnostics(taskType, diagnostics, _syntax.AwaitKeyword); var resource = (SyntaxNode)expressionSyntax ?? declarationSyntax; BoundExpression placeholder = new BoundAwaitableValuePlaceholder(resource, taskType).MakeCompilerGenerated(); awaitOpt = BindAwaitInfo(placeholder, resource, _syntax.AwaitKeyword.GetLocation(), diagnostics, ref hasErrors); } BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals); return(new BoundUsingStatement( _syntax, this.Locals, declarationsOpt, expressionOpt, iDisposableConversion, boundBody, awaitOpt, hasErrors)); }
/// <summary> /// Emits conversion from one CLR type to another using PHP conventions. /// </summary> /// <param name="from">Type of value on top of evaluation stack.</param> /// <param name="fromHint">Type hint in case of a multityple type choices (like PhpValue or PhpNumber or PhpAlias).</param> /// <param name="to">Target CLR type.</param> /// <param name="conversion">Conversion semantic.</param> public void EmitConvert(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to, ConversionKind conversion = ConversionKind.Implicit) { Contract.ThrowIfNull(from); Contract.ThrowIfNull(to); Debug.Assert(!from.IsUnreachable); Debug.Assert(!to.IsUnreachable); Debug.Assert(!to.IsErrorType(), "Conversion to an error type."); // conversion is not needed: if (from.SpecialType == to.SpecialType && (from == to || (to.SpecialType != SpecialType.System_Object && from.IsOfType(to)))) { return; } if (from.SpecialType == SpecialType.System_Void) { // void -> T EmitLoadDefault(to); return; } // from = EmitSpecialize(from, fromHint); if (from != to) { var conv = DeclaringCompilation.Conversions.ClassifyConversion(from, to, conversion); if (conv.Exists) { ConversionsExtensions.EmitConversion(this, conv, from, to, @checked: false); } else { // specialized conversions: if (to == CoreTypes.PhpValue) { EmitConvertToPhpValue(from, fromHint); } else if (to == CoreTypes.PhpString) { // -> PhpString EmitConvertToPhpString(from, fromHint); } else if (to == CoreTypes.PhpAlias) { EmitConvertToPhpValue(from, fromHint); Emit_PhpValue_MakeAlias(); } else if (to.IsReferenceType) { if (to == CoreTypes.PhpArray || to == CoreTypes.IPhpArray || to == CoreTypes.IPhpEnumerable || to == CoreTypes.PhpHashtable) { // -> PhpArray // TODO: try unwrap "value.Object as T" EmitConvertToPhpArray(from, fromHint); } else { // -> Object, PhpResource EmitConvertToClass(from, fromHint, to); } } else if (to.IsNullableType(out var ttype)) { EmitConvertToNullable_T(from, fromHint, to, ttype, conversion); } else if (to.SpecialType == SpecialType.System_DateTime) { EmitConvertToDateTime(from); } else { throw this.NotImplementedException($"Conversion from '{from}' to '{to}'"); } } } }
private static bool IsPossiblyByRefTypeParameter(TypeSymbol type) { if (type.IsTypeParameter()) { return true; } if (type.IsErrorType()) { var byRefReturnType = type as ByRefReturnErrorTypeSymbol; return ((object)byRefReturnType != null) && byRefReturnType.ReferencedType.IsTypeParameter(); } return false; }
/// <summary> /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// </summary> private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return type2; } if ((object)type2 == null || type2.IsErrorType()) { return type1; } var t1tot2 = _conversions.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = _conversions.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return type1; } if (type2.IsDynamic()) { return type2; } if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames)) { return MethodTypeInferrer.MergeTupleNames(type1, type2, MethodTypeInferrer.MergeDynamic(type1, type2, type1, _conversions.CorLibrary), _conversions.CorLibrary); } return null; } if (t1tot2) { return type2; } if (t2tot1) { return type1; } return null; }
private static bool IsReallyAType(TypeSymbol type) { return (object)type != null && !type.IsErrorType() && (type.SpecialType != SpecialType.System_Void); }
// Based on SymbolLoader::ResolveBounds. public static TypeParameterBounds ResolveBounds( this TypeParameterSymbol typeParameter, AssemblySymbol corLibrary, ConsList <TypeParameterSymbol> inProgress, ImmutableArray <TypeSymbol> constraintTypes, bool inherited, CSharpCompilation currentCompilation, ArrayBuilder <TypeParameterDiagnosticInfo> diagnosticsBuilder, ref ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder) { Debug.Assert(currentCompilation == null || typeParameter.IsFromCompilation(currentCompilation)); ImmutableArray <NamedTypeSymbol> interfaces; NamedTypeSymbol effectiveBaseClass = corLibrary.GetSpecialType(typeParameter.HasValueTypeConstraint ? SpecialType.System_ValueType : SpecialType.System_Object); TypeSymbol deducedBaseType = effectiveBaseClass; DynamicTypeEraser dynamicEraser = null; if (constraintTypes.Length == 0) { interfaces = ImmutableArray <NamedTypeSymbol> .Empty; } else { var constraintTypesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(); var interfacesBuilder = ArrayBuilder <NamedTypeSymbol> .GetInstance(); var conversions = new TypeConversions(corLibrary); HashSet <DiagnosticInfo> useSiteDiagnostics = null; // Resolve base types, determine the effective base class and // interfaces, and filter out any constraint types that cause cycles. foreach (var constraintType in constraintTypes) { NamedTypeSymbol constraintEffectiveBase; TypeSymbol constraintDeducedBase; switch (constraintType.TypeKind) { case TypeKind.Dynamic: Debug.Assert(inherited || currentCompilation == null); continue; case TypeKind.TypeParameter: { var containingSymbol = typeParameter.ContainingSymbol; var constraintTypeParameter = (TypeParameterSymbol)constraintType; ConsList <TypeParameterSymbol> constraintsInProgress; if (constraintTypeParameter.ContainingSymbol == containingSymbol) { // The constraint type parameter is from the same containing type or method. if (inProgress.ContainsReference(constraintTypeParameter)) { // "Circular constraint dependency involving '{0}' and '{1}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(constraintTypeParameter, new CSDiagnosticInfo(ErrorCode.ERR_CircularConstraint, constraintTypeParameter, typeParameter))); continue; } constraintsInProgress = inProgress; } else { // The constraint type parameter is from a different containing symbol so no cycle. constraintsInProgress = ConsList <TypeParameterSymbol> .Empty; } // Use the calculated bounds from the constraint type parameter. constraintEffectiveBase = constraintTypeParameter.GetEffectiveBaseClass(constraintsInProgress); constraintDeducedBase = constraintTypeParameter.GetDeducedBaseType(constraintsInProgress); AddInterfaces(interfacesBuilder, constraintTypeParameter.GetInterfaces(constraintsInProgress)); if (constraintTypeParameter.HasValueTypeConstraint && !inherited && currentCompilation != null && constraintTypeParameter.IsFromCompilation(currentCompilation)) { // "Type parameter '{1}' has the 'struct' constraint so '{1}' cannot be used as a constraint for '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ConWithValCon, typeParameter, constraintTypeParameter))); continue; } } break; case TypeKind.Interface: case TypeKind.Class: case TypeKind.Delegate: NamedTypeSymbol erasedConstraintType; if (inherited || currentCompilation == null) { // only inherited constraints may contain dynamic if (dynamicEraser == null) { dynamicEraser = new DynamicTypeEraser(corLibrary.GetSpecialType(SpecialType.System_Object)); } erasedConstraintType = (NamedTypeSymbol)dynamicEraser.EraseDynamic(constraintType); } else { Debug.Assert(!constraintType.ContainsDynamic()); Debug.Assert(constraintType.TypeKind != TypeKind.Delegate); erasedConstraintType = (NamedTypeSymbol)constraintType; } if (constraintType.IsInterfaceType()) { AddInterface(interfacesBuilder, erasedConstraintType); constraintTypesBuilder.Add(constraintType); continue; } else { constraintEffectiveBase = erasedConstraintType; constraintDeducedBase = constraintType; break; } case TypeKind.Struct: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_ValueType); constraintDeducedBase = constraintType; break; case TypeKind.Enum: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_Enum); constraintDeducedBase = constraintType; break; case TypeKind.Array: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_Array); constraintDeducedBase = constraintType; break; case TypeKind.Error: constraintEffectiveBase = (NamedTypeSymbol)constraintType; constraintDeducedBase = constraintType; break; case TypeKind.Submission: default: throw ExceptionUtilities.UnexpectedValue(constraintType.TypeKind); } CheckEffectiveAndDeducedBaseTypes(conversions, constraintEffectiveBase, constraintDeducedBase); constraintTypesBuilder.Add(constraintType); // Determine the more encompassed of the current effective base // class and the previously computed effective base class. if (!deducedBaseType.IsErrorType() && !constraintDeducedBase.IsErrorType()) { if (!IsEncompassedBy(conversions, deducedBaseType, constraintDeducedBase, ref useSiteDiagnostics)) { if (!IsEncompassedBy(conversions, constraintDeducedBase, deducedBaseType, ref useSiteDiagnostics)) { // "Type parameter '{0}' inherits conflicting constraints '{1}' and '{2}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BaseConstraintConflict, typeParameter, constraintDeducedBase, deducedBaseType))); } else { deducedBaseType = constraintDeducedBase; effectiveBaseClass = constraintEffectiveBase; } } } } AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder); CheckEffectiveAndDeducedBaseTypes(conversions, effectiveBaseClass, deducedBaseType); constraintTypes = constraintTypesBuilder.ToImmutableAndFree(); interfaces = interfacesBuilder.ToImmutableAndFree(); } Debug.Assert((effectiveBaseClass.SpecialType == SpecialType.System_Object) || (deducedBaseType.SpecialType != SpecialType.System_Object)); // Only create a TypeParameterBounds instance for this type // parameter if the bounds are not the default values. if ((constraintTypes.Length == 0) && (deducedBaseType.SpecialType == SpecialType.System_Object)) { Debug.Assert(effectiveBaseClass.SpecialType == SpecialType.System_Object); Debug.Assert(interfaces.Length == 0); return(null); } var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType); // Additional constraint checks for overrides. if (inherited) { CheckOverrideConstraints(typeParameter, bounds, diagnosticsBuilder); } return(bounds); }
protected BoundLocalDeclaration BindVariableDeclaration( LocalDeclarationKind kind, bool isVar, VariableDeclaratorSyntax declarator, TypeSyntax typeSyntax, TypeSymbol declTypeOpt, AliasSymbol aliasOpt, DiagnosticBag diagnostics, CSharpSyntaxNode associatedSyntaxNode = null) { Debug.Assert(declarator != null); Debug.Assert((object)declTypeOpt != null || isVar); Debug.Assert(typeSyntax != null); // if we are not given desired syntax, we use declarator associatedSyntaxNode = associatedSyntaxNode ?? declarator; bool hasErrors = false; BoundExpression initializerOpt; SourceLocalSymbol localSymbol = this.LookupLocal(declarator.Identifier); // In error scenarios with misplaced code, it is possible we can't bind the local declaration. // This occurs through the semantic model. In that case concoct a plausible result. if ((object)localSymbol == null) { localSymbol = SourceLocalSymbol.MakeLocal( ContainingMemberOrLambda as MethodSymbol, this, typeSyntax, declarator.Identifier, declarator.Initializer, LocalDeclarationKind.Variable); } // Check for variable declaration errors. hasErrors |= this.EnsureDeclarationInvariantMeaningInScope(localSymbol, diagnostics); EqualsValueClauseSyntax equalsValueClauseSyntax = declarator.Initializer; if (isVar) { aliasOpt = null; var binder = new ImplicitlyTypedLocalBinder(this, localSymbol); initializerOpt = binder.BindInferredVariableInitializer(diagnostics, equalsValueClauseSyntax, declarator); // If we got a good result then swap the inferred type for the "var" if (initializerOpt != null && (object)initializerOpt.Type != null) { declTypeOpt = initializerOpt.Type; if (declTypeOpt.SpecialType == SpecialType.System_Void) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, declarator, declTypeOpt); declTypeOpt = CreateErrorType("var"); hasErrors = true; } if (!declTypeOpt.IsErrorType()) { if (declTypeOpt.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, initializerOpt.Type); hasErrors = true; } } } else { declTypeOpt = CreateErrorType("var"); hasErrors = true; } } else { if (ReferenceEquals(equalsValueClauseSyntax, null)) { initializerOpt = null; } else { // Basically inlined BindVariableInitializer, but with conversion optional. initializerOpt = BindPossibleArrayInitializer(equalsValueClauseSyntax.Value, declTypeOpt, diagnostics); if (kind != LocalDeclarationKind.Fixed) { // If this is for a fixed statement, we'll do our own conversion since there are some special cases. initializerOpt = GenerateConversionForAssignment(declTypeOpt, initializerOpt, diagnostics); } } } Debug.Assert((object)declTypeOpt != null); if (kind == LocalDeclarationKind.Fixed) { // NOTE: this is an error, but it won't prevent further binding. if (isVar) { if (!hasErrors) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedLocalCannotBeFixed, declarator); hasErrors = true; } } if (!declTypeOpt.IsPointerType()) { if (!hasErrors) { Error(diagnostics, ErrorCode.ERR_BadFixedInitType, declarator); hasErrors = true; } } else if (!IsValidFixedVariableInitializer(declTypeOpt, localSymbol, ref initializerOpt, diagnostics)) { hasErrors = true; } } if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declTypeOpt.IsRestrictedType()) { Error(diagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declTypeOpt); hasErrors = true; } DeclareLocalVariable( localSymbol, declarator.Identifier, declTypeOpt); Debug.Assert((object)localSymbol != null); // It is possible that we have a bracketed argument list, like "int x[];" or "int x[123];" // in a non-fixed-size-array declaration . This is a common error made by C++ programmers. // We have already given a good error at parse time telling the user to either make it "fixed" // or to move the brackets to the type. However, we should still do semantic analysis of // the arguments, so that errors in them are discovered, hovering over them in the IDE // gives good results, and so on. var arguments = default(ImmutableArray<BoundExpression>); if (declarator.ArgumentList != null) { var builder = ArrayBuilder<BoundExpression>.GetInstance(); foreach (var argument in declarator.ArgumentList.Arguments) { var boundArgument = BindValue(argument.Expression, diagnostics, BindValueKind.RValue); builder.Add(boundArgument); } arguments = builder.ToImmutableAndFree(); } if (kind == LocalDeclarationKind.Fixed || kind == LocalDeclarationKind.Using) { // CONSIDER: The error message is "you must provide an initializer in a fixed // CONSIDER: or using declaration". The error message could be targetted to // CONSIDER: the actual situation. "you must provide an initializer in a // CONSIDER: 'fixed' declaration." if (initializerOpt == null) { Error(diagnostics, ErrorCode.ERR_FixedMustInit, declarator); hasErrors = true; } } else if (kind == LocalDeclarationKind.Constant && initializerOpt != null) { foreach (var diagnostic in localSymbol.GetConstantValueDiagnostics(initializerOpt)) { diagnostics.Add(diagnostic); hasErrors = true; } } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt); return new BoundLocalDeclaration(associatedSyntaxNode, localSymbol, boundDeclType, initializerOpt, arguments, hasErrors); }
/// <summary> /// Check that the pattern type is valid for the operand. Return true if an error was reported. /// </summary> private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { Debug.Assert((object)operandType != null); Debug.Assert((object)patternType != null); if (operandType.IsErrorType() || patternType.IsErrorType()) { return(false); } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return(true); } else if (patternType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType); return(true); } else if (!isVar) { if (patternType.IsDynamic()) { Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax); return(true); } HashSet <DiagnosticInfo> useSiteDiagnostics = null; var matchPossible = ExpressionOfTypeMatchesPatternType(Conversions, operandType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true); diagnostics.Add(typeSyntax, useSiteDiagnostics); if (matchPossible != false) { if (!conversion.Exists && (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter())) { // permit pattern-matching when one of the types is an open type in C# 7.1. LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion(); if (requiredVersion > Compilation.LanguageVersion) { Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax, operandType, patternType, Compilation.LanguageVersion.ToDisplayString(), new CSharpRequiredLanguageVersion(requiredVersion)); return(true); } } } else { Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); return(true); } } return(false); }
private static bool DoSignaturesMatch( PEModuleSymbol moduleSymbol, TypeSymbol eventType, PEMethodSymbol addMethod, PEMethodSymbol removeMethod) { return (eventType.IsDelegateType() || eventType.IsErrorType()) && DoesSignatureMatch(moduleSymbol, eventType, addMethod) && DoesSignatureMatch(moduleSymbol, eventType, removeMethod) && DoModifiersMatch(addMethod, removeMethod); }
private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, BoundExpression operand, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { Debug.Assert((object)operandType != null); Debug.Assert((object)patternType != null); if (operandType.IsErrorType() || patternType.IsErrorType()) { return false; } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return true; } else if (patternType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType); return true; } else if (!isVar) { HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = operand != null ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast: true) : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true); diagnostics.Add(typeSyntax, useSiteDiagnostics); switch (conversion.Kind) { case ConversionKind.Boxing: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.Unboxing: case ConversionKind.NullLiteral: case ConversionKind.ImplicitNullable: // these are the conversions allowed by a pattern match break; //case ConversionKind.ExplicitNumeric: // we do not perform numeric conversions of the operand //case ConversionKind.ImplicitConstant: //case ConversionKind.ImplicitNumeric: default: Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); return true; } } return false; }
/// <summary> /// Determines what type of conversion, if any, would be used if a given expression was /// converted to a given type using an explicit cast. /// </summary> /// <param name="position">The character position for determining the enclosing declaration /// scope and accessibility.</param> /// <param name="expression">The expression to classify. This expression does not need to be /// present in the syntax tree associated with this object.</param> /// <param name="destination">The type to attempt conversion to.</param> /// <returns>Returns a Conversion object that summarizes whether the conversion was /// possible, and if so, what kind of conversion it was. If no conversion was possible, a /// Conversion object with a false "Exists" property is returned.</returns> /// <remarks>To determine the conversion between two types (instead of an expression and a /// type), use Compilation.ClassifyConversion.</remarks> internal Conversion ClassifyConversionForCast(int position, ExpressionSyntax expression, TypeSymbol destination) { if ((object)destination == null) { throw new ArgumentNullException(nameof(destination)); } position = CheckAndAdjustPosition(position); var binder = this.GetEnclosingBinder(position); if (binder != null) { var diagnostics = DiagnosticBag.GetInstance(); var bnode = binder.BindExpression(expression, diagnostics); diagnostics.Free(); if (bnode != null && !destination.IsErrorType()) { HashSet<DiagnosticInfo> useSiteDiagnostics = null; return binder.Conversions.ClassifyConversionForCast(bnode, destination, ref useSiteDiagnostics); } } return Conversion.NoConversion; }
protected static void GenerateImplicitConversionError(DiagnosticBag diagnostics, Compilation compilation, CSharpSyntaxNode syntax, Conversion conversion, TypeSymbol sourceType, TypeSymbol targetType, ConstantValue sourceConstantValueOpt = null) { Debug.Assert(!conversion.IsImplicit || !conversion.IsValid); // If the either type is an error then an error has already been reported // for some aspect of the analysis of this expression. (For example, something like // "garbage g = null; short s = g;" -- we don't want to report that g is not // convertible to short because we've already reported that g does not have a good type. if (!sourceType.IsErrorType() && !targetType.IsErrorType()) { if (conversion.IsExplicit) { if (sourceType.SpecialType == SpecialType.System_Double && syntax.Kind() == SyntaxKind.NumericLiteralExpression && (targetType.SpecialType == SpecialType.System_Single || targetType.SpecialType == SpecialType.System_Decimal)) { Error(diagnostics, ErrorCode.ERR_LiteralDoubleCast, syntax, (targetType.SpecialType == SpecialType.System_Single) ? "F" : "M", targetType); } else if (conversion.Kind == ConversionKind.ExplicitNumeric && sourceConstantValueOpt != null && sourceConstantValueOpt != ConstantValue.Bad && ConversionsBase.HasImplicitConstantExpressionConversion(new BoundLiteral(syntax, ConstantValue.Bad, sourceType), targetType)) { // CLEVERNESS: By passing ConstantValue.Bad, we tell HasImplicitConstantExpressionConversion to ignore the constant // value and only consider the types. // If there would be an implicit constant conversion for a different constant of the same type // (i.e. one that's not out of range), then it's more helpful to report the range check failure // than to suggest inserting a cast. Error(diagnostics, ErrorCode.ERR_ConstOutOfRange, syntax, sourceConstantValueOpt.Value, targetType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, sourceType, targetType); Error(diagnostics, ErrorCode.ERR_NoImplicitConvCast, syntax, distinguisher.First, distinguisher.Second); } } else if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure) { Debug.Assert(conversion.IsUserDefined); ImmutableArray<MethodSymbol> originalUserDefinedConversions = conversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { Error(diagnostics, ErrorCode.ERR_AmbigUDConv, syntax, originalUserDefinedConversions[0], originalUserDefinedConversions[1], sourceType, targetType); } else { Debug.Assert(originalUserDefinedConversions.Length == 0, "How can there be exactly one applicable user-defined conversion if the conversion doesn't exist?"); SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, sourceType, targetType); Error(diagnostics, ErrorCode.ERR_NoImplicitConv, syntax, distinguisher.First, distinguisher.Second); } } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, sourceType, targetType); Error(diagnostics, ErrorCode.ERR_NoImplicitConv, syntax, distinguisher.First, distinguisher.Second); } } }
protected BoundLocalDeclaration BindVariableDeclaration( SourceLocalSymbol localSymbol, LocalDeclarationKind kind, bool isVar, VariableDeclaratorSyntax declarator, TypeSyntax typeSyntax, TypeSymbol declTypeOpt, AliasSymbol aliasOpt, DiagnosticBag diagnostics, CSharpSyntaxNode associatedSyntaxNode = null) { Debug.Assert(declarator != null); Debug.Assert((object)declTypeOpt != null || isVar); Debug.Assert(typeSyntax != null); var localDiagnostics = DiagnosticBag.GetInstance(); // if we are not given desired syntax, we use declarator associatedSyntaxNode = associatedSyntaxNode ?? declarator; bool hasErrors = false; BoundExpression initializerOpt; // Check for variable declaration errors. hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, localDiagnostics); EqualsValueClauseSyntax equalsValueClauseSyntax = declarator.Initializer; if (isVar) { aliasOpt = null; var binder = new ImplicitlyTypedLocalBinder(this, localSymbol); initializerOpt = binder.BindInferredVariableInitializer(localDiagnostics, equalsValueClauseSyntax, declarator); // If we got a good result then swap the inferred type for the "var" if ((object)initializerOpt?.Type != null) { declTypeOpt = initializerOpt.Type; if (declTypeOpt.SpecialType == SpecialType.System_Void) { Error(localDiagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, declarator, declTypeOpt); declTypeOpt = CreateErrorType("var"); hasErrors = true; } if (!declTypeOpt.IsErrorType()) { if (declTypeOpt.IsStatic) { Error(localDiagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, initializerOpt.Type); hasErrors = true; } } } else { declTypeOpt = CreateErrorType("var"); hasErrors = true; } } else { if (ReferenceEquals(equalsValueClauseSyntax, null)) { initializerOpt = null; } else { // Basically inlined BindVariableInitializer, but with conversion optional. initializerOpt = BindPossibleArrayInitializer(equalsValueClauseSyntax.Value, declTypeOpt, localDiagnostics); if (kind != LocalDeclarationKind.FixedVariable) { // If this is for a fixed statement, we'll do our own conversion since there are some special cases. initializerOpt = GenerateConversionForAssignment(declTypeOpt, initializerOpt, localDiagnostics); } } } Debug.Assert((object)declTypeOpt != null); if (kind == LocalDeclarationKind.FixedVariable) { // NOTE: this is an error, but it won't prevent further binding. if (isVar) { if (!hasErrors) { Error(localDiagnostics, ErrorCode.ERR_ImplicitlyTypedLocalCannotBeFixed, declarator); hasErrors = true; } } if (!declTypeOpt.IsPointerType()) { if (!hasErrors) { Error(localDiagnostics, ErrorCode.ERR_BadFixedInitType, declarator); hasErrors = true; } } else if (!IsValidFixedVariableInitializer(declTypeOpt, localSymbol, ref initializerOpt, localDiagnostics)) { hasErrors = true; } } if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declTypeOpt.IsRestrictedType()) { Error(localDiagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declTypeOpt); hasErrors = true; } DeclareLocalVariable( localSymbol, declarator.Identifier, declTypeOpt); Debug.Assert((object)localSymbol != null); ImmutableArray<BoundExpression> arguments = BindDeclaratorArguments(declarator, localDiagnostics); if (kind == LocalDeclarationKind.FixedVariable || kind == LocalDeclarationKind.UsingVariable) { // CONSIDER: The error message is "you must provide an initializer in a fixed // CONSIDER: or using declaration". The error message could be targetted to // CONSIDER: the actual situation. "you must provide an initializer in a // CONSIDER: 'fixed' declaration." if (initializerOpt == null) { Error(localDiagnostics, ErrorCode.ERR_FixedMustInit, declarator); hasErrors = true; } } else if (kind == LocalDeclarationKind.Constant && initializerOpt != null && !localDiagnostics.HasAnyResolvedErrors()) { var constantValueDiagnostics = localSymbol.GetConstantValueDiagnostics(initializerOpt); foreach (var diagnostic in constantValueDiagnostics) { diagnostics.Add(diagnostic); hasErrors = true; } } diagnostics.AddRangeAndFree(localDiagnostics); var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt); return new BoundLocalDeclaration(associatedSyntaxNode, localSymbol, boundDeclType, initializerOpt, arguments, hasErrors); }
private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return type2; } if ((object)type2 == null || type2.IsErrorType()) { return type1; } var t1tot2 = _conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = _conversions.ClassifyImplicitConversion(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return type1; } if (type2.IsDynamic()) { return type2; } return null; } if (t1tot2) { return type2; } if (t2tot1) { return type1; } return null; }
internal void SetTypeSymbol(TypeSymbol newType) { TypeSymbol originalType = this.type; // In the event that we race to set the type of a local, we should // always deduce the same type, or deduce that the type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() && newType.IsErrorType() || originalType == newType); if ((object)originalType == null) { Interlocked.CompareExchange(ref this.type, newType, null); } }
private static bool IsPossiblyByRefTypeParameter(TypeSymbol type) { if (type.IsTypeParameter()) { return true; } if (type.IsErrorType()) { //var byRefReturnType = type as ByRefReturnErrorTypeSymbol; //return ((object)byRefReturnType != null) && byRefReturnType.ReferencedType.IsTypeParameter(); throw new NotImplementedException(); } return false; }
internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNode syntax, SyntaxToken usingKeyword, SyntaxToken awaitKeyword, Binder originalBinder, UsingStatementBinder usingBinderOpt, DiagnosticBag diagnostics) { bool isUsingDeclaration = syntax.Kind() == SyntaxKind.LocalDeclarationStatement; bool isExpression = !isUsingDeclaration && syntax.Kind() != SyntaxKind.VariableDeclaration; bool hasAwait = awaitKeyword != default; Debug.Assert(isUsingDeclaration || usingBinderOpt != null); TypeSymbol disposableInterface = getDisposableInterface(hasAwait); Debug.Assert((object)disposableInterface != null); bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword); Conversion iDisposableConversion = Conversion.NoConversion; ImmutableArray <BoundLocalDeclaration> declarationsOpt = default; BoundMultipleLocalDeclarations multipleDeclarationsOpt = null; BoundExpression expressionOpt = null; AwaitableInfo awaitOpt = null; TypeSymbol declarationTypeOpt = null; MethodSymbol disposeMethodOpt = null; TypeSymbol awaitableTypeOpt = null; if (isExpression) { expressionOpt = usingBinderOpt.BindTargetExpression(diagnostics, originalBinder); hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: true); } else { VariableDeclarationSyntax declarationSyntax = isUsingDeclaration ? ((LocalDeclarationStatementSyntax)syntax).Declaration : (VariableDeclarationSyntax)syntax; originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarationsOpt); Debug.Assert(!declarationsOpt.IsEmpty); multipleDeclarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarationsOpt); declarationTypeOpt = declarationsOpt[0].DeclaredType.Type; if (declarationTypeOpt.IsDynamic()) { iDisposableConversion = Conversion.ImplicitDynamic; } else { hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: false); } } if (hasAwait) { BoundAwaitableValuePlaceholder placeholderOpt; if (awaitableTypeOpt is null) { placeholderOpt = null; } else { hasErrors |= ReportUseSiteDiagnostics(awaitableTypeOpt, diagnostics, awaitKeyword); placeholderOpt = new BoundAwaitableValuePlaceholder(syntax, awaitableTypeOpt).MakeCompilerGenerated(); } // even if we don't have a proper value to await, we'll still report bad usages of `await` awaitOpt = originalBinder.BindAwaitInfo(placeholderOpt, syntax, awaitKeyword.GetLocation(), diagnostics, ref hasErrors); } // This is not awesome, but its factored. // In the future it might be better to have a seperate shared type that we add the info to, and have the callers create the appropriate bound nodes from it if (isUsingDeclaration) { return(new BoundUsingLocalDeclarations(syntax, disposeMethodOpt, iDisposableConversion, awaitOpt, declarationsOpt, hasErrors)); } else { BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(usingBinderOpt._syntax.Statement, diagnostics); return(new BoundUsingStatement( usingBinderOpt._syntax, usingBinderOpt.Locals, multipleDeclarationsOpt, expressionOpt, iDisposableConversion, boundBody, awaitOpt, disposeMethodOpt, hasErrors)); } // initializes iDisposableConversion, awaitableTypeOpt and disposeMethodOpt bool populateDisposableConversionOrDisposeMethod(bool fromExpression) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics); diagnostics.Add(syntax, useSiteDiagnostics); if (iDisposableConversion.IsImplicit) { if (hasAwait) { awaitableTypeOpt = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask); } return(true); } TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt; // If this is a ref struct, or we're in a valid asynchronous using, try binding via pattern. // We won't need to try and bind a second time if it fails, as async dispose can't be pattern based (ref structs are not allowed in async methods) if (!(type is null) && (type.IsRefLikeType || hasAwait)) { BoundExpression receiver = fromExpression ? expressionOpt : new BoundLocal(syntax, declarationsOpt[0].LocalSymbol, null, type) { WasCompilerGenerated = true }; disposeMethodOpt = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, diagnostics); if (!(disposeMethodOpt is null)) { if (hasAwait) { awaitableTypeOpt = disposeMethodOpt.ReturnType; } return(true); } } if (type is null || !type.IsErrorType()) { // Retry with a different assumption about whether the `using` is async TypeSymbol alternateInterface = getDisposableInterface(!hasAwait); HashSet <DiagnosticInfo> ignored = null; Conversion alternateConversion = classifyConversion(fromExpression, alternateInterface, ref ignored); bool wrongAsync = alternateConversion.IsImplicit; ErrorCode errorCode = wrongAsync ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync) : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp); Error(diagnostics, errorCode, syntax, declarationTypeOpt ?? expressionOpt.Display); } return(false); } Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag) { return(fromExpression ? originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) : originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag)); } TypeSymbol getDisposableInterface(bool isAsync) { return(isAsync ? originalBinder.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : originalBinder.Compilation.GetSpecialType(SpecialType.System_IDisposable)); } }
private static void CheckEffectiveAndDeducedBaseTypes(ConversionsBase conversions, TypeSymbol effectiveBase, TypeSymbol deducedBase) { Debug.Assert((object)deducedBase != null); Debug.Assert((object)effectiveBase != null); HashSet<DiagnosticInfo> useSiteDiagnostics = null; Debug.Assert(deducedBase.IsErrorType() || effectiveBase.IsErrorType() || conversions.HasIdentityOrImplicitReferenceConversion(deducedBase, effectiveBase, ref useSiteDiagnostics) || conversions.HasBoxingConversion(deducedBase, effectiveBase, ref useSiteDiagnostics)); }
/// <summary> /// Emits conversion to an object of given type. /// </summary> /// <param name="from">Type of value on top of the evaluation stack.</param> /// <param name="fromHint">Hint in case of multitype value.</param> /// <param name="to">Target type.</param> private void EmitConvertToClass(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to) { Contract.ThrowIfNull(from); Contract.ThrowIfNull(to); Debug.Assert(to.IsReferenceType); Debug.Assert(to != CoreTypes.PhpAlias); Debug.Assert(!to.IsErrorType(), "Trying to convert to an ErrorType"); // -> IPhpCallable if (to == CoreTypes.IPhpCallable) { EmitConvertToIPhpCallable(from, fromHint); return; } // -> System.Array if (to.IsArray()) { var arrt = (ArrayTypeSymbol)to; if (arrt.IsSZArray) { // byte[] if (arrt.ElementType.SpecialType == SpecialType.System_Byte) { // Template: (PhpString).ToBytes(Context) EmitConvertToPhpString(from, fromHint); // PhpString EmitPhpStringAddr(); this.EmitLoadContext(); // Context EmitCall(ILOpCode.Call, CoreMethods.PhpString.ToBytes_Context) .Expect(to); // ToBytes() return; } throw this.NotImplementedException($"Conversion from {from.Name} to {arrt.ElementType.Name}[] is not implemented."); } throw this.NotImplementedException($"Conversion from {from.Name} to array {to.Name} is not implemented."); } // dereference if (from == CoreTypes.PhpAlias) { // <alias>.Value.AsObject() : object Emit_PhpAlias_GetValueAddr(); from = EmitCall(ILOpCode.Call, CoreMethods.PhpValue.AsObject) .Expect(SpecialType.System_Object); } if (from.IsReferenceType && from.IsOfType(to)) { return; } Debug.Assert(to != CoreTypes.PhpArray && to != CoreTypes.PhpString && to != CoreTypes.PhpAlias); switch (from.SpecialType) { case SpecialType.System_Void: case SpecialType.System_Int32: case SpecialType.System_Int64: case SpecialType.System_Boolean: case SpecialType.System_Double: case SpecialType.System_String: // Template: null EmitPop(from); _il.EmitNullConstant(); return; default: Debug.Assert(from != CoreTypes.PhpAlias); if (from.IsValueType) { if (from == CoreTypes.PhpValue) { if (IsClassOnly(fromHint)) { // <STACK>.Object EmitPhpValueAddr(); from = EmitCall(ILOpCode.Call, CoreMethods.PhpValue.Object.Getter) .Expect(SpecialType.System_Object); } else { // Convert.AsObject( <STACK> ) from = EmitCall(ILOpCode.Call, CoreMethods.Operators.AsObject_PhpValue) .Expect(SpecialType.System_Object); } } else { // null EmitPop(from); _il.EmitNullConstant(); return; } } // break; } // Template: (T)object EmitCastClass(from, to); }
// See TypeBind::CheckSingleConstraint. private static bool CheckConstraints( Symbol containingSymbol, ConversionsBase conversions, TypeMap substitution, TypeParameterSymbol typeParameter, TypeSymbol typeArgument, Compilation currentCompilation, ArrayBuilder<TypeParameterDiagnosticInfo> diagnosticsBuilder, ref ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder, HashSet<TypeParameterSymbol> ignoreTypeConstraintsDependentOnTypeParametersOpt) { Debug.Assert(substitution != null); // The type parameters must be original definitions of type parameters from the containing symbol. Debug.Assert(ReferenceEquals(typeParameter.ContainingSymbol, containingSymbol.OriginalDefinition)); if (typeArgument.IsErrorType()) { return true; } if (typeArgument.IsPointerType() || typeArgument.IsRestrictedType() || typeArgument.SpecialType == SpecialType.System_Void) { // "The type '{0}' may not be used as a type argument" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BadTypeArgument, typeArgument))); return false; } if (typeArgument.IsStatic) { // "'{0}': static types cannot be used as type arguments" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_GenericArgIsStaticClass, typeArgument))); return false; } if (typeParameter.HasReferenceTypeConstraint && !typeArgument.IsReferenceType) { // "The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_RefConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); return false; } if (typeParameter.HasValueTypeConstraint && !typeArgument.IsNonNullableValueType()) { // "The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ValConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); return false; } // The type parameters for a constructed type/method are the type parameters of // the ConstructedFrom type/method, so the constraint types are not substituted. // For instance with "class C<T, U> where T : U", the type parameter for T in "C<object, int>" // has constraint "U", not "int". We need to substitute the constraints from the // original definition of the type parameters using the map from the constructed symbol. var constraintTypes = ArrayBuilder<TypeSymbol>.GetInstance(); HashSet<DiagnosticInfo> useSiteDiagnostics = null; substitution.SubstituteTypesDistinctWithoutModifiers(typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics), constraintTypes, ignoreTypeConstraintsDependentOnTypeParametersOpt); bool hasError = false; foreach (var constraintType in constraintTypes) { if (SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) { continue; } ErrorCode errorCode; if (typeArgument.IsReferenceType) { errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; } else if (typeArgument.IsNullableType()) { errorCode = constraintType.IsInterfaceType() ? ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface : ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum; } else if (typeArgument.TypeKind == TypeKind.TypeParameter) { errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; } else { errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; } SymbolDistinguisher distinguisher = new SymbolDistinguisher(currentCompilation, constraintType, typeArgument); diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(errorCode, containingSymbol.ConstructedFrom(), distinguisher.First, typeParameter, distinguisher.Second))); hasError = true; } if (AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder)) { hasError = true; } constraintTypes.Free(); // Check the constructor constraint. if (typeParameter.HasConstructorConstraint && !SatisfiesConstructorConstraint(typeArgument)) { // "'{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_NewConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); return false; } return !hasError; }
private static bool SatisfiesConstraintType( ConversionsBase conversions, TypeSymbol typeArgument, TypeSymbol constraintType, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { if (constraintType.IsErrorType()) { return false; } // Spec 4.4.4 describes the valid conversions from // type argument A to constraint type C: // "An identity conversion (6.1.1). // An implicit reference conversion (6.1.6). ..." if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument, constraintType, ref useSiteDiagnostics)) { return true; } // "... A boxing conversion (6.1.7), provided that type A is a non-nullable value type. ..." // NOTE: we extend this to allow, for example, a conversion from Nullable<T> to object. if (typeArgument.IsValueType && conversions.HasBoxingConversion(typeArgument.IsNullableType() ? ((NamedTypeSymbol)typeArgument).ConstructedFrom : typeArgument, constraintType, ref useSiteDiagnostics)) { return true; } if (typeArgument.TypeKind == TypeKind.TypeParameter) { var typeParameter = (TypeParameterSymbol)typeArgument; // "... An implicit reference, boxing, or type parameter conversion // from type parameter A to C." if (conversions.HasImplicitTypeParameterConversion(typeParameter, constraintType, ref useSiteDiagnostics)) { return true; } // TypeBind::SatisfiesBound allows cases where one of the // type parameter constraints satisfies the constraint. foreach (var typeArgumentConstraint in typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { if (SatisfiesConstraintType(conversions, typeArgumentConstraint, constraintType, ref useSiteDiagnostics)) { return true; } } } return false; }
/// <summary> /// Emits conversion from one CLR type to another using PHP conventions. /// </summary> /// <param name="from">Type of value on top of evaluation stack.</param> /// <param name="fromHint">Type hint in case of a multityple type choices (like PhpValue or PhpNumber or PhpAlias).</param> /// <param name="to">Target CLR type.</param> public void EmitConvert(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to) { Contract.ThrowIfNull(from); Contract.ThrowIfNull(to); Debug.Assert(!from.IsUnreachable); Debug.Assert(!to.IsUnreachable); Debug.Assert(!to.IsErrorType(), "Conversion to an error type."); // conversion is not needed: if (from.SpecialType == to.SpecialType && (from == to || (to.SpecialType != SpecialType.System_Object && from.IsOfType(to)))) { return; } if (from.SpecialType == SpecialType.System_Void) { // void -> T EmitLoadDefault(to); return; } // from = EmitSpecialize(from, fromHint); if (!this.TryEmitImplicitConversion(from, to)) { // specialized conversions: if (to == CoreTypes.PhpValue) { EmitConvertToPhpValue(from, fromHint); } else if (to == CoreTypes.PhpString) { // -> PhpString EmitConvertToPhpString(from, fromHint); } else if (to == CoreTypes.PhpAlias) { EmitConvertToPhpValue(from, fromHint); Emit_PhpValue_MakeAlias(); } else if (to.IsReferenceType) { if (to == CoreTypes.PhpArray || to == CoreTypes.IPhpArray || to == CoreTypes.IPhpEnumerable || to == CoreTypes.PhpHashtable) { // -> PhpArray // TODO: try unwrap "value.Object as T" EmitConvertToPhpArray(from, fromHint); } else { // -> Object, PhpResource EmitConvertToClass(from, fromHint, to); } } else if (to.IsNullableType(out var ttype)) { // Template: new Nullable<T>( (T)from ) EmitConvert(from, fromHint, ttype); EmitCall(ILOpCode.Newobj, ((NamedTypeSymbol)to).InstanceConstructors[0]); } else if (to.SpecialType == SpecialType.System_DateTime) { EmitConvertToDateTime(from); } else { throw this.NotImplementedException($"Conversion from '{from}' to '{to}'"); } } }
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)); }
// See TypeBind::CheckSingleConstraint. private static bool CheckConstraints( Symbol containingSymbol, ConversionsBase conversions, TypeMap substitution, TypeParameterSymbol typeParameter, TypeSymbol typeArgument, Compilation currentCompilation, ArrayBuilder <TypeParameterDiagnosticInfo> diagnosticsBuilder, ref ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder) { Debug.Assert(substitution != null); // The type parameters must be original definitions of type parameters from the containing symbol. Debug.Assert(ReferenceEquals(typeParameter.ContainingSymbol, containingSymbol.OriginalDefinition)); if (typeArgument.IsErrorType()) { return(true); } if (typeArgument.IsPointerType() || typeArgument.IsRestrictedType() || typeArgument.SpecialType == SpecialType.System_Void) { // "The type '{0}' may not be used as a type argument" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BadTypeArgument, typeArgument))); return(false); } if (typeArgument.IsStatic) { // "'{0}': static types cannot be used as type arguments" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_GenericArgIsStaticClass, typeArgument))); return(false); } if (typeParameter.HasReferenceTypeConstraint && !typeArgument.IsReferenceType) { // "The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_RefConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); return(false); } if (typeParameter.HasValueTypeConstraint && !typeArgument.IsNonNullableValueType()) { // "The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ValConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); return(false); } // The type parameters for a constructed type/method are the type parameters of // the ConstructedFrom type/method, so the constraint types are not substituted. // For instance with "class C<T, U> where T : U", the type parameter for T in "C<object, int>" // has constraint "U", not "int". We need to substitute the constraints from the // original definition of the type parameters using the map from the constructed symbol. var constraintTypes = ArrayBuilder <TypeSymbol> .GetInstance(); HashSet <DiagnosticInfo> useSiteDiagnostics = null; substitution.SubstituteTypesDistinctWithoutModifiers(typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics), constraintTypes); bool hasError = false; foreach (var constraintType in constraintTypes) { if (SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) { continue; } ErrorCode errorCode; if (typeArgument.IsReferenceType) { errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; } else if (typeArgument.IsNullableType()) { errorCode = constraintType.IsInterfaceType() ? ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface : ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum; } else if (typeArgument.TypeKind == TypeKind.TypeParameter) { errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; } else { errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; } SymbolDistinguisher distinguisher = new SymbolDistinguisher(currentCompilation, constraintType, typeArgument); diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(errorCode, containingSymbol.ConstructedFrom(), distinguisher.First, typeParameter, distinguisher.Second))); hasError = true; } if (AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder)) { hasError = true; } constraintTypes.Free(); // Check the constructor constraint. if (typeParameter.HasConstructorConstraint && !SatisfiesConstructorConstraint(typeArgument)) { // "'{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_NewConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); return(false); } return(!hasError); }