private BoundExpression MakeFieldAccess( CSharpSyntaxNode syntax, BoundExpression rewrittenReceiver, FieldSymbol fieldSymbol, ConstantValue constantValueOpt, LookupResultKind resultKind, TypeSymbol type, BoundFieldAccess oldNodeOpt = null) { if (fieldSymbol.IsTupleField) { return MakeTupleFieldAccess(syntax, fieldSymbol, rewrittenReceiver, constantValueOpt, resultKind); } BoundExpression result = oldNodeOpt != null ? oldNodeOpt.Update(rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type) : new BoundFieldAccess(syntax, rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type); if (fieldSymbol.IsFixed) { // a reference to a fixed buffer is translated into its address result = new BoundConversion(syntax, new BoundAddressOfOperator(syntax, result, syntax != null && SyntaxFacts.IsFixedStatementExpression(syntax), type, false), new Conversion(ConversionKind.PointerToPointer), false, false, default(ConstantValue), type, false); } return result; }
public override BoundNode VisitConversion(BoundConversion node) { var rewrittenType = VisitType(node.Type); bool wasInExpressionLambda = inExpressionLambda; if (node.ConversionKind == ConversionKind.AnonymousFunction && !wasInExpressionLambda && rewrittenType.IsExpressionTree()) inExpressionLambda = true; var rewrittenOperand = VisitExpression(node.Operand); inExpressionLambda = wasInExpressionLambda; var result = MakeConversion(node, node.Syntax, rewrittenOperand, node.ConversionKind, node.SymbolOpt, node.Checked, node.ExplicitCastInCode, node.IsExtensionMethod, node.IsArrayIndex, node.ConstantValue, rewrittenType); var toType = node.Type; Debug.Assert(result.Type == toType); // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used. // It means that explicit casts to (double) or (float) should be preserved on the node. // If original conversion has become something else with unknown precision, add an explicit identity cast. if (!inExpressionLambda && node.ExplicitCastInCode && IsFloatPointExpressionOfUnknownPrecision(result)) { result = MakeConversion( node.Syntax, result, Conversion.Identity, toType, @checked: false, explicitCastInCode: true, constantValueOpt: result.ConstantValue); } return result; }
public override BoundNode VisitConversion(BoundConversion conversion) { if (conversion.ConversionKind == ConversionKind.MethodGroup && conversion.SymbolOpt?.MethodKind == MethodKind.LocalFunction) { BoundExpression receiver; MethodSymbol method; var arguments = default(ImmutableArray<BoundExpression>); _lambdaRewriter.RemapLocalFunction( conversion.Syntax, conversion.SymbolOpt, out receiver, out method, ref arguments); return new BoundDelegateCreationExpression( conversion.Syntax, receiver, method, isExtensionMethod: false, type: conversion.Type); } return base.VisitConversion(conversion); }
public BoundReturnStatement Return(BoundExpression expression = null) { if (expression != null) { // If necessary, add a conversion on the return expression. HashSet <DiagnosticInfo> useSiteDiagnostics = null; var conversion = Compilation.Conversions.ClassifyConversion(expression.Type, CurrentMethod.ReturnType, ref useSiteDiagnostics); Debug.Assert(useSiteDiagnostics.IsNullOrEmpty()); Debug.Assert(conversion.Kind != ConversionKind.NoConversion); if (conversion.Kind != ConversionKind.Identity) { expression = BoundConversion.Synthesized(Syntax, expression, conversion, false, false, ConstantValue.NotAvailable, CurrentMethod.ReturnType); } } return(new BoundReturnStatement(Syntax, expression) { WasCompilerGenerated = true }); }
public override BoundNode VisitConversion(BoundConversion node) { if (node.ConversionKind == ConversionKind.MethodGroup) { if (node.SymbolOpt?.MethodKind == MethodKind.LocalFunction) { // Use OriginalDefinition to strip generic type parameters ReferenceVariable(node.Syntax, node.SymbolOpt.OriginalDefinition); MethodsConvertedToDelegates.Add(node.SymbolOpt.OriginalDefinition); } if (node.IsExtensionMethod || ((object)node.SymbolOpt != null && !node.SymbolOpt.IsStatic)) { return(VisitSyntaxWithReceiver(node.Syntax, ((BoundMethodGroup)node.Operand).ReceiverOpt)); } return(null); } else { return(base.VisitConversion(node)); } }
public override BoundNode VisitConversion(BoundConversion conversion) { if (conversion.ConversionKind == ConversionKind.MethodGroup && conversion.SymbolOpt?.MethodKind == MethodKind.LocalFunction) { BoundExpression receiver; MethodSymbol method; var arguments = default(ImmutableArray <BoundExpression>); _lambdaRewriter.RemapLocalFunction( conversion.Syntax, conversion.SymbolOpt, out receiver, out method, ref arguments); var newType = _lambdaRewriter.VisitType(conversion.Type); return(new BoundDelegateCreationExpression( conversion.Syntax, receiver, method, isExtensionMethod: false, type: newType)); } return(base.VisitConversion(conversion)); }
private static bool IsInstanceFieldAccessWithNonThisReceiver(BoundFieldAccess fieldAccess) { BoundExpression receiver = fieldAccess.ReceiverOpt; if (receiver == null || fieldAccess.FieldSymbol.IsStatic) { return(false); } while (receiver.Kind == BoundKind.Conversion) { BoundConversion conversion = (BoundConversion)receiver; if (conversion.ExplicitCastInCode) { break; } receiver = conversion.Operand; } return(receiver.Kind != BoundKind.ThisReference && receiver.Kind != BoundKind.BaseReference); }
public override BoundNode VisitConversion(BoundConversion node) { CheckUnsafeType(node.Operand); CheckUnsafeType(node); bool wasInExpressionLambda = _inExpressionLambda; bool oldReportedUnsafe = _reportedUnsafe; switch (node.ConversionKind) { case ConversionKind.MethodGroup: VisitMethodGroup((BoundMethodGroup)node.Operand, parentIsConversion: true); return(node); case ConversionKind.AnonymousFunction: if (!wasInExpressionLambda && node.Type.IsExpressionTree()) { _inExpressionLambda = true; // we report "unsafe in expression tree" at most once for each expression tree _reportedUnsafe = false; } break; case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node); } break; default: break; } var result = base.VisitConversion(node); _inExpressionLambda = wasInExpressionLambda; _reportedUnsafe = oldReportedUnsafe; return(result); }
private static ConstantValue GetConstantValueForBitwiseOrCheck(BoundExpression operand) { // We might have a nullable conversion on top of an integer constant. But only dig out // one level. if (operand.Kind == BoundKind.Conversion) { BoundConversion conv = (BoundConversion)operand; if (conv.ConversionKind == ConversionKind.ImplicitNullable) { operand = conv.Operand; } } ConstantValue constVal = operand.ConstantValue; if (constVal == null || !constVal.IsIntegral) { return(null); } return(constVal); }
private static BoundExpression MakeFieldAccess( CSharpSyntaxNode syntax, BoundExpression rewrittenReceiver, FieldSymbol fieldSymbol, ConstantValue constantValueOpt, LookupResultKind resultKind, TypeSymbol type, BoundFieldAccess oldNodeOpt = null) { BoundExpression result = oldNodeOpt != null? oldNodeOpt.Update(rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type) : new BoundFieldAccess(syntax, rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type); if (fieldSymbol.IsFixed) { // a reference to a fixed buffer is translated into its address result = new BoundConversion(syntax, new BoundAddressOfOperator(syntax, result, syntax != null && SyntaxFacts.IsFixedStatementExpression(syntax), type, false), new Conversion(ConversionKind.PointerToPointer), false, false, default(ConstantValue), type, false); } return(result); }
public override BoundNode VisitConversion(BoundConversion node) { var rewrittenType = VisitType(node.Type); if (node.ConversionKind == ConversionKind.InterpolatedString) { return RewriteInterpolatedStringConversion(node); } bool wasInExpressionLambda = _inExpressionLambda; _inExpressionLambda = _inExpressionLambda || (node.ConversionKind == ConversionKind.AnonymousFunction && !wasInExpressionLambda && rewrittenType.IsExpressionTree()); var rewrittenOperand = VisitExpression(node.Operand); _inExpressionLambda = wasInExpressionLambda; var result = MakeConversionNode(node, node.Syntax, rewrittenOperand, node.Conversion, node.Checked, node.ExplicitCastInCode, node.ConstantValue, rewrittenType); var toType = node.Type; Debug.Assert(result.Type.Equals(toType, TypeCompareKind.IgnoreDynamicAndTupleNames)); // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used. // It means that explicit casts to (double) or (float) should be preserved on the node. // If original conversion has become something else with unknown precision, add an explicit identity cast. if (!_inExpressionLambda && node.ExplicitCastInCode && IsFloatPointExpressionOfUnknownPrecision(result)) { result = MakeConversionNode( node.Syntax, result, Conversion.Identity, toType, @checked: false, explicitCastInCode: true, constantValueOpt: result.ConstantValue); } return result; }
/// <summary> /// Checks whether the expression represents a boxing conversion of a special value type. /// If it does, it tries to return a string-based representation instead in order /// to avoid allocations. If it can't, the original expression is returned. /// </summary> private BoundExpression ConvertConcatExprToStringIfPossible(CSharpSyntaxNode syntax, BoundExpression expr) { if (expr.Kind == BoundKind.Conversion) { BoundConversion conv = (BoundConversion)expr; if (conv.ConversionKind == ConversionKind.Boxing) { BoundExpression operand = conv.Operand; if (operand != null) { // Is the expression a literal char? If so, we can // simply make it a literal string instead and avoid any // allocations for converting the char to a string at run time. if (operand.Kind == BoundKind.Literal) { ConstantValue cv = ((BoundLiteral)operand).ConstantValue; if (cv != null && cv.SpecialType == SpecialType.System_Char) { return(_factory.StringLiteral(cv.CharValue.ToString())); } } // Can the expression be optimized with a ToString call? // If so, we can synthesize a ToString call to avoid boxing. if (ConcatExprCanBeOptimizedWithToString(operand.Type)) { var toString = GetSpecialTypeMethod(syntax, SpecialMember.System_Object__ToString); return(BoundCall.Synthesized(syntax, operand, toString)); } } } } // Optimization not possible; just return the original expression. return(expr); }
private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conversion) { Debug.Assert(conversion.ConversionKind == ConversionKind.InterpolatedString); BoundExpression format; ArrayBuilder<BoundExpression> expressions; MakeInterpolatedStringFormat((BoundInterpolatedString)conversion.Operand, out format, out expressions); expressions.Insert(0, format); var stringFactory = _factory.WellKnownType(WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory); // The normal pattern for lowering is to lower subtrees before the enclosing tree. However we cannot lower // the arguments first in this situation because we do not know what conversions will be // produced for the arguments until after we've done overload resolution. So we produce the invocation // and then lower it along with its arguments. var result = _factory.StaticCall(stringFactory, "Create", expressions.ToImmutableAndFree(), allowUnexpandedForm: false // if an interpolation expression is the null literal, it should not match a params parameter. ); if (!result.HasAnyErrors) { result = VisitExpression(result); // lower the arguments AND handle expanded form, argument conversions, etc. result = MakeImplicitConversion(result, conversion.Type); } return result; }
public override BoundNode VisitConversion(BoundConversion node) { BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol type = this.VisitType(node.Type); if (operand.Kind != BoundKind.SpillSequence) { return node.Update(operand, node.ConversionKind, node.SymbolOpt, node.Checked, node.ExplicitCastInCode, node.IsExtensionMethod, node.IsArrayIndex, node.ConstantValueOpt, node.ResultKind, type); } var spill = (BoundSpillSequence)operand; return RewriteSpillSequence(spill, node.Update( spill.Value, node.ConversionKind, node.SymbolOpt, node.Checked, node.ExplicitCastInCode, node.IsExtensionMethod, node.IsArrayIndex, node.ConstantValueOpt, node.ResultKind, type)); }
public override BoundNode VisitConversion(BoundConversion node) { visitConversion(node.Conversion); if (!_mightAssignSomething) { base.VisitConversion(node); } return(null); void visitConversion(Conversion conversion) { switch (conversion.Kind) { case ConversionKind.MethodGroup: if (conversion.Method.MethodKind == MethodKind.LocalFunction) { _mightAssignSomething = true; } break; default: if (!conversion.UnderlyingConversions.IsDefault) { foreach (var underlying in conversion.UnderlyingConversions) { visitConversion(underlying); if (_mightAssignSomething) { return; } } } break; } } }
private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment( CSharpSyntaxNode node, ExpressionSyntax left, BoundExpression boundRHS, ArrayBuilder <DeconstructionVariable> checkedVariables, bool resultIsUsed, DiagnosticBag diagnostics) { uint rightEscape = GetValEscape(boundRHS, this.LocalScopeDepth); if ((object)boundRHS.Type == null || boundRHS.Type.IsErrorType()) { // we could still not infer a type for the RHS FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape); var voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node); var type = boundRHS.Type ?? voidType; return(new BoundDeconstructionAssignmentOperator( node, DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: true), new BoundConversion(boundRHS.Syntax, boundRHS, Conversion.Deconstruction, @checked: false, explicitCastInCode: false, conversionGroupOpt: null, constantValueOpt: null, type: type, hasErrors: true), resultIsUsed, voidType, hasErrors: true)); } Conversion conversion; bool hasErrors = !MakeDeconstructionConversion( boundRHS.Type, node, boundRHS.Syntax, diagnostics, checkedVariables, out conversion); FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape); var lhsTuple = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: diagnostics.HasAnyErrors() || !resultIsUsed); TypeSymbol returnType = hasErrors ? CreateErrorType() : lhsTuple.Type; uint leftEscape = GetBroadestValEscape(lhsTuple, this.LocalScopeDepth); boundRHS = ValidateEscape(boundRHS, leftEscape, isByRef: false, diagnostics: diagnostics); var boundConversion = new BoundConversion( boundRHS.Syntax, boundRHS, conversion, @checked: false, explicitCastInCode: false, conversionGroupOpt: null, constantValueOpt: null, type: returnType, hasErrors: hasErrors) { WasCompilerGenerated = true }; return(new BoundDeconstructionAssignmentOperator(node, lhsTuple, boundConversion, resultIsUsed, returnType)); }
public override BoundNode VisitConversion(BoundConversion node) { if (node.ConversionKind == ConversionKind.MethodGroup) { if (node.SymbolOpt?.MethodKind == MethodKind.LocalFunction) { // Use OriginalDefinition to strip generic type parameters ReferenceVariable(node.Syntax, node.SymbolOpt.OriginalDefinition); MethodsConvertedToDelegates.Add(node.SymbolOpt.OriginalDefinition); } if (node.IsExtensionMethod || ((object)node.SymbolOpt != null && !node.SymbolOpt.IsStatic)) { return VisitSyntaxWithReceiver(node.Syntax, ((BoundMethodGroup)node.Operand).ReceiverOpt); } return null; } else { return base.VisitConversion(node); } }
private BoundExpression VisitConversion(BoundConversion node) { switch (node.ConversionKind) { case ConversionKind.MethodGroup: { var mg = (BoundMethodGroup)node.Operand; return DelegateCreation(mg.ReceiverOpt, node.SymbolOpt, node.Type, node.SymbolOpt.IsStatic && !node.IsExtensionMethod); } case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: case ConversionKind.IntPtr: { var method = node.SymbolOpt; var operandType = node.Operand.Type; var strippedOperandType = operandType.StrippedType(); var conversionInputType = method.Parameters[0].Type; var isLifted = operandType != conversionInputType && strippedOperandType == conversionInputType; bool requireAdditionalCast = strippedOperandType != ((node.ConversionKind == ConversionKind.ExplicitUserDefined) ? conversionInputType : conversionInputType.StrippedType()); var resultType = (isLifted && method.ReturnType.IsNonNullableValueType() && node.Type.IsNullableType()) ? _nullableType.Construct(method.ReturnType) : method.ReturnType; var e1 = requireAdditionalCast ? Convert(Visit(node.Operand), node.Operand.Type, method.Parameters[0].Type, node.Checked, false) : Visit(node.Operand); var e2 = ExprFactory("Convert", e1, _bound.Typeof(resultType), _bound.MethodInfo(method)); return Convert(e2, resultType, node.Type, node.Checked, false); } case ConversionKind.ImplicitReference: case ConversionKind.Identity: { var operand = Visit(node.Operand); return node.ExplicitCastInCode ? Convert(operand, node.Type, false) : operand; } case ConversionKind.ImplicitNullable: if (node.Operand.Type.IsNullableType()) { return Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode); } else { // the native compiler performs this conversion in two steps, so we follow suit var nullable = (NamedTypeSymbol)node.Type; var intermediate = nullable.TypeArgumentsNoUseSiteDiagnostics[0]; var e1 = Convert(Visit(node.Operand), node.Operand.Type, intermediate, node.Checked, false); return Convert(e1, intermediate, node.Type, node.Checked, false); } case ConversionKind.NullLiteral: return Convert(Constant(_bound.Null(_objectType)), _objectType, node.Type, false, node.ExplicitCastInCode); default: return Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode); } }
private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, Binder originalBinder) { // Use the right binder to avoid seeing iteration variable BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); // These should only occur when special types are missing or malformed. hasErrors = hasErrors || (object)builder.GetEnumeratorMethod == null || (object)builder.MoveNextMethod == null || (object)builder.CurrentPropertyGetter == null; // Check for local variable conflicts in the *enclosing* binder; obviously the *current* // binder has a local that matches! var hasNameConflicts = originalBinder.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics); // If the type in syntax is "var", then the type should be set explicitly so that the // Type property doesn't fail. TypeSyntax typeSyntax = _syntax.Type; bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); TypeSymbol iterationVariableType; if (isVar) { iterationVariableType = inferredType ?? CreateErrorType("var"); } else { Debug.Assert((object)declType != null); iterationVariableType = declType; } BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType); this.IterationVariable.SetTypeSymbol(iterationVariableType); BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); hasErrors = hasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts). if (hasErrors) { return new BoundForEachStatement( _syntax, null, // can't be sure that it's complete default(Conversion), boundIterationVariableType, this.IterationVariable, collectionExpr, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); } hasErrors |= hasNameConflicts; var foreachKeyword = _syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false); // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case, // but it turns out that these are equivalent (when both are available). HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics); if (!elementConversion.IsValid) { ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false); } // Spec (§8.8.4): // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) // to the System.Collections.IEnumerable interface (§6.1.8). builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics); diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); // Due to the way we extracted the various types, these conversions should always be possible. // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we // can't convert from object to a pointer type. Similarly, if we're iterating over an array of // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a // value type. This doesn't matter in practice, since we won't actually use the enumerator pattern // when we lower the loop. Debug.Assert(builder.CollectionConversion.IsValid); Debug.Assert(builder.CurrentConversion.IsValid || (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) || (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray())); Debug.Assert(builder.EnumeratorConversion.IsValid || this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error || !useSiteDiagnostics.IsNullOrEmpty(), "Conversions to object succeed unless there's a problem with the object type or the source type"); // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute. Debug.Assert((object)builder.CollectionConversion.Method == null, "Conversion from collection expression to collection type should not be user-defined"); Debug.Assert((object)builder.CurrentConversion.Method == null, "Conversion from Current property type to element type should not be user-defined"); Debug.Assert((object)builder.EnumeratorConversion.Method == null, "Conversion from GetEnumerator return type to System.Object should not be user-defined"); // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, builder.CollectionConversion, CheckOverflowAtRuntime, false, ConstantValue.NotAvailable, builder.CollectionType); return new BoundForEachStatement( _syntax, builder.Build(this.Flags), elementConversion, boundIterationVariableType, this.IterationVariable, convertedCollectionExpression, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); }
private static bool IsSafeForReordering(BoundExpression expression, RefKind kind) { // To be safe for reordering an expression must not cause any observable side effect *or // observe any side effect*. Accessing a local by value, for example, is possibly not // safe for reordering because reading a local can give a different result if reordered // with respect to a write elsewhere. var current = expression; while (true) { if (current.ConstantValue != null) { return(true); } switch (current.Kind) { default: return(false); case BoundKind.Parameter: case BoundKind.Local: // A ref to a local variable or formal parameter is safe to reorder; it // never has a side effect or consumes one. return(kind != RefKind.None); case BoundKind.Conversion: { BoundConversion conv = (BoundConversion)current; switch (conv.ConversionKind) { case ConversionKind.AnonymousFunction: case ConversionKind.ImplicitConstant: case ConversionKind.MethodGroup: case ConversionKind.NullLiteral: return(true); case ConversionKind.Boxing: case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: case ConversionKind.ExplicitEnumeration: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitNumeric: case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitEnumeration: case ConversionKind.ImplicitNullable: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitReference: case ConversionKind.Unboxing: case ConversionKind.PointerToInteger: case ConversionKind.PointerToPointer: case ConversionKind.PointerToVoid: case ConversionKind.NullToPointer: case ConversionKind.IntegerToPointer: current = conv.Operand; break; case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: return(false); default: Debug.Assert(false, "Unhandled conversion kind in reordering logic"); return(false); } break; } } } }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); BoundStatement[] result = new BoundStatement[1 + synthesizedFields.Count]; var sessionReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var submissionGetter = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__GetSubmission ); var submissionAdder = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__SetSubmission ); // TODO: report missing adder/getter Debug.Assert((object)submissionAdder != null && (object)submissionGetter != null); var intType = compilation.GetSpecialType(SpecialType.System_Int32); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; int i = 0; // hostObject = (THostObject)SessionHelpers.SetSubmission(<session>, <slot index>, this); var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); BoundExpression setSubmission = BoundCall.Synthesized(syntax, null, submissionAdder, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }, thisReference ); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { setSubmission = new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, setSubmission, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type ) { WasCompilerGenerated = true }; } result[i++] = new BoundExpressionStatement(syntax, setSubmission) { WasCompilerGenerated = true }; foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptClass = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionId = targetScriptClass.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionId >= 0); // this.<field> = (<FieldType>)SessionHelpers.GetSubmission(<session>, <i>); result[i++] = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, BoundCall.Synthesized(syntax, null, submissionGetter, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionId), intType) { WasCompilerGenerated = true }), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptClass ), targetScriptClass ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; } Debug.Assert(i == result.Length); return(result.AsImmutableOrNull()); }
private BoundExpression RewriteDecimalConversion(BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression operand, TypeSymbol fromType, TypeSymbol toType) { Debug.Assert(fromType.SpecialType == SpecialType.System_Decimal || toType.SpecialType == SpecialType.System_Decimal); // call the method SpecialMember member = DecimalConversionMethod(fromType, toType); var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert((object)method != null); // Should have been checked during Warnings pass if (inExpressionLambda && oldNode != null) { ConversionKind conversionKind = oldNode.ConversionKind.IsImplicitConversion() ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; return oldNode.Update( operand, conversionKind, oldNode.ResultKind, isBaseConversion: oldNode.IsBaseConversion, symbolOpt: method, @checked: oldNode.Checked, explicitCastInCode: oldNode.ExplicitCastInCode, isExtensionMethod: oldNode.IsExtensionMethod, isArrayIndex: oldNode.IsArrayIndex, constantValueOpt: oldNode.ConstantValueOpt, type: toType); } else { Debug.Assert(method.ReturnType == toType); return BoundCall.Synthesized(syntax, null, method, operand); } }
private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics) { BoundExpression collectionExpr = this.Next.BindValue(syntax.Expression, diagnostics, BindValueKind.RValue); //bind with next to avoid seeing iteration variable ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); // These should only occur when special types are missing or malformed. hasErrors = hasErrors || (object)builder.GetEnumeratorMethod == null || (object)builder.MoveNextMethod == null || (object)builder.CurrentPropertyGetter == null; // Check for local variable conflicts in the *enclosing* binder; obviously the *current* // binder has a local that matches! hasErrors |= this.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics); // If the type in syntax is "var", then the type should be set explicitly so that the // Type property doesn't fail. TypeSyntax typeSyntax = this.syntax.Type; bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); TypeSymbol iterationVariableType; if (isVar) { iterationVariableType = inferredType ?? CreateErrorType("var"); } else { Debug.Assert((object)declType != null); iterationVariableType = declType; } BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType); this.IterationVariable.SetTypeSymbol(iterationVariableType); BoundStatement body = BindPossibleEmbeddedStatement(syntax.Statement, diagnostics); hasErrors = hasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error. if (hasErrors) { return(new BoundForEachStatement( syntax, ImmutableArray <LocalSymbol> .Empty, null, // can't be sure that it's complete default(Conversion), boundIterationVariableType, this.IterationVariable, collectionExpr, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors)); } var foreachKeyword = syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false); // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case, // but it turns out that these are equivalent (when both are available). HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics); if (!elementConversion.IsValid) { ImmutableArray <MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { diagnostics.Add(ErrorCode.ERR_AmbigUDConv, syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, syntax.ForEachKeyword, hasBaseReceiver: false); } // Spec (§8.8.4): // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) // to the System.Collections.IEnumerable interface (§6.1.8). builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, this.syntax), ref useSiteDiagnostics); diagnostics.Add(syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); // Due to the way we extracted the various types, these conversions should always be possible. // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we // can't convert from object to a pointer type. Similarly, if we're iterating over an array of // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a // value type. This doesn't matter in practice, since we won't actually use the enumerator pattern // when we lower the loop. Debug.Assert(builder.CollectionConversion.IsValid); Debug.Assert(builder.CurrentConversion.IsValid || (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) || (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray())); Debug.Assert(builder.EnumeratorConversion.IsValid || this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error, "Conversions to object succeed unless there's a problem with the object type"); // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute. Debug.Assert((object)builder.CollectionConversion.Method == null, "Conversion from collection expression to collection type should not be user-defined"); Debug.Assert((object)builder.CurrentConversion.Method == null, "Conversion from Current property type to element type should not be user-defined"); Debug.Assert((object)builder.EnumeratorConversion.Method == null, "Conversion from GetEnumerator return type to System.Object should not be user-defined"); // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, builder.CollectionConversion, CheckOverflowAtRuntime, false, ConstantValue.NotAvailable, builder.CollectionType); return(new BoundForEachStatement( syntax, ImmutableArray <LocalSymbol> .Empty, builder.Build(this.Flags), elementConversion, boundIterationVariableType, this.IterationVariable, convertedCollectionExpression, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors)); }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); var statements = new List <BoundStatement>(2 + synthesizedFields.Count); var submissionArrayReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var intType = compilation.GetSpecialType(SpecialType.System_Int32); var objectType = compilation.GetSpecialType(SpecialType.System_Object); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); // <submission_array>[<slot_index] = this; statements.Add(new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }), objectType) { WasCompilerGenerated = true }, thisReference, RefKind.None, thisReference.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { // <host_object> = (<host_object_type>)<submission_array>[0] statements.Add( new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(0), intType) { WasCompilerGenerated = true }), objectType), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptType = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionIndex = targetScriptType.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionIndex >= 0); // this.<field> = (<target_script_type>)<submission_array>[<target_submission_index>]; statements.Add( new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionIndex), intType) { WasCompilerGenerated = true }), objectType) { WasCompilerGenerated = true }, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptType ), targetScriptType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } return(statements.AsImmutableOrNull()); }
private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { // We have a successful tuple conversion; rather than producing a separate conversion node // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments. Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType()); var destinationWithoutNullable = destination; var conversionWithoutNullable = conversion; if (conversion.Kind == ConversionKind.ImplicitNullable) { destinationWithoutNullable = destination.GetNullableUnderlyingType(); conversionWithoutNullable = conversion.UnderlyingConversions[0]; } Debug.Assert(conversionWithoutNullable.IsTupleLiteralConversion); NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable; if (targetType.IsTupleType) { var destTupleType = (TupleTypeSymbol)targetType; // do not lose the original element names in the literal if different from names in the target TupleTypeSymbol.ReportNamesMismatchesIfAny(targetType, sourceTuple, diagnostics); // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013) targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt); } var arguments = sourceTuple.Arguments; var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length); ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible(); Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?"); var underlyingConversions = conversionWithoutNullable.UnderlyingConversions; for (int i = 0; i < arguments.Length; i++) { var argument = arguments[i]; var destType = targetElementTypes[i]; var elementConversion = underlyingConversions[i]; convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics)); } BoundExpression result = new BoundConvertedTupleLiteral( sourceTuple.Syntax, sourceTuple.Type, convertedArguments.ToImmutableAndFree(), targetType); if (sourceTuple.Type != destination) { // literal cast is applied to the literal result = new BoundConversion( sourceTuple.Syntax, result, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination); } // If we had a cast in the code, keep conversion in the tree. // even though the literal is already converted to the target type. if (isCast) { result = new BoundConversion( syntax, result, Conversion.Identity, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination); } return result; }
private BoundExpression RewriteIntPtrConversion( BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, MethodSymbol symbolOpt, bool @checked, bool explicitCastInCode, bool isExtensionMethod, bool isArrayIndex, ConstantValue constantValueOpt, TypeSymbol rewrittenType) { Debug.Assert(rewrittenOperand != null); Debug.Assert((object)rewrittenType != null); TypeSymbol source = rewrittenOperand.Type; TypeSymbol target = rewrittenType; SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType); MethodSymbol method = GetSpecialTypeMethod(syntax, member); Debug.Assert(!method.ReturnsVoid); Debug.Assert(method.ParameterCount == 1); if (source.IsNullableType() && target.IsNullableType()) { Debug.Assert(target.IsNullableType()); return RewriteLiftedUserDefinedConversion(syntax, rewrittenOperand, method, rewrittenType, ConversionKind.IntPtr); } else if (source.IsNullableType()) { rewrittenOperand = MakeConversion(rewrittenOperand, source.StrippedType(), @checked); } rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked); var returnType = method.ReturnType; Debug.Assert((object)returnType != null); if (_inExpressionLambda) { return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType); } var rewrittenCall = MakeCall( syntax: syntax, rewrittenReceiver: null, method: method, rewrittenArguments: ImmutableArray.Create(rewrittenOperand), type: returnType); return MakeConversion(rewrittenCall, rewrittenType, @checked); }
private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node) { BoundExpression left = node.Left; if (!node.Operator.Kind.IsDynamic() && !node.LeftConversion.IsIdentity && node.LeftConversion.Exists) { // Need to represent the implicit conversion as a node in order to be able to produce correct diagnostics. left = new BoundConversion(left.Syntax, left, node.LeftConversion, node.Operator.Kind.IsChecked(), explicitCastInCode: false, constantValueOpt: null, type: node.Operator.LeftType); } CheckForBitwiseOrSignExtend(node, node.Operator.Kind, left, node.Right); CheckLiftedCompoundAssignment(node); if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node); } }
private BoundExpression VisitConversion(BoundConversion node) { switch (node.ConversionKind) { case ConversionKind.MethodGroup: { var mg = (BoundMethodGroup)node.Operand; return(DelegateCreation(mg.ReceiverOpt, node.SymbolOpt, node.Type, node.SymbolOpt.IsStatic && !node.IsExtensionMethod)); } case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: case ConversionKind.IntPtr: { var method = node.SymbolOpt; var operandType = node.Operand.Type; var strippedOperandType = operandType.StrippedType(); var conversionInputType = method.Parameters[0].Type; var isLifted = operandType != conversionInputType && strippedOperandType == conversionInputType; bool requireAdditionalCast = strippedOperandType != ((node.ConversionKind == ConversionKind.ExplicitUserDefined) ? conversionInputType : conversionInputType.StrippedType()); var resultType = (isLifted && method.ReturnType.IsNonNullableValueType() && node.Type.IsNullableType()) ? _nullableType.Construct(method.ReturnType) : method.ReturnType; var e1 = requireAdditionalCast ? Convert(Visit(node.Operand), node.Operand.Type, method.Parameters[0].Type, node.Checked, false) : Visit(node.Operand); var e2 = ExprFactory("Convert", e1, _bound.Typeof(resultType), _bound.MethodInfo(method)); return(Convert(e2, resultType, node.Type, node.Checked, false)); } case ConversionKind.ImplicitReference: case ConversionKind.Identity: { var operand = Visit(node.Operand); return(node.ExplicitCastInCode ? Convert(operand, node.Type, false) : operand); } case ConversionKind.IdentityValue: { return(Visit(node.Operand)); } case ConversionKind.ImplicitNullable: if (node.Operand.Type.IsNullableType()) { return(Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode)); } else { // the native compiler performs this conversion in two steps, so we follow suit var nullable = (NamedTypeSymbol)node.Type; var intermediate = nullable.TypeArgumentsNoUseSiteDiagnostics[0]; var e1 = Convert(Visit(node.Operand), node.Operand.Type, intermediate, node.Checked, false); return(Convert(e1, intermediate, node.Type, node.Checked, false)); } case ConversionKind.NullLiteral: return(Convert(Constant(_bound.Null(_objectType)), _objectType, node.Type, false, node.ExplicitCastInCode)); default: return(Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode)); } }
public override BoundNode VisitConversion(BoundConversion node) { if (node.ConversionKind == ConversionKind.MethodGroup) { if (node.IsExtensionMethod || ((object)node.SymbolOpt != null && !node.SymbolOpt.IsStatic)) { return VisitSyntaxWithReceiver(node.Syntax, ((BoundMethodGroup)node.Operand).ReceiverOpt); } return null; } else { return base.VisitConversion(node); } }
/// <summary> /// Returns an expression which converts the given expression into a string (or null). /// If necessary, this invokes .ToString() on the expression, to avoid boxing value types. /// </summary> private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpression expr) { // If it's a value type, it'll have been boxed by the +(string, object) or +(object, string) // operator. Undo that. if (expr.Kind == BoundKind.Conversion) { BoundConversion conv = (BoundConversion)expr; if (conv.ConversionKind == ConversionKind.Boxing) { expr = conv.Operand; } } // Is the expression a literal char? If so, we can // simply make it a literal string instead and avoid any // allocations for converting the char to a string at run time. // Similarly if it's a literal null, don't do anything special. if (expr.Kind == BoundKind.Literal) { ConstantValue cv = ((BoundLiteral)expr).ConstantValue; if (cv != null) { if (cv.SpecialType == SpecialType.System_Char) { return(_factory.StringLiteral(cv.CharValue.ToString())); } else if (cv.IsNull) { return(expr); } } } // If it's a string already, just return it if (expr.Type.IsStringType()) { return(expr); } // Evaluate toString at the last possible moment, to avoid spurious diagnostics if it's missing. // All code paths below here use it. var objectToStringMethod = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_Object__ToString); // If it's a struct which has overridden ToString, find that method. Note that we might fail to // find it, e.g. if object.ToString is missing MethodSymbol structToStringMethod = null; if (expr.Type.IsValueType && !expr.Type.IsTypeParameter()) { var type = (NamedTypeSymbol)expr.Type; var typeToStringMembers = type.GetMembers(objectToStringMethod.Name); foreach (var member in typeToStringMembers) { if (member is MethodSymbol toStringMethod && toStringMethod.GetLeastOverriddenMethod(type) == (object)objectToStringMethod) { structToStringMethod = toStringMethod; break; } } } // If it's a special value type (and not a field of a MarshalByRef object), it should have its own ToString method (but we might fail to find // it if object.ToString is missing). Assume that this won't be removed, and emit a direct call rather // than a constrained virtual call. This keeps in the spirit of #7079, but expands the range of // types to all special value types. if (structToStringMethod != null && (expr.Type.SpecialType != SpecialType.None && !isFieldOfMarshalByRef(expr, _compilation))) { return(BoundCall.Synthesized(expr.Syntax, expr, structToStringMethod)); } // - It's a reference type (excluding unconstrained generics): no copy // - It's a constant: no copy // - The type definitely doesn't have its own ToString method (i.e. we're definitely calling // object.ToString on a struct type, not type parameter): no copy (yes this is a versioning issue, // but that doesn't matter) // - We're calling the type's own ToString method, and it's effectively readonly (the method or the whole // type is readonly): no copy // - Otherwise: copy // This is to mimic the old behaviour, where value types would be boxed before ToString was called on them, // but with optimizations for readonly methods. bool callWithoutCopy = expr.Type.IsReferenceType || expr.ConstantValue != null || (structToStringMethod == null && !expr.Type.IsTypeParameter()) || structToStringMethod?.IsEffectivelyReadOnly == true; // No need for a conditional access if it's a value type - we know it's not null. if (expr.Type.IsValueType) { if (!callWithoutCopy) { expr = new BoundPassByCopy(expr.Syntax, expr, expr.Type); } return(BoundCall.Synthesized(expr.Syntax, expr, objectToStringMethod)); } if (callWithoutCopy) { return(makeConditionalAccess(expr)); } else { // If we do conditional access on a copy, we need a proper BoundLocal rather than a // BoundPassByCopy (as it's accessed multiple times). If we don't do this, and the // receiver is an unconstrained generic parameter, BoundLoweredConditionalAccess has // to generate a lot of code to ensure it only accesses the copy once (which is pointless). var temp = _factory.StoreToTemp(expr, out var store); return(_factory.Sequence( ImmutableArray.Create(temp.LocalSymbol), ImmutableArray.Create <BoundExpression>(store), makeConditionalAccess(temp))); } BoundExpression makeConditionalAccess(BoundExpression receiver) { int currentConditionalAccessID = ++_currentConditionalAccessID; return(new BoundLoweredConditionalAccess( syntax, receiver, hasValueMethodOpt: null, whenNotNull: BoundCall.Synthesized( syntax, new BoundConditionalReceiver(syntax, currentConditionalAccessID, expr.Type), objectToStringMethod), whenNullOpt: null, id: currentConditionalAccessID, type: _compilation.GetSpecialType(SpecialType.System_String))); }
protected BoundExpression CreateUserDefinedConversion(CSharpSyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { if (!conversion.IsValid) { GenerateImplicitConversionError(diagnostics, syntax, conversion, source, destination); return new BoundConversion( syntax, source, conversion, CheckOverflowAtRuntime, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: true) { WasCompilerGenerated = source.WasCompilerGenerated }; } // Due to an oddity in the way we create a non-lifted user-defined conversion from A to D? // (required backwards compatibility with the native compiler) we can end up in a situation // where we have: // a standard conversion from A to B? // then a standard conversion from B? to B // then a user-defined conversion from B to C // then a standard conversion from C to C? // then a standard conversion from C? to D? // // In that scenario, the "from type" of the conversion will be B? and the "from conversion" will be // from A to B?. Similarly the "to type" of the conversion will be C? and the "to conversion" // of the conversion will be from C? to D?. // // Therefore, we might need to introduce an extra conversion on the source side, from B? to B. // Now, you might think we should also introduce an extra conversion on the destination side, // from C to C?. But that then gives us the following bad situation: If we in fact bind this as // // (D?)(C?)(C)(B)(B?)(A)x // // then what we are in effect doing is saying "convert C? to D? by checking for null, unwrapping, // converting C to D, and then wrapping". But we know that the C? will never be null. In this case // we should actually generate // // (D?)(C)(B)(B?)(A)x // // And thereby skip the unnecessary nullable conversion. // Original expression --> conversion's "from" type BoundExpression convertedOperand = CreateConversion( syntax: source.Syntax, source: source, conversion: conversion.UserDefinedFromConversion, isCast: false, wasCompilerGenerated: true, destination: conversion.BestUserDefinedConversionAnalysis.FromType, diagnostics: diagnostics); TypeSymbol conversionParameterType = conversion.BestUserDefinedConversionAnalysis.Operator.ParameterTypes[0]; HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (conversion.BestUserDefinedConversionAnalysis.Kind == UserDefinedConversionAnalysisKind.ApplicableInNormalForm && conversion.BestUserDefinedConversionAnalysis.FromType != conversionParameterType) { // Conversion's "from" type --> conversion method's parameter type. convertedOperand = CreateConversion( syntax: syntax, source: convertedOperand, conversion: Conversions.ClassifyStandardConversion(null, convertedOperand.Type, conversionParameterType, ref useSiteDiagnostics), isCast: false, wasCompilerGenerated: true, destination: conversionParameterType, diagnostics: diagnostics); } BoundExpression userDefinedConversion; TypeSymbol conversionReturnType = conversion.BestUserDefinedConversionAnalysis.Operator.ReturnType; TypeSymbol conversionToType = conversion.BestUserDefinedConversionAnalysis.ToType; Conversion toConversion = conversion.UserDefinedToConversion; if (conversion.BestUserDefinedConversionAnalysis.Kind == UserDefinedConversionAnalysisKind.ApplicableInNormalForm && conversionToType != conversionReturnType) { // Conversion method's parameter type --> conversion method's return type // NB: not calling CreateConversion here because this is the recursive base case. userDefinedConversion = new BoundConversion( syntax, convertedOperand, conversion, @checked: false, // There are no checked user-defined conversions, but the conversions on either side might be checked. explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: conversionReturnType) { WasCompilerGenerated = true }; if (conversionToType.IsNullableType() && conversionToType.GetNullableUnderlyingType() == conversionReturnType) { // Skip introducing the conversion from C to C?. The "to" conversion is now wrong though, // because it will still assume converting C? to D?. toConversion = Conversions.ClassifyConversion(conversionReturnType, destination, ref useSiteDiagnostics); Debug.Assert(toConversion.Exists); } else { // Conversion method's return type --> conversion's "to" type userDefinedConversion = CreateConversion( syntax: syntax, source: userDefinedConversion, conversion: Conversions.ClassifyStandardConversion(null, conversionReturnType, conversion.BestUserDefinedConversionAnalysis.ToType, ref useSiteDiagnostics), isCast: false, wasCompilerGenerated: true, destination: conversion.BestUserDefinedConversionAnalysis.ToType, diagnostics: diagnostics); } } else { // Conversion method's parameter type --> conversion method's "to" type // NB: not calling CreateConversion here because this is the recursive base case. userDefinedConversion = new BoundConversion( syntax, convertedOperand, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: conversion.BestUserDefinedConversionAnalysis.ToType) { WasCompilerGenerated = true }; } diagnostics.Add(syntax, useSiteDiagnostics); // Conversion's "to" type --> final type BoundExpression finalConversion = CreateConversion( syntax: syntax, source: userDefinedConversion, conversion: toConversion, isCast: false, wasCompilerGenerated: true, // NOTE: doesn't necessarily set flag on resulting bound expression. destination: destination, diagnostics: diagnostics); finalConversion.ResetCompilerGenerated(source.WasCompilerGenerated); return finalConversion; }
private BoundExpression RewriteLiftedConversionInExpressionTree( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, bool @checked, bool explicitCastInCode, TypeSymbol rewrittenType) { Debug.Assert((object)rewrittenType != null); TypeSymbol rewrittenOperandType = rewrittenOperand.Type; Debug.Assert(rewrittenType.IsNullableType() || rewrittenOperandType.IsNullableType()); TypeSymbol typeFrom = rewrittenOperandType.StrippedType(); TypeSymbol typeTo = rewrittenType.StrippedType(); if (typeFrom != typeTo && (typeFrom.SpecialType == SpecialType.System_Decimal || typeTo.SpecialType == SpecialType.System_Decimal)) { // take special care if the underlying conversion is a decimal conversion TypeSymbol typeFromUnderlying = typeFrom; TypeSymbol typeToUnderlying = typeTo; // They can't both be enums, since one of them is decimal. if (typeFrom.IsEnumType()) { typeFromUnderlying = typeFrom.GetEnumUnderlyingType(); // NOTE: Dev10 converts enum? to underlying?, rather than directly to underlying. rewrittenOperandType = rewrittenOperandType.IsNullableType() ? ((NamedTypeSymbol)rewrittenOperandType.OriginalDefinition).Construct(typeFromUnderlying) : typeFromUnderlying; rewrittenOperand = BoundConversion.SynthesizedNonUserDefined(syntax, rewrittenOperand, ConversionKind.ImplicitEnumeration, rewrittenOperandType); } else if (typeTo.IsEnumType()) { typeToUnderlying = typeTo.GetEnumUnderlyingType(); } var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(DecimalConversionMethod(typeFromUnderlying, typeToUnderlying)); conversionKind = conversionKind.IsImplicitConversion() ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; var result = new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, default(ConstantValue), rewrittenType); return result; } else { return new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind), @checked, explicitCastInCode, default(ConstantValue), rewrittenType); } }
public override BoundNode VisitConversion(BoundConversion node) { BoundSpillSequence2 ss = null; var operand = VisitExpression(ref ss, node.Operand); return UpdateExpression( ss, node.Update( operand, node.ConversionKind, node.ResultKind, isBaseConversion: node.IsBaseConversion, symbolOpt: node.SymbolOpt, @checked: node.Checked, explicitCastInCode: node.ExplicitCastInCode, isExtensionMethod: node.IsExtensionMethod, isArrayIndex: node.IsArrayIndex, constantValueOpt: node.ConstantValueOpt, type: node.Type)); }
/// <summary> /// Helper method to generate a lowered conversion. /// </summary> private BoundExpression MakeConversion( BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, MethodSymbol symbolOpt, bool @checked, bool explicitCastInCode, bool isExtensionMethod, bool isArrayIndex, ConstantValue constantValueOpt, TypeSymbol rewrittenType) { Debug.Assert(oldNode == null || oldNode.Syntax == syntax); Debug.Assert((object)rewrittenType != null); @checked = @checked && (inExpressionLambda && (explicitCastInCode || DistinctSpecialTypes(rewrittenOperand.Type, rewrittenType)) || NeedsChecked(rewrittenOperand.Type, rewrittenType)); switch (conversionKind) { case ConversionKind.Identity: // Spec 6.1.1: // An identity conversion converts from any type to the same type. // This conversion exists such that an entity that already has a required type can be said to be convertible to that type. // Because object and dynamic are considered equivalent there is an identity conversion between object and dynamic, // and between constructed types that are the same when replacing all occurrences of dynamic with object. // Why ignoreDynamic: false? // Lowering phase treats object and dynamic as equivalent types. So we don't need to produce any conversion here, // but we need to change the Type property on the resulting BoundExpression to match the rewrittenType. // This is necessary so that subsequent lowering transformations see that the expression is dynamic. if (inExpressionLambda || !rewrittenOperand.Type.Equals(rewrittenType, ignoreCustomModifiers: false, ignoreDynamic: false)) { break; } // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used. // If this is not an identity conversion of a float with unknown precision, strip away the identity conversion. if (!(explicitCastInCode && IsFloatPointExpressionOfUnknownPrecision(rewrittenOperand))) { return rewrittenOperand; } break; case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: return RewriteUserDefinedConversion( syntax: syntax, rewrittenOperand: rewrittenOperand, method: symbolOpt, rewrittenType: rewrittenType, conversionKind: conversionKind); case ConversionKind.IntPtr: return RewriteIntPtrConversion(oldNode, syntax, rewrittenOperand, conversionKind, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType); case ConversionKind.ImplicitNullable: case ConversionKind.ExplicitNullable: return RewriteNullableConversion( syntax: syntax, rewrittenOperand: rewrittenOperand, conversionKind: conversionKind, @checked: @checked, explicitCastInCode: explicitCastInCode, rewrittenType: rewrittenType); case ConversionKind.Boxing: if (!inExpressionLambda) { // We can perform some optimizations if we have a nullable value type // as the operand and we know its nullability: // * (object)new int?() is the same as (object)null // * (object)new int?(123) is the same as (object)123 if (NullableNeverHasValue(rewrittenOperand)) { return new BoundDefaultOperator(syntax, rewrittenType); } BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand); if (nullableValue != null) { // Recurse, eliminating the unnecessary ctor. return MakeConversion(oldNode, syntax, nullableValue, conversionKind, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType); } } break; case ConversionKind.NullLiteral: if (!inExpressionLambda || !explicitCastInCode) { return new BoundDefaultOperator(syntax, rewrittenType); } break; case ConversionKind.ImplicitReference: case ConversionKind.ExplicitReference: if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode)) { return new BoundDefaultOperator(syntax, rewrittenType); } break; case ConversionKind.ImplicitNumeric: case ConversionKind.ExplicitNumeric: if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode)) { return new BoundDefaultOperator(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal) { return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, rewrittenType); } break; case ConversionKind.ImplicitEnumeration: // A conversion from constant zero to nullable is actually classified as an // implicit enumeration conversion, not an implicit nullable conversion. // Lower it to (E?)(E)0. if (rewrittenType.IsNullableType()) { var operand = MakeConversion( oldNode, syntax, rewrittenOperand, ConversionKind.ImplicitEnumeration, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, false, constantValueOpt, rewrittenType.GetNullableUnderlyingType()); return MakeConversion( oldNode, syntax, operand, ConversionKind.ImplicitNullable, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType); } goto case ConversionKind.ExplicitEnumeration; case ConversionKind.ExplicitEnumeration: if (!rewrittenType.IsNullableType() && rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode)) { return new BoundDefaultOperator(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal) { Debug.Assert(rewrittenOperand.Type.IsEnumType()); var underlyingTypeFrom = rewrittenOperand.Type.GetEnumUnderlyingType(); rewrittenOperand = MakeConversion(rewrittenOperand, underlyingTypeFrom, false); return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, underlyingTypeFrom, rewrittenType); } else if (rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal) { // This is where we handle conversion from Decimal to Enum: e.g., E e = (E) d; // where 'e' is of type Enum E and 'd' is of type Decimal. // Conversion can be simply done by applying its underlying numeric type to RewriteDecimalConversion(). Debug.Assert(rewrittenType.IsEnumType()); var underlyingTypeTo = rewrittenType.GetEnumUnderlyingType(); var rewrittenNode = RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, underlyingTypeTo); // However, the type of the rewritten node becomes underlying numeric type, not Enum type, // which violates the overall constraint saying the type cannot be changed during rewriting (see LocalRewriter.cs). // Instead of loosening this constraint, we return BoundConversion from underlying numeric type to Enum type, // which will be eliminated during emitting (see EmitEnumConversion): e.g., E e = (E)(int) d; return new BoundConversion( syntax, rewrittenNode, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: symbolOpt, @checked: false, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: false, constantValueOpt: constantValueOpt, type: rewrittenType); } break; case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: Debug.Assert((object)symbolOpt == null); Debug.Assert(!isExtensionMethod); Debug.Assert(constantValueOpt == null); return dynamicFactory.MakeDynamicConversion(rewrittenOperand, explicitCastInCode || conversionKind == ConversionKind.ExplicitDynamic, isArrayIndex, @checked, rewrittenType).ToExpression(); default: break; } return oldNode != null ? oldNode.Update( rewrittenOperand, conversionKind, oldNode.ResultKind, isBaseConversion: oldNode.IsBaseConversion, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType) : new BoundConversion( syntax, rewrittenOperand, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType); }
private void CheckVacuousComparisons(BoundBinaryOperator tree, ConstantValue constantValue, BoundNode operand) { Debug.Assert(tree != null); Debug.Assert(constantValue != null); Debug.Assert(operand != null); // We wish to detect comparisons between integers and constants which are likely to be wrong // because we know at compile time whether they will be true or false. For example: // // const short s = 1000; // byte b = whatever; // if (b < s) // // We know that this will always be true because when b and s are both converted to int for // the comparison, the left side will always be less than the right side. // // We only give the warning if there is no explicit conversion involved on the operand. // For example, if we had: // // const uint s = 1000; // sbyte b = whatever; // if ((byte)b < s) // // Then we do not give a warning. // // Note that the native compiler has what looks to be some dead code. It checks to see // if the conversion on the operand is from an enum type. But this is unnecessary if // we are rejecting cases with explicit conversions. The only possible cases are: // // enum == enumConstant -- enum types must be the same, so it must be in range. // enum == integralConstant -- not legal unless the constant is zero, which is in range. // enum == (ENUM)anyConstant -- if the constant is out of range then this is not legal in the first place // unless we're in an unchecked context, in which case, the user probably does // not want the warning. // integral == enumConstant -- never legal in the first place // // Since it seems pointless to try to check enums, we simply look for vacuous comparisons of // integral types here. for (BoundConversion conversion = operand as BoundConversion; conversion != null; conversion = conversion.Operand as BoundConversion) { if (conversion.ConversionKind != ConversionKind.ImplicitNumeric && conversion.ConversionKind != ConversionKind.ImplicitConstant) { return; } // As in dev11, we don't dig through explicit casts (see ExpressionBinder::WarnAboutBadRelationals). if (conversion.ExplicitCastInCode) { return; } if (!conversion.Operand.Type.SpecialType.IsIntegralType() || !conversion.Type.SpecialType.IsIntegralType()) { return; } if (!Binder.CheckConstantBounds(conversion.Operand.Type.SpecialType, constantValue)) { Error(ErrorCode.WRN_VacuousIntegralComp, tree, conversion.Operand.Type); return; } } }
private BoundExpression RewriteIntPtrConversion( BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, MethodSymbol symbolOpt, bool @checked, bool explicitCastInCode, bool isExtensionMethod, bool isArrayIndex, ConstantValue constantValueOpt, TypeSymbol rewrittenType) { Debug.Assert(rewrittenOperand != null); Debug.Assert((object)rewrittenType != null); TypeSymbol source = rewrittenOperand.Type; TypeSymbol target = rewrittenType; TypeSymbol t0 = target.StrippedType(); TypeSymbol s0 = source.StrippedType(); if (t0 != target && s0 != source) { // UNDONE: RewriteLiftedIntPtrConversion return oldNode != null ? oldNode.Update( rewrittenOperand, conversionKind, oldNode.ResultKind, isBaseConversion: oldNode.IsBaseConversion, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType) : new BoundConversion( syntax, rewrittenOperand, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType); } SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType); MethodSymbol method = GetSpecialTypeMethod(syntax, member); Debug.Assert(!method.ReturnsVoid); Debug.Assert(method.ParameterCount == 1); rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked); var returnType = method.ReturnType; Debug.Assert((object)returnType != null); if (inExpressionLambda) { return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType); } var rewrittenCall = inExpressionLambda && oldNode != null ? new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, returnType) : MakeCall( syntax: syntax, rewrittenReceiver: null, method: method, rewrittenArguments: ImmutableArray.Create<BoundExpression>(rewrittenOperand), type: returnType); return MakeConversion(rewrittenCall, rewrittenType, @checked); }
// A "surprising" sign extension is: // // * a conversion with no cast in source code that goes from a smaller // signed type to a larger signed or unsigned type. // // * an conversion (with or without a cast) from a smaller // signed type to a larger unsigned type. private static ulong FindSurprisingSignExtensionBits(BoundExpression expr) { if (expr.Kind != BoundKind.Conversion) { return(0); } BoundConversion conv = (BoundConversion)expr; TypeSymbol from = conv.Operand.Type; TypeSymbol to = conv.Type; if ((object)from == null || (object)to == null) { return(0); } if (from.IsNullableType()) { from = from.GetNullableUnderlyingType(); } if (to.IsNullableType()) { to = to.GetNullableUnderlyingType(); } SpecialType fromSpecialType = from.SpecialType; SpecialType toSpecialType = to.SpecialType; if (!fromSpecialType.IsIntegralType() || !toSpecialType.IsIntegralType()) { return(0); } int fromSize = fromSpecialType.SizeInBytes(); int toSize = toSpecialType.SizeInBytes(); if (fromSize == 0 || toSize == 0) { return(0); } // The operand might itself be a conversion, and might be contributing // surprising bits. We might have more, fewer or the same surprising bits // as the operand. ulong recursive = FindSurprisingSignExtensionBits(conv.Operand); if (fromSize == toSize) { // No change. return(recursive); } if (toSize < fromSize) { // We are casting from a larger type to a smaller type, and are therefore // losing surprising bits. switch (toSize) { case 1: return(unchecked ((ulong)(byte)recursive)); case 2: return(unchecked ((ulong)(ushort)recursive)); case 4: return(unchecked ((ulong)(uint)recursive)); } Debug.Assert(false, "How did we get here?"); return(recursive); } // We are converting from a smaller type to a larger type, and therefore might // be adding surprising bits. First of all, the smaller type has got to be signed // for there to be sign extension. bool fromSigned = fromSpecialType.IsSignedIntegralType(); if (!fromSigned) { return(recursive); } // OK, we know that the "from" type is a signed integer that is smaller than the // "to" type, so we are going to have sign extension. Is it surprising? The only // time that sign extension is *not* surprising is when we have a cast operator // to a *signed* type. That is, (int)myShort is not a surprising sign extension. if (conv.ExplicitCastInCode && toSpecialType.IsSignedIntegralType()) { return(recursive); } // Note that we *could* be somewhat more clever here. Consider the following edge case: // // (ulong)(int)(uint)(ushort)mySbyte // // We could reason that the sbyte-to-ushort conversion is going to add one byte of // unexpected sign extension. The conversion from ushort to uint adds no more bytes. // The conversion from uint to int adds no more bytes. Does the conversion from int // to ulong add any more bytes of unexpected sign extension? Well, no, because we // know that the previous conversion from ushort to uint will ensure that the top bit // of the uint is off! // // But we are not going to try to be that clever. In the extremely unlikely event that // someone does this, we will record that the unexpectedly turned-on bits are // 0xFFFFFFFF0000FF00, even though we could in theory deduce that only 0x000000000000FF00 // are the unexpected bits. ulong result = recursive; for (int i = fromSize; i < toSize; ++i) { result |= (0xFFUL) << (i * 8); } return(result); }
private BoundExpression BindCodeblock(SyntaxNode syntax, UnboundLambda unboundLambda, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { if (!Compilation.Options.HasRuntime) { return(null); } var isCodeblock = syntax.XIsCodeBlock; if (!isCodeblock) { isCodeblock = !destination.IsDelegateType() && !destination.IsExpressionTree(); } if (!isCodeblock) { return(null); } Conversion conv = Conversion.ImplicitReference; if (destination != Compilation.CodeBlockType() && !destination.IsObjectType()) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; conv = Conversions.ClassifyConversionFromType(Compilation.CodeBlockType(), destination, ref useSiteDiagnostics); diagnostics.Add(syntax, useSiteDiagnostics); } if (Compilation.Options.HasRuntime) { Debug.Assert(destination == Compilation.CodeBlockType() || conv.Exists); } if (!syntax.XIsCodeBlock && !Compilation.Options.MacroScript && !syntax.XNode.IsAliasExpression()) { Error(diagnostics, ErrorCode.ERR_CodeblockWithLambdaSyntax, syntax); } AnonymousTypeManager manager = this.Compilation.AnonymousTypeManager; var delegateSignature = new TypeSymbol[unboundLambda.ParameterCount + 1]; var usualType = this.Compilation.UsualType(); for (int i = 0; i < delegateSignature.Length; i++) { delegateSignature[i] = usualType; } NamedTypeSymbol cbType = manager.ConstructCodeblockTypeSymbol(delegateSignature, syntax.Location); var delType = manager.GetCodeblockDelegateType(cbType); var _boundLambda = unboundLambda.Bind(delType); diagnostics.AddRange(_boundLambda.Diagnostics); var cbDel = new BoundConversion( syntax, _boundLambda, conversion, @checked: false, explicitCastInCode: false, constantValueOpt: ConstantValue.NotAvailable, type: delType) { WasCompilerGenerated = unboundLambda.WasCompilerGenerated }; var cbSrc = new BoundLiteral(syntax, ConstantValue.Create(syntax.XCodeBlockSource), Compilation.GetSpecialType(SpecialType.System_String)); BoundExpression cbInst = new BoundAnonymousObjectCreationExpression(syntax, cbType.InstanceConstructors[0], new BoundExpression[] { cbDel, cbSrc }.ToImmutableArrayOrEmpty(), System.Collections.Immutable.ImmutableArray <BoundAnonymousPropertyDeclaration> .Empty, cbType) { WasCompilerGenerated = unboundLambda.WasCompilerGenerated };; if (conv != Conversion.ImplicitReference) { cbInst = new BoundConversion(syntax, cbInst, Conversion.ImplicitReference, false, false, ConstantValue.NotAvailable, Compilation.CodeBlockType()) { WasCompilerGenerated = unboundLambda.WasCompilerGenerated };; } if (!conv.IsValid || (!isCast && conv.IsExplicit)) { GenerateImplicitConversionError(diagnostics, syntax, conv, cbInst, destination); return(new BoundConversion( syntax, cbInst, conv, false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: true) { WasCompilerGenerated = unboundLambda.WasCompilerGenerated }); } return(new BoundConversion( syntax, cbInst, conv, false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination) { WasCompilerGenerated = unboundLambda.WasCompilerGenerated }); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// /// Note, if System.Threading.Interlocked.CompareExchange<T> is not available, /// we emit the following code and mark the method Synchronized (unless it is a struct). /// /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -= /// /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove; MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(updateMethodId); BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; if (updateMethod == null) { MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId); diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name), syntax.Location)); return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(@return)) { WasCompilerGenerated = true }); } Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; BoundExpression delegateUpdate; MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); if ((object)compareExchangeMethod == null) { // (DelegateType)Delegate.Combine(_event, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // _event = (DelegateType)Delegate.Combine(_event, value); BoundStatement eventUpdate = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundBackingField, right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( eventUpdate, @return)) { WasCompilerGenerated = true }); } compareExchangeMethod = compareExchangeMethod.Construct(ImmutableArray.Create <TypeSymbol>(delegateType)); Binder.ReportUseSiteDiagnostics(compareExchangeMethod, diagnostics, syntax); GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { // We have a successful tuple conversion; rather than producing a separate conversion node // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments. Debug.Assert(conversion.Kind == ConversionKind.ImplicitTupleLiteral || conversion.Kind == ConversionKind.ImplicitNullable); Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType()); TypeSymbol destinationWithoutNullable = conversion.Kind == ConversionKind.ImplicitNullable ? destinationWithoutNullable = destination.GetNullableUnderlyingType() : destination; NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable; if (targetType.IsTupleType) { var destTupleType = (TupleTypeSymbol)targetType; // do not lose the original element names in the literal if different from names in the target // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013) targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt); } var arguments = sourceTuple.Arguments; var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length); ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible(); Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?"); for (int i = 0; i < arguments.Length; i++) { var argument = arguments[i]; var destType = targetElementTypes[i]; HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion; if (isCast) { elementConversion = this.Conversions.ClassifyConversionForCast(argument, destType, ref useSiteDiagnostics); } else { elementConversion = this.Conversions.ClassifyConversionFromExpression(argument, destType, ref useSiteDiagnostics); } diagnostics.Add(syntax, useSiteDiagnostics); convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics)); } BoundExpression result = new BoundConvertedTupleLiteral( sourceTuple.Syntax, sourceTuple.Type, convertedArguments.ToImmutableAndFree(), targetType); // We need to preserve any conversion that changes the type (even identity conversions), // or that was explicitly written in code (so that GetSemanticInfo can find the syntax in the bound tree). if (!isCast && targetType == destination) { return result; } // if we have a nullable cast combined with a name/dynamic cast // name/dynamic cast must happen before converting to nullable if (conversion.Kind == ConversionKind.ImplicitNullable && destinationWithoutNullable != targetType) { Debug.Assert(destinationWithoutNullable.Equals(targetType, ignoreDynamic: true)); result = new BoundConversion( syntax, result, Conversion.Identity, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destinationWithoutNullable) { WasCompilerGenerated = sourceTuple.WasCompilerGenerated }; } return new BoundConversion( syntax, result, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination) { WasCompilerGenerated = sourceTuple.WasCompilerGenerated }; }
private TypedConstant VisitConversion(BoundConversion node, DiagnosticBag diagnostics, ref bool attrHasErrors, bool curArgumentHasErrors) { Debug.Assert(node.ConstantValue == null); // We have a bound conversion with a non-constant value. // According to statement 2) of the spec comment, this is not a valid attribute argument. // However, native compiler allows conversions to object type if the conversion operand is a valid attribute argument. // See method AttributeHelper::VerifyAttrArg(EXPR *arg). // We will match native compiler's behavior here. // Devdiv Bug #8763: Additionally we allow conversions from array type to object[], provided a conversion exists and each array element is a valid attribute argument. var type = node.Type; var operand = node.Operand; var operandType = operand.Type; if ((object)type != null && (object)operandType != null) { if (type.SpecialType == SpecialType.System_Object || operandType.IsArray() && type.IsArray() && ((ArrayTypeSymbol)type).ElementType.SpecialType == SpecialType.System_Object) { var typedConstantKind = operandType.GetAttributeParameterTypedConstantKind(_binder.Compilation); return VisitExpression(operand, typedConstantKind, diagnostics, ref attrHasErrors, curArgumentHasErrors); } } return CreateTypedConstant(node, TypedConstantKind.Error, diagnostics, ref attrHasErrors, curArgumentHasErrors); }
public override BoundNode VisitConversion(BoundConversion node) { CheckUnsafeType(node.Operand); CheckUnsafeType(node); bool wasInExpressionLambda = _inExpressionLambda; bool oldReportedUnsafe = _reportedUnsafe; switch (node.ConversionKind) { case ConversionKind.MethodGroup: VisitMethodGroup((BoundMethodGroup)node.Operand, parentIsConversion: true); return node; case ConversionKind.AnonymousFunction: if (!wasInExpressionLambda && node.Type.IsExpressionTree()) { _inExpressionLambda = true; // we report "unsafe in expression tree" at most once for each expression tree _reportedUnsafe = false; } break; case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node); } break; default: break; } var result = base.VisitConversion(node); _inExpressionLambda = wasInExpressionLambda; _reportedUnsafe = oldReportedUnsafe; return result; }