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); }
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); }
/// <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 StarkPlatform.Compiler.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.Stark.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 void MakeSubmissionInitialization( ArrayBuilder <BoundStatement> statements, SyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 1); 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, new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }, objectType) { WasCompilerGenerated = true }, thisReference, false, 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, new BoundLiteral(syntax, ConstantValue.Create(0), intType) { WasCompilerGenerated = true }, objectType), Conversion.ExplicitReference, false, explicitCastInCode: true, conversionGroupOpt: null, ConstantValue.NotAvailable, hostObjectField.Type.TypeSymbol ), hostObjectField.Type.TypeSymbol) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptType = (ImplicitNamedTypeSymbol)field.Type.TypeSymbol; 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, new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionIndex), intType) { WasCompilerGenerated = true }, objectType) { WasCompilerGenerated = true }, Conversion.ExplicitReference, false, explicitCastInCode: true, conversionGroupOpt: null, ConstantValue.NotAvailable, targetScriptType ), targetScriptType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } }
/// <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.TypeSymbol; 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, refKind: RefKind.None, 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(BoundBlock.SynthesizedNoLocals(syntax, @return)); } Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type.TypeSymbol) { 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)), conversion: Conversion.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(BoundBlock.SynthesizedNoLocals(syntax, statements: ImmutableArray.Create <BoundStatement>( eventUpdate, @return))); } 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, TypeSymbolWithAnnotations.Create(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)), conversion: Conversion.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 }); }
// 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 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 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)); }