From 02fe8863c8b144914e9a01b6816c865b3416e37a Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Thu, 22 Mar 2012 11:38:04 +0000 Subject: [PATCH] Initial separation of state machine and anonymous method story to allow both of them exist in same block --- mcs/errors/cs4005-2.cs | 13 ++++ mcs/mcs/anonymous.cs | 57 +++++++++-------- mcs/mcs/async.cs | 42 +------------ mcs/mcs/iterators.cs | 25 +++----- mcs/mcs/method.cs | 2 +- mcs/mcs/statement.cs | 136 ++++++++++++++++++++++++++++------------ mcs/tests/test-debug-13-ref.xml | 8 +-- mcs/tests/test-debug-19-ref.xml | 12 +--- mcs/tests/ver-il-net_4_5.xml | 26 ++++---- 9 files changed, 169 insertions(+), 152 deletions(-) create mode 100644 mcs/errors/cs4005-2.cs diff --git a/mcs/errors/cs4005-2.cs b/mcs/errors/cs4005-2.cs new file mode 100644 index 00000000000..920f1b0a828 --- /dev/null +++ b/mcs/errors/cs4005-2.cs @@ -0,0 +1,13 @@ +// CS4005: Async methods cannot have unsafe parameters +// Line: 11 +// Compiler options: -unsafe + +class C +{ + unsafe delegate void D (int* i); + + public static void Main () + { + D d = async delegate { }; + } +} diff --git a/mcs/mcs/anonymous.cs b/mcs/mcs/anonymous.cs index 91781cf97b2..0e1aa9ec017 100644 --- a/mcs/mcs/anonymous.cs +++ b/mcs/mcs/anonymous.cs @@ -199,7 +199,7 @@ namespace Mono.CSharp { protected override void DoEmit (EmitContext ec) { - hoisted_this.EmitHoistingAssignment (ec); + hoisted_this.EmitAssign (ec, new CompilerGeneratedThis (ec.CurrentType, loc), false, false); } protected override void CloneTo (CloneContext clonectx, Statement target) @@ -311,7 +311,13 @@ namespace Mono.CSharp { public void CaptureLocalVariable (ResolveContext ec, LocalVariable local_info) { - ec.CurrentBlock.Explicit.HasCapturedVariable = true; + if (this is StateMachine) { + if (ec.CurrentBlock.ParametersBlock != local_info.Block.ParametersBlock) + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } else { + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } + if (ec.CurrentBlock.Explicit != local_info.Block.Explicit) AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); @@ -329,7 +335,10 @@ namespace Mono.CSharp { public void CaptureParameter (ResolveContext ec, ParameterReference param_ref) { - ec.CurrentBlock.Explicit.HasCapturedVariable = true; + if (!(this is StateMachine)) { + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } + AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); if (param_ref.GetHoistedVariable (ec) != null) @@ -813,13 +822,6 @@ namespace Mono.CSharp { return field; } } - - public void EmitHoistingAssignment (EmitContext ec) - { - SimpleAssign a = new SimpleAssign (GetFieldExpression (ec), new CompilerGeneratedThis (ec.CurrentType, field.Location)); - if (a.Resolve (new ResolveContext (ec.MemberContext)) != null) - a.EmitStatement (ec); - } } // @@ -1030,12 +1032,8 @@ namespace Mono.CSharp { } using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) { - var body = CompatibleMethodBody (ec, tic, InternalType.Arglist, delegate_type); + var body = CompatibleMethodBody (ec, tic, null, delegate_type); if (body != null) { - if (Block.IsAsync) { - AsyncInitializer.Create (ec, body.Block, body.Parameters, ec.CurrentMemberDefinition.Parent.PartialContainer, null, loc); - } - am = body.Compatible (ec, body); } else { am = null; @@ -1122,18 +1120,6 @@ namespace Mono.CSharp { am = CreateExpressionTree (ec, delegate_type); } } else { - if (Block.IsAsync) { - var rt = body.ReturnType; - if (rt.Kind != MemberKind.Void && - rt != ec.Module.PredefinedTypes.Task.TypeSpec && - !rt.IsGenericTask) { - ec.Report.Error (4010, loc, "Cannot convert async {0} to delegate type `{1}'", - GetSignatureForError (), type.GetSignatureForError ()); - } - - AsyncInitializer.Create (ec, body.Block, body.Parameters, ec.CurrentMemberDefinition.Parent.PartialContainer, rt, loc); - } - am = body.Compatible (ec); } } catch (CompletionResult) { @@ -1261,7 +1247,19 @@ namespace Mono.CSharp { ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block; - return CompatibleMethodFactory (return_type, delegate_type, p, b); + if (b.IsAsync) { + var rt = return_type; + if (rt != null && rt.Kind != MemberKind.Void && rt != ec.Module.PredefinedTypes.Task.TypeSpec && !rt.IsGenericTask) { + ec.Report.Error (4010, loc, "Cannot convert async {0} to delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + + return null; + } + + b = b.ConvertToAsyncTask (ec, ec.CurrentMemberDefinition.Parent.PartialContainer, p, return_type); + } + + return CompatibleMethodFactory (return_type ?? InternalType.Arglist, delegate_type, p, b); } protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b) @@ -1531,7 +1529,8 @@ namespace Mono.CSharp { // Modifiers modifiers; - if (Block.HasCapturedVariable || Block.HasCapturedThis) { + var src_block = Block.Original.Explicit; + if (src_block.HasCapturedVariable || src_block.HasCapturedThis) { storey = FindBestMethodStorey (); modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE; } else { diff --git a/mcs/mcs/async.cs b/mcs/mcs/async.cs index 3fa90a545d5..fba2704ebd0 100644 --- a/mcs/mcs/async.cs +++ b/mcs/mcs/async.cs @@ -405,12 +405,6 @@ namespace Mono.CSharp } } - public Block OriginalBlock { - get { - return block.Parent; - } - } - public TypeInferenceContext ReturnTypeInference { get { return return_inference; @@ -419,38 +413,6 @@ namespace Mono.CSharp #endregion - public static void Create (IMemberContext context, ParametersBlock block, ParametersCompiled parameters, TypeDefinition host, TypeSpec returnType, Location loc) - { - for (int i = 0; i < parameters.Count; i++) { - Parameter p = parameters[i]; - Parameter.Modifier mod = p.ModFlags; - if ((mod & Parameter.Modifier.RefOutMask) != 0) { - host.Compiler.Report.Error (1988, p.Location, - "Async methods cannot have ref or out parameters"); - return; - } - - if (p is ArglistParameter) { - host.Compiler.Report.Error (4006, p.Location, - "__arglist is not allowed in parameter list of async methods"); - return; - } - - if (parameters.Types[i].IsPointer) { - host.Compiler.Report.Error (4005, p.Location, - "Async methods cannot have unsafe parameters"); - return; - } - } - - if (!block.HasAwait) { - host.Compiler.Report.Warning (1998, 1, loc, - "Async block lacks `await' operator and will run synchronously"); - } - - block.WrapIntoAsyncTask (context, host, returnType); - } - protected override BlockContext CreateBlockContext (ResolveContext rc) { var ctx = base.CreateBlockContext (rc); @@ -501,8 +463,8 @@ namespace Mono.CSharp Dictionary> stack_fields; Dictionary> awaiter_fields; - public AsyncTaskStorey (IMemberContext context, AsyncInitializer initializer, TypeSpec type) - : base (initializer.OriginalBlock, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async", MemberKind.Class) + public AsyncTaskStorey (ParametersBlock block, IMemberContext context, AsyncInitializer initializer, TypeSpec type) + : base (block, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async", MemberKind.Class) { return_type = type; awaiter_fields = new Dictionary> (); diff --git a/mcs/mcs/iterators.cs b/mcs/mcs/iterators.cs index dffaefd0b74..47f9aba156b 100644 --- a/mcs/mcs/iterators.cs +++ b/mcs/mcs/iterators.cs @@ -702,8 +702,6 @@ namespace Mono.CSharp protected override Expression DoResolve (ResolveContext ec) { - storey = (StateMachine) block.Parent.ParametersBlock.AnonymousMethodStorey; - var ctx = CreateBlockContext (ec); Block.Resolve (ctx); @@ -730,7 +728,7 @@ namespace Mono.CSharp public override void Emit (EmitContext ec) { // - // Load Iterator storey instance + // Load state machine instance // storey.Instance.Emit (ec); } @@ -749,11 +747,7 @@ namespace Mono.CSharp iterator_body_end = ec.DefineLabel (); - if (ec.EmitAccurateDebugInfo && ec.Mark (Block.Original.StartLocation)) { - ec.Emit (OpCodes.Nop); - } - - block.Emit (ec); + block.EmitEmbedded (ec); ec.MarkLabel (iterator_body_end); @@ -816,11 +810,7 @@ namespace Mono.CSharp iterator_body_end = ec.DefineLabel (); - if (ec.EmitAccurateDebugInfo && ec.Mark (Block.Original.StartLocation)) { - ec.Emit (OpCodes.Nop); - } - - block.Emit (ec); + block.EmitEmbedded (ec); ec.MarkLabel (iterator_body_end); @@ -905,6 +895,11 @@ namespace Mono.CSharp ec.Emit (OpCodes.Stloc, skip_finally); } } + + public void SetStateMachine (StateMachine stateMachine) + { + this.storey = stateMachine; + } } // @@ -925,7 +920,7 @@ namespace Mono.CSharp this.type = method.ReturnType; } - public Block Container { + public ToplevelBlock Container { get { return OriginalMethod.Block; } } @@ -1089,7 +1084,7 @@ namespace Mono.CSharp parent.Compiler.Report.Error (1629, method.Location, "Unsafe code may not appear in iterators"); } - method.Block.WrapIntoIterator (method, parent, iterator_type, is_enumerable); + method.Block = method.Block.ConvertToIterator (method, parent, iterator_type, is_enumerable); } static bool CheckType (TypeSpec ret, TypeContainer parent, out TypeSpec original_iterator_type, out bool is_enumerable) diff --git a/mcs/mcs/method.cs b/mcs/mcs/method.cs index 3b608feaaaa..b8685ec632b 100644 --- a/mcs/mcs/method.cs +++ b/mcs/mcs/method.cs @@ -1196,7 +1196,7 @@ namespace Mono.CSharp { Report.Error (1983, Location, "The return type of an async method must be void, Task, or Task"); } - AsyncInitializer.Create (this, block, parameters, Parent.PartialContainer, ReturnType, Location); + block = (ToplevelBlock) block.ConvertToAsyncTask (this, Parent.PartialContainer, parameters, ReturnType); ModFlags |= Modifiers.DEBUGGER_HIDDEN; } } diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index 4009a434f1f..2a85bee28c6 100644 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -2430,7 +2430,9 @@ namespace Mono.CSharp { } public bool HasCapturedVariable { - set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; } + set { + flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; + } get { return (flags & Flags.HasCapturedVariable) != 0; } @@ -2478,8 +2480,9 @@ namespace Mono.CSharp { public override void Emit (EmitContext ec) { - if (am_storey != null) { - DefineAnonymousStorey (ec); + // TODO: The is check should go once state machine is fully separated + if (am_storey != null && !(am_storey is StateMachine)) { + DefineStoreyContainer (ec, am_storey); am_storey.EmitStoreyInstantiation (ec, this); } @@ -2503,7 +2506,7 @@ namespace Mono.CSharp { } } - void DefineAnonymousStorey (EmitContext ec) + protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey) { // // Creates anonymous method storey @@ -2512,33 +2515,33 @@ namespace Mono.CSharp { // // Creates parent storey reference when hoisted this is accessible // - if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) { - ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit; + if (storey.OriginalSourceBlock.Explicit.HasCapturedThis) { + ExplicitBlock parent = storey.OriginalSourceBlock.Explicit.Parent.Explicit; // // Hoisted this exists in top-level parent storey only // - while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey) + while (parent.AnonymousMethodStorey == null || parent.AnonymousMethodStorey.Parent is AnonymousMethodStorey) parent = parent.Parent.Explicit; - am_storey.AddParentStoreyReference (ec, parent.am_storey); + storey.AddParentStoreyReference (ec, parent.AnonymousMethodStorey); } - am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey); + storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey); // TODO MemberCache: Review - am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator; + storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator; } - am_storey.CreateContainer (); - am_storey.DefineContainer (); + storey.CreateContainer (); + storey.DefineContainer (); - var ref_blocks = am_storey.ReferencesFromChildrenBlock; + var ref_blocks = storey.ReferencesFromChildrenBlock; if (ref_blocks != null) { foreach (ExplicitBlock ref_block in ref_blocks) { - for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) { - if (b.am_storey != null) { - b.am_storey.AddParentStoreyReference (ec, am_storey); + for (ExplicitBlock b = ref_block.Explicit; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) { + if (b.AnonymousMethodStorey != null) { + b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey); // Stop propagation inside same top block if (b.ParametersBlock.Original == ParametersBlock.Original) @@ -2552,8 +2555,8 @@ namespace Mono.CSharp { } } - am_storey.Define (); - am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey); + storey.Define (); + storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey); } public void RegisterAsyncAwait () @@ -2562,7 +2565,7 @@ namespace Mono.CSharp { while ((block.flags & Flags.AwaitBlock) == 0) { block.flags |= Flags.AwaitBlock; - if (block.Parent == null) + if (block is ParametersBlock) return; block = block.Parent.Explicit; @@ -2714,6 +2717,7 @@ namespace Mono.CSharp { bool resolved; protected bool unreachable; protected ToplevelBlock top_block; + protected StateMachine state_machine; public ParametersBlock (Block parent, ParametersCompiled parameters, Location start) : base (parent, 0, start, start) @@ -2753,6 +2757,7 @@ namespace Mono.CSharp { this.resolved = true; this.unreachable = source.unreachable; this.am_storey = source.am_storey; + this.state_machine = source.state_machine; ParametersBlock = this; @@ -2847,6 +2852,26 @@ namespace Mono.CSharp { return base.CreateExpressionTree (ec); } + public override void Emit (EmitContext ec) + { + if (state_machine != null) { + DefineStoreyContainer (ec, state_machine); + state_machine.EmitStoreyInstantiation (ec, this); + } + + base.Emit (ec); + } + + public void EmitEmbedded (EmitContext ec) + { + if (state_machine != null) { + DefineStoreyContainer (ec, state_machine); + state_machine.EmitStoreyInstantiation (ec, this); + } + + base.Emit (ec); + } + public ParameterInfo GetParameterInfo (Parameter p) { for (int i = 0; i < parameters.Count; ++i) { @@ -2969,37 +2994,70 @@ namespace Mono.CSharp { } } - public void WrapIntoIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable) + public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable) { - ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, Location.Null); - pb.statements = statements; - pb.Original = this; + var iterator = new Iterator (this, method, host, iterator_type, is_enumerable); + var stateMachine = new IteratorStorey (iterator); - var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable); - am_storey = new IteratorStorey (iterator); + am_storey = stateMachine; + iterator.SetStateMachine (stateMachine); - statements = new List (1); - AddStatement (new Return (iterator, iterator.Location)); - flags &= ~Flags.YieldBlock; - IsCompilerGenerated = true; + var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null); + tlb.IsCompilerGenerated = true; + tlb.state_machine = stateMachine; + tlb.AddStatement (new Return (iterator, iterator.Location)); + return tlb; } - public void WrapIntoAsyncTask (IMemberContext context, TypeDefinition host, TypeSpec returnType) + public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType) { - ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, Location.Null); - pb.statements = statements; - pb.Original = this; + for (int i = 0; i < parameters.Count; i++) { + Parameter p = parameters[i]; + Parameter.Modifier mod = p.ModFlags; + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + host.Compiler.Report.Error (1988, p.Location, + "Async methods cannot have ref or out parameters"); + return this; + } + + if (p is ArglistParameter) { + host.Compiler.Report.Error (4006, p.Location, + "__arglist is not allowed in parameter list of async methods"); + return this; + } + + if (parameters.Types[i].IsPointer) { + host.Compiler.Report.Error (4005, p.Location, + "Async methods cannot have unsafe parameters"); + return this; + } + } + + if (!HasAwait) { + host.Compiler.Report.Warning (1998, 1, loc, + "Async block lacks `await' operator and will run synchronously"); + } var block_type = host.Module.Compiler.BuiltinTypes.Void; - var initializer = new AsyncInitializer (pb, host, block_type); + var initializer = new AsyncInitializer (this, host, block_type); initializer.Type = block_type; - am_storey = new AsyncTaskStorey (context, initializer, returnType); + var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType); - statements = new List (1); - AddStatement (new StatementExpression (initializer)); - flags &= ~Flags.AwaitBlock; - IsCompilerGenerated = true; + am_storey = stateMachine; + initializer.SetStateMachine (stateMachine); + + var b = this is ToplevelBlock ? + new ToplevelBlock (host.Compiler, Parameters, Location.Null) : + new ParametersBlock (Parent, parameters, Location.Null) { + IsAsync = true, + Original = this + }; + + b.IsCompilerGenerated = true; + b.state_machine = stateMachine; + b.AddStatement (new StatementExpression (initializer)); + return b; } } diff --git a/mcs/tests/test-debug-13-ref.xml b/mcs/tests/test-debug-13-ref.xml index e84a56a79f9..d9bdcf29731 100644 --- a/mcs/tests/test-debug-13-ref.xml +++ b/mcs/tests/test-debug-13-ref.xml @@ -54,9 +54,7 @@