// Return true if we actually report a failure. protected bool TryReportLvalueFailure(EXPR expr, CheckLvalueKind kind) { Debug.Assert(expr != null); // We have a lvalue failure. Was the reason because this field // was marked readonly? Give special messages for this case. bool isNested = false; // Did we recurse on a field or property to give a better error? EXPR walk = expr; while (true) { Debug.Assert(walk != null); if (walk.isANYLOCAL_OK()) { ReportLocalError(walk.asANYLOCAL().local, kind, isNested); return(true); } EXPR pObject = null; if (walk.isPROP()) { // We've already reported read-only-property errors. Debug.Assert(walk.asPROP().mwtSet != null); pObject = walk.asPROP().GetMemberGroup().GetOptionalObject(); } else if (walk.isFIELD()) { EXPRFIELD field = walk.asFIELD(); if (field.fwt.Field().isReadOnly) { ReportReadOnlyError(field, kind, isNested); return(true); } if (!field.fwt.Field().isStatic) { pObject = field.GetOptionalObject(); } } if (pObject != null && pObject.type.isStructOrEnum()) { if (pObject.isCALL() || pObject.isPROP()) { // assigning to RHS of method or property getter returning a value-type on the stack or // passing RHS of method or property getter returning a value-type on the stack, as ref or out ErrorContext.Error(ErrorCode.ERR_ReturnNotLValue, pObject.GetSymWithType()); return(true); } if (pObject.isCAST()) { // An unboxing conversion. // // In the static compiler, we give the following error here: // ErrorContext.Error(pObject.GetTree(), ErrorCode.ERR_UnboxNotLValue); // // But in the runtime, we allow this - mark that we're doing an // unbox here, so that we gen the correct expression tree for it. pObject.flags |= EXPRFLAG.EXF_UNBOXRUNTIME; return(false); } } // everything else if (pObject != null && !pObject.isLvalue() && (walk.isFIELD() || (!isNested && walk.isPROP()))) { Debug.Assert(pObject.type.isStructOrEnum()); walk = pObject; } else { ErrorContext.Error(GetStandardLvalueError(kind)); return(true); } isNested = true; } }
//////////////////////////////////////////////////////////////////////////////// // Bind the simple assignment operator =. public EXPR bindAssignment(EXPR op1, EXPR op2, bool allowExplicit) { bool fOp2NotAddrOp = false; bool fOp2WasCast = false; if (!op1.isANYLOCAL_OK()) { if (!checkLvalue(op1, CheckLvalueKind.Assignment)) { EXPR rval = GetExprFactory().CreateAssignment(op1, op2); rval.SetError(); return rval; } } else { if (op2.type.IsArrayType()) { return BindPtrToArray(op1.asANYLOCAL(), op2); } if (op2.type == GetReqPDT(PredefinedType.PT_STRING)) { op2 = bindPtrToString(op2); } else if (op2.kind == ExpressionKind.EK_ADDR) { op2.flags |= EXPRFLAG.EXF_ADDRNOCONV; } else if (op2.isOK()) { fOp2NotAddrOp = true; fOp2WasCast = (op2.isCAST()); } } op2 = GenerateAssignmentConversion(op1, op2, allowExplicit); if (op2.isOK() && fOp2NotAddrOp) { // Only report these errors if the convert succeeded if (fOp2WasCast) { ErrorContext.Error(ErrorCode.ERR_BadCastInFixed); } else { ErrorContext.Error(ErrorCode.ERR_FixedNotNeeded); } } return GenerateOptimizedAssignment(op1, op2); }