//
// 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 ());
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);
}
FixedVariable = 1 << 6,
UsingVariable = 1 << 7,
IsLocked = 1 << 8,
+ SymbolFileHidden = 1 << 9,
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
get {
return (flags & Flags.FixedVariable) != 0;
}
+ set {
+ flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
+ }
}
bool INamedBlockVariable.IsParameter {
// All fixed variabled are pinned, a slot has to be alocated
//
builder = ec.DeclareLocal (Type, IsFixed);
- if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
+ if ((flags & Flags.SymbolFileHidden) == 0)
ec.DefineLocalVariable (name, builder);
}
- public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
+ public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
{
LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
+ if (!writeToSymbolFile)
+ li.flags |= Flags.SymbolFileHidden;
+
li.Type = type;
return li;
}
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);
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;
}
}
this.loc = loc;
}
+ protected virtual void EmitBeginException (EmitContext ec)
+ {
+ ec.BeginExceptionBlock ();
+ }
+
protected virtual void EmitTryBodyPrepare (EmitContext ec)
{
StateMachineInitializer state_machine = null;
ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
}
- ec.BeginExceptionBlock ();
+ EmitBeginException (ec);
if (resume_points != null) {
ec.MarkLabel (resume_point);
}
}
- 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);
return ok;
}
+ protected override void EmitBeginException (EmitContext ec)
+ {
+ if (fini.HasAwait && stmt is TryCatch)
+ ec.BeginExceptionBlock ();
+
+ base.EmitBeginException (ec);
+ }
+
protected override void EmitTryBody (EmitContext ec)
{
if (fini.HasAwait) {
ec.TryFinallyUnwind.Add (this);
stmt.Emit (ec);
+
+ if (stmt is TryCatch)
+ ec.EndExceptionBlock ();
+
ec.TryFinallyUnwind.Remove (this);
if (start_fin_label != null)
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 ();
+ }
+ }
}