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