Merge pull request #4380 from alexanderkyte/conflicting_attrs
[mono.git] / mcs / mcs / codegen.cs
index a499e31ba5fe3c43c52c134813e70527d534461f..c580a8a7291562eeeb31b519ece86ec395d146b7 100644 (file)
@@ -222,6 +222,8 @@ namespace Mono.CSharp
 
                public List<TryFinally> TryFinallyUnwind { get; set; }
 
+               public Label RecursivePatternLabel { get; set; }
+
                #endregion
 
                public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
@@ -260,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;
                }
 
@@ -317,24 +317,20 @@ namespace Mono.CSharp
                        ig.BeginFinallyBlock ();
                }
 
-               public void BeginScope ()
+               public void BeginScope (int scopeIndex)
                {
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
-                       methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
-#endif
+                       methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset, scopeIndex);
                }
 
-               public void BeginCompilerScope ()
+               public void BeginCompilerScope (int scopeIndex)
                {
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
-                       methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
-#endif
+                       methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset, scopeIndex);
                }
 
                public void EndExceptionBlock ()
@@ -347,9 +343,7 @@ namespace Mono.CSharp
                        if ((flags & Options.OmitDebugInfo) != 0)
                                return;
 
-#if NET_4_0
                        methodSymbols.EndBlock (ig.ILOffset);
-#endif
                }
 
                public void CloseConditionalAccess (TypeSpec type)
@@ -396,6 +390,22 @@ namespace Mono.CSharp
                        if (IsAnonymousStoreyMutateRequired)
                                type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
 
+                       if (pinned) {
+                               //
+                               // This is for .net compatibility. I am not sure why pinned
+                               // pointer temps are converted to & even if they are pointers to
+                               // pointers.
+                               //
+                               var pt = type as PointerContainer;
+                               if (pt != null) {
+                                       type = pt.Element;
+                                       if (type.Kind == MemberKind.Void)
+                                               type = Module.Compiler.BuiltinTypes.IntPtr;
+                                       
+                                       return ig.DeclareLocal (type.GetMetaInfo ().MakeByRefType (), true);
+                               }
+                       }
+
                        return ig.DeclareLocal (type.GetMetaInfo (), pinned);
                }
 
@@ -547,13 +557,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;
@@ -769,12 +776,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:
@@ -901,6 +904,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;
@@ -1064,7 +1070,10 @@ namespace Mono.CSharp
                                        var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
 
                                        if (Arguments == null) {
-                                               ie.EmitLoad (ec);
+                                               if (ConditionalAccess)
+                                                       ie.Emit (ec, true);
+                                               else
+                                                       ie.EmitLoad (ec, true);
                                        }
                                } else if (!InstanceExpressionOnStack) {
                                        var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
@@ -1208,42 +1217,86 @@ namespace Mono.CSharp
                                unwrap = null;
                        }
 
+                       IMemoryLocation instance_address = null;
+                       bool conditional_access_dup = false;
+
                        if (unwrap != null) {
                                unwrap.Store (ec);
                                unwrap.EmitCheck (ec);
                                ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
                        } else {
-                               EmitLoad (ec);
+                               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);
+
+                                               conditional_access_dup = true;
+                                       } else {
+                                               instance.Emit (ec);
+                                       }
+                               } else {
+                                       EmitLoad (ec, !conditionalAccess);
+
+                                       if (conditionalAccess) {
+                                               conditional_access_dup = !ExpressionAnalyzer.IsInexpensiveLoad (instance);
+                                               if (conditional_access_dup)
+                                                       ec.Emit (OpCodes.Dup);
+                                       }
+                               }
 
                                if (conditionalAccess) {
-                                       ec.Emit (OpCodes.Dup);
+                                       if (instance.Type.Kind == MemberKind.TypeParameter)
+                                               ec.Emit (OpCodes.Box, instance.Type);
+
                                        ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
-                                       ec.Emit (OpCodes.Pop);
+
+                                       if (conditional_access_dup)
+                                               ec.Emit (OpCodes.Pop);
                                }
                        }
 
                        if (conditionalAccess) {
                                if (!ec.ConditionalAccess.Statement) {
-                                       if (ec.ConditionalAccess.Type.IsNullableType)
-                                               Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
-                                       else
+                                       var t = ec.ConditionalAccess.Type;
+                                       if (t.IsNullableType)
+                                               Nullable.LiftedNull.Create (t, Location.Null).Emit (ec);
+                                       else {
                                                ec.EmitNull ();
+
+                                               if (t.IsGenericParameter)
+                                                       ec.Emit (OpCodes.Unbox_Any, t);
+                                       }
                                }
 
                                ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
                                ec.MarkLabel (NullOperatorLabel);
 
-                               if (unwrap != null) {
+                               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);
+                                       if (addressRequired) {
+                                               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;
 
@@ -1274,9 +1327,7 @@ 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 && ExpressionAnalyzer.RequiresBoxing (instance)) {
                                ec.Emit (OpCodes.Box, instance_type);
                        }
                }
@@ -1293,5 +1344,49 @@ namespace Mono.CSharp
 
                        return instance_type;
                }
+
+
+       }
+
+       static class ExpressionAnalyzer
+       {
+               public static bool RequiresBoxing (Expression instance)
+               {
+                       var instance_type = instance.Type;
+                       if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
+                               return true;
+
+                       if (instance_type.IsStructOrEnum)
+                               return true;
+
+                       return false;
+               }
+
+               //
+               // Returns true for cheap race-free load, where we can avoid using dup
+               //
+               public static bool IsInexpensiveLoad (Expression instance)
+               {
+                       if (instance is Constant)
+                               return instance.IsSideEffectFree;
+
+                       if (RequiresBoxing (instance))
+                               return false;
+
+                       var vr = instance as VariableReference;
+                       if (vr != null) {
+                               // Load from captured local would be racy without dup
+                               return !vr.IsRef && !vr.IsHoisted;
+                       }
+
+                       if (instance is LocalTemporary)
+                               return true;
+
+                       var fe = instance as FieldExpr;
+                       if (fe != null)
+                               return fe.IsStatic || fe.InstanceExpression is This;
+
+                       return false;
+               }
        }
 }