// 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;
}
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;
}
}
return true;
}
- // TODO: Better error message
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",
}
}
} 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);
async_return.EmitAssign (ec);
ec.EmitEpilogue ();
-
- ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
}
+ ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
return;
}
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)
{
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;
}
#endif
// int assignable_slots;
- bool unreachable_shown;
- bool unreachable;
-
+
public Block (Block parent, Location start, Location end)
: this (parent, 0, start, end)
{
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)) {
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;
}
//
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");
+ }
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;
}
if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
return ec.CurrentAnonymousMethod.Storey;
- //
- // When referencing a variable inside iterator where all
- // variables are already captured and we don't need to create
- // another storey context
- //
- if (ParametersBlock.am_storey is StateMachine) {
- return ParametersBlock.am_storey;
- }
-
if (am_storey == null) {
MemberBase mc = ec.MemberContext as MemberBase;
public override void Emit (EmitContext ec)
{
- // TODO: The is check should go once state machine is fully separated
- if (am_storey != null && !(am_storey is StateMachine)) {
+ if (am_storey != null) {
DefineStoreyContainer (ec, am_storey);
am_storey.EmitStoreyInstantiation (ec, this);
}
protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
{
if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
- //
- // Creates parent storey reference when access to hoisted this is required
- //
- var block = storey.OriginalSourceBlock;
- if (block.HasCapturedThis && block.Parent != null) {
- var parent = block.Parent.Explicit;
-
- //
- // Hoisted this variable lives in top-level storey only
- //
- while (parent.AnonymousMethodStorey == null || parent.AnonymousMethodStorey.Parent is AnonymousMethodStorey)
- parent = parent.Parent.Explicit;
-
- storey.AddParentStoreyReference (ec, parent.AnonymousMethodStorey);
- }
-
storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
}
storey.CreateContainer ();
storey.DefineContainer ();
+ if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
+
+ //
+ // Only first storey in path will hold this reference. All children blocks will
+ // reference it indirectly using $ref field
+ //
+ 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;
+ }
+ }
+
+ if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
+ storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
+ 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);
+
+ if (block_on_path == null)
+ continue;
+
+ 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;
+
+ //
+ // Stop propagation inside same top block
+ //
+ 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;
+ }
+ }
+ }
+ }
+
var ref_blocks = storey.ReferencesFromChildrenBlock;
if (ref_blocks != null) {
foreach (ExplicitBlock ref_block in ref_blocks) {
- for (ExplicitBlock b = ref_block.Explicit; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
+ for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
if (b.AnonymousMethodStorey != null) {
b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
// Overwrite original for comparison purposes when linking cross references
// between anonymous methods
//
- Original = source;
+ Original = source.Original;
}
#region Properties
public override void Emit (EmitContext ec)
{
- if (state_machine != null) {
+ if (state_machine != null && state_machine.OriginalSourceBlock != this) {
DefineStoreyContainer (ec, state_machine);
state_machine.EmitStoreyInstantiation (ec, this);
}
public void EmitEmbedded (EmitContext ec)
{
- if (state_machine != null) {
+ if (state_machine != null && state_machine.OriginalSourceBlock != this) {
DefineStoreyContainer (ec, state_machine);
state_machine.EmitStoreyInstantiation (ec, this);
}
var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
var stateMachine = new IteratorStorey (iterator);
- am_storey = stateMachine;
+ state_machine = stateMachine;
iterator.SetStateMachine (stateMachine);
var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
- am_storey = stateMachine;
+ state_machine = stateMachine;
initializer.SetStateMachine (stateMachine);
var b = this is ToplevelBlock ?
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)
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
//
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
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);
+ var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted);
if (ci > 0) {
- cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
+ cond = new Binary (Binary.Operator.LogicalOr, cond, e);
} else {
cond = e;
}
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, 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);
}
if (variable_ref == null)
return false;
- for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.variable.Location));
+ for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
bool ok = true;
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);
if (variable_ref == null)
return false;
- for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), variable.Location));
+ 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);
target.type = type.Clone (clonectx);
target.expr = expr.Clone (clonectx);
target.body = (Block) body.Clone (clonectx);
+ target.statement = statement.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)