// in unreachable code, for instance.
//
- if (warn)
+ bool unreachable = false;
+ if (warn && !ec.UnreachableReported) {
+ ec.UnreachableReported = true;
+ unreachable = true;
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
+ }
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
ec.KillFlowBranching ();
+ if (unreachable) {
+ ec.UnreachableReported = false;
+ }
+
return ok;
}
{
ec.Mark (loc);
DoEmit (ec);
+
+ if (ec.StatementEpilogue != null) {
+ ec.EmitEpilogue ();
+ }
}
//
public Expression expr;
public Statement EmbeddedStatement;
- public Do (Statement statement, BooleanExpression bool_expr, Location l)
+ public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
{
expr = bool_expr;
EmbeddedStatement = statement;
- loc = l;
+ loc = doLocation;
+ WhileLocation = whileLocation;
+ }
+
+ public Location WhileLocation {
+ get; private set;
}
public override bool Resolve (BlockContext ec)
ec.MarkLabel (ec.LoopBegin);
// Mark start of while condition
- ec.Mark (expr.Location);
+ ec.Mark (WhileLocation);
//
// Dead code elimination
ec.MarkLabel (ec.LoopBegin);
- ec.Mark (expr.Location);
+ ec.Mark (loc);
expr.EmitBranchable (ec, while_loop, true);
ec.MarkLabel (ec.LoopEnd);
public sealed override bool Resolve (BlockContext ec)
{
- if (!DoResolve (ec))
- return false;
-
+ var res = DoResolve (ec);
unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
ec.CurrentBranching.CurrentUsageVector.Goto ();
- return true;
+ return res;
}
}
if (ec.CurrentIterator != null) {
Error_ReturnFromIterator (ec);
- } else {
+ } else if (ec.ReturnType != InternalType.ErrorType) {
ec.Report.Error (126, loc,
"An object of a type convertible to `{0}' is required for the return statement",
ec.ReturnType.GetSignatureForError ());
ec.Report.Error (127, loc,
"`{0}': A return keyword must not be followed by any expression when method returns void",
ec.GetSignatureForError ());
+
+ return false;
}
} else {
if (am.IsIterator) {
return true;
}
+ if (async_type.Kind == MemberKind.Void) {
+ ec.Report.Error (127, loc,
+ "`{0}': A return keyword must not be followed by any expression when method returns void",
+ ec.GetSignatureForError ());
+
+ return false;
+ }
+
if (!async_type.IsGenericTask) {
if (this is ContextualReturn)
return true;
ec.Report.Error (1997, loc,
- "`{0}': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>",
+ "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
ec.GetSignatureForError ());
return false;
}
//
// The return type is actually Task<T> type argument
//
- block_return_type = async_type.TypeArguments[0];
+ if (expr.Type == async_type) {
+ ec.Report.Error (4016, loc,
+ "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
+ ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
+ } else {
+ block_return_type = async_type.TypeArguments[0];
+ }
}
} else {
+ // Same error code as .NET but better error message
+ if (block_return_type.Kind == MemberKind.Void) {
+ ec.Report.Error (127, loc,
+ "`{0}': A return keyword must not be followed by any expression when delegate returns void",
+ am.GetSignatureForError ());
+
+ return false;
+ }
+
var l = am as AnonymousMethodBody;
if (l != null && l.ReturnTypeInference != null && expr != null) {
l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
if (expr == null)
return false;
- if (expr.Type != block_return_type) {
+ if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
if (expr == null) {
if (async_return != null) {
async_return.EmitAssign (ec);
- ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
+ ec.EmitEpilogue ();
}
+ ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
return;
}
+ ec.EmitEpilogue ();
+
if (unwind_protect || ec.EmitAccurateDebugInfo)
ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
return false;
}
- if (!ec.Switch.GotDefault) {
+ if (ec.Switch.DefaultLabel == null) {
FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
return false;
}
protected override void DoEmit (EmitContext ec)
{
- ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
+ ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
}
public override object Accept (StructuralVisitor visitor)
res = c;
} else {
TypeSpec type = ec.Switch.SwitchType;
- res = c.TryReduce (ec, type, c.Location);
+ res = c.Reduce (ec, type);
if (res == null) {
- c.Error_ValueCannotBeConverted (ec, loc, type, true);
+ c.Error_ValueCannotBeConverted (ec, type, true);
return false;
}
protected FullNamedExpression type_expr;
protected LocalVariable li;
protected List<Declarator> declarators;
+ TypeSpec type;
public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
{
declarators.Add (decl);
}
- void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
+ static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
{
if (bc.Report.Errors != 0)
return;
public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
{
- if (li.Type == null) {
- TypeSpec type = null;
+ if (type == null && !li.IsCompilerGenerated) {
var vexpr = type_expr as VarExpr;
//
if (declarators != null) {
foreach (var d in declarators) {
d.Variable.CreateBuilder (ec);
- if (d.Initializer != null)
+ if (d.Initializer != null) {
+ ec.Mark (d.Variable.Location);
((ExpressionStatement) d.Initializer).EmitStatement (ec);
+ }
}
}
}
if (TypeSpec.IsReferenceType (li.Type))
initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
else
- initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
+ initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
return null;
}
}
}
+ public bool IsCompilerGenerated {
+ get {
+ return (flags & Flags.CompilerGenerated) != 0;
+ }
+ }
+
public bool IsConstant {
get {
return (flags & Flags.Constant) != 0;
#endif
// int assignable_slots;
- bool unreachable_shown;
- bool unreachable;
-
+
public Block (Block parent, Location start, Location end)
: this (parent, 0, start, end)
{
#endregion
- public Block CreateSwitchBlock (Location start)
- {
- // FIXME: Only explicit block should be created
- var new_block = new Block (this, start, start);
- new_block.IsCompilerGenerated = true;
- return new_block;
- }
-
public void SetEndLocation (Location loc)
{
EndLocation = loc;
Block prev_block = ec.CurrentBlock;
bool ok = true;
+ bool unreachable = ec.IsUnreachable;
+ bool prev_unreachable = unreachable;
ec.CurrentBlock = this;
ec.StartFlowBranching (this);
if (s is EmptyStatement)
continue;
- if (!unreachable_shown && !(s is LabeledStatement)) {
+ if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
- unreachable_shown = true;
+ ec.UnreachableReported = true;
}
-
- Block c_block = s as Block;
- if (c_block != null)
- c_block.unreachable = c_block.unreachable_shown = true;
}
//
if (!s.Resolve (ec)) {
ok = false;
- if (ec.IsInProbingMode)
- break;
+ if (!ec.IsInProbingMode)
+ statements [ix] = new EmptyStatement (s.loc);
- statements [ix] = new EmptyStatement (s.loc);
continue;
}
- if (unreachable && !(s is LabeledStatement) && !(s is Block))
+ if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
statements [ix] = new EmptyStatement (s.loc);
unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
- if (unreachable && s is LabeledStatement)
- throw new InternalErrorException ("should not happen");
+ if (unreachable) {
+ ec.IsUnreachable = true;
+ } else if (ec.IsUnreachable)
+ ec.IsUnreachable = false;
+ }
+
+ if (unreachable != prev_unreachable) {
+ ec.IsUnreachable = prev_unreachable;
+ ec.UnreachableReported = false;
}
while (ec.CurrentBranching is FlowBranchingLabeled)
public override bool ResolveUnreachable (BlockContext ec, bool warn)
{
- unreachable_shown = true;
- unreachable = true;
-
- if (warn)
+ bool unreachable = false;
+ if (warn && !ec.UnreachableReported) {
+ ec.UnreachableReported = true;
+ unreachable = true;
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
+ }
- ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
+ var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
+ fb.CurrentUsageVector.IsUnreachable = true;
bool ok = Resolve (ec);
ec.KillFlowBranching ();
+ if (unreachable)
+ ec.UnreachableReported = false;
+
return ok;
}
EmitScopeInitializers (ec);
DoEmit (ec);
-
- if (SymbolWriter.HasSymbolWriter)
- EmitSymbolInfo (ec);
}
protected void EmitScopeInitializers (EmitContext ec)
s.Emit (ec);
}
- protected virtual void EmitSymbolInfo (EmitContext ec)
- {
- }
-
#if DEBUG
public override string ToString ()
{
}
public bool HasCapturedThis {
- set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
+ set {
+ flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
+ }
get {
return (flags & Flags.HasCapturedThis) != 0;
}
}
+ //
+ // Used to indicate that the block has reference to parent
+ // block and cannot be made static when defining anonymous method
+ //
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;
}
if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
return ec.CurrentAnonymousMethod.Storey;
- //
- // When referencing a variable in parent iterator/async storey
- // from nested anonymous method
- //
- if (ParametersBlock.am_storey is StateMachine) {
- return ParametersBlock.am_storey;
- }
-
if (am_storey == null) {
MemberBase mc = ec.MemberContext as MemberBase;
//
// Creates anonymous method storey for this block
//
- am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
+ am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
}
return am_storey;
public override void Emit (EmitContext ec)
{
if (am_storey != null) {
- DefineAnonymousStorey (ec);
+ DefineStoreyContainer (ec, am_storey);
am_storey.EmitStoreyInstantiation (ec, this);
}
ec.Emit (OpCodes.Nop);
}
- bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
- if (emit_debug_info)
+ if (Parent != null)
ec.BeginScope ();
DoEmit (ec);
- if (SymbolWriter.HasSymbolWriter)
- EmitSymbolInfo (ec);
-
- if (emit_debug_info)
+ if (Parent != null)
ec.EndScope ();
if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
}
}
- void DefineAnonymousStorey (EmitContext ec)
+ protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
{
+ if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
+ storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
+ storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
+ }
+
//
// Creates anonymous method storey
//
- if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
+ storey.CreateContainer ();
+ storey.DefineContainer ();
+
+ if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
+
//
- // Creates parent storey reference when hoisted this is accessible
+ // Only first storey in path will hold this reference. All children blocks will
+ // reference it indirectly using $ref field
//
- if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
- ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
+ for (Block b = Original.Explicit; b != null; b = b.Parent) {
+ if (b.Parent != null) {
+ var s = b.Parent.Explicit.AnonymousMethodStorey;
+ if (s != null) {
+ storey.HoistedThis = s.HoistedThis;
+ break;
+ }
+ }
- //
- // Hoisted this exists in top-level parent storey only
- //
- while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
- parent = parent.Parent.Explicit;
+ if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
+ storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
- am_storey.AddParentStoreyReference (ec, parent.am_storey);
+ if (storey.HoistedThis != null)
+ break;
+ }
}
+
+ //
+ // We are the first storey on path and 'this' has to be hoisted
+ //
+ if (storey.HoistedThis == null) {
+ foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
+ //
+ // ThisReferencesFromChildrenBlock holds all reference even if they
+ // are not on this path. It saves some memory otherwise it'd have to
+ // be in every explicit block. We run this check to see if the reference
+ // is valid for this storey
+ //
+ Block block_on_path = ref_block;
+ for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
- am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
+ if (block_on_path == null)
+ continue;
- // TODO MemberCache: Review
- am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
- }
+ if (storey.HoistedThis == null) {
+ storey.AddCapturedThisField (ec);
+ }
+
+ for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
+ if (b.AnonymousMethodStorey != null) {
+ b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
+ b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
- am_storey.CreateContainer ();
- am_storey.DefineContainer ();
+ //
+ // Stop propagation inside same top block
+ //
+ if (b.ParametersBlock == ParametersBlock.Original)
+ break;
- var ref_blocks = am_storey.ReferencesFromChildrenBlock;
+ b = b.ParametersBlock;
+ }
+
+ var pb = b as ParametersBlock;
+ if (pb != null && pb.StateMachine != null) {
+ if (pb.StateMachine == storey)
+ break;
+
+ pb.StateMachine.AddParentStoreyReference (ec, storey);
+ }
+
+ b.HasCapturedVariable = true;
+ }
+ }
+ }
+ }
+
+ 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; 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)
+ //
+ if (b.ParametersBlock == ParametersBlock.Original)
break;
b = b.ParametersBlock;
}
+ var pb = b as ParametersBlock;
+ if (pb != null && pb.StateMachine != null) {
+ if (pb.StateMachine == storey)
+ break;
+
+ pb.StateMachine.AddParentStoreyReference (ec, storey);
+ }
+
b.HasCapturedVariable = true;
}
}
}
- am_storey.Define ();
- am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
+ storey.Define ();
+ storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
}
public void RegisterAsyncAwait ()
while ((block.flags & Flags.AwaitBlock) == 0) {
block.flags |= Flags.AwaitBlock;
- if (block.Parent == null)
+ if (block is ParametersBlock)
return;
block = block.Parent.Explicit;
#region Properties
- public Block Block {
+ public ParametersBlock Block {
+ get {
+ return block;
+ }
+ }
+
+ Block INamedBlockVariable.Block {
get {
return block;
}
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)
this.resolved = true;
this.unreachable = source.unreachable;
this.am_storey = source.am_storey;
+ this.state_machine = source.state_machine;
ParametersBlock = this;
// Overwrite original for comparison purposes when linking cross references
// between anonymous methods
//
- Original = source;
+ Original = source.Original;
}
#region Properties
}
}
+ public StateMachine StateMachine {
+ get {
+ return state_machine;
+ }
+ }
+
public ToplevelBlock TopBlock {
get {
return top_block;
return base.CreateExpressionTree (ec);
}
+ public override void Emit (EmitContext ec)
+ {
+ if (state_machine != null && state_machine.OriginalSourceBlock != this) {
+ DefineStoreyContainer (ec, state_machine);
+ state_machine.EmitStoreyInstantiation (ec, this);
+ }
+
+ base.Emit (ec);
+ }
+
+ public void EmitEmbedded (EmitContext ec)
+ {
+ if (state_machine != null && state_machine.OriginalSourceBlock != this) {
+ 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) {
throw new ArgumentException ("Invalid parameter");
}
- public Expression GetParameterReference (int index, Location loc)
+ public ParameterReference GetParameterReference (int index, Location loc)
{
return new ParameterReference (parameter_info[index], loc);
}
unreachable = top_level.End ();
}
} catch (Exception e) {
- if (e is CompletionResult || rc.Report.IsDisabled)
+ if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
throw;
if (rc.CurrentBlock != null) {
for (int i = 0; i < orig_count; ++i) {
Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
- if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
+ if ((mod & Parameter.Modifier.OUT) == 0)
continue;
VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
}
}
- 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);
+ state_machine = stateMachine;
+ iterator.SetStateMachine (stateMachine);
- statements = new List<Statement> (1);
- AddStatement (new Return (iterator, iterator.Location));
- flags &= ~Flags.YieldBlock;
- IsCompilerGenerated = true;
+ var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
+ tlb.Original = this;
+ 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, Location loc)
{
- ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
- pb.EndLocation = EndLocation;
- 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<Statement> (1);
- AddStatement (new StatementExpression (initializer));
- flags &= ~Flags.AwaitBlock;
+ state_machine = stateMachine;
+ initializer.SetStateMachine (stateMachine);
+
+ var b = this is ToplevelBlock ?
+ new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
+ new ParametersBlock (Parent, parameters, Location.Null) {
+ IsAsync = true,
+ };
+
+ b.Original = this;
+ b.IsCompilerGenerated = true;
+ b.state_machine = stateMachine;
+ b.AddStatement (new StatementExpression (initializer));
+ return b;
}
}
Dictionary<string, object> names;
Dictionary<string, object> labels;
- public HoistedVariable HoistedThisVariable;
-
- public Report Report {
- get { return compiler.Report; }
- }
+ List<ExplicitBlock> this_references;
public ToplevelBlock (CompilerContext ctx, Location loc)
: this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
}
}
+ public Report Report {
+ get {
+ return compiler.Report;
+ }
+ }
+
+ //
+ // Used by anonymous blocks to track references of `this' variable
+ //
+ public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
+ get {
+ return this_references;
+ }
+ }
+
+ //
+ // Returns the "this" instance variable of this block.
+ // See AddThisVariable() for more information.
+ //
+ public LocalVariable ThisVariable {
+ get {
+ return this_variable;
+ }
+ }
+
public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
{
if (names == null)
//
// A collision checking between local names
//
+ var variable_block = li.Block.Explicit;
for (int i = 0; i < existing_list.Count; ++i) {
existing = existing_list[i];
Block b = existing.Block.Explicit;
// Collision at same level
- if (li.Block.Explicit == b) {
+ if (variable_block == b) {
li.Block.Error_AlreadyDeclared (name, li);
break;
}
// Collision with parent
- Block parent = li.Block.Explicit;
+ Block parent = variable_block;
while ((parent = parent.Parent) != null) {
if (parent == b) {
li.Block.Error_AlreadyDeclared (name, li, "parent or current");
}
}
- if (!ignoreChildrenBlocks) {
+ if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
// Collision with children
while ((b = b.Parent) != null) {
- if (li.Block.Explicit == b) {
+ if (variable_block == b) {
li.Block.Error_AlreadyDeclared (name, li, "child");
i = existing_list.Count;
break;
existing_list.Add (label);
}
+ public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
+ {
+ if (this_references == null)
+ this_references = new List<ExplicitBlock> ();
+
+ if (!this_references.Contains (block))
+ this_references.Add (block);
+ }
+
+ public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
+ {
+ this_references.Remove (block);
+ }
+
//
// Creates an arguments set from all parameters, useful for method proxy calls
//
if (label != null) {
if (label.Block == b.Original)
return label;
-
- // TODO: Temporary workaround for the switch block implicit label block
- if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
- return label;
} else {
List<LabeledStatement> list = (List<LabeledStatement>) value;
for (int i = 0; i < list.Count; ++i) {
label = list[i];
if (label.Block == b.Original)
return label;
-
- // TODO: Temporary workaround for the switch block implicit label block
- if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
- return label;
}
}
return null;
}
- // <summary>
- // Returns the "this" instance variable of this block.
- // See AddThisVariable() for more information.
- // </summary>
- public LocalVariable ThisVariable {
- get { return this_variable; }
- }
-
// <summary>
// This is used by non-static `struct' constructors which do not have an
// initializer - in this case, the constructor must initialize all of the
}
#endif
}
-
- protected override void EmitSymbolInfo (EmitContext ec)
- {
- AnonymousExpression ae = ec.CurrentAnonymousMethod;
- if ((ae != null) && (ae.Storey != null))
- SymbolWriter.DefineScopeVariable (ae.Storey.ID);
-
- base.EmitSymbolInfo (ec);
- }
}
- public class SwitchLabel {
+ public class SwitchLabel : Statement
+ {
Expression label;
Constant converted;
- readonly Location loc;
Label? il_label;
}
}
+ public bool SectionStart { get; set; }
+
public Label GetILLabel (EmitContext ec)
{
if (il_label == null){
return il_label.Value;
}
+ protected override void DoEmit (EmitContext ec)
+ {
+ ec.MarkLabel (GetILLabel (ec));
+ }
+
+ public override bool Resolve (BlockContext bc)
+ {
+ bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
+
+ return base.Resolve (bc);
+ }
+
//
// Resolves the expression, reduces it to a literal if possible
// and then converts it to the requested type.
ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
}
- public SwitchLabel Clone (CloneContext clonectx)
- {
- if (label == null)
- return this;
-
- return new SwitchLabel (label.Clone (clonectx), loc);
- }
- }
-
- public class SwitchSection {
- public readonly List<SwitchLabel> Labels;
- public readonly Block Block;
-
- public SwitchSection (List<SwitchLabel> labels, Block block)
+ protected override void CloneTo (CloneContext clonectx, Statement target)
{
- Labels = labels;
- Block = block;
+ var t = (SwitchLabel) target;
+ if (label != null)
+ t.label = label.Clone (clonectx);
}
- public SwitchSection Clone (CloneContext clonectx)
+ public override object Accept (StructuralVisitor visitor)
{
- var cloned_labels = new List<SwitchLabel> ();
-
- foreach (SwitchLabel sl in Labels)
- cloned_labels.Add (sl.Clone (clonectx));
-
- return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
+ return visitor.Visit (this);
}
}
-
+
public class Switch : Statement
{
// structure used to hold blocks of keys while calculating table switch
}
}
- sealed class LabelMarker : Statement
+ sealed class DispatchStatement : Statement
{
- readonly Switch s;
- readonly List<SwitchLabel> labels;
+ readonly Switch body;
- public LabelMarker (Switch s, List<SwitchLabel> labels)
+ public DispatchStatement (Switch body)
{
- this.s = s;
- this.labels = labels;
+ this.body = body;
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
+ throw new NotImplementedException ();
}
protected override void DoEmit (EmitContext ec)
{
- foreach (var l in labels) {
- if (l.IsDefault)
- ec.MarkLabel (s.DefaultLabel);
- else
- ec.MarkLabel (l.GetILLabel (ec));
- }
+ body.EmitDispatch (ec);
}
}
- public List<SwitchSection> Sections;
public Expression Expr;
//
//
Dictionary<long, SwitchLabel> labels;
Dictionary<string, SwitchLabel> string_labels;
+ List<SwitchLabel> case_labels;
/// <summary>
/// The governing switch type
/// </summary>
public TypeSpec SwitchType;
- //
- // Computed
- //
- Label default_target;
- Label null_target;
Expression new_expr;
- bool is_constant;
- SwitchSection constant_section;
- SwitchSection default_section;
- SwitchLabel null_section;
+ SwitchLabel case_null;
+ SwitchLabel case_default;
- Statement simple_stmt;
+ Label defaultLabel, nullLabel;
VariableReference value;
ExpressionStatement string_dictionary;
FieldExpr switch_cache_field;
//
Nullable.Unwrap unwrap;
- public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
+ public Switch (Expression e, ExplicitBlock block, Location l)
{
Expr = e;
this.block = block;
- Sections = sects;
loc = l;
}
}
}
- public Label DefaultLabel {
- get {
- return default_target;
- }
- }
-
- public bool GotDefault {
+ public SwitchLabel DefaultLabel {
get {
- return default_section != null;
+ return case_default;
}
}
// It also returns a hashtable with the keys that we will later
// use to compute the switch tables
//
- bool CheckSwitch (ResolveContext ec)
+ bool ResolveLabels (ResolveContext ec, Constant value)
{
bool error = false;
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
- string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
- else
- labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
-
- foreach (SwitchSection ss in Sections){
- foreach (SwitchLabel sl in ss.Labels){
- if (sl.IsDefault){
- if (default_section != null){
- sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
- error = true;
- }
- default_section = ss;
- continue;
- }
+ if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
+ string_labels = new Dictionary<string, SwitchLabel> ();
+ } else {
+ labels = new Dictionary<long, SwitchLabel> ();
+ }
- if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
+ case_labels = new List<SwitchLabel> ();
+ int default_label_index = -1;
+ bool constant_label_found = false;
+
+ for (int i = 0; i < block.Statements.Count; ++i) {
+ var s = block.Statements[i];
+
+ var sl = s as SwitchLabel;
+ if (sl == null) {
+ continue;
+ }
+
+ case_labels.Add (sl);
+
+ if (sl.IsDefault) {
+ if (case_default != null) {
+ sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
error = true;
- continue;
}
-
- try {
- if (string_labels != null) {
- string s = sl.Converted.GetValue () as string;
- if (s == null)
- null_section = sl;
- else
- string_labels.Add (s, sl);
+
+ default_label_index = i;
+ case_default = sl;
+ continue;
+ }
+
+ if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
+ error = true;
+ continue;
+ }
+
+ try {
+ if (string_labels != null) {
+ string string_value = sl.Converted.GetValue () as string;
+ if (string_value == null)
+ case_null = sl;
+ else
+ string_labels.Add (string_value, sl);
+ } else {
+ if (sl.Converted is NullLiteral) {
+ case_null = sl;
} else {
- if (sl.Converted is NullLiteral) {
- null_section = sl;
- } else {
- labels.Add (sl.Converted.GetValueAsLong (), sl);
- }
+ labels.Add (sl.Converted.GetValueAsLong (), sl);
}
- } catch (ArgumentException) {
- if (string_labels != null)
- sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
- else
- sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
-
- error = true;
}
+ } catch (ArgumentException) {
+ if (string_labels != null)
+ sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
+ else
+ sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
+
+ error = true;
+ }
+
+ if (value != null) {
+ var constant_label = constant_label_found ? null : FindLabel (value);
+ if (constant_label == null || constant_label != sl)
+ block.Statements[i] = new EmptyStatement (s.loc);
+ else
+ constant_label_found = true;
}
}
+
+ if (value != null && constant_label_found && default_label_index >= 0)
+ block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
+
return !error;
}
//
void EmitTableSwitch (EmitContext ec, Expression val)
{
- Label lbl_default = default_target;
-
if (labels != null && labels.Count > 0) {
List<LabelsRange> ranges;
if (string_labels != null) {
ranges.Sort ();
}
+ Label lbl_default = defaultLabel;
TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
LabelsRange kb = ranges[range_index];
- lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
+ lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
// Optimize small ranges using simple equality check
if (kb.Range <= 2) {
foreach (var key in kb.label_values) {
SwitchLabel sl = labels[key];
- if (sl.Converted.IsDefaultValue) {
+ if (sl == case_default || sl == case_null)
+ continue;
+
+ if (sl.Converted.IsZeroInteger) {
val.EmitBranchable (ec, sl.GetILLabel (ec), false);
} else {
val.Emit (ec);
if (ranges.Count > 0)
ec.Emit (OpCodes.Br, lbl_default);
}
-
- // now emit the code for the sections
- bool found_default = false;
-
- foreach (SwitchSection ss in Sections) {
- foreach (SwitchLabel sl in ss.Labels) {
- if (sl.IsDefault) {
- ec.MarkLabel (lbl_default);
- found_default = true;
- if (null_section == null)
- ec.MarkLabel (null_target);
- } else if (sl.Converted.IsNull) {
- ec.MarkLabel (null_target);
- }
-
- ec.MarkLabel (sl.GetILLabel (ec));
- }
-
- ss.Block.Emit (ec);
- }
-
- if (!found_default) {
- ec.MarkLabel (lbl_default);
- if (null_section == null) {
- ec.MarkLabel (null_target);
- }
- }
}
-
+
SwitchLabel FindLabel (Constant value)
{
SwitchLabel sl = null;
if (string_labels != null) {
string s = value.GetValue () as string;
if (s == null) {
- if (null_section != null)
- sl = null_section;
- else if (default_section != null)
- sl = default_section.Labels[0];
+ if (case_null != null)
+ sl = case_null;
+ else if (case_default != null)
+ sl = case_default;
} else {
string_labels.TryGetValue (s, out sl);
}
} else {
if (value is NullLiteral) {
- sl = null_section;
+ sl = case_null;
} else {
labels.TryGetValue (value.GetValueAsLong (), out sl);
}
return sl;
}
- SwitchSection FindSection (SwitchLabel label)
- {
- foreach (SwitchSection ss in Sections){
- foreach (SwitchLabel sl in ss.Labels){
- if (label == sl)
- return ss;
- }
- }
-
- return null;
- }
-
public override bool Resolve (BlockContext ec)
{
Expr = Expr.Resolve (ec);
return false;
}
- if (!CheckSwitch (ec))
- return false;
+ if (block.Statements.Count == 0)
+ return true;
- Switch old_switch = ec.Switch;
- ec.Switch = this;
- ec.Switch.SwitchType = SwitchType;
+ var constant = new_expr as Constant;
- ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
+ if (!ResolveLabels (ec, constant))
+ return false;
- var constant = new_expr as Constant;
- if (constant != null) {
- is_constant = true;
- SwitchLabel label = FindLabel (constant);
- if (label != null)
- constant_section = FindSection (label);
-
- if (constant_section == null)
- constant_section = default_section;
- } else {
+ //
+ // Don't need extra variable for constant switch or switch with
+ // only default case
+ //
+ if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
//
- // Store switch expression for comparission purposes
+ // Store switch expression for comparison purposes
//
value = new_expr as VariableReference;
- if (value == null)
+ if (value == null) {
+ // Create temporary variable inside switch scope
+ var current_block = ec.CurrentBlock;
+ ec.CurrentBlock = Block;
value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
+ value.Resolve (ec);
+ ec.CurrentBlock = current_block;
+ }
}
- bool first = true;
- bool ok = true;
- foreach (SwitchSection ss in Sections){
- if (!first)
- ec.CurrentBranching.CreateSibling (
- null, FlowBranching.SiblingType.SwitchSection);
- else
- first = false;
+ Switch old_switch = ec.Switch;
+ ec.Switch = this;
+ ec.Switch.SwitchType = SwitchType;
- if (is_constant && (ss != constant_section)) {
- // If we're a constant switch, we're only emitting
- // one single section - mark all the others as
- // unreachable.
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- if (!ss.Block.ResolveUnreachable (ec, true)) {
- ok = false;
- }
- } else {
- if (!ss.Block.Resolve (ec))
- ok = false;
- }
- }
+ ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
+
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
+
+ var ok = block.Resolve (ec);
- if (default_section == null)
- ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
+ if (case_default == null)
+ ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
ec.EndFlowBranching ();
ec.Switch = old_switch;
if (!ok)
return false;
- if (!is_constant) {
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
- if (string_labels.Count < 7)
- ResolveSimpleSwitch (ec);
- else
- ResolveStringSwitchMap (ec);
- } else if (labels.Count < 3 && !IsNullable) {
- ResolveSimpleSwitch (ec);
- }
+ if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
+ ResolveStringSwitchMap (ec);
}
+ //
+ // Needed to emit anonymous storey initialization before
+ // any generated switch dispatch
+ //
+ block.AddScopeStatement (new DispatchStatement (this));
+
return true;
}
return sl;
}
- //
- // Prepares switch using simple if/else comparison for small label count (4 + optional default)
- //
- void ResolveSimpleSwitch (BlockContext bc)
- {
- simple_stmt = default_section != null ? default_section.Block : null;
-
- for (int i = Sections.Count - 1; i >= 0; --i) {
- var s = Sections[i];
-
- if (s == default_section) {
- s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
- continue;
- }
-
- s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
-
- Expression cond = null;
- for (int ci = 0; ci < s.Labels.Count; ++ci) {
- var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
-
- if (ci > 0) {
- cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
- } else {
- cond = e;
- }
- }
-
- //
- // Compiler generated, hide from symbol file
- //
- simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
- }
-
- // It's null for empty switch
- if (simple_stmt != null)
- simple_stmt.Resolve (bc);
- }
-
//
// Converts string switch into string hashtable
//
var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
Field field = new Field (ctype, string_dictionary_type,
Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
- new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
+ new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
if (!field.Define ())
return;
ctype.AddField (field);
var init = new List<Expression> ();
- int counter = 0;
+ int counter = -1;
labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
string value = null;
- foreach (SwitchSection section in Sections) {
- bool contains_label = false;
- foreach (SwitchLabel sl in section.Labels) {
- if (sl.IsDefault || sl.Converted.IsNull)
- continue;
- if (!contains_label) {
- labels.Add (counter, sl);
- contains_label = true;
- }
+ foreach (SwitchLabel sl in case_labels) {
- value = (string) sl.Converted.GetValue ();
- var init_args = new List<Expression> (2);
- init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
+ if (sl.SectionStart)
+ labels.Add (++counter, sl);
- sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
- init_args.Add (sl.Converted);
+ if (sl == case_default || sl == case_null)
+ continue;
- init.Add (new CollectionElementInitializer (init_args, loc));
- }
+ value = (string) sl.Converted.GetValue ();
+ var init_args = new List<Expression> (2);
+ init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
- //
- // Don't add empty sections
- //
- if (contains_label)
- ++counter;
- }
+ sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
+ init_args.Add (sl.Converted);
+ init.Add (new CollectionElementInitializer (init_args, loc));
+ }
+
Arguments args = new Arguments (1);
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
Expression initializer = new NewInitialize (string_dictionary_type, args,
//
// Skip initialization when value is null
//
- value.EmitBranchable (ec, null_target, false);
+ value.EmitBranchable (ec, nullLabel, false);
//
// Check if string dictionary is initialized and initialize
//
// A value was not found, go to default case
//
- get_item.EmitBranchable (ec, default_target, false);
+ get_item.EmitBranchable (ec, defaultLabel, false);
} else {
Arguments get_value_args = new Arguments (1);
get_value_args.Add (new Argument (value));
LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
get_item_object.EmitAssign (ec, get_item, true, false);
- ec.Emit (OpCodes.Brfalse, default_target);
+ ec.Emit (OpCodes.Brfalse, defaultLabel);
ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
string_switch_variable.Release (ec);
}
- protected override void DoEmit (EmitContext ec)
+ //
+ // Emits switch using simple if/else comparison for small label count (4 + optional default)
+ //
+ void EmitShortSwitch (EmitContext ec)
{
- // Workaround broken flow-analysis
- block.HasUnreachableClosingBrace = true;
+ MethodSpec equal_method = null;
+ if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
+ equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
+ }
+
+ if (equal_method != null) {
+ value.EmitBranchable (ec, nullLabel, false);
+ }
+
+ for (int i = 0; i < case_labels.Count; ++i) {
+ var label = case_labels [i];
+ if (label == case_default || label == case_null)
+ continue;
+
+ var constant = label.Converted;
+
+ if (equal_method != null) {
+ value.Emit (ec);
+ constant.Emit (ec);
+
+ var call = new CallEmitter ();
+ call.EmitPredefined (ec, equal_method, new Arguments (0));
+ ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
+ continue;
+ }
+
+ if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
+ value.EmitBranchable (ec, label.GetILLabel (ec), false);
+ continue;
+ }
+
+ value.Emit (ec);
+ constant.Emit (ec);
+ ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
+ }
+
+ ec.Emit (OpCodes.Br, defaultLabel);
+ }
+
+ void EmitDispatch (EmitContext ec)
+ {
+ if (value == null) {
+ //
+ // Constant switch, we already done the work
+ //
+ return;
+ }
//
- // Needed to emit anonymous storey initialization
- // Otherwise it does not contain any statements for now
+ // Mark sequence point explicitly to switch
//
- block.Emit (ec);
+ ec.Mark (block.StartLocation);
+ block.IsCompilerGenerated = true;
- default_target = ec.DefineLabel ();
- null_target = ec.DefineLabel ();
-
- if (IsNullable) {
- unwrap.EmitCheck (ec);
- ec.Emit (OpCodes.Brfalse, null_target);
- value.EmitAssign (ec, new_expr, false, false);
- } else if (new_expr != value && !is_constant) {
- value.EmitAssign (ec, new_expr, false, false);
+ if (string_dictionary != null) {
+ DoEmitStringSwitch (ec);
+ } else if (case_labels.Count < 4 || string_labels != null) {
+ EmitShortSwitch (ec);
+ } else {
+ EmitTableSwitch (ec, value);
}
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ // Workaround broken flow-analysis
+ block.HasUnreachableClosingBrace = true;
//
// Setup the codegen context
//
Label old_end = ec.LoopEnd;
Switch old_switch = ec.Switch;
-
+
ec.LoopEnd = ec.DefineLabel ();
ec.Switch = this;
- // Emit Code.
- if (is_constant) {
- if (constant_section != null)
- constant_section.Block.Emit (ec);
- } else if (string_dictionary != null) {
- DoEmitStringSwitch (ec);
- } else if (simple_stmt != null) {
- simple_stmt.Emit (ec);
- } else {
- EmitTableSwitch (ec, value);
+ defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
+ nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
+
+ if (value != null) {
+ ec.Mark (loc);
+ if (IsNullable) {
+ unwrap.EmitCheck (ec);
+ ec.Emit (OpCodes.Brfalse, nullLabel);
+ value.EmitAssign (ec, new_expr, false, false);
+ } else if (new_expr != value) {
+ value.EmitAssign (ec, new_expr, false, false);
+ }
}
+ block.Emit (ec);
+
// Restore context state.
ec.MarkLabel (ec.LoopEnd);
Switch target = (Switch) t;
target.Expr = Expr.Clone (clonectx);
- target.Sections = new List<SwitchSection> ();
- foreach (SwitchSection ss in Sections){
- target.Sections.Add (ss.Clone (clonectx));
- }
+ target.block = (ExplicitBlock) block.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
protected Statement stmt;
Label dispose_try_block;
bool prepared_for_dispose, emitted_dispose;
+ Method finally_host;
protected TryFinallyBlock (Statement stmt, Location loc)
: base (loc)
#endregion
protected abstract void EmitTryBody (EmitContext ec);
- protected abstract void EmitFinallyBody (EmitContext ec);
+ public abstract void EmitFinallyBody (EmitContext ec);
public override Label PrepareForDispose (EmitContext ec, Label end)
{
}
ec.MarkLabel (start_finally);
- EmitFinallyBody (ec);
+
+ if (finally_host != null) {
+ finally_host.Define ();
+ finally_host.Emit ();
+
+ // Now it's safe to add, to close it properly and emit sequence points
+ finally_host.Parent.AddMember (finally_host);
+
+ var ce = new CallEmitter ();
+ ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
+ ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
+ } else {
+ EmitFinallyBody (ec);
+ }
ec.EndExceptionBlock ();
}
bool emit_dispatcher = j < labels.Length;
if (emit_dispatcher) {
- //SymbolWriter.StartIteratorDispatcher (ec.ig);
ec.Emit (OpCodes.Ldloc, pc);
ec.EmitInt (first_resume_pc);
ec.Emit (OpCodes.Sub);
ec.Emit (OpCodes.Switch, labels);
- //SymbolWriter.EndIteratorDispatcher (ec.ig);
}
foreach (ResumableStatement s in resume_points)
ec.BeginFinallyBlock ();
- EmitFinallyBody (ec);
+ if (finally_host != null) {
+ var ce = new CallEmitter ();
+ ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
+ ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
+ } else {
+ EmitFinallyBody (ec);
+ }
ec.EndExceptionBlock ();
}
+
+ public override bool Resolve (BlockContext bc)
+ {
+ //
+ // Finally block inside iterator is called from MoveNext and
+ // Dispose methods that means we need to lift the block into
+ // newly created host method to emit the body only once. The
+ // original block then simply calls the newly generated method.
+ //
+ if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
+ var b = stmt as Block;
+ if (b != null && b.Explicit.HasYield) {
+ finally_host = bc.CurrentIterator.CreateFinallyHost (this);
+ }
+ }
+
+ return base.Resolve (bc);
+ }
}
//
locked = false;
}
- using (ec.Set (ResolveContext.Options.LockScope)) {
- ec.StartFlowBranching (this);
- Statement.Resolve (ec);
- ec.EndFlowBranching ();
- }
-
- if (lv != null) {
- lv.IsLockedByStatement = locked;
- }
-
- base.Resolve (ec);
-
//
// Have to keep original lock value around to unlock same location
- // in the case the original has changed or is null
+ // in the case of original value has changed or is null
//
expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
expr_copy.Resolve (ec);
lock_taken.Resolve (ec);
}
+ using (ec.Set (ResolveContext.Options.LockScope)) {
+ ec.StartFlowBranching (this);
+ Statement.Resolve (ec);
+ ec.EndFlowBranching ();
+ }
+
+ if (lv != null) {
+ lv.IsLockedByStatement = locked;
+ }
+
+ base.Resolve (ec);
+
return true;
}
Statement.Emit (ec);
}
- protected override void EmitFinallyBody (EmitContext ec)
+ public override void EmitFinallyBody (EmitContext ec)
{
//
// if (lock_taken) Monitor.Exit (expr_copy)
// fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
//
converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
- new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
- new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
+ new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
+ new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
new NullLiteral (loc),
converted, loc);
if (ok)
ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
+
using (ec.With (ResolveContext.Options.FinallyScope, true)) {
if (!fini.Resolve (ec))
ok = false;
stmt.Emit (ec);
}
- protected override void EmitFinallyBody (EmitContext ec)
+ public override void EmitFinallyBody (EmitContext ec)
{
fini.Emit (ec);
}
// Add conditional call when disposing possible null variable
if (!type.IsStruct || type.IsNullableType)
- dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
+ dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
return dispose;
}
{
for (int i = declarators.Count - 1; i >= 0; --i) {
var d = declarators [i];
- var vd = new VariableDeclaration (d.Variable, type_expr.Location);
+ var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
vd.Initializer = d.Initializer;
vd.IsNested = true;
vd.dispose_call = CreateDisposeCall (bc, d.Variable);
stmt.Emit (ec);
}
- protected override void EmitFinallyBody (EmitContext ec)
+ public override void EmitFinallyBody (EmitContext ec)
{
decl.EmitDispose (ec);
}
/// </summary>
public class Foreach : Statement
{
- sealed class ArrayForeach : Statement
+ abstract class IteratorStatement : Statement
{
- readonly Foreach for_each;
- readonly Statement statement;
+ protected readonly Foreach for_each;
+
+ protected IteratorStatement (Foreach @foreach)
+ {
+ this.for_each = @foreach;
+ this.loc = @foreach.expr.Location;
+ }
- Expression conv;
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ if (ec.EmitAccurateDebugInfo) {
+ ec.Emit (OpCodes.Nop);
+ }
+
+ base.Emit (ec);
+ }
+ }
+
+ sealed class ArrayForeach : IteratorStatement
+ {
TemporaryVariableReference[] lengths;
Expression [] length_exprs;
StatementExpression[] counter;
TemporaryVariableReference[] variables;
TemporaryVariableReference copy;
- Expression access;
- LocalVariableReference variable;
public ArrayForeach (Foreach @foreach, int rank)
+ : base (@foreach)
{
- for_each = @foreach;
- statement = for_each.statement;
- loc = @foreach.loc;
-
counter = new StatementExpression[rank];
variables = new TemporaryVariableReference[rank];
length_exprs = new Expression [rank];
lengths = new TemporaryVariableReference [rank];
}
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new NotImplementedException ();
- }
-
public override bool Resolve (BlockContext ec)
{
Block variables_block = for_each.variable.Block;
for (int i = 0; i < rank; i++) {
var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
variables[i] = v;
- counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
+ counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
counter[i].Resolve (ec);
if (rank == 1) {
list.Add (new Argument (v));
}
- access = new ElementAccess (copy, list, loc).Resolve (ec);
+ var access = new ElementAccess (copy, list, loc).Resolve (ec);
if (access == null)
return false;
var_type = access.Type;
} else {
var_type = for_each.type.ResolveAsType (ec);
+
+ if (var_type == null)
+ return false;
+
+ access = Convert.ExplicitConversion (ec, access, var_type, loc);
+ if (access == null)
+ return false;
}
- if (var_type == null)
- return false;
+ for_each.variable.Type = var_type;
- conv = Convert.ExplicitConversion (ec, access, var_type, loc);
- if (conv == null)
+ var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
+ if (variable_ref == null)
return false;
+ for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
+
bool ok = true;
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
ec.CurrentBranching.CreateSibling ();
- for_each.variable.Type = conv.Type;
- variable = new LocalVariableReference (for_each.variable, loc);
- variable.Resolve (ec);
-
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
- if (!statement.Resolve (ec))
+ if (!for_each.body.Resolve (ec))
ok = false;
ec.EndFlowBranching ();
ec.MarkLabel (loop [i]);
}
- variable.local_info.CreateBuilder (ec);
- variable.EmitAssign (ec, conv, false, false);
-
- statement.Emit (ec);
+ for_each.body.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
+ ec.Mark (for_each.expr.Location);
for (int i = rank - 1; i >= 0; i--){
counter [i].Emit (ec);
}
}
- sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
+ sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
{
- class Body : Statement
- {
- TypeSpec type;
- LocalVariableReference variable;
- Expression current, conv;
- Statement statement;
-
- public Body (TypeSpec type, LocalVariable variable,
- Expression current, Statement statement,
- Location loc)
- {
- this.type = type;
- this.variable = new LocalVariableReference (variable, loc);
- this.current = current;
- this.statement = statement;
- this.loc = loc;
- }
-
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new NotImplementedException ();
- }
-
- public override bool Resolve (BlockContext ec)
- {
- current = current.Resolve (ec);
- if (current == null)
- return false;
-
- conv = Convert.ExplicitConversion (ec, current, type, loc);
- if (conv == null)
- return false;
-
- variable.local_info.Type = conv.Type;
- variable.Resolve (ec);
-
- if (!statement.Resolve (ec))
- return false;
-
- return true;
- }
-
- protected override void DoEmit (EmitContext ec)
- {
- variable.local_info.CreateBuilder (ec);
- variable.EmitAssign (ec, conv, false, false);
-
- statement.Emit (ec);
- }
- }
-
class RuntimeDispose : Using.VariableDeclaration
{
public RuntimeDispose (LocalVariable lv, Location loc)
var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
dispose_variable.CreateReferenceExpression (bc, loc),
new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
- loc), new NullLiteral (loc), loc);
+ loc), new NullLiteral (loc));
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
LocalVariable variable;
Expression expr;
Statement statement;
- Expression var_type;
ExpressionStatement init;
TemporaryVariableReference enumerator_variable;
bool ambiguous_getenumerator_name;
- public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
+ public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
+ : base (@foreach)
{
- this.var_type = var_type;
this.variable = var;
this.expr = expr;
- statement = stmt;
- loc = l;
- }
-
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new NotImplementedException ();
}
void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
if (mg != null) {
mg.InstanceExpression = expr;
Arguments args = new Arguments (0);
- mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
+ mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
// For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
if (ambiguous_getenumerator_name)
if (!gen_ienumerable.Define ())
gen_ienumerable = null;
- do {
- var ifaces = t.Interfaces;
- if (ifaces != null) {
- foreach (var iface in ifaces) {
- if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
- if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
- rc.Report.SymbolRelatedToPreviousError (expr.Type);
- rc.Report.Error (1640, loc,
- "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
- expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
-
- return null;
- }
-
- // TODO: Cache this somehow
- iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
- MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
-
- continue;
+ var ifaces = t.Interfaces;
+ if (ifaces != null) {
+ foreach (var iface in ifaces) {
+ if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
+ if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
+ rc.Report.SymbolRelatedToPreviousError (expr.Type);
+ rc.Report.Error (1640, loc,
+ "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
+ expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
+
+ return null;
}
- if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
- iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
- }
- }
- }
+ // TODO: Cache this somehow
+ iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
+ MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
- if (t.IsGenericParameter)
- t = t.BaseType;
- else
- t = null;
+ continue;
+ }
- } while (t != null);
+ if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
+ iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
+ }
+ }
+ }
if (iface_candidate == null) {
if (expr.Type != InternalType.ErrorType) {
if (current_pe == null)
return false;
- VarExpr ve = var_type as VarExpr;
+ VarExpr ve = for_each.type as VarExpr;
if (ve != null) {
if (is_dynamic) {
current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
}
- variable.Type = var_type.ResolveAsType (ec);
+ variable.Type = for_each.type.ResolveAsType (ec);
+
+ if (variable.Type == null)
+ return false;
+
+ current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
+ if (current_pe == null)
+ return false;
}
- if (variable.Type == null)
+ var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
+ if (variable_ref == null)
return false;
+ for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
+
var init = new Invocation (get_enumerator_mg, null);
statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
- new Body (variable.Type, variable, current_pe, statement, variable.Location), Location.Null);
+ for_each.body, Location.Null);
var enum_type = enumerator_variable.Type;
LocalVariable variable;
Expression expr;
Statement statement;
+ Block body;
- public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
+ public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
{
this.type = type;
this.variable = var;
this.expr = expr;
- statement = stmt;
+ this.statement = stmt;
+ this.body = body;
loc = l;
}
get { return variable; }
}
-
public override bool Resolve (BlockContext ec)
{
expr = expr.Resolve (ec);
return false;
}
+ body.AddStatement (statement);
+
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
statement = new ArrayForeach (this, 1);
} else if (expr.Type is ArrayContainer) {
return false;
}
- statement = new CollectionForeach (type, variable, expr, statement, loc);
+ statement = new CollectionForeach (this, variable, expr);
}
return statement.Resolve (ec);
protected override void DoEmit (EmitContext ec)
{
+ variable.CreateBuilder (ec);
+
Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
ec.LoopBegin = ec.DefineLabel ();
ec.LoopEnd = ec.DefineLabel ();
target.type = type.Clone (clonectx);
target.expr = expr.Clone (clonectx);
+ target.body = (Block) body.Clone (clonectx);
target.statement = statement.Clone (clonectx);
}