Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mcs / mcs / codegen.cs
index b346a5bbc4f43c1a6f218202b6d1bd7829086bcf..429c3b9d3b0d1f23db2fb66109e2d5ab93f6ca03 100644 (file)
@@ -121,6 +121,8 @@ namespace Mono.CSharp
                        }
                }
 
+               public ConditionalAccessContext ConditionalAccess { get; set; }
+
                public TypeSpec CurrentType {
                        get { return member_context.CurrentType; }
                }
@@ -220,6 +222,8 @@ namespace Mono.CSharp
 
                public List<TryFinally> TryFinallyUnwind { get; set; }
 
+               public Label RecursivePatternLabel { get; set; }
+
                #endregion
 
                public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
@@ -258,9 +262,7 @@ namespace Mono.CSharp
                        if (sf.IsHiddenLocation (loc))
                                return false;
 
-#if NET_4_0
                        methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
-#endif
                        return true;
                }
 
@@ -320,9 +322,7 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
-#endif
                }
 
                public void BeginCompilerScope ()
@@ -330,9 +330,7 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
-#endif
                }
 
                public void EndExceptionBlock ()
@@ -345,9 +343,16 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.EndBlock (ig.ILOffset);
-#endif
+               }
+
+               public void CloseConditionalAccess (TypeSpec type)
+               {
+                       if (type != null)
+                               Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
+
+                       MarkLabel (ConditionalAccess.EndLabel);
+                       ConditionalAccess = null;
                }
 
                //
@@ -536,13 +541,10 @@ namespace Mono.CSharp
                        switch (type.BuiltinType) {
                        case BuiltinTypeSpec.Type.Bool:
                                //
-                               // Workaround MSIL limitation. Load bool element as single bit,
-                               // bool array can actually store any byte value
+                               // bool array can actually store any byte value in underlying byte slot
+                               // and C# spec does not specify any normalization rule, except the result
+                               // is undefined
                                //
-                               ig.Emit (OpCodes.Ldelem_U1);
-                               ig.Emit (OpCodes.Ldc_I4_0);
-                               ig.Emit (OpCodes.Cgt_Un);
-                               break;
                        case BuiltinTypeSpec.Type.Byte:
                                ig.Emit (OpCodes.Ldelem_U1);
                                break;
@@ -758,12 +760,8 @@ namespace Mono.CSharp
                                ig.Emit (OpCodes.Ldind_U1);
                                break;
                        case BuiltinTypeSpec.Type.SByte:
-                               ig.Emit (OpCodes.Ldind_I1);
-                               break;
                        case BuiltinTypeSpec.Type.Bool:
                                ig.Emit (OpCodes.Ldind_I1);
-                               ig.Emit (OpCodes.Ldc_I4_0);
-                               ig.Emit (OpCodes.Cgt_Un);
                                break;
                        case BuiltinTypeSpec.Type.ULong:
                        case BuiltinTypeSpec.Type.Long:
@@ -890,6 +888,9 @@ namespace Mono.CSharp
 
                                ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
                                break;
+                       case MemberKind.PointerType:
+                               ig.Emit (OpCodes.Stind_I);
+                               break;
                        default:
                                ig.Emit (OpCodes.Stind_Ref);
                                break;
@@ -978,6 +979,19 @@ namespace Mono.CSharp
                }
        }
 
