//
// The return type is actually Task<T> type argument
//
- if (expr.Type == async_type) {
+ if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
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 ());
// Special case hoisted return value (happens in try/finally scenario)
//
if (ec.TryFinallyUnwind != null) {
- if (storey.HoistedReturnValue is VariableReference) {
- storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type);
- }
-
exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
+ // Goto to unreachable label
+ if (label == null)
+ return true;
+
if (fc.AddReachedLabel (label))
return true;
if (try_finally != null) {
if (try_finally.FinallyBlock.HasReachableClosingBrace) {
- label.AddGotoReference (rc, false);
+ label.AddGotoReference (rc);
} else {
- label.AddGotoReference (rc, true);
+ label = null;
}
} else {
- label.AddGotoReference (rc, false);
+ label.AddGotoReference (rc);
}
return Reachability.CreateUnreachable ();
protected override void DoEmit (EmitContext ec)
{
+ // This should only happen for goto from try block to unrechable label
if (label == null)
- throw new InternalErrorException ("goto emitted before target resolved");
+ return;
Label l = label.LabelTarget (ec);
string name;
bool defined;
bool referenced;
- bool finalTarget;
Label label;
Block block;
{
LabelTarget (ec);
ec.MarkLabel (label);
-
- if (finalTarget)
- ec.Emit (OpCodes.Br_S, label);
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
return rc;
}
- public void AddGotoReference (Reachability rc, bool finalTarget)
+ public void AddGotoReference (Reachability rc)
{
if (referenced)
return;
referenced = true;
MarkReachable (rc);
- //
- // Label is final target when goto jumps out of try block with
- // finally clause. In that case we need leave with target but in C#
- // terms the label is unreachable. Using finalTarget we emit
- // explicit label not just marker
- //
- if (finalTarget) {
- this.finalTarget = true;
- return;
- }
-
block.ScanGotoJump (this);
}
get {
return (flags & Flags.FixedVariable) != 0;
}
+ set {
+ flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
+ }
}
bool INamedBlockVariable.IsParameter {
if (end_unreachable) {
bool after_goto_case = goto_flow_analysis && s is GotoCase;
- for (++startIndex; startIndex < statements.Count; ++startIndex) {
- s = statements[startIndex];
- if (s is SwitchLabel) {
- if (!after_goto_case)
- s.FlowAnalysis (fc);
+ var f = s as TryFinally;
+ if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
+ //
+ // Special case for try-finally with unreachable code after
+ // finally block. Try block has to include leave opcode but there is
+ // no label to leave to after unreachable finally block closing
+ // brace. This sentinel ensures there is always IL instruction to
+ // leave to even if we know it'll never be reached.
+ //
+ statements.Insert (startIndex + 1, new SentinelStatement ());
+ } else {
+ for (++startIndex; startIndex < statements.Count; ++startIndex) {
+ s = statements [startIndex];
+ if (s is SwitchLabel) {
+ if (!after_goto_case)
+ s.FlowAnalysis (fc);
- break;
- }
+ break;
+ }
- if (s.IsUnreachable) {
- s.FlowAnalysis (fc);
- statements [startIndex] = RewriteUnreachableStatement (s);
+ if (s.IsUnreachable) {
+ s.FlowAnalysis (fc);
+ statements [startIndex] = RewriteUnreachableStatement (s);
+ }
}
}
// L:
// v = 1;
- if (s is BlockVariable || s is EmptyStatement)
+ if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
return s;
return new EmptyStatement (s.loc);
public class ExplicitBlock : Block
{
protected AnonymousMethodStorey am_storey;
+ int debug_scope_index;
public ExplicitBlock (Block parent, Location start, Location end)
: this (parent, (Flags) 0, start, end)
public override void Emit (EmitContext ec)
{
- if (Parent != null)
- ec.BeginScope ();
+ if (Parent != null) {
+ // TODO: It's needed only when scope has variable (normal or lifted)
+ ec.BeginScope (GetDebugSymbolScopeIndex ());
+ }
EmitScopeInitialization (ec);
var parent_this_block = pb;
while (parent_this_block.Parent != null) {
parent_this_block = parent_this_block.Parent.ParametersBlock;
- if (parent_this_block.StateMachine != null) {
+ if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
break;
}
}
storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
}
+ public int GetDebugSymbolScopeIndex ()
+ {
+ if (debug_scope_index == 0)
+ debug_scope_index = ++ParametersBlock.debug_scope_index;
+
+ return debug_scope_index;
+ }
+
public void RegisterAsyncAwait ()
{
var block = this;
return new ParameterReference (parameter_info[index], loc);
}
- public Statement PerformClone ()
+ public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
{
+ undeclaredVariables = TopBlock.GetUndeclaredVariables ();
+
CloneContext clonectx = new CloneContext ();
return Clone (clonectx);
}
base.CheckControlExit (fc, dat);
}
+ public HashSet<LocalVariable> GetUndeclaredVariables ()
+ {
+ if (names == null)
+ return null;
+
+ HashSet<LocalVariable> variables = null;
+
+ foreach (var entry in names) {
+ var complex = entry.Value as List<INamedBlockVariable>;
+ if (complex != null) {
+ foreach (var centry in complex) {
+ if (IsUndeclaredVariable (centry)) {
+ if (variables == null)
+ variables = new HashSet<LocalVariable> ();
+
+ variables.Add ((LocalVariable) centry);
+ }
+ }
+ } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
+ if (variables == null)
+ variables = new HashSet<LocalVariable> ();
+
+ variables.Add ((LocalVariable)entry.Value);
+ }
+ }
+
+ return variables;
+ }
+
+ static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
+ {
+ var lv = namedBlockVariable as LocalVariable;
+ return lv != null && !lv.IsDeclared;
+ }
+
+ public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
+ {
+ if (names == null)
+ return;
+
+ foreach (var entry in names) {
+ var complex = entry.Value as List<INamedBlockVariable>;
+ if (complex != null) {
+ foreach (var centry in complex) {
+ var lv = centry as LocalVariable;
+ if (lv != null && undeclaredVariables.Contains (lv)) {
+ lv.Type = null;
+ }
+ }
+ } else {
+ var lv = entry.Value as LocalVariable;
+ if (lv != null && undeclaredVariables.Contains (lv))
+ lv.Type = null;
+ }
+ }
+ }
+
public override void Emit (EmitContext ec)
{
if (Report.Errors > 0)
}
}
- class ExpressionEmitter : Emitter {
- public ExpressionEmitter (Expression converted, LocalVariable li) :
- base (converted, li)
+ sealed class ExpressionEmitter : Emitter {
+ public ExpressionEmitter (Expression converted, LocalVariable li)
+ : base (converted, li)
{
}
LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
vi.Location);
pinned_string.Type = rc.BuiltinTypes.String;
+ vi.IsFixed = false;
eclass = ExprClass.Variable;
type = rc.BuiltinTypes.Int;
//
// Case 1: Array
//
- if (res.Type.IsArray) {
- TypeSpec array_type = TypeManager.GetElementType (res.Type);
+ var ac = res.Type as ArrayContainer;
+ if (ac != null) {
+ TypeSpec array_type = ac.Element;
//
// Provided that array_type is unmanaged,
if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
return null;
+ Expression res_init;
+ if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
+ res_init = res;
+ } else {
+ var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
+ res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
+ res = expr_variable.CreateReferenceExpression (bc, loc);
+ }
+
//
// and T* is implicitly convertible to the
// pointer type given in the fixed statement.
// 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, res, new NullLiteral (loc)),
+ new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
new NullLiteral (loc),
converted, loc);
for_each.variable.Type = var_type;
+ var prev_block = ec.CurrentBlock;
+ ec.CurrentBlock = variables_block;
var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
+ ec.CurrentBlock = prev_block;
+
if (variable_ref == null)
return false;
return false;
}
+ var prev_block = ec.CurrentBlock;
+ ec.CurrentBlock = for_each.variable.Block;
var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
+ ec.CurrentBlock = prev_block;
if (variable_ref == null)
return false;
ec.LoopEnd = ec.DefineLabel ();
if (!(Statement is Block))
- ec.BeginCompilerScope ();
+ ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
variable.CreateBuilder (ec);
return visitor.Visit (this);
}
}
+
+ class SentinelStatement: Statement
+ {
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ var l = ec.DefineLabel ();
+ ec.MarkLabel (l);
+ ec.Emit (OpCodes.Br_S, l);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotImplementedException ();
+ }
+ }
}