protected override Expression DoResolve(ResolveContext rc) { base.DoResolve(rc); if (type != null) { type = NullableInfo.GetUnderlyingType(type); } return(this); }
public static Expression Create(Expression expr, TypeSpec type) { // // Avoid unwraping and wraping of the same type // Unwrap unwrap = expr as Unwrap; if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType(type)) { return(unwrap.Original); } return(new Wrap(expr, type)); }
public override void Emit(EmitContext ec) { Label end_label = ec.DefineLabel(); if (unwrap != null) { Label is_null_label = ec.DefineLabel(); unwrap.EmitCheck(ec); ec.Emit(OpCodes.Brfalse, is_null_label); // // When both expressions are nullable the unwrap // is needed only for null check not for value uwrap // if (type.IsNullableType && TypeSpecComparer.IsEqual(NullableInfo.GetUnderlyingType(type), unwrap.Type)) { unwrap.Load(ec); } else { left.Emit(ec); } ec.Emit(OpCodes.Br, end_label); ec.MarkLabel(is_null_label); right.Emit(ec); ec.MarkLabel(end_label); return; } left.Emit(ec); ec.Emit(OpCodes.Dup); // Only to make verifier happy if (left.Type.IsGenericParameter) { ec.Emit(OpCodes.Box, left.Type); } ec.Emit(OpCodes.Brtrue, end_label); ec.Emit(OpCodes.Pop); right.Emit(ec); ec.MarkLabel(end_label); }
protected override void EmitOperator(EmitContext ec, TypeSpec l) { if (user_operator != null) { user_operator.Emit(ec); return; } if (left.Type.IsNullableType) { l = NullableInfo.GetUnderlyingType(left.Type); left = EmptyCast.Create(left, l); } if (right.Type.IsNullableType) { right = EmptyCast.Create(right, NullableInfo.GetUnderlyingType(right.Type)); } base.EmitOperator(ec, l); }
public override void Emit(EmitContext ec) { Label is_null_label = ec.DefineLabel (); Label end_label = ec.DefineLabel (); unwrap.EmitCheck (ec); ec.Emit (OpCodes.Brfalse, is_null_label); if (user_operator != null) { user_operator.Emit (ec); } else { EmitOperator (ec, NullableInfo.GetUnderlyingType (type)); } ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); ec.Emit (OpCodes.Br_S, end_label); ec.MarkLabel (is_null_label); LiftedNull.Create (type, loc).Emit (ec); ec.MarkLabel (end_label); }
public override void Emit(EmitContext ec) { Label end_label = ec.DefineLabel(); if (unwrap != null) { Label is_null_label = ec.DefineLabel(); unwrap.EmitCheck(ec); ec.Emit(OpCodes.Brfalse, is_null_label); // // When both expressions are nullable the unwrap // is needed only for null check not for value uwrap // if (type.IsNullableType && TypeSpecComparer.IsEqual(NullableInfo.GetUnderlyingType(type), unwrap.Type)) { unwrap.Load(ec); } else { left.Emit(ec); } ec.Emit(OpCodes.Br, end_label); ec.MarkLabel(is_null_label); right.Emit(ec); ec.MarkLabel(end_label); return; } // // Null check is done on original expression not after expression is converted to // result type. This is in most cases same but when user conversion is involved // we can end up in situation when user operator does the null handling which is // not what the operator is supposed to do. // There is tricky case where cast of left expression is meant to be cast of // whole source expression (null check is done on it) and cast from right-to-left // conversion needs to do null check on unconverted source expression. // if (user_conversion_left) { var op_expr = (UserCast)left; op_expr.Source.Emit(ec); LocalTemporary temp; // TODO: More load kinds can be special cased if (!(op_expr.Source is VariableReference)) { temp = new LocalTemporary(op_expr.Source.Type); temp.Store(ec); temp.Emit(ec); op_expr.Source = temp; } else { temp = null; } var right_label = ec.DefineLabel(); ec.Emit(OpCodes.Brfalse_S, right_label); left.Emit(ec); ec.Emit(OpCodes.Br, end_label); ec.MarkLabel(right_label); if (temp != null) { temp.Release(ec); } } else { // // Common case where expression is not modified before null check and // we generate better/smaller code // left.Emit(ec); ec.Emit(OpCodes.Dup); // Only to make verifier happy if (left.Type.IsGenericParameter) { ec.Emit(OpCodes.Box, left.Type); } ec.Emit(OpCodes.Brtrue, end_label); ec.Emit(OpCodes.Pop); } right.Emit(ec); ec.MarkLabel(end_label); }
Expression LiftResult(ResolveContext ec, Expression res_expr) { TypeExpr lifted_type; // // Avoid double conversion // if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) { lifted_type = new NullableType(left.Type, loc); lifted_type = lifted_type.ResolveAsTypeTerminal(ec, false); if (lifted_type == null) { return(null); } if (left is UserCast || left is TypeCast) { left.Type = lifted_type.Type; } else { left = EmptyCast.Create(left, lifted_type.Type); } } if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) { lifted_type = new NullableType(right.Type, loc); lifted_type = lifted_type.ResolveAsTypeTerminal(ec, false); if (lifted_type == null) { return(null); } var r = right; if (r is ReducedExpression) { r = ((ReducedExpression)r).OriginalExpression; } if (r is UserCast || r is TypeCast) { r.Type = lifted_type.Type; } else { right = EmptyCast.Create(right, lifted_type.Type); } } if ((Oper & Operator.ComparisonMask) == 0) { lifted_type = new NullableType(res_expr.Type, loc); lifted_type = lifted_type.ResolveAsTypeTerminal(ec, false); if (lifted_type == null) { return(null); } wrap_ctor = NullableInfo.GetConstructor(lifted_type.Type); type = res_expr.Type = lifted_type.Type; } if (IsLeftNullLifted) { left = LiftedNull.Create(right.Type, left.Location); // // Special case for bool?, the result depends on both null right side and left side value // if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType(type).BuiltinType == BuiltinTypeSpec.Type.Bool) { return(res_expr); } if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) { return(LiftedNull.CreateFromExpression(ec, res_expr)); } // // Value types and null comparison // if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0) { return(CreateNullConstant(ec, right_orig)); } } if (IsRightNullLifted) { right = LiftedNull.Create(left.Type, right.Location); // // Special case for bool?, the result depends on both null right side and left side value // if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType(type).BuiltinType == BuiltinTypeSpec.Type.Bool) { return(res_expr); } if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) { return(LiftedNull.CreateFromExpression(ec, res_expr)); } // // Value types and null comparison // if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0) { return(CreateNullConstant(ec, left_orig)); } } return(res_expr); }