+       public class ConditionalAccessContext
+       {
+               public ConditionalAccessContext (TypeSpec type, Label endLabel)
+               {
+                       Type = type;
+                       EndLabel = endLabel;
+               }
+
+               public bool Statement { get; set; }
+               public Label EndLabel { get; private set; }
+               public TypeSpec Type { get; private set; }
+       }
+
        struct CallEmitter
        {
                public Expression InstanceExpression;
@@ -998,7 +1012,7 @@ namespace Mono.CSharp
                //
                public bool HasAwaitArguments;
 
-               public bool NullShortCircuit;
+               public bool ConditionalAccess;
 
                //
                // When dealing with await arguments the original arguments are converted
@@ -1006,8 +1020,6 @@ namespace Mono.CSharp
                //
                public Arguments EmittedArguments;
 
-               public Label NullOperatorLabel;
-
                public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
                {
                        EmitPredefined (ec, method, Arguments, false, loc);
@@ -1031,32 +1043,22 @@ namespace Mono.CSharp
 
                        OpCode call_op;
                        LocalTemporary lt = null;
-                       InstanceEmitter ie = new InstanceEmitter ();
 
                        if (method.IsStatic) {
                                call_op = OpCodes.Call;
                        } else {
-                               if (IsVirtualCallRequired (InstanceExpression, method)) {
-                                       call_op = OpCodes.Callvirt;
-                               } else {
-                                       call_op = OpCodes.Call;
-                               }
+                               call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
 
                                if (HasAwaitArguments) {
                                        instance_copy = InstanceExpression.EmitToField (ec);
-                                       ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+                                       var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
 
                                        if (Arguments == null) {
-                                               ie.EmitLoad (ec);
+                                               ie.EmitLoad (ec, true);
                                        }
                                } else if (!InstanceExpressionOnStack) {
-                                       ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
-                                       ie.NullShortCircuit = NullShortCircuit;
-                                       ie.Emit (ec);
-
-                                       if (NullShortCircuit) {
-                                               NullOperatorLabel = ie.NullOperatorLabel;
-                                       }
+                                       var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
+                                       ie.Emit (ec, ConditionalAccess);
 
                                        if (DuplicateArguments) {
                                                ec.Emit (OpCodes.Dup);
@@ -1073,8 +1075,8 @@ namespace Mono.CSharp
                                EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
                                if (EmittedArguments != null) {
                                        if (instance_copy != null) {
-                                               ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
-                                               ie.Emit (ec);
+                                               var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+                                               ie.Emit (ec, ConditionalAccess);
 
                                                if (lt != null)
                                                        lt.Release (ec);
@@ -1120,10 +1122,6 @@ namespace Mono.CSharp
                        //
                        if (statement && method.ReturnType.Kind != MemberKind.Void)
                                ec.Emit (OpCodes.Pop);
-
-                       if (NullShortCircuit && !DuplicateArguments) {
-                               ie.EmitResultLift (ec, method.ReturnType, statement);
-                       }
                }
 
                static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
@@ -1159,8 +1157,7 @@ namespace Mono.CSharp
                        // It's non-virtual and will never be null and it can be determined
                        // whether it's known value or reference type by verifier
                        //
-                       if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation) &&
-                               !instance.Type.IsGenericParameter)
+                       if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
                                return false;
 
                        return true;
@@ -1178,54 +1175,102 @@ namespace Mono.CSharp
        {
                readonly Expression instance;
                readonly bool addressRequired;
-               bool value_on_stack;
-
-               public bool NullShortCircuit;
-               public Label NullOperatorLabel;
 
                public InstanceEmitter (Expression instance, bool addressLoad)
                {
                        this.instance = instance;
                        this.addressRequired = addressLoad;
-                       NullShortCircuit = false;
-                       NullOperatorLabel = new Label ();
-                       value_on_stack = false;
                }
 
-               public void Emit (EmitContext ec)
+               public void Emit (EmitContext ec, bool conditionalAccess)
                {
+                       Label NullOperatorLabel;
                        Nullable.Unwrap unwrap;
 
-                       if (NullShortCircuit) {
+                       if (conditionalAccess && Expression.IsNeverNull (instance))
+                               conditionalAccess = false;
+
+                       if (conditionalAccess) {
                                NullOperatorLabel = ec.DefineLabel ();
                                unwrap = instance as Nullable.Unwrap;
                        } else {
+                               NullOperatorLabel = new Label ();
                                unwrap = null;
                        }
 
+                       IMemoryLocation instance_address = null;
+                       bool conditional_access_dup = false;
+
                        if (unwrap != null) {
                                unwrap.Store (ec);
                                unwrap.EmitCheck (ec);
-                               ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
-                               unwrap.Emit (ec);
-                               var tmp = ec.GetTemporaryLocal (unwrap.Type);
-                               ec.Emit (OpCodes.Stloc, tmp);
-                               ec.Emit (OpCodes.Ldloca, tmp);
-                               ec.FreeTemporaryLocal (tmp, unwrap.Type);
-                               return;
-                       }
+                               ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+                       } else {
+                               if (conditionalAccess && addressRequired) {
+                                       //
+                                       // Don't allocate temp variable when instance load is cheap and load and load-address
+                                       // operate on same memory
+                                       //
+                                       instance_address = instance as VariableReference;
+                                       if (instance_address == null)
+                                               instance_address = instance as LocalTemporary;
+
+                                       if (instance_address == null) {
+                                               EmitLoad (ec, false);
+                                               ec.Emit (OpCodes.Dup);
+                                               ec.EmitLoadFromPtr (instance.Type);
 
-                       EmitLoad (ec);
+                                               conditional_access_dup = true;
+                                       } else {
+                                               instance.Emit (ec);
+                                       }
+                               } else {
+                                       EmitLoad (ec, !conditionalAccess);
 
-                       if (NullShortCircuit) {
-                               ec.Emit (OpCodes.Dup);
-                               ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
+                                       if (conditionalAccess) {
+                                               conditional_access_dup = !IsInexpensiveLoad ();
+                                               if (conditional_access_dup)
+                                                       ec.Emit (OpCodes.Dup);
+                                       }
+                               }
+
+                               if (conditionalAccess) {
+                                       if (instance.Type.Kind == MemberKind.TypeParameter)
+                                               ec.Emit (OpCodes.Box, instance.Type);
+
+                                       ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+
+                                       if (conditional_access_dup)
+                                               ec.Emit (OpCodes.Pop);
+                               }
                        }
 
-                       value_on_stack = true;
+                       if (conditionalAccess) {
+                               if (!ec.ConditionalAccess.Statement) {
+                                       if (ec.ConditionalAccess.Type.IsNullableType)
+                                               Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
+                                       else
+                                               ec.EmitNull ();
+                               }
+
+                               ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
+                               ec.MarkLabel (NullOperatorLabel);
+
+                               if (instance_address != null) {
+                                       instance_address.AddressOf (ec, AddressOp.Load);
+                               } else if (unwrap != null) {
+                                       unwrap.Emit (ec);
+                                       var tmp = ec.GetTemporaryLocal (unwrap.Type);
+                                       ec.Emit (OpCodes.Stloc, tmp);
+                                       ec.Emit (OpCodes.Ldloca, tmp);
+                                       ec.FreeTemporaryLocal (tmp, unwrap.Type);
+                               } else if (!conditional_access_dup) {
+                                       instance.Emit (ec);
+                               }
+                       }
                }
 
-               public void EmitLoad (EmitContext ec)
+               public void EmitLoad (EmitContext ec, bool boxInstance)
                {
                        var instance_type = instance.Type;
 
@@ -1256,62 +1301,61 @@ namespace Mono.CSharp
                        instance.Emit (ec);
 
                        // Only to make verifier happy
-                       if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) {
-                               ec.Emit (OpCodes.Box, instance_type);
-                       } else if (instance_type.IsStructOrEnum) {
+                       if (boxInstance && RequiresBoxing ()) {
                                ec.Emit (OpCodes.Box, instance_type);
                        }
                }
 
-               public void EmitResultLift (EmitContext ec, TypeSpec type, bool statement)
+               public TypeSpec GetStackType (EmitContext ec)
                {
-                       if (!NullShortCircuit)
-                               throw new InternalErrorException ();
+                       var instance_type = instance.Type;
 
-                       bool value_rt = TypeSpec.IsValueType (type);
-                       TypeSpec lifted;
-                       if (value_rt) {
-                               if (type.IsNullableType)
-                                       lifted = type;
-                               else {
-                                       lifted = Nullable.NullableInfo.MakeType (ec.Module, type);
-                                       ec.Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (lifted));
-                               }
-                       } else {
-                               lifted = null;
-                       }
+                       if (addressRequired)
+                               return ReferenceContainer.MakeType (ec.Module, instance_type);
 
-                       var end = ec.DefineLabel ();
-                       if (value_on_stack || !statement) {
-                               ec.Emit (OpCodes.Br_S, end);
-                       }
+                       if (instance_type.IsStructOrEnum)
+                               return ec.Module.Compiler.BuiltinTypes.Object;
 
-                       ec.MarkLabel (NullOperatorLabel);
+                       return instance_type;
+               }
 
-                       if (value_on_stack)
-                               ec.Emit (OpCodes.Pop);
+               bool RequiresBoxing ()
+               {
+                       var instance_type = instance.Type;
+                       if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
+                               return true;
 
-                       if (!statement) {
-                               if (value_rt)
-                                       Nullable.LiftedNull.Create (lifted, Location.Null).Emit (ec);
-                               else
-                                       ec.EmitNull ();
-                       }
+                       if (instance_type.IsStructOrEnum)
+                               return true;
 
-                       ec.MarkLabel (end);
+                       return false;
                }
 
-               public TypeSpec GetStackType (EmitContext ec)
+               //
+               // Returns true for cheap race-free load, where we can avoid using dup
+               //
+               bool IsInexpensiveLoad ()
                {
-                       var instance_type = instance.Type;
+                       if (instance is Constant)
+                               return instance.IsSideEffectFree;
 
-                       if (addressRequired)
-                               return ReferenceContainer.MakeType (ec.Module, instance_type);
+                       if (RequiresBoxing ())
+                               return false;
 
-                       if (instance_type.IsStructOrEnum)
-                               return ec.Module.Compiler.BuiltinTypes.Object;
+                       var vr = instance as VariableReference;
+                       if (vr != null) {
+                               // Load from captured local would be racy without dup
+                               return !vr.IsRef && !vr.IsHoisted;
+                       }
 
-                       return instance_type;
+                       if (instance is LocalTemporary)
+                               return true;
+
+                       var fe = instance as FieldExpr;
+                       if (fe != null)
+                               return fe.IsStatic || fe.InstanceExpression is This;
+
+                       return false;
                }
        }
 }