//
// 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);
}
// C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
// same name exists or as a keyword when no type was found
//
- if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
+ if (vexpr != null && !vexpr.IsPossibleType (bc)) {
if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
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 (!s.Resolve (bc)) {
ok = false;
- if (!bc.IsInProbingMode)
- statements [ix] = new EmptyStatement (s.loc);
-
+ statements [ix] = new EmptyStatement (s.loc);
continue;
}
}
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);
}
if (parent_storey_block.AnonymousMethodStorey == null) {
- pb.StateMachine.AddCapturedThisField (ec, null);
- b.HasCapturedThis = true;
+ if (pb.StateMachine.HoistedThis == null) {
+ pb.StateMachine.AddCapturedThisField (ec, null);
+ b.HasCapturedThis = true;
+ }
+
continue;
}
- pb.StateMachine.AddParentStoreyReference (ec, storey);
+ 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 && parent_this_block.StateMachine.HoistedThis != null) {
+ break;
+ }
+ }
+
+ //
+ // Add reference to closest storey which holds captured this
+ //
+ pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
}
//
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);
if (expr == null)
return false;
- if (!TypeSpec.IsReferenceType (expr.Type)) {
+ if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
ec.Report.Error (185, loc,
"`{0}' is not a reference type as required by the lock statement",
expr.Type.GetSignatureForError ());
}
}
- 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;
return null;
}
- //
- // The rules for the possible declarators are pretty wise,
- // but the production on the grammar is more concise.
- //
- // So we have to enforce these rules here.
- //
- // We do not resolve before doing the case 1 test,
- // because the grammar is explicit in that the token &
- // is present, so we need to test for this particular case.
- //
-
- if (initializer is Cast) {
- bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
- return null;
- }
-
- initializer = initializer.Resolve (bc);
-
- if (initializer == null)
+ var res = initializer.Resolve (bc);
+ if (res == null)
return null;
//
// Case 1: Array
//
- if (initializer.Type.IsArray) {
- TypeSpec array_type = TypeManager.GetElementType (initializer.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.
//
- ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
+ ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
if (converted == null)
// 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)),
- new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, 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);
//
// Case 2: string
//
- if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
- return new StringEmitter (initializer, li).Resolve (bc);
+ if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
+ return new StringEmitter (res, li).Resolve (bc);
}
// Case 3: fixed buffer
- if (initializer is FixedBufferPtr) {
- return new ExpressionEmitter (initializer, li);
+ if (res is FixedBufferPtr) {
+ return new ExpressionEmitter (res, li);
}
+ bool already_fixed = true;
+
//
// Case 4: & object.
//
- bool already_fixed = true;
- Unary u = initializer as Unary;
- if (u != null && u.Oper == Unary.Operator.AddressOf) {
- IVariableReference vr = u.Expr as IVariableReference;
- if (vr == null || !vr.IsFixed) {
- already_fixed = false;
+ Unary u = res as Unary;
+ if (u != null) {
+ if (u.Oper == Unary.Operator.AddressOf) {
+ IVariableReference vr = u.Expr as IVariableReference;
+ if (vr == null || !vr.IsFixed) {
+ already_fixed = false;
+ }
}
+ } else if (initializer is Cast) {
+ bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
+ return null;
}
if (already_fixed) {
bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
}
- initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
- return new ExpressionEmitter (initializer, li);
+ res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
+ return new ExpressionEmitter (res, li);
}
}
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 ();
+ }
+ }
}