//
using System;
-using System.Text;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
using System.Collections.Generic;
+#if STATIC
+using IKVM.Reflection.Emit;
+#else
+using System.Reflection.Emit;
+#endif
+
namespace Mono.CSharp {
public abstract class Statement {
this.initializer = initializer;
}
+ public Declarator (Declarator clone, Expression initializer)
+ {
+ this.li = clone.li;
+ this.initializer = initializer;
+ }
+
#region Properties
public LocalVariable Variable {
Expression initializer;
protected FullNamedExpression type_expr;
- LocalVariable li;
+ protected LocalVariable li;
protected List<Declarator> declarators;
public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
public override bool Resolve (BlockContext bc)
{
- TypeSpec type = null;
- if (type_expr is VarExpr) {
- //
- // 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
- //
- var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
- if (texpr == null) {
- if (RootContext.Version < LanguageVersion.V_3)
- bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
-
- if (li.IsFixed) {
- bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
- return false;
- }
+ if (li.Type == null) {
+ TypeSpec type = null;
+ if (type_expr is VarExpr) {
+ //
+ // 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
+ //
+ var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
+ if (texpr == null) {
+ if (RootContext.Version < LanguageVersion.V_3)
+ bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
+
+ if (li.IsFixed) {
+ bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
+ return false;
+ }
- if (li.IsConstant) {
- bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
- return false;
- }
+ if (li.IsConstant) {
+ bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
+ return false;
+ }
- if (Initializer == null) {
- bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
- return false;
- }
+ if (Initializer == null) {
+ bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
+ return false;
+ }
- if (declarators != null) {
- bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
- declarators = null;
- }
+ if (declarators != null) {
+ bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
+ declarators = null;
+ }
- Initializer = Initializer.Resolve (bc);
- if (Initializer != null) {
- ((VarExpr) type_expr).InferType (bc, Initializer);
- type = type_expr.Type;
+ Initializer = Initializer.Resolve (bc);
+ if (Initializer != null) {
+ ((VarExpr) type_expr).InferType (bc, Initializer);
+ type = type_expr.Type;
+ }
}
}
- }
- if (type == null) {
- var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
- if (texpr == null)
- return false;
+ if (type == null) {
+ var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
+ if (texpr == null)
+ return false;
- type = texpr.Type;
+ type = texpr.Type;
- if (li.IsConstant && !type.IsConstantCompatible) {
- Const.Error_InvalidConstantType (type, loc, bc.Report);
+ if (li.IsConstant && !type.IsConstantCompatible) {
+ Const.Error_InvalidConstantType (type, loc, bc.Report);
+ }
}
- }
- if (type.IsStatic)
- FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
+ if (type.IsStatic)
+ FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
- if (type.IsPointer && !bc.IsUnsafe)
- Expression.UnsafeError (bc, loc);
+ if (type.IsPointer && !bc.IsUnsafe)
+ Expression.UnsafeError (bc, loc);
- li.Type = type;
+ li.Type = type;
+ }
bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
if (eval_global) {
if (declarators != null) {
foreach (var d in declarators) {
- d.Variable.Type = type;
+ d.Variable.Type = li.Type;
if (eval_global) {
CreateEvaluatorVariable (bc, d.Variable);
} else {
{
BlockVariableDeclaration t = (BlockVariableDeclaration) target;
- t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
+ if (type_expr != null)
+ t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
+
if (initializer != null)
t.initializer = initializer.Clone (clonectx);
if (declarators != null) {
t.declarators = null;
foreach (var d in declarators)
- t.AddDeclarator (new Declarator (d.Variable, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
+ t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
}
}
}
FixedVariable = 1 << 6,
UsingVariable = 1 << 7,
// DefinitelyAssigned = 1 << 8,
+ IsLocked = 1 << 9,
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
#region Properties
+ public bool AddressTaken {
+ get { return (flags & Flags.AddressTaken) != 0; }
+ set { flags |= Flags.AddressTaken; }
+ }
+
public Block Block {
get {
return block;
}
}
+ public bool IsLocked {
+ get {
+ return (flags & Flags.IsLocked) != 0;
+ }
+ set {
+ flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
+ }
+ }
+
public bool IsThis {
get {
return (flags & Flags.IsThis) != 0;
public void Emit (EmitContext ec)
{
+ // TODO: Need something better for temporary variables
+ if ((flags & Flags.CompilerGenerated) != 0)
+ CreateBuilder (ec);
+
ec.Emit (OpCodes.Ldloc, builder);
}
ec.Emit (OpCodes.Ldloca, builder);
}
+ public string GetReadOnlyContext ()
+ {
+ switch (flags & Flags.ReadonlyMask) {
+ case Flags.FixedVariable:
+ return "fixed variable";
+ case Flags.ForeachVariable:
+ return "foreach iteration variable";
+ case Flags.UsingVariable:
+ return "using variable";
+ }
+
+ throw new InternalErrorException ("Variable is not readonly");
+ }
+
public bool IsThisAssigned (BlockContext ec, Block block)
{
if (VariableInfo == null)
flags |= Flags.Used;
}
- public bool AddressTaken {
- get { return (flags & Flags.AddressTaken) != 0; }
- set { flags |= Flags.AddressTaken; }
- }
-
public override string ToString ()
{
return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
}
-
- public string GetReadOnlyContext ()
- {
- switch (flags & Flags.ReadonlyMask) {
- case Flags.FixedVariable:
- return "fixed variable";
- case Flags.ForeachVariable:
- return "foreach iteration variable";
- case Flags.UsingVariable:
- return "using variable";
- }
-
- throw new InternalErrorException ("Variable is not readonly");
- }
}
/// <summary>
{
if (am_storey != null) {
DefineAnonymousStorey (ec);
- am_storey.EmitStoreyInstantiation (ec);
+ am_storey.EmitStoreyInstantiation (ec, this);
}
bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
readonly ParametersBlock block;
readonly int index;
public VariableInfo VariableInfo;
+ bool is_locked;
public ParameterInfo (ParametersBlock block, int index)
{
}
}
+ public bool IsLocked {
+ get {
+ return is_locked;
+ }
+ set {
+ is_locked = value;
+ }
+ }
+
public Location Location {
get {
return Parameter.Location;
Block b = block;
if (variable != null) {
do {
- if (variable.Block == b)
+ if (variable.Block == b.Original)
return true;
b = b.Parent;
for (int i = 0; i < list.Count; ++i) {
variable = list[i];
do {
- if (variable.Block == b)
+ if (variable.Block == b.Original)
return true;
b = b.Parent;
var label = value as LabeledStatement;
Block b = block;
if (label != null) {
- if (label.Block == b)
+ 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)
+ 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)
+ 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)
+ if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
return label;
}
}
public SwitchLabel Clone (CloneContext clonectx)
{
+ if (label == null)
+ return this;
+
return new SwitchLabel (label.Clone (clonectx), loc);
}
}
{
var cloned_labels = new List<SwitchLabel> ();
- foreach (SwitchLabel sl in cloned_labels)
+ foreach (SwitchLabel sl in Labels)
cloned_labels.Add (sl.Clone (clonectx));
return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
public class Lock : ExceptionStatement {
Expression expr;
- TemporaryVariableReference temp;
+ TemporaryVariableReference expr_copy;
+ TemporaryVariableReference lock_taken;
public Lock (Expression expr, Statement stmt, Location loc)
: base (stmt, loc)
if (!TypeManager.IsReferenceType (expr.Type)){
ec.Report.Error (185, loc,
- "`{0}' is not a reference type as required by the lock statement",
- TypeManager.CSharpName (expr.Type));
- return false;
+ "`{0}' is not a reference type as required by the lock statement",
+ expr.Type.GetSignatureForError ());
+ }
+
+ if (expr.Type.IsGenericParameter) {
+ expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
+ }
+
+ VariableReference lv = expr as VariableReference;
+ bool locked;
+ if (lv != null) {
+ locked = lv.IsLockedByStatement;
+ lv.IsLockedByStatement = true;
+ } else {
+ lv = null;
+ locked = false;
}
ec.StartFlowBranching (this);
- bool ok = Statement.Resolve (ec);
+ Statement.Resolve (ec);
ec.EndFlowBranching ();
- ok &= base.Resolve (ec);
+ if (lv != null) {
+ lv.IsLockedByStatement = locked;
+ }
- temp = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
- temp.Resolve (ec);
+ base.Resolve (ec);
- if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
- TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
- TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
- monitor_type, "Enter", loc, TypeManager.object_type);
- TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
- monitor_type, "Exit", loc, TypeManager.object_type);
+ //
+ // Have to keep original lock value around to unlock same location
+ // in the case the original has changed or is null
+ //
+ expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
+ expr_copy.Resolve (ec);
+
+ //
+ // Ensure Monitor methods are available
+ //
+ if (ResolvePredefinedMethods (ec) > 1) {
+ lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
+ lock_taken.Resolve (ec);
}
-
- return ok;
+
+ return true;
}
protected override void EmitPreTryBody (EmitContext ec)
{
- temp.EmitAssign (ec, expr);
- temp.Emit (ec);
- ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+ expr_copy.EmitAssign (ec, expr);
+
+ if (lock_taken != null) {
+ //
+ // Initialize ref variable
+ //
+ lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
+ } else {
+ //
+ // Monitor.Enter (expr_copy)
+ //
+ expr_copy.Emit (ec);
+ ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+ }
}
protected override void EmitTryBody (EmitContext ec)
{
+ //
+ // Monitor.Enter (expr_copy, ref lock_taken)
+ //
+ if (lock_taken != null) {
+ expr_copy.Emit (ec);
+ lock_taken.LocalInfo.CreateBuilder (ec);
+ lock_taken.AddressOf (ec, AddressOp.Load);
+ ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+ }
+
Statement.Emit (ec);
}
protected override void EmitFinallyBody (EmitContext ec)
{
- temp.Emit (ec);
+ //
+ // if (lock_taken) Monitor.Exit (expr_copy)
+ //
+ Label skip = ec.DefineLabel ();
+
+ if (lock_taken != null) {
+ lock_taken.Emit (ec);
+ ec.Emit (OpCodes.Brfalse_S, skip);
+ }
+
+ expr_copy.Emit (ec);
ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
+ ec.MarkLabel (skip);
+ }
+
+ int ResolvePredefinedMethods (ResolveContext rc)
+ {
+ if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
+ TypeSpec monitor_type = rc.Module.PredefinedTypes.Monitor.Resolve (loc);
+
+ if (monitor_type == null)
+ return 0;
+
+ // Try 4.0 Monitor.Enter (object, ref bool) overload first
+ var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
+ new[] {
+ new ParameterData (null, Parameter.Modifier.NONE),
+ new ParameterData (null, Parameter.Modifier.REF)
+ },
+ new[] {
+ TypeManager.object_type,
+ TypeManager.bool_type
+ }, false), null);
+
+ TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
+ if (TypeManager.void_monitor_enter_object == null) {
+ TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
+ monitor_type, "Enter", loc, TypeManager.object_type);
+ }
+
+ TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
+ monitor_type, "Exit", loc, TypeManager.object_type);
+ }
+
+ return TypeManager.void_monitor_enter_object.Parameters.Count;
}
protected override void CloneTo (CloneContext clonectx, Statement t)
pinned_string.Type = TypeManager.string_type;
if (TypeManager.int_get_offset_to_string_data == null) {
- TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
- TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
+ var helper = rc.Module.PredefinedTypes.RuntimeHelpers.Resolve (loc);
+ if (helper != null) {
+ TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (helper,
+ "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
+ }
}
eclass = ExprClass.Variable;
}
if (General != null) {
- if (CodeGen.Assembly.WrapNonExceptionThrows) {
- foreach (Catch c in Specific){
- if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
- ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
- }
- }
+ foreach (Catch c in Specific) {
+ if (c.CatchType != TypeManager.exception_type)
+ continue;
+
+ if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
+ continue;
+
+ if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
+ continue;
+
+ ec.Report.Warning (1058, 1, c.loc,
+ "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
}
ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
}
}
- // FIXME: Why is it almost exact copy of Using ??
- public class UsingTemporary : ExceptionStatement
+ public class Using : ExceptionStatement
{
- protected TemporaryVariableReference local_copy;
- Expression expr;
- protected Statement dispose_call;
-
- public UsingTemporary (Expression expr, Statement stmt, Location loc)
- : base (stmt, loc)
+ public class VariableDeclaration : BlockVariableDeclaration
{
- this.expr = expr;
- }
+ Statement dispose_call;
- #region Properties
- public Expression Expression {
- get {
- return expr;
+ public VariableDeclaration (FullNamedExpression type, LocalVariable li)
+ : base (type, li)
+ {
}
- }
- #endregion
-
- protected virtual bool DoResolve (BlockContext ec)
- {
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
-
- if (expr.Type != TypeManager.idisposable_type && !expr.Type.ImplementsInterface (TypeManager.idisposable_type, false)) {
- if (TypeManager.IsNullableType (expr.Type)) {
- // it's handled it a custom code bellow
- } else if (expr.Type == InternalType.Dynamic) {
- expr = Convert.ImplicitConversionStandard (ec, expr, TypeManager.idisposable_type, loc);
- } else {
- Using.Error_IsNotConvertibleToIDisposable (ec, expr.Type, expr.Location);
- return false;
- }
+ public VariableDeclaration (LocalVariable li, Location loc)
+ : base (li)
+ {
+ this.loc = loc;
}
- var expr_type = expr.Type;
-
- local_copy = TemporaryVariableReference.Create (expr_type, ec.CurrentBlock.Parent, loc);
- local_copy.Resolve (ec);
-
- if (TypeManager.void_dispose_void == null) {
- TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
- TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+ public VariableDeclaration (Expression expr)
+ : base (null)
+ {
+ loc = expr.Location;
+ Initializer = expr;
}
- var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
- dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
- new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
- local_copy;
-
- dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
-
- // Add conditional call when disposing possible null variable
- if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
- dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
-
- return dispose_call.Resolve (ec);
- }
-
- public override bool Resolve (BlockContext ec)
- {
- bool ok = DoResolve (ec);
-
- ec.StartFlowBranching (this);
-
- ok &= stmt.Resolve (ec);
-
- ec.EndFlowBranching ();
-
- ok &= base.Resolve (ec);
-
- return ok;
- }
-
- protected override void EmitPreTryBody (EmitContext ec)
- {
- //local_copy.Variable.
- local_copy.EmitAssign (ec, expr);
- }
-
- protected override void EmitTryBody (EmitContext ec)
- {
- stmt.Emit (ec);
- }
-
- protected override void EmitFinallyBody (EmitContext ec)
- {
- dispose_call.Emit (ec);
- }
-
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- UsingTemporary target = (UsingTemporary) t;
+ #region Properties
- target.expr = expr.Clone (clonectx);
- target.stmt = stmt.Clone (clonectx);
- }
- }
+ public bool IsNested { get; private set; }
- public class Using : ExceptionStatement
- {
- public class VariableDeclaration : BlockVariableDeclaration
- {
- public VariableDeclaration (FullNamedExpression type, LocalVariable li)
- : base (type, li)
- {
- }
+ #endregion
- private VariableDeclaration (LocalVariable li, Location loc)
- : base (li)
+ public void EmitDispose (EmitContext ec)
{
- this.loc = loc;
+ dispose_call.Emit (ec);
}
public override bool Resolve (BlockContext bc)
{
- if (type_expr == null)
+ if (IsNested)
return true;
return base.Resolve (bc);
}
- protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
+ public Expression ResolveExpression (BlockContext bc)
{
- Assign assign;
+ var e = Initializer.Resolve (bc);
+ if (e == null)
+ return null;
+
+ li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
+ Initializer = ResolveInitializer (bc, Variable, e);
+ return e;
+ }
+ protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
+ {
if (li.Type == InternalType.Dynamic) {
initializer = initializer.Resolve (bc);
if (initializer == null)
return null;
- initializer = Convert.ImplicitConversionRequired (bc, initializer, TypeManager.idisposable_type, loc);
+ // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
+ Arguments args = new Arguments (1);
+ args.Add (new Argument (initializer));
+ initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
if (initializer == null)
return null;
- var var = TemporaryVariableReference.Create (TypeManager.idisposable_type, bc.CurrentBlock, loc);
- assign = new SimpleAssign (var, initializer, loc);
- assign.ResolveStatement (bc);
- return assign;
+ var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
+ dispose_call = CreateDisposeCall (bc, var);
+ dispose_call.Resolve (bc);
+
+ return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
}
if (li == Variable) {
- if (li.Type != TypeManager.idisposable_type && !li.Type.ImplementsInterface (TypeManager.idisposable_type, false)) {
- Using.Error_IsNotConvertibleToIDisposable (bc, li.Type, type_expr.Location);
- return null;
- }
+ CheckIDiposableConversion (bc, li, initializer);
+ dispose_call = CreateDisposeCall (bc, li);
+ dispose_call.Resolve (bc);
}
return base.ResolveInitializer (bc, li, initializer);
}
+ protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
+ {
+ var type = li.Type;
+
+ if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
+ if (TypeManager.IsNullableType (type)) {
+ // it's handled in CreateDisposeCall
+ return;
+ }
+
+ bc.Report.SymbolRelatedToPreviousError (type);
+ var loc = type_expr == null ? initializer.Location : type_expr.Location;
+ bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
+ type.GetSignatureForError ());
+
+ return;
+ }
+ }
+
+ protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
+ {
+ var lvr = lv.CreateReferenceExpression (bc, lv.Location);
+ var type = lv.Type;
+ var loc = lv.Location;
+
+ if (TypeManager.void_dispose_void == null) {
+ TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
+ TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+ }
+
+ var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
+ dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
+ new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
+ lvr;
+
+ Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
+
+ // Add conditional call when disposing possible null variable
+ if (!type.IsStruct || TypeManager.IsNullableType (type))
+ dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
+
+ return dispose;
+ }
+
public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
{
for (int i = declarators.Count - 1; i >= 0; --i) {
var d = declarators [i];
var vd = new VariableDeclaration (d.Variable, type_expr.Location);
vd.Initializer = d.Initializer;
+ vd.IsNested = true;
+ vd.dispose_call = CreateDisposeCall (bc, d.Variable);
+ vd.dispose_call.Resolve (bc);
+
stmt = new Using (vd, stmt, d.Variable.Location);
}
this.decl = decl;
}
+ public Using (Expression expr, Statement stmt, Location loc)
+ : base (stmt, loc)
+ {
+ this.decl = new VariableDeclaration (expr);
+ }
+
#region Properties
+ public Expression Expression {
+ get {
+ return decl.Variable == null ? decl.Initializer : null;
+ }
+ }
+
public BlockVariableDeclaration Variables {
get {
return decl;
#endregion
- static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, TypeSpec type, Location loc)
- {
- ec.Report.SymbolRelatedToPreviousError (type);
- ec.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
- type.GetSignatureForError ());
- }
-
protected override void EmitPreTryBody (EmitContext ec)
{
decl.Emit (ec);
protected override void EmitFinallyBody (EmitContext ec)
{
- Label skip = ec.DefineLabel ();
-
- var var = ((Assign) decl.Initializer).Target;
- bool emit_null_check = !TypeManager.IsValueType (var.Type);
- if (emit_null_check) {
- var.Emit (ec);
- ec.Emit (OpCodes.Brfalse, skip);
- }
-
- Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
-
- if (emit_null_check)
- ec.MarkLabel (skip);
+ decl.EmitDispose (ec);
}
public override bool Resolve (BlockContext ec)
{
+ VariableReference vr;
+ bool vr_locked = false;
+
using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
- if (!decl.Resolve (ec))
- return false;
+ if (decl.Variable == null) {
+ vr = decl.ResolveExpression (ec) as VariableReference;
+ if (vr != null) {
+ vr_locked = vr.IsLockedByStatement;
+ vr.IsLockedByStatement = true;
+ }
+ } else {
+ if (!decl.Resolve (ec))
+ return false;
+
+ if (decl.Declarators != null) {
+ stmt = decl.RewriteForDeclarators (ec, stmt);
+ }
- if (decl.Declarators != null) {
- stmt = decl.RewriteForDeclarators (ec, stmt);
+ vr = null;
}
}
ec.StartFlowBranching (this);
- bool ok = stmt.Resolve (ec);
+ stmt.Resolve (ec);
ec.EndFlowBranching ();
- ok &= base.Resolve (ec);
+ if (vr != null)
+ vr.IsLockedByStatement = vr_locked;
- if (TypeManager.void_dispose_void == null) {
- TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
- TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
- }
+ base.Resolve (ec);
- return ok;
+ return true;
}
protected override void CloneTo (CloneContext clonectx, Statement t)
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
ec.CurrentBranching.CreateSibling ();
- // TODO: dynamic
variable.local_info.Type = conv.Type;
variable.Resolve (ec);
}
}
- class Dispose : UsingTemporary
+ class RuntimeDispose : Using.VariableDeclaration
{
- LocalTemporary dispose;
+ public RuntimeDispose (LocalVariable lv, Location loc)
+ : base (lv, loc)
+ {
+ }
- public Dispose (TemporaryVariableReference variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
- : base (expr, statement, loc)
+ protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
{
- base.local_copy = variable;
- this.dispose = dispose;
+ // Defered to runtime check
}
- protected override bool DoResolve (BlockContext ec)
+ protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
{
if (TypeManager.void_dispose_void == null) {
TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
}
- Expression dispose_var = (Expression) dispose ?? local_copy;
-
- var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
- dispose_mg.InstanceExpression = dispose_var;
-
- dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
-
- if (!dispose_var.Type.IsStruct)
- dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
+ //
+ // Fabricates code like
+ //
+ // if ((temp = vr as IDisposable) != null) temp.Dispose ();
+ //
- return dispose_call.Resolve (ec);
- }
+ var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
- protected override void EmitFinallyBody (EmitContext ec)
- {
- Label call_dispose = ec.DefineLabel ();
- if (dispose != null) {
- local_copy.Emit (ec, false);
- ec.Emit (OpCodes.Isinst, dispose.Type);
- dispose.Store (ec);
- }
+ 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);
- base.EmitFinallyBody (ec);
+ var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
+ dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
- if (dispose != null) {
- ec.MarkLabel (call_dispose);
- dispose.Release (ec);
- }
+ Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
+ return new If (idisaposable_test, dispose, loc);
}
}
// Option 2: Try to match using IEnumerable interfaces with preference of generic version
//
TypeSpec iface_candidate = null;
- for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
+ var t = expr.Type;
+ do {
var ifaces = t.Interfaces;
if (ifaces != null) {
foreach (var iface in ifaces) {
}
}
}
- }
+
+ if (t.IsGenericParameter)
+ t = t.BaseType;
+ else
+ t = null;
+
+ } while (t != null);
if (iface_candidate == null) {
rc.Report.Error (1579, loc,
public override bool Resolve (BlockContext ec)
{
bool is_dynamic = expr.Type == InternalType.Dynamic;
- if (is_dynamic)
+
+ if (is_dynamic) {
expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
+ } else if (TypeManager.IsNullableType (expr.Type)) {
+ expr = new Nullable.UnwrapCall (expr).Resolve (ec);
+ }
var get_enumerator_mg = ResolveGetEnumerator (ec);
if (get_enumerator_mg == null) {
variable.Type = var_type.Type;
var init = new Invocation (get_enumerator_mg, null);
- init.Resolve (ec);
statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
new Body (var_type.Type, variable, current_pe, statement, loc), loc);
//
// Runtime Dispose check
//
- var tv = new LocalTemporary (TypeManager.idisposable_type);
- statement = new Dispose (enumerator_variable, tv, init, statement, loc);
+ var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
+ vd.Initializer = init;
+ statement = new Using (vd, statement, loc);
} else {
//
// No Dispose call needed
//
// Static Dispose check
//
- statement = new Dispose (enumerator_variable, null, init, statement, loc);
+ var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
+ vd.Initializer = init;
+ statement = new Using (vd, statement, loc);
}
return statement.Resolve (ec);