1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Diagnostics; 7 using Microsoft.CSharp.RuntimeBinder.Errors; 8 9 namespace Microsoft.CSharp.RuntimeBinder.Semantics 10 { 11 internal sealed partial class ExpressionBinder 12 { ReportReadOnlyError(ExprField field, bool isNested)13 private RuntimeBinderException ReportReadOnlyError(ExprField field, bool isNested) 14 { 15 Debug.Assert(field != null); 16 17 FieldWithType fieldWithType = field.FieldWithType; 18 bool isStatic = fieldWithType.Field().isStatic; 19 ErrArg[] args; 20 ErrorCode err; 21 if (isNested) 22 { 23 args = new ErrArg[]{ fieldWithType }; 24 err = isStatic ? ErrorCode.ERR_AssgReadonlyStatic2 : ErrorCode.ERR_AssgReadonly2; 25 } 26 else 27 { 28 args = Array.Empty<ErrArg>(); 29 err = isStatic ? ErrorCode.ERR_AssgReadonlyStatic : ErrorCode.ERR_AssgReadonly; 30 } 31 32 return ErrorContext.Error(err, args); 33 } 34 TryReportLvalueFailure(Expr expr, CheckLvalueKind kind)35 private void TryReportLvalueFailure(Expr expr, CheckLvalueKind kind) 36 { 37 Debug.Assert(expr != null); 38 Debug.Assert(!(expr is ExprLocal)); 39 40 // We have a lvalue failure. Was the reason because this field 41 // was marked readonly? Give special messages for this case. 42 43 bool isNested = false; // Did we recurse on a field or property to give a better error? 44 45 while (true) 46 { 47 Debug.Assert(expr != null); 48 49 Expr pObject = null; 50 51 if (expr is ExprProperty prop) 52 { 53 // We've already reported read-only-property errors. 54 Debug.Assert(prop.MethWithTypeSet != null); 55 pObject = prop.MemberGroup.OptionalObject; 56 } 57 else if (expr is ExprField field) 58 { 59 if (field.FieldWithType.Field().isReadOnly) 60 { 61 throw ReportReadOnlyError(field, isNested); 62 } 63 if (!field.FieldWithType.Field().isStatic) 64 { 65 pObject = field.OptionalObject; 66 } 67 } 68 69 if (pObject != null && pObject.Type.isStructOrEnum()) 70 { 71 if (pObject is ExprWithArgs withArgs) 72 { 73 // assigning to RHS of method or property getter returning a value-type on the stack or 74 // passing RHS of method or property getter returning a value-type on the stack, as ref or out 75 throw ErrorContext.Error(ErrorCode.ERR_ReturnNotLValue, withArgs.GetSymWithType()); 76 } 77 if (pObject is ExprCast) 78 { 79 // An unboxing conversion. 80 // 81 // In the static compiler, we give the following error here: 82 // ErrorContext.Error(pObject.GetTree(), ErrorCode.ERR_UnboxNotLValue); 83 // 84 // But in the runtime, we allow this - mark that we're doing an 85 // unbox here, so that we gen the correct expression tree for it. 86 pObject.Flags |= EXPRFLAG.EXF_UNBOXRUNTIME; 87 return; 88 } 89 } 90 91 // everything else 92 if (pObject != null && !pObject.isLvalue() && (expr is ExprField || !isNested)) 93 { 94 Debug.Assert(pObject.Type.isStructOrEnum()); 95 Debug.Assert(!(pObject is ExprLocal)); 96 expr = pObject; 97 } 98 else 99 { 100 throw ErrorContext.Error(GetStandardLvalueError(kind)); 101 } 102 103 isNested = true; 104 } 105 } 106 } 107 } 108