/// <summary> /// Emits address as in & /// /// May introduce a temp which it will return. (otherwise returns null) /// </summary> private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addressKind) { switch (expression.Kind) { case BoundKind.RefValueOperator: EmitRefValueAddress((BoundRefValueOperator)expression); break; case BoundKind.Local: EmitLocalAddress((BoundLocal)expression); break; case BoundKind.Dup: Debug.Assert(((BoundDup)expression).RefKind != RefKind.None, "taking address of a stack value?"); builder.EmitOpCode(ILOpCode.Dup); break; case BoundKind.Parameter: EmitParameterAddress((BoundParameter)expression); break; case BoundKind.FieldAccess: return EmitFieldAddress((BoundFieldAccess)expression); case BoundKind.ArrayAccess: //arrays are covariant, but elements can be written to. //the flag tells that we do not intend to use the address for writing. EmitArrayElementAddress((BoundArrayAccess)expression, addressKind); break; case BoundKind.ThisReference: Debug.Assert(expression.Type.IsValueType, "only valuetypes may need a ref to this"); builder.EmitOpCode(ILOpCode.Ldarg_0); break; case BoundKind.PreviousSubmissionReference: // script references are lowered to a this reference and a field access throw ExceptionUtilities.UnexpectedValue(expression.Kind); case BoundKind.BaseReference: Debug.Assert(false, "base is always a reference type, why one may need a reference to it?"); break; case BoundKind.Sequence: return EmitSequenceAddress((BoundSequence)expression, addressKind); case BoundKind.PointerIndirectionOperator: // The address of a dereferenced address is that address. BoundExpression operand = ((BoundPointerIndirectionOperator)expression).Operand; Debug.Assert(operand.Type.IsPointerType()); EmitExpression(operand, used: true); break; default: Debug.Assert(!HasHome(expression)); return EmitAddressOfTempClone(expression); } return null; }
private BoundExpression BindElementAccessCore(ElementAccessExpressionSyntax node, BoundExpression expr, IList<BoundExpression> arguments, IList<string> argumentNames) { Debug.Assert(node != null); Debug.Assert(expr != null); Debug.Assert(arguments != null); Debug.Assert(argumentNames != null); // UNDONE: Suppose we have an indexed property P on an instance c. Suppose the // UNDONE: type of the property is int[]. When binding c.P[123] we must // UNDONE: treat this as an invocation of the indexed property, not as a // UNDONE: dereference of the array returned by the property. //if (expr.isPROP() && expr.asPROP().IsUnboundIndexedProperty() && // !IsPropertyBindingInsideBindIndexerContext(node.Expression)) //{ // return BindIndexerAccess(node, expr, arguments, argumentNames); //} var exprType = expr.GetExpressionType(); // UNDONE: Ensure that the type of the expression has members defined. if (exprType is ArrayTypeSymbol) { return BindArrayAccess(node, expr, arguments, argumentNames); } else if (exprType is PointerTypeSymbol) { return BindPointerElementAccess(node, expr, arguments, argumentNames); } else { return BindIndexerAccess(node, expr, arguments, argumentNames); } }
public BoundIfStatement(BoundExpression condition, BoundStatement consequence, BoundStatement alternativeOpt) : base(BoundNodeKind.IfStatement) { Condition = condition; Consequence = consequence; AlternativeOpt = alternativeOpt; }
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.Dynamic: 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: current = conv.Operand; break; case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: return false; default: Debug.Fail("Unhandled conversion kind in reordering logic"); return false; } break; } } } }
public BoundForStatement(BoundMultipleVariableDeclarations declaration, BoundExpression initializer, BoundExpression condition, BoundExpression incrementor, BoundStatement body) : base(BoundNodeKind.ForStatement) { Declarations = declaration; Initializer = initializer; Condition = condition; Incrementor = incrementor; Body = body; }
private static BoundStatement RewriteIfStatement( SyntaxNode syntax, BoundExpression rewrittenCondition, BoundStatement rewrittenConsequence, BoundStatement rewrittenAlternativeOpt, bool hasErrors) { var afterif = new GeneratedLabelSymbol("afterif"); // if (condition) // consequence; // // becomes // // GotoIfFalse condition afterif; // consequence; // afterif: if (rewrittenAlternativeOpt == null) { return BoundStatementList.Synthesized(syntax, new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, afterif), rewrittenConsequence, new BoundLabelStatement(syntax, afterif)); } // if (condition) // consequence; // else // alternative // // becomes // // GotoIfFalse condition alt; // consequence // goto afterif; // alt: // alternative; // afterif: var alt = new GeneratedLabelSymbol("alternative"); return BoundStatementList.Synthesized(syntax, hasErrors, new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, alt), rewrittenConsequence, new BoundGotoStatement(syntax, afterif), new BoundLabelStatement(syntax, alt), rewrittenAlternativeOpt, new BoundLabelStatement(syntax, afterif)); }
private void EmitExpressionCoreWithStackGuard(BoundExpression expression, bool used) { Debug.Assert(_recursionDepth == 1); try { EmitExpressionCore(expression, used); Debug.Assert(_recursionDepth == 1); } catch (Exception ex) when (StackGuard.IsInsufficientExecutionStackException(ex)) { _diagnostics.Add(ErrorCode.ERR_InsufficientStack, BoundTreeVisitor.CancelledByStackGuardException.GetTooLongOrComplexExpressionErrorLocation(expression)); throw new EmitCancelledException(); } }
public static string GetErrorReportingName(BoundExpression expression) { switch(expression.Kind) { case BoundKind.Literal: if (expression.ConstantValue.IsNull) return "<null>"; break; case BoundKind.Lambda: case BoundKind.UnboundLambda: return "lambda expression"; // UNDONE: or "anonymous method" case BoundKind.MethodGroup: return "method group"; } return GetErrorReportingName(expression.GetExpressionType()); }
private BoundExpression BindArrayAccess(ElementAccessExpressionSyntax node, BoundExpression expr, IList<BoundExpression> arguments, IList<string> argumentNames) { Debug.Assert(node != null); Debug.Assert(expr != null); Debug.Assert(arguments != null); Debug.Assert(argumentNames != null); // For an array access, the primary-no-array-creation-expression of the element-access must // be a value of an array-type. Furthermore, the argument-list of an array access is not // allowed to contain named arguments.The number of expressions in the argument-list must // be the same as the rank of the array-type, and each expression must be of type // int, uint, long, ulong, or must be implicitly convertible to one or more of these types. if (argumentNames.Any(x => x != null)) { Error(ErrorCode.ERR_NamedArgumentForArray, node); } var arrayType = (ArrayTypeSymbol)expr.GetExpressionType(); // Note that the spec says to determine which of {int, uint, long, ulong} *each* // index expression is convertible to. That is not what C# 1 through 4 // did; the implementations instead determined which of those four // types *all* of the index expressions converted to. int rank = arrayType.Rank; if (arguments.Count != arrayType.Rank) { Error(ErrorCode.ERR_BadIndexCount, node, rank); return BoundArrayAccess.AsError(node, expr, arguments, arrayType.ElementType); } var convertedArguments = arguments.Select(x => ConvertToArrayIndex(x)).ToList(); return new BoundArrayAccess(node, expr, convertedArguments, arrayType.ElementType); }
private void EmitExpression(BoundExpression expression, bool used) { if (expression == null) { return; } var constantValue = expression.ConstantValue; if (constantValue != null) { if (!used) { // unused constants have no side-effects. return; } if ((object)expression.Type == null || expression.Type.SpecialType != SpecialType.System_Decimal) { EmitConstantExpression(expression.Type, constantValue, used, expression.Syntax); return; } } _recursionDepth++; if (_recursionDepth > 1) { StackGuard.EnsureSufficientExecutionStack(_recursionDepth); EmitExpressionCore(expression, used); } else { EmitExpressionCoreWithStackGuard(expression, used); } _recursionDepth--; }
public BoundEqualsValue(BoundExpression value) : base(BoundNodeKind.EqualsValue) { Value = value; }
public BoundExpressionStatement(BoundExpression expression) { Expression = expression; }
private static OverloadResolutionResult <BinaryOperatorSignature> LookupBinaryOperator(BinaryOperatorKind operatorKind, BoundExpression left, BoundExpression right) { return(BinaryOperator.Resolve(operatorKind, left.Type, right.Type)); }
private static bool IsStackAlloc(BoundExpression expr) { return expr.Kind == BoundKind.StackAllocArrayCreation || expr.Kind == BoundKind.Conversion && ((BoundConversion)expr).Operand.Kind == BoundKind.StackAllocArrayCreation; }
internal override ImmutableArray <Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue) { Debug.Assert(boundInitValue != null); MakeConstantTuple(inProgress: null, boundInitValue: boundInitValue); return(this.constantTuple == null ? default(ImmutableArray <Diagnostic>) : this.constantTuple.Diagnostics); }
private void InPlaceInit(BoundExpression target, bool used) { var temp = EmitAddress(target, AddressKind.Writeable); Debug.Assert(temp == null, "in-place init target should not create temps"); _builder.EmitOpCode(ILOpCode.Initobj); // intitobj <MyStruct> EmitSymbolToken(target.Type, target.Syntax); if (used) { Debug.Assert(TargetIsNotOnHeap(target), "cannot read-back the target since it could have been modified"); EmitExpression(target, used); } }
internal override ImmutableBindingDiagnostic <AssemblySymbol> GetConstantValueDiagnostics(BoundExpression boundInitValue) { Debug.Assert(boundInitValue != null); MakeConstantTuple(inProgress: null, boundInitValue: boundInitValue); return(_constantTuple == null ? ImmutableBindingDiagnostic <AssemblySymbol> .Empty : _constantTuple.Diagnostics); }
protected override TypeWithAnnotations InferTypeOfVarVariable(BindingDiagnosticBag diagnostics) { BoundExpression initializerOpt = this._initializerBinder.BindInferredVariableInitializer(diagnostics, RefKind, _initializer, _initializer); return(TypeWithAnnotations.Create(initializerOpt?.Type)); }
internal override ImmutableBindingDiagnostic <AssemblySymbol> GetConstantValueDiagnostics(BoundExpression boundInitValue) { return(ImmutableBindingDiagnostic <AssemblySymbol> .Empty); }
public void EmitConvert(BoundExpression expr, TypeSymbol to) { // bind target expression type expr.Access = expr.Access.WithRead(to); // constants if (expr.ConstantObject.HasValue && to != null) { EmitConvert(EmitLoadConstant(expr.ConstantObject.Value, to), 0, to); return; } // loads value from place most effectively without runtime type checking var place = PlaceOrNull(expr); var type = TryEmitVariableSpecialize(place, expr.TypeRefMask); if (type != null) { EmitConvert(type, 0, to); return; } // avoiding of load of full value if (place != null && place.HasAddress) { if (place.TypeOpt == CoreTypes.PhpNumber) { if (to.SpecialType == SpecialType.System_Int64) { // <place>.ToLong() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToLong); return; } if (to.SpecialType == SpecialType.System_Double) { // <place>.ToDouble() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToDouble); return; } if (to.SpecialType == SpecialType.System_Boolean) { // <place>.ToBoolean() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToBoolean); return; } if (to.SpecialType == SpecialType.System_String) { // <place>.ToString(<ctx>) place.EmitLoadAddress(_il); EmitLoadContext(); EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToString_Context); return; } if (to == CoreTypes.PhpValue) { // TODO } // TODO: Object, Array } else if (place.TypeOpt == CoreTypes.PhpValue) { if (to.SpecialType == SpecialType.System_Int64) { // <place>.ToLong() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToLong); return; } if (to.SpecialType == SpecialType.System_Double) { // <place>.ToDouble() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToDouble); return; } if (to.SpecialType == SpecialType.System_Boolean) { // <place>.ToBoolean() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToBoolean); return; } if (to.SpecialType == SpecialType.System_String) { // <place>.ToString(<ctx>) place.EmitLoadAddress(_il); EmitLoadContext(); EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToString_Context); return; } if (to.SpecialType == SpecialType.System_Object) { // <place>.ToClass() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToClass); return; } //if (to == CoreTypes.PhpArray) //{ // // <place>.AsArray() // place.EmitLoadAddress(_il); // EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToArray); // return; //} } else if (place.TypeOpt == CoreTypes.Long) { if (to.SpecialType == SpecialType.System_String) { // <place>.ToString() place.EmitLoadAddress(_il); EmitCall(ILOpCode.Call, CoreMethods.Operators.Long_ToString); return; } } } // EmitConvert(expr.Emit(this), expr.TypeRefMask, to); }
public BoundUpdateLineCountExpression(SyntaxNode syntax, BoundExpression left, BoundUpdateLineCountOperatorKind operatorKind, BoundExpression right) : base(syntax) => (this.Left, this.OperatorKind, this.Right) = (left, operatorKind, right);
private void EmitSpecialUserDefinedConversion(BoundConversion conversion, bool used, BoundExpression operand) { var typeTo = (NamedTypeSymbol)conversion.Type; if (!TryEmitReadonlySpanAsBlobWrapper(typeTo, operand, used, inPlace: false)) { // there are several reasons that could prevent us from emitting a wrapper // in such case we just emit the operand and then invoke the conversion method EmitExpression(operand, used); if (used) { // consumes 1 argument (array) and produces one result (span) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0); EmitSymbolToken(conversion.SymbolOpt, conversion.Syntax, optArgList: null); } } }
private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo) { Debug.Assert(method.IsExtensionMethod); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) { return(method); } // We never resolve extension methods on a dynamic receiver. if (thisType.IsDynamic()) { return(null); } var containingAssembly = method.ContainingAssembly; var errorNamespace = containingAssembly.GlobalNamespace; var conversions = new TypeConversions(containingAssembly.CorLibrary); // There is absolutely no plausible syntax/tree that we could use for these // synthesized literals. We could be speculatively binding a call to a PE method. var syntaxTree = CSharpSyntaxTree.Dummy; var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); // Create an argument value for the "this" argument of specific type, // and pass the same bad argument value for all other arguments. var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true }; var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false); var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true }; var paramCount = method.ParameterCount; var arguments = new BoundExpression[paramCount]; for (int i = 0; i < paramCount; i++) { var argument = (i == 0) ? thisArgumentValue : otherArgumentValue; arguments[i] = argument; } var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument( conversions, method, arguments.AsImmutable(), useSiteInfo: ref useSiteInfo); if (typeArgs.IsDefault) { return(null); } // For the purpose of constraint checks we use error type symbol in place of type arguments that we couldn't infer from the first argument. // This prevents constraint checking from failing for corresponding type parameters. int firstNullInTypeArgs = -1; var notInferredTypeParameters = PooledHashSet <TypeParameterSymbol> .GetInstance(); var typeParams = method.TypeParameters; var typeArgsForConstraintsCheck = typeArgs; for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++) { if (!typeArgsForConstraintsCheck[i].HasType) { firstNullInTypeArgs = i; var builder = ArrayBuilder <TypeWithAnnotations> .GetInstance(); builder.AddRange(typeArgsForConstraintsCheck, firstNullInTypeArgs); for (; i < typeArgsForConstraintsCheck.Length; i++) { var typeArg = typeArgsForConstraintsCheck[i]; if (!typeArg.HasType) { notInferredTypeParameters.Add(typeParams[i]); builder.Add(TypeWithAnnotations.Create(ErrorTypeSymbol.UnknownResultType)); } else { builder.Add(typeArg); } } typeArgsForConstraintsCheck = builder.ToImmutableAndFree(); break; } } // Check constraints. var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance(); var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck); ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null; var success = method.CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(compilation, conversions, includeNullability: false, NoLocation.Singleton, diagnostics: null, template: new CompoundUseSiteInfo <AssemblySymbol>(useSiteInfo)), substitution, typeParams, typeArgsForConstraintsCheck, diagnosticsBuilder, nullabilityDiagnosticsBuilderOpt: null, ref useSiteDiagnosticsBuilder, ignoreTypeConstraintsDependentOnTypeParametersOpt: notInferredTypeParameters.Count > 0 ? notInferredTypeParameters : null); diagnosticsBuilder.Free(); notInferredTypeParameters.Free(); if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0) { foreach (var diag in useSiteDiagnosticsBuilder) { useSiteInfo.Add(diag.UseSiteInfo); } } if (!success) { return(null); } // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument. ImmutableArray <TypeWithAnnotations> typeArgsForConstruct = typeArgs; if (typeArgs.Any(t => !t.HasType)) { typeArgsForConstruct = typeArgs.ZipAsArray( method.TypeParameters, (t, tp) => t.HasType ? t : TypeWithAnnotations.Create(tp)); } return(method.Construct(typeArgsForConstruct)); }
/// <summary> /// Used to decide if we need to emit call or callvirt. /// It basically checks if the receiver expression cannot be null, but it is not 100% precise. /// There are cases where it really can be null, but we do not care. /// </summary> private bool CanUseCallOnRefTypeReceiver(BoundExpression receiver) { // It seems none of the ways that could produce a receiver typed as a type param // can guarantee that it is not null. if (receiver.Type.IsTypeParameter()) { return false; } Debug.Assert(receiver.Type.IsVerifierReference(), "this is not a reference"); Debug.Assert(receiver.Kind != BoundKind.BaseReference, "base should always use call"); var constVal = receiver.ConstantValue; if (constVal != null) { // only when this is a constant Null, we need a callvirt return !constVal.IsNull; } switch (receiver.Kind) { case BoundKind.ArrayCreation: return true; case BoundKind.ObjectCreationExpression: //NOTE: there are cases involving ProxyAttribute //where newobj may produce null return true; case BoundKind.Conversion: var conversion = (BoundConversion)receiver; switch (conversion.ConversionKind) { case ConversionKind.Boxing: //NOTE: boxing can produce null for Nullable, but any call through that //will result in null reference exceptions anyways. return true; case ConversionKind.MethodGroup: case ConversionKind.AnonymousFunction: return true; case ConversionKind.ExplicitReference: case ConversionKind.ImplicitReference: return CanUseCallOnRefTypeReceiver(conversion.Operand); } break; case BoundKind.ThisReference: //NOTE: these actually can be null if called from a different language //if that has already happen, we will just propagate the behavior. return true; case BoundKind.DelegateCreationExpression: return true; case BoundKind.Sequence: var seqValue = ((BoundSequence)(receiver)).Value; return CanUseCallOnRefTypeReceiver(seqValue); case BoundKind.AssignmentOperator: var rhs = ((BoundAssignmentOperator)receiver).Right; return CanUseCallOnRefTypeReceiver(rhs); case BoundKind.TypeOfOperator: return true; case BoundKind.FieldAccess: return ((BoundFieldAccess)receiver).FieldSymbol.IsCapturedFrame; case BoundKind.ConditionalReceiver: return true; //TODO: there could be more cases where we can be sure that receiver is not a null. } return false; }
// returns true when receiver is already a ref. // in such cases calling through a ref could be preferred over // calling through indirectly loaded value. private bool IsRef(BoundExpression receiver) { switch (receiver.Kind) { case BoundKind.Local: return ((BoundLocal)receiver).LocalSymbol.RefKind != RefKind.None; case BoundKind.Parameter: return ((BoundParameter)receiver).ParameterSymbol.RefKind != RefKind.None; case BoundKind.Call: return ((BoundCall)receiver).Method.RefKind != RefKind.None; case BoundKind.Dup: return ((BoundDup)receiver).RefKind != RefKind.None; case BoundKind.Sequence: return IsRef(((BoundSequence)receiver).Value); } return false; }
internal override ImmutableArray <Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue) => _underlyingLocal.GetConstantValueDiagnostics(boundInitValue);
// partial ctor results are not observable when target is not on the heap. // we also must not be in a try, otherwise if ctor throws // partially assigned value may be observed in the handler. private bool PartialCtorResultCannotEscape(BoundExpression left) { if (TargetIsNotOnHeap(left)) { if (_tryNestingLevel != 0) { var local = left as BoundLocal; if (local != null && !_builder.PossiblyDefinedOutsideOfTry(GetLocal(local))) { // local defined inside immediate Try - cannot escape return true; } // local defined outside of immediate try or it is a parameter - can escape return false; } // we are not in a try - locals, parameters cannot escape return true; } // left is a reference, partial initializations can escape. return false; }
public BoundBinaryExpression(BoundExpression left, BoundBinaryOperator @operator, BoundExpression right) { Left = left; Operator = @operator; Right = right; }
private void EmitArgument(BoundExpression argument, RefKind refKind) { if (refKind == RefKind.None) { EmitExpression(argument, true); } else { var temp = EmitAddress(argument, AddressKind.Writeable); Debug.Assert(temp == null, "passing args byref should not clone them into temps"); } }
internal override ImmutableArray<Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue) { return ImmutableArray<Diagnostic>.Empty; }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { BoundExpression originalLeft = node.Left; if (originalLeft.Kind != BoundKind.Local) { return base.VisitAssignmentOperator(node); } var leftLocal = (BoundLocal)originalLeft; BoundExpression originalRight = node.Right; if (leftLocal.LocalSymbol.RefKind != RefKind.None && node.RefKind != RefKind.None && NeedsProxy(leftLocal.LocalSymbol)) { Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol)); Debug.Assert(!IsStackAlloc(originalRight)); //spilling ref local variables throw ExceptionUtilities.Unreachable; } if (NeedsProxy(leftLocal.LocalSymbol) && !proxies.ContainsKey(leftLocal.LocalSymbol)) { Debug.Assert(leftLocal.LocalSymbol.DeclarationKind == LocalDeclarationKind.None); // spilling temp variables throw ExceptionUtilities.Unreachable; } BoundExpression rewrittenLeft = (BoundExpression)this.Visit(leftLocal); BoundExpression rewrittenRight = (BoundExpression)this.Visit(originalRight); TypeSymbol rewrittenType = VisitType(node.Type); // Check if we're assigning the result of stackalloc to a hoisted local. // If we are, we need to store the result in a temp local and then assign // the value of the local to the field corresponding to the hoisted local. // If the receiver of the field is on the stack when the stackalloc happens, // popping it will free the memory (?) or otherwise cause verification issues. // DevDiv Bugs 59454 if (rewrittenLeft.Kind != BoundKind.Local && IsStackAlloc(originalRight)) { // From ILGENREC::genAssign: // DevDiv Bugs 59454: Handle hoisted local initialized with a stackalloc // NOTE: Need to check for cast of stackalloc on RHS. // If LHS isLocal, then genAddr is a noop so regular case works fine. SyntheticBoundNodeFactory factory = new SyntheticBoundNodeFactory(this.CurrentMethod, rewrittenLeft.Syntax, this.CompilationState, this.Diagnostics); BoundAssignmentOperator tempAssignment; BoundLocal tempLocal = factory.StoreToTemp(rewrittenRight, out tempAssignment); Debug.Assert(node.RefKind == RefKind.None); BoundAssignmentOperator rewrittenAssignment = node.Update(rewrittenLeft, tempLocal, node.RefKind, rewrittenType); return new BoundSequence( node.Syntax, ImmutableArray.Create<LocalSymbol>(tempLocal.LocalSymbol), ImmutableArray.Create<BoundExpression>(tempAssignment), rewrittenAssignment, rewrittenType); } return node.Update(rewrittenLeft, rewrittenRight, node.RefKind, rewrittenType); }
internal override ImmutableArray<Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue) { Debug.Assert(boundInitValue != null); MakeConstantTuple(inProgress: null, boundInitValue: boundInitValue); return _constantTuple == null ? ImmutableArray<Diagnostic>.Empty : _constantTuple.Diagnostics; }
private static bool BaseReferenceInReceiverWasRewritten(BoundExpression originalReceiver, BoundExpression rewrittenReceiver) { return originalReceiver != null && originalReceiver.Kind == BoundKind.BaseReference && rewrittenReceiver != null && rewrittenReceiver.Kind != BoundKind.BaseReference; }
internal static BoundExpression ConvertToLocalType(CSharpCompilation compilation, BoundExpression expr, TypeSymbol type, DiagnosticBag diagnostics) { if (type.IsPointerType()) { var syntax = expr.Syntax; var intPtrType = compilation.GetSpecialType(SpecialType.System_IntPtr); Binder.ReportUseSiteDiagnostics(intPtrType, diagnostics, syntax); MethodSymbol conversionMethod; if (Binder.TryGetSpecialTypeMember(compilation, SpecialMember.System_IntPtr__op_Explicit_ToPointer, syntax, diagnostics, out conversionMethod)) { var temp = ConvertToLocalTypeHelper(compilation, expr, intPtrType, diagnostics); expr = BoundCall.Synthesized( syntax, receiverOpt: null, method: conversionMethod, arg0: temp, binder: null); } else { return(new BoundBadExpression( syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(expr), type)); } } return(ConvertToLocalTypeHelper(compilation, expr, type, diagnostics)); }
private static BoundExpression ConvertToLocalTypeHelper(CSharpCompilation compilation, BoundExpression expr, TypeSymbol type, DiagnosticBag diagnostics) { // NOTE: This conversion can fail if some of the types involved are from not-yet-loaded modules. // For example, if System.Exception hasn't been loaded, then this call will fail for $stowedexception. HashSet <DiagnosticInfo> useSiteDiagnostics = null; var conversion = compilation.Conversions.ClassifyConversionFromExpression(expr, type, ref useSiteDiagnostics); diagnostics.Add(expr.Syntax, useSiteDiagnostics); Debug.Assert(conversion.IsValid || diagnostics.HasAnyErrors()); return(BoundConversion.Synthesized( expr.Syntax, expr, conversion, @checked: false, explicitCastInCode: false, conversionGroupOpt: null, constantValueOpt: null, type: type, hasErrors: !conversion.IsValid)); }
public Evaluator(BoundExpression root, Scope scope) { this.root = root; this.scope = scope; }
public BoundExpressionStatement(BoundExpression expression) : base(BoundNodeKind.ExpressionStatement) { Expression = expression; }
private object EvaluateExpression(BoundExpression node) { if (node is BoundLiteralExpression n) { return(n.Value); } if (node is BoundVariableExpression v) { var symbol = scope[v.Name]; return(symbol.Value); } if (node is BoundAssignementExpression a) { var value = EvaluateExpression(a.Expression); var symbol = scope[a.Name.Identifier]; symbol.Value = value; return(value); } if (node is BoundUnaryExpression u) { var operand = EvaluateExpression(u.Operand); switch (u.Op.Kind) { case BoundUnaryOperatorKind.Identity: return((int)operand); case BoundUnaryOperatorKind.Negation: return(-(int)operand); case BoundUnaryOperatorKind.LogicalNegation: return(!(bool)operand); default: throw new Exception($"Unexpected unary operator {u.Op}"); } } if (node is BoundBinaryExpression b) { var left = EvaluateExpression(b.Left); var right = EvaluateExpression(b.Right); switch (b.Op.Kind) { case BoundBinaryOperatorKind.Addition: return((int)left + (int)right); case BoundBinaryOperatorKind.Subtraction: return((int)left - (int)right); case BoundBinaryOperatorKind.Multiplication: return((int)left * (int)right); case BoundBinaryOperatorKind.Division: return((int)left / (int)right); case BoundBinaryOperatorKind.LogicalAnd: return((bool)left && (bool)right); case BoundBinaryOperatorKind.LogicalOr: return((bool)left || (bool)right); case BoundBinaryOperatorKind.Equals: return(Equals(left, right)); case BoundBinaryOperatorKind.NotEquals: return(!Equals(left, right)); default: throw new Exception($"Unexpected binary operator {b.Op}"); } } throw new Exception($"Unexpected node {node.Kind}"); }
internal static bool FieldLoadMustUseRef(BoundExpression expr) { var type = expr.Type; // type parameter values must be boxed to get access to fields if (type.IsTypeParameter()) { return true; } // From Dev12/symbol.cpp // // // Used by ILGEN to determine if the type of this AggregateSymbol is one that the CLR // // will consider ambiguous to an unmanaged pointer when it is on the stack (see VSW #396011) // bool AggregateSymbol::IsCLRAmbigStruct() // . . . switch (type.SpecialType) { // case PT_BYTE: case SpecialType.System_Byte: // case PT_SHORT: case SpecialType.System_Int16: // case PT_INT: case SpecialType.System_Int32: // case PT_LONG: case SpecialType.System_Int64: // case PT_CHAR: case SpecialType.System_Char: // case PT_BOOL: case SpecialType.System_Boolean: // case PT_SBYTE: case SpecialType.System_SByte: // case PT_USHORT: case SpecialType.System_UInt16: // case PT_UINT: case SpecialType.System_UInt32: // case PT_ULONG: case SpecialType.System_UInt64: // case PT_INTPTR: case SpecialType.System_IntPtr: // case PT_UINTPTR: case SpecialType.System_UIntPtr: // case PT_FLOAT: case SpecialType.System_Single: // case PT_DOUBLE: case SpecialType.System_Double: // case PT_TYPEHANDLE: case SpecialType.System_RuntimeTypeHandle: // case PT_FIELDHANDLE: case SpecialType.System_RuntimeFieldHandle: // case PT_METHODHANDLE: case SpecialType.System_RuntimeMethodHandle: //case PT_ARGUMENTHANDLE: case SpecialType.System_RuntimeArgumentHandle: return true; } // this is for value__ // I do not know how to hit this, since value__ is not bindable in C#, but Dev12 has code to handle this return type.IsEnumType(); }
internal static bool ReportDefaultParameterErrors( Binder binder, Symbol owner, ParameterSyntax parameterSyntax, SourceParameterSymbol parameter, BoundExpression defaultExpression, DiagnosticBag diagnostics) { bool hasErrors = false; // SPEC VIOLATION: The spec says that the conversion from the initializer to the // parameter type is required to be either an identity or a nullable conversion, but // that is not right: // // void M(short myShort = 10) {} // * not an identity or nullable conversion but should be legal // // void M(object obj = (dynamic)null) {} // * an identity conversion, but should be illegal // // void M(MyStruct? myStruct = default(MyStruct)) {} // * a nullable conversion, but must be illegal because we cannot generate metadata for it // // Even if the expression is thoroughly illegal, we still want to bind it and // stick it in the parameter because we want to be able to analyze it for // IntelliSense purposes. TypeSymbol parameterType = parameter.Type; HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = binder.Conversions.ClassifyImplicitConversionFromExpression(defaultExpression, parameterType, ref useSiteDiagnostics); diagnostics.Add(defaultExpression.Syntax, useSiteDiagnostics); var refKind = GetModifiers(parameterSyntax.Modifiers, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword); // CONSIDER: We are inconsistent here regarding where the error is reported; is it // CONSIDER: reported on the parameter name, or on the value of the initializer? // CONSIDER: Consider making this consistent. if (refKind == RefKind.Ref || refKind == RefKind.Out) { // error CS1741: A ref or out parameter cannot have a default value diagnostics.Add(ErrorCode.ERR_RefOutDefaultValue, refnessKeyword.GetLocation()); hasErrors = true; } else if (paramsKeyword.Kind() == SyntaxKind.ParamsKeyword) { // error CS1751: Cannot specify a default value for a parameter array diagnostics.Add(ErrorCode.ERR_DefaultValueForParamsParameter, paramsKeyword.GetLocation()); hasErrors = true; } else if (thisKeyword.Kind() == SyntaxKind.ThisKeyword) { // Only need to report CS1743 for the first parameter. The caller will // have reported CS1100 if 'this' appeared on another parameter. if (parameter.Ordinal == 0) { // error CS1743: Cannot specify a default value for the 'this' parameter diagnostics.Add(ErrorCode.ERR_DefaultValueForExtensionParameter, thisKeyword.GetLocation()); hasErrors = true; } } else if (!defaultExpression.HasAnyErrors && !IsValidDefaultValue(defaultExpression)) { // error CS1736: Default parameter value for '{0}' must be a compile-time constant diagnostics.Add(ErrorCode.ERR_DefaultValueMustBeConstant, parameterSyntax.Default.Value.Location, parameterSyntax.Identifier.ValueText); hasErrors = true; } else if (!conversion.Exists || conversion.IsUserDefined || conversion.IsIdentity && parameterType.SpecialType == SpecialType.System_Object && defaultExpression.Type.IsDynamic()) { // If we had no implicit conversion, or a user-defined conversion, report an error. // // Even though "object x = (dynamic)null" is a legal identity conversion, we do not allow it. // CONSIDER: We could. Doesn't hurt anything. // error CS1750: A value of type '{0}' cannot be used as a default parameter because there are no standard conversions to type '{1}' diagnostics.Add(ErrorCode.ERR_NoConversionForDefaultParam, parameterSyntax.Identifier.GetLocation(), defaultExpression.Display, parameterType); hasErrors = true; } else if (conversion.IsReference && (parameterType.SpecialType == SpecialType.System_Object || parameterType.Kind == SymbolKind.DynamicType) && (object)defaultExpression.Type != null && defaultExpression.Type.SpecialType == SpecialType.System_String || conversion.IsBoxing) { // We don't allow object x = "hello", object x = 123, dynamic x = "hello", etc. // error CS1763: '{0}' is of type '{1}'. A default parameter value of a reference type other than string can only be initialized with null diagnostics.Add(ErrorCode.ERR_NotNullRefDefaultParameter, parameterSyntax.Identifier.GetLocation(), parameterSyntax.Identifier.ValueText, parameterType); hasErrors = true; } else if (conversion.IsNullable && !defaultExpression.Type.IsNullableType() && !(parameterType.GetNullableUnderlyingType().IsEnumType() || parameterType.GetNullableUnderlyingType().IsIntrinsicType())) { // We can do: // M(int? x = default(int)) // M(int? x = default(int?)) // M(MyEnum? e = default(enum)) // M(MyEnum? e = default(enum?)) // M(MyStruct? s = default(MyStruct?)) // // but we cannot do: // // M(MyStruct? s = default(MyStruct)) // error CS1770: // A value of type '{0}' cannot be used as default parameter for nullable parameter '{1}' because '{0}' is not a simple type diagnostics.Add(ErrorCode.ERR_NoConversionForNubDefaultParam, parameterSyntax.Identifier.GetLocation(), defaultExpression.Type, parameterSyntax.Identifier.ValueText); hasErrors = true; } // Certain contexts allow default parameter values syntactically but they are ignored during // semantic analysis. They are: // 1. Explicitly implemented interface methods; since the method will always be called // via the interface, the defaults declared on the implementation will not // be seen at the call site. // // UNDONE: 2. The "actual" side of a partial method; the default values are taken from the // UNDONE: "declaring" side of the method. // // UNDONE: 3. An indexer with only one formal parameter; it is illegal to omit every argument // UNDONE: to an indexer. // // 4. A user-defined operator; it is syntactically impossible to omit the argument. if (owner.IsExplicitInterfaceImplementation() || owner.IsPartialImplementation() || owner.IsOperator()) { // CS1066: The default value specified for parameter '{0}' will have no effect because it applies to a // member that is used in contexts that do not allow optional arguments diagnostics.Add(ErrorCode.WRN_DefaultValueForUnconsumedLocation, parameterSyntax.Identifier.GetLocation(), parameterSyntax.Identifier.ValueText); } return(hasErrors); }
/// <summary> /// checks if receiver is effectively ldarg.0 /// </summary> private bool IsThisReceiver(BoundExpression receiver) { switch (receiver.Kind) { case BoundKind.ThisReference: return true; case BoundKind.Sequence: var seqValue = ((BoundSequence)(receiver)).Value; return IsThisReceiver(seqValue); } return false; }
private void EmitExpressionCore(BoundExpression expression, bool used) { switch (expression.Kind) { case BoundKind.AssignmentOperator: EmitAssignmentExpression((BoundAssignmentOperator)expression, used ? UseKind.UsedAsValue : UseKind.Unused); break; case BoundKind.Call: EmitCallExpression((BoundCall)expression, used ? UseKind.UsedAsValue : UseKind.Unused); break; case BoundKind.ObjectCreationExpression: EmitObjectCreationExpression((BoundObjectCreationExpression)expression, used); break; case BoundKind.DelegateCreationExpression: EmitDelegateCreationExpression((BoundDelegateCreationExpression)expression, used); break; case BoundKind.ArrayCreation: EmitArrayCreationExpression((BoundArrayCreation)expression, used); break; case BoundKind.StackAllocArrayCreation: EmitStackAllocArrayCreationExpression((BoundStackAllocArrayCreation)expression, used); break; case BoundKind.Conversion: EmitConversionExpression((BoundConversion)expression, used); break; case BoundKind.Local: EmitLocalLoad((BoundLocal)expression, used); break; case BoundKind.Dup: EmitDupExpression((BoundDup)expression, used); break; case BoundKind.Parameter: if (used) // unused parameter has no side-effects { EmitParameterLoad((BoundParameter)expression); } break; case BoundKind.FieldAccess: EmitFieldLoad((BoundFieldAccess)expression, used); break; case BoundKind.ArrayAccess: EmitArrayElementLoad((BoundArrayAccess)expression, used); break; case BoundKind.ArrayLength: EmitArrayLength((BoundArrayLength)expression, used); break; case BoundKind.ThisReference: if (used) // unused this has no side-effects { EmitThisReferenceExpression((BoundThisReference)expression); } break; case BoundKind.PreviousSubmissionReference: // Script references are lowered to a this reference and a field access. throw ExceptionUtilities.UnexpectedValue(expression.Kind); case BoundKind.BaseReference: if (used) // unused base has no side-effects { var thisType = _method.ContainingType; _builder.EmitOpCode(ILOpCode.Ldarg_0); if (thisType.IsValueType) { EmitLoadIndirect(thisType, expression.Syntax); EmitBox(thisType, expression.Syntax); } } break; case BoundKind.Sequence: EmitSequenceExpression((BoundSequence)expression, used); break; case BoundKind.SequencePointExpression: EmitSequencePointExpression((BoundSequencePointExpression)expression, used); break; case BoundKind.UnaryOperator: EmitUnaryOperatorExpression((BoundUnaryOperator)expression, used); break; case BoundKind.BinaryOperator: EmitBinaryOperatorExpression((BoundBinaryOperator)expression, used); break; case BoundKind.NullCoalescingOperator: EmitNullCoalescingOperator((BoundNullCoalescingOperator)expression, used); break; case BoundKind.IsOperator: EmitIsExpression((BoundIsOperator)expression, used); break; case BoundKind.AsOperator: EmitAsExpression((BoundAsOperator)expression, used); break; case BoundKind.DefaultOperator: EmitDefaultExpression((BoundDefaultOperator)expression, used); break; case BoundKind.TypeOfOperator: if (used) // unused typeof has no side-effects { EmitTypeOfExpression((BoundTypeOfOperator)expression); } break; case BoundKind.SizeOfOperator: if (used) // unused sizeof has no side-effects { EmitSizeOfExpression((BoundSizeOfOperator)expression); } break; case BoundKind.ModuleVersionId: Debug.Assert(used); EmitModuleVersionIdLoad((BoundModuleVersionId)expression); break; case BoundKind.ModuleVersionIdString: Debug.Assert(used); EmitModuleVersionIdStringLoad((BoundModuleVersionIdString)expression); break; case BoundKind.InstrumentationPayloadRoot: Debug.Assert(used); EmitInstrumentationPayloadRootLoad((BoundInstrumentationPayloadRoot)expression); break; case BoundKind.MethodDefIndex: Debug.Assert(used); EmitMethodDefIndexExpression((BoundMethodDefIndex)expression); break; case BoundKind.MaximumMethodDefIndex: Debug.Assert(used); EmitMaximumMethodDefIndexExpression((BoundMaximumMethodDefIndex)expression); break; case BoundKind.SourceDocumentIndex: Debug.Assert(used); EmitSourceDocumentIndex((BoundSourceDocumentIndex)expression); break; case BoundKind.MethodInfo: if (used) { EmitMethodInfoExpression((BoundMethodInfo)expression); } break; case BoundKind.FieldInfo: if (used) { EmitFieldInfoExpression((BoundFieldInfo)expression); } break; case BoundKind.ConditionalOperator: EmitConditionalOperator((BoundConditionalOperator)expression, used); break; case BoundKind.AddressOfOperator: EmitAddressOfExpression((BoundAddressOfOperator)expression, used); break; case BoundKind.PointerIndirectionOperator: EmitPointerIndirectionOperator((BoundPointerIndirectionOperator)expression, used); break; case BoundKind.ArgList: EmitArgList(used); break; case BoundKind.ArgListOperator: Debug.Assert(used); EmitArgListOperator((BoundArgListOperator)expression); break; case BoundKind.RefTypeOperator: EmitRefTypeOperator((BoundRefTypeOperator)expression, used); break; case BoundKind.MakeRefOperator: EmitMakeRefOperator((BoundMakeRefOperator)expression, used); break; case BoundKind.RefValueOperator: EmitRefValueOperator((BoundRefValueOperator)expression, used); break; case BoundKind.LoweredConditionalAccess: EmitLoweredConditionalAccessExpression((BoundLoweredConditionalAccess)expression, used); break; case BoundKind.ConditionalReceiver: EmitConditionalReceiver((BoundConditionalReceiver)expression, used); break; case BoundKind.ComplexConditionalReceiver: EmitComplexConditionalReceiver((BoundComplexConditionalReceiver)expression, used); break; case BoundKind.PseudoVariable: EmitPseudoVariableValue((BoundPseudoVariable)expression, used); break; case BoundKind.Void: Debug.Assert(!used); break; default: // Code gen should not be invoked if there are errors. Debug.Assert(expression.Kind != BoundKind.BadExpression); // node should have been lowered: throw ExceptionUtilities.UnexpectedValue(expression.Kind); } }
private bool SafeToGetWriteableReference(BoundExpression left) { if (!HasHome(left)) { return false; } // because of array covariance, taking a reference to an element of // generic array may fail even though assignment "arr[i] = default(T)" would always succeed. if (left.Kind == BoundKind.ArrayAccess && left.Type.TypeKind == TypeKind.TypeParameter && !left.Type.IsValueType) { return false; } if (left.Kind == BoundKind.FieldAccess) { var fieldAccess = (BoundFieldAccess)left; if (fieldAccess.FieldSymbol.IsVolatile || DiagnosticsPass.IsNonAgileFieldAccess(fieldAccess, _module.Compilation)) { return false; } } return true; }
// In special case of loading the sequence of field accesses we can perform all the // necessary field loads using the following IL: // // <expr>.a.b...y.z // | // V // Unbox -or- Load.Ref (<expr>) // Ldflda a // Ldflda b // ... // Ldflda y // Ldfld z // // Returns 'true' if the receiver was actually emitted this way private bool EmitFieldLoadReceiverAddress(BoundExpression receiver) { if (receiver == null || !receiver.Type.IsValueType) { return false; } else if (receiver.Kind == BoundKind.Conversion) { var conversion = (BoundConversion)receiver; if (conversion.ConversionKind == ConversionKind.Unboxing) { EmitExpression(conversion.Operand, true); _builder.EmitOpCode(ILOpCode.Unbox); EmitSymbolToken(receiver.Type, receiver.Syntax); return true; } } else if (receiver.Kind == BoundKind.FieldAccess) { var fieldAccess = (BoundFieldAccess)receiver; var field = fieldAccess.FieldSymbol; if (!field.IsStatic && EmitFieldLoadReceiverAddress(fieldAccess.ReceiverOpt)) { Debug.Assert(!field.IsVolatile, "volatile valuetype fields are unexpected"); _builder.EmitOpCode(ILOpCode.Ldflda); EmitSymbolToken(field, fieldAccess.Syntax); return true; } } return false; }
private void InPlaceCtorCall(BoundExpression target, BoundObjectCreationExpression objCreation, bool used) { var temp = EmitAddress(target, AddressKind.Writeable); Debug.Assert(temp == null, "in-place ctor target should not create temps"); var constructor = objCreation.Constructor; EmitArguments(objCreation.Arguments, constructor.Parameters); // -2 to adjust for consumed target address and not produced value. var stackAdjustment = GetObjCreationStackBehavior(objCreation) - 2; _builder.EmitOpCode(ILOpCode.Call, stackAdjustment); // for variadic ctors emit expanded ctor token EmitSymbolToken(constructor, objCreation.Syntax, constructor.IsVararg ? (BoundArgListOperator)objCreation.Arguments[objCreation.Arguments.Length - 1] : null); if (used) { Debug.Assert(TargetIsNotOnHeap(target), "cannot read-back the target since it could have been modified"); EmitExpression(target, used: true); } }
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) { AnonymousTypeManager manager = ((AnonymousTypeTemplateSymbol)this.ContainingType).Manager; SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics); // Method body: // // { // return String.Format( // "{ <name1> = {0}", <name2> = {1}", ... <nameN> = {N-1}", // this.backingFld_1, // this.backingFld_2, // ... // this.backingFld_N // } // Type expression AnonymousTypeTemplateSymbol anonymousType = (AnonymousTypeTemplateSymbol)this.ContainingType; // build arguments int fieldCount = anonymousType.Properties.Length; BoundExpression retExpression = null; if (fieldCount > 0) { // we do have fields, so have to use String.Format(...) BoundExpression[] arguments = new BoundExpression[fieldCount]; // process properties PooledStringBuilder formatString = PooledStringBuilder.GetInstance(); for (int i = 0; i < fieldCount; i++) { AnonymousTypePropertySymbol property = anonymousType.Properties[i]; // build format string formatString.Builder.AppendFormat(i == 0 ? "{{{{ {0} = {{{1}}}" : ", {0} = {{{1}}}", property.Name, i); // build argument arguments[i] = F.Convert(manager.System_Object, new BoundLoweredConditionalAccess(F.Syntax, F.Field(F.This(), property.BackingField), null, F.Call(new BoundConditionalReceiver( F.Syntax, id: i, type: property.BackingField.Type), manager.System_Object__ToString), null, id: i, type: manager.System_String), ConversionKind.ImplicitReference); } formatString.Builder.Append(" }}"); // add format string argument BoundExpression format = F.Literal(formatString.ToStringAndFree()); // Generate expression for return statement // retExpression <= System.String.Format(args) var formatMethod = manager.System_String__Format_IFormatProvider; retExpression = F.StaticCall(manager.System_String, formatMethod, F.Null(formatMethod.Parameters[0].Type), format, F.Array(manager.System_Object, arguments)); } else { // this is an empty anonymous type, just return "{ }" retExpression = F.Literal("{ }"); } F.CloseMethod(F.Block(F.Return(retExpression))); }
// returns True when assignment target is definitely not on the heap private static bool TargetIsNotOnHeap(BoundExpression left) { switch (left.Kind) { case BoundKind.Parameter: return ((BoundParameter)left).ParameterSymbol.RefKind == RefKind.None; case BoundKind.Local: // NOTE: stack locals are either homeless or refs, no need to special case them // they will never be assigned in-place. return ((BoundLocal)left).LocalSymbol.RefKind == RefKind.None; } return false; }
internal static BoundExpression ConvertToLocalType(CSharpCompilation compilation, BoundExpression expr, TypeSymbol type, DiagnosticBag diagnostics) { if (type.IsPointerType()) { var syntax = expr.Syntax; var intPtrType = compilation.GetSpecialType(SpecialType.System_IntPtr); Binder.ReportUseSiteDiagnostics(intPtrType, diagnostics, syntax); MethodSymbol conversionMethod; if (Binder.TryGetSpecialTypeMember(compilation, SpecialMember.System_IntPtr__op_Explicit_ToPointer, syntax, diagnostics, out conversionMethod)) { var temp = ConvertToLocalTypeHelper(compilation, expr, intPtrType, diagnostics); expr = BoundCall.Synthesized( syntax, receiverOpt: null, method: conversionMethod, arg0: temp); } else { return new BoundBadExpression( syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(expr), type); } } return ConvertToLocalTypeHelper(compilation, expr, type, diagnostics); }
// Implicit casts are not emitted. As a result verifier may operate on a different // types from the types of operands when performing stack merges in coalesce/ternary. // Such differences are in general irrelevant since merging rules work the same way // for base and derived types. // // Situation becomes more complicated with delegates, arrays and interfaces since they // allow implicit casts from types that do not derive from them. In such cases // we may need to introduce static casts in the code to prod the verifier to the // right direction // // This helper returns actual type of array|interface|delegate expression ignoring implicit // casts. This would be the effective stack merge type in the verifier. // // NOTE: In cases where stack merge type cannot be determined, we just return null. // We still must assume that it can be an array, delegate or interface though. private TypeSymbol StackMergeType(BoundExpression expr) { // these cases are not interesting. Merge type is the same or derived. No difference. if (!(expr.Type.IsArray() || expr.Type.IsInterfaceType() || expr.Type.IsDelegateType())) { return expr.Type; } // Dig through casts. We only need to check for expressions that - // 1) implicit casts // 2) transparently return operands, so we need to dig deeper // 3) stack values switch (expr.Kind) { case BoundKind.Conversion: var conversion = (BoundConversion)expr; var conversionKind = conversion.ConversionKind; if (conversionKind.IsImplicitConversion() && conversionKind != ConversionKind.MethodGroup && conversionKind != ConversionKind.NullLiteral) { return StackMergeType(conversion.Operand); } break; case BoundKind.AssignmentOperator: var assignment = (BoundAssignmentOperator)expr; return StackMergeType(assignment.Right); case BoundKind.Sequence: var sequence = (BoundSequence)expr; return StackMergeType(sequence.Value); case BoundKind.Local: var local = (BoundLocal)expr; if (this.IsStackLocal(local.LocalSymbol)) { // stack value, we cannot be sure what it is return null; } break; case BoundKind.Dup: // stack value, we cannot be sure what it is return null; } return expr.Type; }
private static OverloadResolutionResult <UnaryOperatorSignature> LookupUnaryOperator(UnaryOperatorKind operatorKind, BoundExpression operand) { return(UnaryOperator.Resolve(operatorKind, operand.Type)); }
internal static ConstantValue GetAndValidateConstantValue( BoundExpression boundValue, Symbol thisSymbol, TypeSymbol typeSymbol, Location initValueNodeLocation, DiagnosticBag diagnostics) { var value = ConstantValue.Bad; if (!boundValue.HasAnyErrors) { if (typeSymbol.TypeKind == TypeKind.TypeParameter) { diagnostics.Add(ErrorCode.ERR_InvalidConstantDeclarationType, initValueNodeLocation, thisSymbol, typeSymbol); } else { bool hasDynamicConversion = false; var unconvertedBoundValue = boundValue; while (unconvertedBoundValue.Kind == BoundKind.Conversion) { var conversion = (BoundConversion)unconvertedBoundValue; hasDynamicConversion = hasDynamicConversion || conversion.ConversionKind.IsDynamic(); unconvertedBoundValue = conversion.Operand; } // If we have already computed the unconverted constant value, then this call is cheap // because BoundConversions store their constant values (i.e. not recomputing anything). var constantValue = boundValue.ConstantValue; var unconvertedConstantValue = unconvertedBoundValue.ConstantValue; if (unconvertedConstantValue != null && !unconvertedConstantValue.IsNull && typeSymbol.IsReferenceType && typeSymbol.SpecialType != SpecialType.System_String) { // Suppose we are in this case: // // const object x = "some_string" // // A constant of type object can only be initialized to // null; it may not contain an implicit reference conversion // from string. // // Give a special error for that case. diagnostics.Add(ErrorCode.ERR_NotNullConstRefField, initValueNodeLocation, thisSymbol, typeSymbol); // If we get here, then the constantValue will likely be null. // However, it seems reasonable to assume that the programmer will correct the error not // by changing the value to "null", but by updating the type of the constant. Consequently, // we retain the unconverted constant value so that it can propagate through the rest of // constant folding. constantValue = constantValue ?? unconvertedConstantValue; } if (constantValue != null && !hasDynamicConversion) { value = constantValue; } else { diagnostics.Add(ErrorCode.ERR_NotConstantExpression, initValueNodeLocation, thisSymbol); } } } return(value); }
internal override ImmutableArray <Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue) { return(default(ImmutableArray <Diagnostic>)); }
private LocalDefinition EmitFieldLoadReceiver(BoundExpression receiver) { // ldfld can work with structs directly or with their addresses // accessing via address is typically same or cheaper, but not for homeless values, obviously // there are also cases where we must emit receiver as a reference if (FieldLoadMustUseRef(receiver) || FieldLoadPrefersRef(receiver)) { return EmitFieldLoadReceiverAddress(receiver) ? null : EmitReceiverRef(receiver); } EmitExpression(receiver, true); return null; }
public BoundIfStatement(BoundExpression condition, BoundStatement thenStatement, BoundStatement elseStatement) { Condition = condition; ThenStatement = thenStatement; ElseStatement = elseStatement; }
// ldfld can work with structs directly or with their addresses // In some cases it results in same native code emitted, but in some cases JIT pushes values for real // resulting in much worse code (on x64 in particular). // So, we will always prefer references here except when receiver is a struct non-ref local or parameter. private bool FieldLoadPrefersRef(BoundExpression receiver) { // only fields of structs can be accessed via value if (!receiver.Type.IsVerifierValue()) { return true; } // can unbox directly into a ref. if (receiver.Kind == BoundKind.Conversion && ((BoundConversion)receiver).ConversionKind == ConversionKind.Unboxing) { return true; } // can we take address at all? if (!HasHome(receiver)) { return false; } switch (receiver.Kind) { case BoundKind.Parameter: // prefer ldarg over ldarga return ((BoundParameter)receiver).ParameterSymbol.RefKind != RefKind.None; case BoundKind.Local: // prefer ldloc over ldloca return ((BoundLocal)receiver).LocalSymbol.RefKind != RefKind.None; case BoundKind.Sequence: return FieldLoadPrefersRef(((BoundSequence)receiver).Value); case BoundKind.FieldAccess: var fieldAccess = (BoundFieldAccess)receiver; if (fieldAccess.FieldSymbol.IsStatic) { return true; } if (DiagnosticsPass.IsNonAgileFieldAccess(fieldAccess, _module.Compilation)) { return false; } return FieldLoadPrefersRef(fieldAccess.ReceiverOpt); } return true; }
public BoundVariableDeclaration(VariableSymbol variable, BoundExpression initializer) { Variable = variable; Initializer = initializer; }
internal static bool ReportDefaultParameterErrors( Binder binder, Symbol owner, ParameterSyntax parameterSyntax, SourceParameterSymbol parameter, BoundExpression defaultExpression, DiagnosticBag diagnostics) { bool hasErrors = false; // SPEC VIOLATION: The spec says that the conversion from the initializer to the // parameter type is required to be either an identity or a nullable conversion, but // that is not right: // // void M(short myShort = 10) {} // * not an identity or nullable conversion but should be legal // // void M(object obj = (dynamic)null) {} // * an identity conversion, but should be illegal // // void M(MyStruct? myStruct = default(MyStruct)) {} // * a nullable conversion, but must be illegal because we cannot generate metadata for it // // Even if the expression is thoroughly illegal, we still want to bind it and // stick it in the parameter because we want to be able to analyze it for // IntelliSense purposes. TypeSymbol parameterType = parameter.Type; HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = binder.Conversions.ClassifyImplicitConversionFromExpression(defaultExpression, parameterType, ref useSiteDiagnostics); diagnostics.Add(defaultExpression.Syntax, useSiteDiagnostics); // SPEC VIOLATION: // By the spec an optional parameter initializer is required to be either: // * a constant, // * new S() where S is a value type // * default(S) where S is a value type. // // The native compiler considers default(T) to be a valid // initializer regardless of whether T is a value type // reference type, type parameter type, and so on. // We should consider simply allowing this in the spec. // // Also when valuetype S has a parameterless constructor, // new S() is clearly not a constant expression and should produce an error bool isValidDefaultValue = (defaultExpression.ConstantValue != null) || (defaultExpression.Kind == BoundKind.DefaultOperator) || (defaultExpression.Kind == BoundKind.ObjectCreationExpression && ((BoundObjectCreationExpression)defaultExpression).Constructor.IsDefaultValueTypeConstructor()); SyntaxToken outKeyword; SyntaxToken refKeyword; SyntaxToken paramsKeyword; SyntaxToken thisKeyword; GetModifiers(parameterSyntax.Modifiers, out outKeyword, out refKeyword, out paramsKeyword, out thisKeyword); // CONSIDER: We are inconsistent here regarding where the error is reported; is it // CONSIDER: reported on the parameter name, or on the value of the initializer? // CONSIDER: Consider making this consistent. if (outKeyword.Kind() == SyntaxKind.OutKeyword) { // error CS1741: A ref or out parameter cannot have a default value diagnostics.Add(ErrorCode.ERR_RefOutDefaultValue, outKeyword.GetLocation()); hasErrors = true; } else if (refKeyword.Kind() == SyntaxKind.RefKeyword) { // error CS1741: A ref or out parameter cannot have a default value diagnostics.Add(ErrorCode.ERR_RefOutDefaultValue, refKeyword.GetLocation()); hasErrors = true; } else if (paramsKeyword.Kind() == SyntaxKind.ParamsKeyword) { // error CS1751: Cannot specify a default value for a parameter array diagnostics.Add(ErrorCode.ERR_DefaultValueForParamsParameter, paramsKeyword.GetLocation()); hasErrors = true; } else if (thisKeyword.Kind() == SyntaxKind.ThisKeyword) { // Only need to report CS1743 for the first parameter. The caller will // have reported CS1100 if 'this' appeared on another parameter. if (parameter.Ordinal == 0) { // error CS1743: Cannot specify a default value for the 'this' parameter diagnostics.Add(ErrorCode.ERR_DefaultValueForExtensionParameter, thisKeyword.GetLocation()); hasErrors = true; } } else if (!defaultExpression.HasAnyErrors && !isValidDefaultValue) { // error CS1736: Default parameter value for '{0}' must be a compile-time constant diagnostics.Add(ErrorCode.ERR_DefaultValueMustBeConstant, parameterSyntax.Default.Value.Location, parameterSyntax.Identifier.ValueText); hasErrors = true; } else if (!conversion.Exists || conversion.IsUserDefined || conversion.IsIdentity && parameterType.SpecialType == SpecialType.System_Object && defaultExpression.Type.IsDynamic()) { // If we had no implicit conversion, or a user-defined conversion, report an error. // // Even though "object x = (dynamic)null" is a legal identity conversion, we do not allow it. // CONSIDER: We could. Doesn't hurt anything. // error CS1750: A value of type '{0}' cannot be used as a default parameter because there are no standard conversions to type '{1}' diagnostics.Add(ErrorCode.ERR_NoConversionForDefaultParam, parameterSyntax.Identifier.GetLocation(), defaultExpression.Type ?? defaultExpression.Display, parameterType); hasErrors = true; } else if (conversion.IsReference && (parameterType.SpecialType == SpecialType.System_Object || parameterType.Kind == SymbolKind.DynamicType) && (object)defaultExpression.Type != null && defaultExpression.Type.SpecialType == SpecialType.System_String || conversion.IsBoxing) { // We don't allow object x = "hello", object x = 123, dynamic x = "hello", etc. // error CS1763: '{0}' is of type '{1}'. A default parameter value of a reference type other than string can only be initialized with null diagnostics.Add(ErrorCode.ERR_NotNullRefDefaultParameter, parameterSyntax.Identifier.GetLocation(), parameterSyntax.Identifier.ValueText, parameterType); hasErrors = true; } else if (conversion.IsNullable && defaultExpression.Kind == BoundKind.DefaultOperator && !defaultExpression.Type.IsNullableType() && !(parameterType.GetNullableUnderlyingType().IsEnumType() || parameterType.GetNullableUnderlyingType().IsIntrinsicType())) { // We can do: // M(int? x = default(int)) // M(int? x = default(int?)) // M(MyEnum? e = default(enum)) // M(MyEnum? e = default(enum?)) // M(MyStruct? s = default(MyStruct?)) // // but we cannot do: // // M(MyStruct? s = default(MyStruct)) // error CS1770: // A value of type '{0}' cannot be used as default parameter for nullable parameter '{1}' because '{0}' is not a simple type diagnostics.Add(ErrorCode.ERR_NoConversionForNubDefaultParam, parameterSyntax.Identifier.GetLocation(), defaultExpression.Type, parameterSyntax.Identifier.ValueText); hasErrors = true; } // Certain contexts allow default parameter values syntactically but they are ignored during // semantic analysis. They are: // 1. Explicitly implemented interface methods; since the method will always be called // via the interface, the defaults declared on the implementation will not // be seen at the call site. // // UNDONE: 2. The "actual" side of a partial method; the default values are taken from the // UNDONE: "declaring" side of the method. // // UNDONE: 3. An indexer with only one formal parameter; it is illegal to omit every argument // UNDONE: to an indexer. // // 4. A user-defined operator; it is syntactically impossible to omit the argument. if (owner.IsExplicitInterfaceImplementation() || owner.IsPartialImplementation() || owner.IsOperator()) { // CS1066: The default value specified for parameter '{0}' will have no effect because it applies to a // member that is used in contexts that do not allow optional arguments diagnostics.Add(ErrorCode.WRN_DefaultValueForUnconsumedLocation, parameterSyntax.Identifier.GetLocation(), parameterSyntax.Identifier.ValueText); } return hasErrors; }
/// <summary> /// In case expression is of type <c>Int32</c> or <c>bool</c> or <c>PhpNumber</c>, /// converts it to <c>double</c> and leaves the result on evaluation stack. Otherwise /// just emits expression and leaves it on evaluation stack. /// </summary> internal TypeSymbol EmitExprConvertNumberToDouble(BoundExpression expr) { // emit number literal directly as double var constant = expr.ConstantValue; if (constant.HasValue) { if (constant.Value is long) { _il.EmitDoubleConstant((long)constant.Value); return(this.CoreTypes.Double); } if (constant.Value is int) { _il.EmitDoubleConstant((int)constant.Value); return(this.CoreTypes.Double); } if (constant.Value is bool) { _il.EmitDoubleConstant((bool)constant.Value ? 1.0 : 0.0); return(this.CoreTypes.Double); } } // emit fast ToDouble() in case of a PhpNumber variable var place = PlaceOrNull(expr); var type = TryEmitVariableSpecialize(place, expr.TypeRefMask); if (type == null) { if (place != null && place.HasAddress) { if (place.Type == CoreTypes.PhpNumber) { place.EmitLoadAddress(_il); return(EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToDouble) .Expect(SpecialType.System_Double)); } } type = EmitSpecialize(expr); } Debug.Assert(type != null); if (type.SpecialType == SpecialType.System_Int32 || type.SpecialType == SpecialType.System_Int64 || type.SpecialType == SpecialType.System_Boolean) { _il.EmitOpCode(ILOpCode.Conv_r8); // int|bool -> long type = this.CoreTypes.Double; } else if (type == CoreTypes.PhpNumber) { EmitPhpNumberAddr(); EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToDouble); // number -> double type = this.CoreTypes.Double; } // return(type); }
private static BoundExpression ConvertToLocalTypeHelper(CSharpCompilation compilation, BoundExpression expr, TypeSymbol type, DiagnosticBag diagnostics) { // NOTE: This conversion can fail if some of the types involved are from not-yet-loaded modules. // For example, if System.Exception hasn't been loaded, then this call will fail for $stowedexception. HashSet<DiagnosticInfo> useSiteDiagnostics = null; var conversion = compilation.Conversions.ClassifyConversionFromExpression(expr, type, ref useSiteDiagnostics); diagnostics.Add(expr.Syntax, useSiteDiagnostics); Debug.Assert(conversion.IsValid || diagnostics.HasAnyErrors()); return BoundConversion.Synthesized( expr.Syntax, expr, conversion, @checked: false, explicitCastInCode: false, constantValueOpt: null, type: type, hasErrors: !conversion.IsValid); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { BoundExpression expression = (BoundExpression)this.Visit(node.Expression); TypeSymbol type = this.VisitType(node.Type); return node.Update(expression, VisitMethodSymbol(node.GetAwaiter), VisitPropertySymbol(node.IsCompleted), VisitMethodSymbol(node.GetResult), type); }