//
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 {
if (Expr == null) {
if (ec.ReturnType == TypeManager.void_type)
return true;
-
- ec.Report.Error (126, loc,
- "An object of a type convertible to `{0}' is required for the return statement",
- TypeManager.CSharpName (ec.ReturnType));
+
+ if (ec.CurrentIterator != null) {
+ Error_ReturnFromIterator (ec);
+ } else {
+ ec.Report.Error (126, loc,
+ "An object of a type convertible to `{0}' is required for the return statement",
+ ec.ReturnType.GetSignatureForError ());
+ }
+
return false;
}
- if (ec.CurrentBlock.Toplevel.IsIterator) {
- ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
- "statement to return a value, or yield break to end the iteration");
- }
+ Expr = Expr.Resolve (ec);
AnonymousExpression am = ec.CurrentAnonymousMethod;
- if (am == null && ec.ReturnType == TypeManager.void_type) {
- ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
- ec.GetSignatureForError ());
+ if (am == null) {
+ if (ec.ReturnType == TypeManager.void_type) {
+ ec.Report.Error (127, loc,
+ "`{0}': A return keyword must not be followed by any expression when method returns void",
+ ec.GetSignatureForError ());
+ }
+ } else {
+ if (am.IsIterator) {
+ Error_ReturnFromIterator (ec);
+ return false;
+ }
+
+ var l = am as AnonymousMethodBody;
+ if (l != null && l.ReturnTypeInference != null && Expr != null) {
+ l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
+ return true;
+ }
}
- Expr = Expr.Resolve (ec);
if (Expr == null)
return false;
- if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
- ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
- return true;
- }
-
if (Expr.Type != ec.ReturnType) {
Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
ec.Emit (OpCodes.Ret);
}
+ void Error_ReturnFromIterator (ResolveContext rc)
+ {
+ rc.Report.Error (1622, loc,
+ "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Return target = (Return) t;
bool defined;
bool referenced;
Label label;
+ Block block;
FlowBranching.UsageVector vectors;
- public LabeledStatement (string name, Location l)
+ public LabeledStatement (string name, Block block, Location l)
{
this.name = name;
+ this.block = block;
this.loc = l;
}
return label;
}
+ public Block Block {
+ get {
+ return block;
+ }
+ }
+
public string Name {
get { return name; }
}
protected override void DoEmit (EmitContext ec)
{
+ if (!HasBeenReferenced)
+ ec.Report.Warning (164, 2, loc, "This label has not been referenced");
+
LabelTarget (ec);
ec.MarkLabel (label);
}
void EmitAddressOf (EmitContext ec);
}
- public interface IKnownVariable {
+ public interface INamedBlockVariable
+ {
Block Block { get; }
+ Expression CreateReferenceExpression (ResolveContext rc, Location loc);
+ bool IsDeclared { get; }
Location Location { get; }
}
- //
- // The information about a user-perceived local variable
- //
- public class LocalInfo : IKnownVariable, ILocalVariable {
- public readonly FullNamedExpression Type;
+ public class BlockVariableDeclaration : Statement
+ {
+ public class Declarator
+ {
+ LocalVariable li;
+ Expression initializer;
- public TypeSpec VariableType;
- public readonly string Name;
- public readonly Location Location;
- public readonly Block Block;
+ public Declarator (LocalVariable li, Expression initializer)
+ {
+ if (li.Type != null)
+ throw new ArgumentException ("Expected null variable type");
- public VariableInfo VariableInfo;
- HoistedVariable hoisted_variant;
+ this.li = li;
+ this.initializer = initializer;
+ }
- [Flags]
- enum Flags : byte {
- Used = 1,
- ReadOnly = 2,
- Pinned = 4,
- IsThis = 8,
- AddressTaken = 32,
- CompilerGenerated = 64,
- IsConstant = 128
- }
+ public Declarator (Declarator clone, Expression initializer)
+ {
+ this.li = clone.li;
+ this.initializer = initializer;
+ }
+
+ #region Properties
+
+ public LocalVariable Variable {
+ get {
+ return li;
+ }
+ }
+
+ public Expression Initializer {
+ get {
+ return initializer;
+ }
+ set {
+ initializer = value;
+ }
+ }
- public enum ReadOnlyContext: byte {
- Using,
- Foreach,
- Fixed
+ #endregion
}
- Flags flags;
- ReadOnlyContext ro_context;
- LocalBuilder builder;
+ Expression initializer;
+ protected FullNamedExpression type_expr;
+ protected LocalVariable li;
+ protected List<Declarator> declarators;
- public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
+ public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
{
- Type = type;
- Name = name;
- Block = block;
- Location = l;
+ this.type_expr = type;
+ this.li = li;
+ this.loc = type_expr.Location;
}
- public LocalInfo (TypeContainer ds, Block block, Location l)
+ protected BlockVariableDeclaration (LocalVariable li)
{
- VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
- Block = block;
- Location = l;
+ this.li = li;
}
- public void ResolveVariable (EmitContext ec)
- {
- if (HoistedVariant != null)
- return;
+ #region Properties
- if (builder == null) {
- builder = ec.DeclareLocal (VariableType, Pinned);
+ public List<Declarator> Declarators {
+ get {
+ return declarators;
}
}
- public void Emit (EmitContext ec)
- {
- ec.Emit (OpCodes.Ldloc, builder);
+ public Expression Initializer {
+ get {
+ return initializer;
+ }
+ set {
+ initializer = value;
+ }
}
- public void EmitAssign (EmitContext ec)
- {
- ec.Emit (OpCodes.Stloc, builder);
+ public FullNamedExpression TypeExpression {
+ get {
+ return type_expr;
+ }
}
- public void EmitAddressOf (EmitContext ec)
- {
- ec.Emit (OpCodes.Ldloca, builder);
+ public LocalVariable Variable {
+ get {
+ return li;
+ }
}
- public void EmitSymbolInfo (EmitContext ec)
+ #endregion
+
+ public void AddDeclarator (Declarator decl)
{
- if (builder != null)
- ec.DefineLocalVariable (Name, builder);
- }
+ if (declarators == null)
+ declarators = new List<Declarator> ();
- //
- // Hoisted local variable variant
- //
- public HoistedVariable HoistedVariant {
- get {
- return hoisted_variant;
- }
- set {
- hoisted_variant = value;
- }
+ declarators.Add (decl);
}
- public bool IsThisAssigned (BlockContext ec, Block block)
+ void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
{
- if (VariableInfo == null)
- throw new Exception ();
+ var container = bc.CurrentMemberDefinition.Parent;
- if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
- return true;
+ Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
+ new MemberName (li.Name, li.Location), null);
- return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
+ container.AddField (f);
+ f.Define ();
+ Evaluator.QueueField (f);
+
+ li.HoistedVariant = new HoistedEvaluatorVariable (f);
+ li.SetIsUsed ();
}
- public bool IsAssigned (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- if (VariableInfo == null)
- throw new Exception ();
+ 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;
+ }
- return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
- }
+ if (li.IsConstant) {
+ bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
+ return false;
+ }
- public bool Resolve (ResolveContext ec)
- {
- if (VariableType != null)
- return true;
+ if (Initializer == null) {
+ bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
+ return false;
+ }
- TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
- if (texpr == null)
- return false;
-
- VariableType = texpr.Type;
+ if (declarators != null) {
+ bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
+ declarators = null;
+ }
- if (VariableType.IsStatic) {
- FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
- return false;
- }
+ Initializer = Initializer.Resolve (bc);
+ if (Initializer != null) {
+ ((VarExpr) type_expr).InferType (bc, Initializer);
+ type = type_expr.Type;
+ }
+ }
+ }
- if (VariableType.IsPointer && !ec.IsUnsafe)
- Expression.UnsafeError (ec, Location);
+ if (type == null) {
+ var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
+ if (texpr == null)
+ return false;
- return true;
- }
+ type = texpr.Type;
- public bool IsConstant {
- get { return (flags & Flags.IsConstant) != 0; }
- set { flags |= Flags.IsConstant; }
- }
+ if (li.IsConstant && !type.IsConstantCompatible) {
+ Const.Error_InvalidConstantType (type, loc, bc.Report);
+ }
+ }
- public bool AddressTaken {
- get { return (flags & Flags.AddressTaken) != 0; }
- set { flags |= Flags.AddressTaken; }
- }
+ if (type.IsStatic)
+ FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
- public bool CompilerGenerated {
- get { return (flags & Flags.CompilerGenerated) != 0; }
- set { flags |= Flags.CompilerGenerated; }
- }
+ if (type.IsPointer && !bc.IsUnsafe)
+ Expression.UnsafeError (bc, loc);
- public override string ToString ()
- {
- return String.Format ("LocalInfo ({0},{1},{2},{3})",
- Name, Type, VariableInfo, Location);
- }
+ li.Type = type;
+ }
- public bool Used {
- get { return (flags & Flags.Used) != 0; }
- set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
- }
+ bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
+ if (eval_global) {
+ CreateEvaluatorVariable (bc, li);
+ } else {
+ li.PrepareForFlowAnalysis (bc);
+ }
+
+ if (initializer != null) {
+ initializer = ResolveInitializer (bc, li, initializer);
+ // li.Variable.DefinitelyAssigned
+ }
+
+ if (declarators != null) {
+ foreach (var d in declarators) {
+ d.Variable.Type = li.Type;
+ if (eval_global) {
+ CreateEvaluatorVariable (bc, d.Variable);
+ } else {
+ d.Variable.PrepareForFlowAnalysis (bc);
+ }
+
+ if (d.Initializer != null) {
+ d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
+ // d.Variable.DefinitelyAssigned
+ }
+ }
+ }
- public bool ReadOnly {
- get { return (flags & Flags.ReadOnly) != 0; }
+ return true;
}
- public void SetReadOnlyContext (ReadOnlyContext context)
+ protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
{
- flags |= Flags.ReadOnly;
- ro_context = context;
+ var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
+ return a.ResolveStatement (bc);
}
- public string GetReadOnlyContext ()
+ protected override void DoEmit (EmitContext ec)
{
- if (!ReadOnly)
- throw new InternalErrorException ("Variable is not readonly");
+ if (li.IsConstant)
+ return;
- switch (ro_context) {
- case ReadOnlyContext.Fixed:
- return "fixed variable";
- case ReadOnlyContext.Foreach:
- return "foreach iteration variable";
- case ReadOnlyContext.Using:
- return "using variable";
+ li.CreateBuilder (ec);
+
+ if (Initializer != null)
+ ((ExpressionStatement) Initializer).EmitStatement (ec);
+
+ if (declarators != null) {
+ foreach (var d in declarators) {
+ d.Variable.CreateBuilder (ec);
+ if (d.Initializer != null)
+ ((ExpressionStatement) d.Initializer).EmitStatement (ec);
+ }
}
- throw new NotImplementedException ();
}
- //
- // Whether the variable is pinned, if Pinned the variable has been
- // allocated in a pinned slot with DeclareLocal.
- //
- public bool Pinned {
- get { return (flags & Flags.Pinned) != 0; }
- set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
- }
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ BlockVariableDeclaration t = (BlockVariableDeclaration) target;
- public bool IsThis {
- get { return (flags & Flags.IsThis) != 0; }
- set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
- }
+ if (type_expr != null)
+ t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
- Block IKnownVariable.Block {
- get { return Block; }
+ if (initializer != null)
+ t.initializer = initializer.Clone (clonectx);
+
+ if (declarators != null) {
+ t.declarators = null;
+ foreach (var d in declarators)
+ t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
+ }
}
+ }
- Location IKnownVariable.Location {
- get { return Location; }
+ class BlockConstantDeclaration : BlockVariableDeclaration
+ {
+ public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
+ : base (type, li)
+ {
}
- public LocalInfo Clone (CloneContext clonectx)
+ protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
{
- //
- // Variables in anonymous block are not resolved yet
- //
- if (VariableType == null)
- return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
+ initializer = initializer.Resolve (bc);
+ if (initializer == null)
+ return null;
- //
- // Variables in method block are resolved
- //
- LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
- li.VariableType = VariableType;
- return li;
- }
- }
+ var c = initializer as Constant;
+ if (c == null) {
+ initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
+ return null;
+ }
- /// <summary>
- /// Block represents a C# block.
- /// </summary>
- ///
- /// <remarks>
- /// This class is used in a number of places: either to represent
- /// explicit blocks that the programmer places or implicit blocks.
- ///
- /// Implicit blocks are used as labels or to introduce variable
- /// declarations.
- ///
- /// Top-level blocks derive from Block, and they are called ToplevelBlock
- /// they contain extra information that is not necessary on normal blocks.
- /// </remarks>
- public class Block : Statement {
- public Block Parent;
- public Location StartLocation;
- public Location EndLocation = Location.Null;
+ c = c.ConvertImplicitly (bc, li.Type);
+ if (c == null) {
+ if (TypeManager.IsReferenceType (li.Type))
+ initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
+ else
+ initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
- public ExplicitBlock Explicit;
- public ToplevelBlock Toplevel; // TODO: Use Explicit
+ return null;
+ }
+
+ li.ConstantValue = c;
+ return initializer;
+ }
+ }
+ //
+ // The information about a user-perceived local variable
+ //
+ public class LocalVariable : INamedBlockVariable, ILocalVariable
+ {
[Flags]
public enum Flags
{
- Unchecked = 1,
- BlockUsed = 2,
- VariablesInitialized = 4,
- HasRet = 8,
- Unsafe = 16,
- IsIterator = 32,
- HasCapturedVariable = 64,
- HasCapturedThis = 1 << 7,
- IsExpressionTree = 1 << 8
+ Used = 1,
+ IsThis = 1 << 1,
+ AddressTaken = 1 << 2,
+ CompilerGenerated = 1 << 3,
+ Constant = 1 << 4,
+ ForeachVariable = 1 << 5,
+ FixedVariable = 1 << 6,
+ UsingVariable = 1 << 7,
+// DefinitelyAssigned = 1 << 8,
+ IsLocked = 1 << 9,
+
+ ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
- protected Flags flags;
+ TypeSpec type;
+ readonly string name;
+ readonly Location loc;
+ readonly Block block;
+ Flags flags;
+ Constant const_value;
- public bool Unchecked {
- get { return (flags & Flags.Unchecked) != 0; }
- set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
+ public VariableInfo VariableInfo;
+ HoistedVariable hoisted_variant;
+
+ LocalBuilder builder;
+
+ public LocalVariable (Block block, string name, Location loc)
+ {
+ this.block = block;
+ this.name = name;
+ this.loc = loc;
}
- public bool Unsafe {
- get { return (flags & Flags.Unsafe) != 0; }
- set { flags |= Flags.Unsafe; }
+ public LocalVariable (Block block, string name, Flags flags, Location loc)
+ : this (block, name, loc)
+ {
+ this.flags = flags;
}
//
- // The statements in this block
+ // Used by variable declarators
//
- protected List<Statement> statements;
+ public LocalVariable (LocalVariable li, string name, Location loc)
+ : this (li.block, name, li.flags, loc)
+ {
+ }
- //
- // An array of Blocks. We keep track of children just
- // to generate the local variable declarations.
- //
- // Statements and child statements are handled through the
- // statements.
- //
- List<Block> children;
+ #region Properties
- //
- // Labels. (label, block) pairs.
- //
- protected Dictionary<string, LabeledStatement> labels;
+ public bool AddressTaken {
+ get { return (flags & Flags.AddressTaken) != 0; }
+ set { flags |= Flags.AddressTaken; }
+ }
- //
- // Keeps track of (name, type) pairs
- //
- Dictionary<string, LocalInfo> variables;
-
- //
- // Keeps track of constants
- Dictionary<string, Expression> constants;
-
- //
- // Temporary variables.
- //
- List<LocalInfo> temporary_variables;
-
- //
- // If this is a switch section, the enclosing switch block.
- //
- Block switch_block;
-
- protected List<Statement> scope_initializers;
-
- List<ToplevelBlock> anonymous_children;
-
- int? resolving_init_idx;
-
- protected static int id;
-
- int this_id;
-
- int assignable_slots;
- bool unreachable_shown;
- bool unreachable;
-
- public Block (Block parent)
- : this (parent, (Flags) 0, Location.Null, Location.Null)
- { }
-
- public Block (Block parent, Flags flags)
- : this (parent, flags, Location.Null, Location.Null)
- { }
+ public Block Block {
+ get {
+ return block;
+ }
+ }
- public Block (Block parent, Location start, Location end)
- : this (parent, (Flags) 0, start, end)
- { }
+ public Constant ConstantValue {
+ get {
+ return const_value;
+ }
+ set {
+ const_value = value;
+ }
+ }
//
- // Useful when TopLevel block is downgraded to normal block
+ // Hoisted local variable variant
//
- public Block (ToplevelBlock parent, ToplevelBlock source)
- : this (parent, source.flags, source.StartLocation, source.EndLocation)
- {
- statements = source.statements;
- children = source.children;
- labels = source.labels;
- variables = source.variables;
- constants = source.constants;
- switch_block = source.switch_block;
+ public HoistedVariable HoistedVariant {
+ get {
+ return hoisted_variant;
+ }
+ set {
+ hoisted_variant = value;
+ }
}
- public Block (Block parent, Flags flags, Location start, Location end)
- {
- if (parent != null) {
- parent.AddChild (this);
-
- // the appropriate constructors will fixup these fields
- Toplevel = parent.Toplevel;
- Explicit = parent.Explicit;
+ public bool IsDeclared {
+ get {
+ return type != null;
}
-
- this.Parent = parent;
- this.flags = flags;
- this.StartLocation = start;
- this.EndLocation = end;
- this.loc = start;
- this_id = id++;
- statements = new List<Statement> (4);
}
- #region Properties
-
- public int ID {
- get { return this_id; }
+ public bool IsConstant {
+ get {
+ return (flags & Flags.Constant) != 0;
+ }
}
- public IDictionary<string, LocalInfo> Variables {
+ public bool IsLocked {
get {
- if (variables == null)
- variables = new Dictionary<string, LocalInfo> ();
- return variables;
+ return (flags & Flags.IsLocked) != 0;
+ }
+ set {
+ flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
}
}
- #endregion
-
- public Block CreateSwitchBlock (Location start)
- {
- // FIXME: should this be implicit?
- Block new_block = new ExplicitBlock (this, start, start);
- new_block.switch_block = this;
- return new_block;
+ public bool IsThis {
+ get {
+ return (flags & Flags.IsThis) != 0;
+ }
}
- void AddChild (Block b)
- {
- if (children == null)
- children = new List<Block> (1);
-
- children.Add (b);
+ public bool IsFixed {
+ get {
+ return (flags & Flags.FixedVariable) != 0;
+ }
}
- public void SetEndLocation (Location loc)
- {
- EndLocation = loc;
+ public bool IsReadonly {
+ get {
+ return (flags & Flags.ReadonlyMask) != 0;
+ }
}
- protected void Error_158 (string name, Location loc)
- {
- Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
- "by the same name in a contained scope", name);
+ public Location Location {
+ get {
+ return loc;
+ }
}
- /// <summary>
- /// Adds a label to the current block.
- /// </summary>
- ///
- /// <returns>
- /// false if the name already exists in this block. true
- /// otherwise.
- /// </returns>
- ///
- public bool AddLabel (LabeledStatement target)
- {
- if (switch_block != null)
- return switch_block.AddLabel (target);
-
- string name = target.Name;
-
- Block cur = this;
- while (cur != null) {
- LabeledStatement s = cur.DoLookupLabel (name);
- if (s != null) {
- Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
- Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
- return false;
- }
-
- if (this == Explicit)
- break;
-
- cur = cur.Parent;
+ public string Name {
+ get {
+ return name;
}
+ }
- while (cur != null) {
- if (cur.DoLookupLabel (name) != null) {
- Error_158 (name, target.loc);
- return false;
- }
+ public TypeSpec Type {
+ get {
+ return type;
+ }
+ set {
+ type = value;
+ }
+ }
- if (children != null) {
- foreach (Block b in children) {
- LabeledStatement s = b.DoLookupLabel (name);
- if (s == null)
- continue;
+ #endregion
- Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
- Error_158 (name, target.loc);
- return false;
- }
+ public void CreateBuilder (EmitContext ec)
+ {
+ if ((flags & Flags.Used) == 0) {
+ if (VariableInfo == null) {
+ // Missing flow analysis or wrong variable flags
+ throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
}
- cur = cur.Parent;
+ if (VariableInfo.IsEverAssigned)
+ ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
+ else
+ ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
}
- Toplevel.CheckError158 (name, target.loc);
+ if (HoistedVariant != null)
+ return;
- if (labels == null)
- labels = new Dictionary<string, LabeledStatement> ();
+ if (builder != null) {
+ if ((flags & Flags.CompilerGenerated) != 0)
+ return;
- labels.Add (name, target);
- return true;
+ // To avoid Used warning duplicates
+ throw new InternalErrorException ("Already created variable `{0}'", name);
+ }
+
+ //
+ // All fixed variabled are pinned, a slot has to be alocated
+ //
+ builder = ec.DeclareLocal (Type, IsFixed);
+ if (SymbolWriter.HasSymbolWriter)
+ ec.DefineLocalVariable (name, builder);
}
- public LabeledStatement LookupLabel (string name)
+ public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
{
- LabeledStatement s = DoLookupLabel (name);
- if (s != null)
- return s;
+ LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
+ li.Type = type;
+ return li;
+ }
- if (children == null)
- return null;
+ public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
+ {
+ if (IsConstant && const_value != null)
+ return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
- foreach (Block child in children) {
- if (Explicit != child.Explicit)
- continue;
+ return new LocalVariableReference (this, loc);
+ }
- s = child.LookupLabel (name);
- if (s != null)
- return s;
- }
+ public void Emit (EmitContext ec)
+ {
+ // TODO: Need something better for temporary variables
+ if ((flags & Flags.CompilerGenerated) != 0)
+ CreateBuilder (ec);
- return null;
+ ec.Emit (OpCodes.Ldloc, builder);
}
- LabeledStatement DoLookupLabel (string name)
+ public void EmitAssign (EmitContext ec)
{
- if (switch_block != null)
- return switch_block.LookupLabel (name);
+ // TODO: Need something better for temporary variables
+ if ((flags & Flags.CompilerGenerated) != 0)
+ CreateBuilder (ec);
- if (labels != null)
- if (labels.ContainsKey (name))
- return labels [name];
+ ec.Emit (OpCodes.Stloc, builder);
+ }
- return null;
+ public void EmitAddressOf (EmitContext ec)
+ {
+ ec.Emit (OpCodes.Ldloca, builder);
}
- public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
+ public string GetReadOnlyContext ()
{
- Block b = this;
- IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
- while (kvi == null) {
- b = b.Explicit.Parent;
- if (b == null)
- return true;
- kvi = b.Explicit.GetKnownVariable (name);
+ switch (flags & Flags.ReadonlyMask) {
+ case Flags.FixedVariable:
+ return "fixed variable";
+ case Flags.ForeachVariable:
+ return "foreach iteration variable";
+ case Flags.UsingVariable:
+ return "using variable";
}
- if (kvi.Block == b)
- return true;
+ throw new InternalErrorException ("Variable is not readonly");
+ }
- // Is kvi.Block nested inside 'b'
- if (b.Explicit != kvi.Block.Explicit) {
- //
- // If a variable by the same name it defined in a nested block of this
- // block, we violate the invariant meaning in a block.
- //
- if (b == this) {
- Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
- Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
- return false;
- }
+ public bool IsThisAssigned (BlockContext ec, Block block)
+ {
+ if (VariableInfo == null)
+ throw new Exception ();
- //
- // It's ok if the definition is in a nested subblock of b, but not
- // nested inside this block -- a definition in a sibling block
- // should not affect us.
- //
+ if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
return true;
- }
- //
- // Block 'b' and kvi.Block are the same textual block.
- // However, different variables are extant.
- //
- // Check if the variable is in scope in both blocks. We use
- // an indirect check that depends on AddVariable doing its
- // part in maintaining the invariant-meaning-in-block property.
- //
- if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
- return true;
+ return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
+ }
- if (this is ToplevelBlock) {
- Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
- e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
- return false;
- }
+ public bool IsAssigned (BlockContext ec)
+ {
+ if (VariableInfo == null)
+ throw new Exception ();
- //
- // Even though we detected the error when the name is used, we
- // treat it as if the variable declaration was in error.
- //
- Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
- Error_AlreadyDeclared (kvi.Location, name, "parent or current");
- return false;
+ return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
}
- protected bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
+ public void PrepareForFlowAnalysis (BlockContext bc)
{
- LocalInfo vi = GetLocalInfo (name);
- if (vi != null) {
- block.Report.SymbolRelatedToPreviousError (vi.Location, name);
- if (Explicit == vi.Block.Explicit) {
- Error_AlreadyDeclared (l, name, null);
- } else {
- Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
- "parent or current" : "parent");
- }
- return false;
- }
+ //
+ // No need for definitely assigned check for these guys
+ //
+ if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
+ return;
- if (block != null) {
- var tblock = block.CheckParameterNameConflict (name);
- if (tblock != null) {
- if (block == tblock && block is Linq.QueryBlock)
- Error_AlreadyDeclared (loc, name);
- else
- Error_AlreadyDeclared (loc, name, "parent or current");
+ VariableInfo = new VariableInfo (this, bc.FlowOffset);
+ bc.FlowOffset += VariableInfo.Length;
+ }
- return false;
- }
- }
+ //
+ // Mark the variables as referenced in the user code
+ //
+ public void SetIsUsed ()
+ {
+ flags |= Flags.Used;
+ }
- return true;
+ public override string ToString ()
+ {
+ return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
}
+ }
- public LocalInfo AddVariable (Expression type, string name, Location l)
+ /// <summary>
+ /// Block represents a C# block.
+ /// </summary>
+ ///
+ /// <remarks>
+ /// This class is used in a number of places: either to represent
+ /// explicit blocks that the programmer places or implicit blocks.
+ ///
+ /// Implicit blocks are used as labels or to introduce variable
+ /// declarations.
+ ///
+ /// Top-level blocks derive from Block, and they are called ToplevelBlock
+ /// they contain extra information that is not necessary on normal blocks.
+ /// </remarks>
+ public class Block : Statement {
+ [Flags]
+ public enum Flags
{
- if (!CheckParentConflictName (Toplevel, name, l))
- return null;
+ Unchecked = 1,
+ HasRet = 8,
+ Unsafe = 16,
+ IsIterator = 32,
+ HasCapturedVariable = 64,
+ HasCapturedThis = 1 << 7,
+ IsExpressionTree = 1 << 8,
+ CompilerGenerated = 1 << 9
+ }
- IKnownVariable kvi = Explicit.GetKnownVariable (name);
- if (kvi != null) {
- Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
- Error_AlreadyDeclared (l, name, "child");
- return null;
- }
+ public Block Parent;
+ public Location StartLocation;
+ public Location EndLocation;
- LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
- AddVariable (vi);
+ public ExplicitBlock Explicit;
+ public ParametersBlock ParametersBlock;
- if ((flags & Flags.VariablesInitialized) != 0)
- throw new InternalErrorException ("block has already been resolved");
+ protected Flags flags;
- return vi;
- }
+ //
+ // The statements in this block
+ //
+ protected List<Statement> statements;
+
+ protected List<Statement> scope_initializers;
+
+ int? resolving_init_idx;
+
+ Block original;
+
+#if DEBUG
+ static int id;
+ public int ID = id++;
+
+ static int clone_id_counter;
+ int clone_id;
+#endif
+
+// int assignable_slots;
+ bool unreachable_shown;
+ bool unreachable;
- protected virtual void AddVariable (LocalInfo li)
+ public Block (Block parent, Location start, Location end)
+ : this (parent, 0, start, end)
{
- Variables.Add (li.Name, li);
- Explicit.AddKnownVariable (li.Name, li);
}
- protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
+ public Block (Block parent, Flags flags, Location start, Location end)
{
- if (reason == null) {
- Error_AlreadyDeclared (loc, var);
- return;
+ if (parent != null) {
+ // the appropriate constructors will fixup these fields
+ ParametersBlock = parent.ParametersBlock;
+ Explicit = parent.Explicit;
}
- Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
- "in this scope because it would give a different meaning " +
- "to `{0}', which is already used in a `{1}' scope " +
- "to denote something else", var, reason);
+ this.Parent = parent;
+ this.flags = flags;
+ this.StartLocation = start;
+ this.EndLocation = end;
+ this.loc = start;
+ statements = new List<Statement> (4);
+
+ this.original = this;
}
- protected virtual void Error_AlreadyDeclared (Location loc, string name)
- {
- Toplevel.Report.Error (128, loc,
- "A local variable named `{0}' is already defined in this scope", name);
+ #region Properties
+
+ public bool HasRet {
+ get { return (flags & Flags.HasRet) != 0; }
}
-
- public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
- {
- Toplevel.Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
- name, conflict);
+
+ public Block Original {
+ get {
+ return original;
+ }
}
- public bool AddConstant (Expression type, string name, Expression value, Location l)
- {
- if (AddVariable (type, name, l) == null)
- return false;
-
- if (constants == null)
- constants = new Dictionary<string, Expression> ();
+ public bool IsCompilerGenerated {
+ get { return (flags & Flags.CompilerGenerated) != 0; }
+ set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
+ }
- constants.Add (name, value);
+ public bool Unchecked {
+ get { return (flags & Flags.Unchecked) != 0; }
+ set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
+ }
- // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
- Use ();
- return true;
+ public bool Unsafe {
+ get { return (flags & Flags.Unsafe) != 0; }
+ set { flags |= Flags.Unsafe; }
}
- static int next_temp_id = 0;
+ #endregion
- public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
+ public Block CreateSwitchBlock (Location start)
{
- Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
-
- if (temporary_variables == null)
- temporary_variables = new List<LocalInfo> ();
+ // FIXME: Only explicit block should be created
+ var new_block = new Block (this, start, start);
+ new_block.IsCompilerGenerated = true;
+ return new_block;
+ }
- int id = ++next_temp_id;
- string name = "$s_" + id.ToString ();
+ public void SetEndLocation (Location loc)
+ {
+ EndLocation = loc;
+ }
- LocalInfo li = new LocalInfo (te, name, this, loc);
- li.CompilerGenerated = true;
- temporary_variables.Add (li);
- return li;
+ public void AddLabel (LabeledStatement target)
+ {
+ ParametersBlock.TopBlock.AddLabel (target.Name, target);
}
- public LocalInfo GetLocalInfo (string name)
+ public void AddLocalName (LocalVariable li)
{
- LocalInfo ret;
- for (Block b = this; b != null; b = b.Parent) {
- if (b.variables != null && b.variables.TryGetValue (name, out ret)) {
- return ret;
- }
- }
+ AddLocalName (li.Name, li);
+ }
- return null;
+ public virtual void AddLocalName (string name, INamedBlockVariable li)
+ {
+ ParametersBlock.TopBlock.AddLocalName (name, li);
}
- public Expression GetVariableType (string name)
+ public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
{
- LocalInfo vi = GetLocalInfo (name);
- return vi == null ? null : vi.Type;
+ if (reason == null) {
+ Error_AlreadyDeclared (name, variable);
+ return;
+ }
+
+ ParametersBlock.TopBlock.Report.Error (136, variable.Location,
+ "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
+ "to `{0}', which is already used in a `{1}' scope to denote something else",
+ name, reason);
}
- public Expression GetConstantExpression (string name)
+ public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
{
- Expression ret;
- for (Block b = this; b != null; b = b.Parent) {
- if (b.constants != null) {
- if (b.constants.TryGetValue (name, out ret))
- return ret;
+ var pi = variable as ParametersBlock.ParameterInfo;
+ if (pi != null) {
+ var p = pi.Parameter;
+ if (p is AnonymousTypeClass.GeneratedParameter) {
+ ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
+ p.Name);
+ } else {
+ ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
}
+
+ return;
}
- return null;
+
+ ParametersBlock.TopBlock.Report.Error (128, variable.Location,
+ "A local variable named `{0}' is already defined in this scope", name);
+ }
+
+ public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
+ {
+ ParametersBlock.TopBlock.Report.Error (412, loc,
+ "The type parameter name `{0}' is the same as local variable or parameter name",
+ name);
}
//
public void AddStatement (Statement s)
{
statements.Add (s);
- flags |= Flags.BlockUsed;
- }
-
- public bool Used {
- get { return (flags & Flags.BlockUsed) != 0; }
- }
-
- public void Use ()
- {
- flags |= Flags.BlockUsed;
- }
-
- public bool HasRet {
- get { return (flags & Flags.HasRet) != 0; }
}
public int AssignableSlots {
get {
-// TODO: Re-enable
-// if ((flags & Flags.VariablesInitialized) == 0)
-// throw new Exception ("Variables have not been initialized yet");
- return assignable_slots;
+ // FIXME: HACK, we don't know the block available variables count now, so set this high enough
+ return 4096;
+// return assignable_slots;
}
}
- public IList<ToplevelBlock> AnonymousChildren {
- get { return anonymous_children; }
- }
-
- public void AddAnonymousChild (ToplevelBlock b)
- {
- if (anonymous_children == null)
- anonymous_children = new List<ToplevelBlock> ();
-
- anonymous_children.Add (b);
- }
-
- void DoResolveConstants (BlockContext ec)
- {
- if (constants == null)
- return;
-
- if (variables == null)
- throw new InternalErrorException ("cannot happen");
-
- foreach (var de in variables) {
- string name = de.Key;
- LocalInfo vi = de.Value;
- TypeSpec variable_type = vi.VariableType;
-
- if (variable_type == null) {
- if (vi.Type is VarExpr)
- ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
-
- continue;
- }
-
- Expression cv;
- if (!constants.TryGetValue (name, out cv))
- continue;
-
- // Don't let 'const int Foo = Foo;' succeed.
- // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
- // which in turn causes the 'must be constant' error to be triggered.
- constants.Remove (name);
-
- if (!variable_type.IsConstantCompatible) {
- Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
- continue;
- }
-
- ec.CurrentBlock = this;
- Expression e;
- using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
- e = cv.Resolve (ec);
- }
- if (e == null)
- continue;
-
- Constant ce = e as Constant;
- if (ce == null) {
- e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
- continue;
- }
-
- e = ce.ConvertImplicitly (ec, variable_type);
- if (e == null) {
- if (TypeManager.IsReferenceType (variable_type))
- ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
- else
- ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
- continue;
- }
-
- constants.Add (name, e);
- vi.IsConstant = true;
- }
- }
-
- protected void ResolveMeta (BlockContext ec, int offset)
- {
- Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
-
- // If some parent block was unsafe, we remain unsafe even if this block
- // isn't explicitly marked as such.
- using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
- flags |= Flags.VariablesInitialized;
-
- if (variables != null) {
- foreach (LocalInfo li in variables.Values) {
- if (!li.Resolve (ec))
- continue;
- li.VariableInfo = new VariableInfo (li, offset);
- offset += li.VariableInfo.Length;
- }
- }
- assignable_slots = offset;
-
- DoResolveConstants (ec);
-
- if (children == null)
- return;
- foreach (Block b in children)
- b.ResolveMeta (ec, offset);
- }
- }
-
- //
- // Emits the local variable declarations for a block
- //
- public virtual void EmitMeta (EmitContext ec)
- {
- if (variables != null){
- foreach (LocalInfo vi in variables.Values)
- vi.ResolveVariable (ec);
- }
-
- if (temporary_variables != null) {
- for (int i = 0; i < temporary_variables.Count; i++)
- ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
- }
-
- if (children != null) {
- for (int i = 0; i < children.Count; i++)
- ((Block)children[i]).EmitMeta(ec);
- }
- }
-
- void UsageWarning (BlockContext ec)
+ public LabeledStatement LookupLabel (string name)
{
- if (variables == null || ec.Report.WarningLevel < 3)
- return;
-
- foreach (var de in variables) {
- LocalInfo vi = de.Value;
-
- if (!vi.Used) {
- string name = de.Key;
-
- // vi.VariableInfo can be null for 'catch' variables
- if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
- ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
- else
- ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
- }
- }
- }
-
- static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
- {
- Statement body;
-
- // Some statements are wrapped by a Block. Since
- // others' internal could be changed, here I treat
- // them as possibly wrapped by Block equally.
- Block b = s as Block;
- if (b != null && b.statements.Count == 1)
- s = (Statement) b.statements [0];
-
- if (s is Lock)
- body = ((Lock) s).Statement;
- else if (s is For)
- body = ((For) s).Statement;
- else if (s is Foreach)
- body = ((Foreach) s).Statement;
- else if (s is While)
- body = ((While) s).Statement;
- else if (s is Fixed)
- body = ((Fixed) s).Statement;
- else if (s is Using)
- body = ((Using) s).EmbeddedStatement;
- else if (s is UsingTemporary)
- body = ((UsingTemporary) s).Statement;
- else
- return;
-
- if (body == null || body is EmptyStatement)
- ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
+ return ParametersBlock.TopBlock.GetLabel (name, this);
}
public override bool Resolve (BlockContext ec)
Block prev_block = ec.CurrentBlock;
bool ok = true;
- int errors = ec.Report.Errors;
-
ec.CurrentBlock = this;
ec.StartFlowBranching (this);
int statement_count = statements.Count;
for (int ix = 0; ix < statement_count; ix++){
Statement s = statements [ix];
- // Check possible empty statement (CS0642)
- if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
- statements [ix + 1] is ExplicitBlock)
- CheckPossibleMistakenEmptyStatement (ec, s);
//
// Warn if we detect unreachable code.
// If we're a non-static `struct' constructor which doesn't have an
// initializer, then we must initialize all of the struct's fields.
- if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
+ if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
ok = false;
- if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
- foreach (LabeledStatement label in labels.Values)
- if (!label.HasBeenReferenced)
- ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
- }
-
- if (ok && errors == ec.Report.Errors)
- UsageWarning (ec);
-
return ok;
}
protected virtual void EmitSymbolInfo (EmitContext ec)
{
- if (variables != null) {
- foreach (LocalInfo vi in variables.Values) {
- vi.EmitSymbolInfo (ec);
- }
- }
}
+#if DEBUG
public override string ToString ()
{
- return String.Format ("{0} ({1}:{2})", GetType (), this_id, StartLocation);
+ return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
}
+#endif
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Block target = (Block) t;
+#if DEBUG
+ target.clone_id = clone_id_counter++;
+#endif
clonectx.AddBlockMap (this, target);
- target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
- target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
+ target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
+ target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
+
if (Parent != null)
target.Parent = clonectx.RemapBlockCopy (Parent);
- if (variables != null){
- target.variables = new Dictionary<string, LocalInfo> ();
-
- foreach (var de in variables){
- LocalInfo newlocal = de.Value.Clone (clonectx);
- target.variables [de.Key] = newlocal;
- clonectx.AddVariableMap (de.Value, newlocal);
- }
- }
-
target.statements = new List<Statement> (statements.Count);
foreach (Statement s in statements)
target.statements.Add (s.Clone (clonectx));
-
- if (target.children != null){
- target.children = new List<Block> (children.Count);
- foreach (Block b in children){
- target.children.Add (clonectx.LookupBlock (b));
- }
- }
-
- //
- // TODO: labels, switch_block, constants (?), anonymous_children
- //
}
}
public class ExplicitBlock : Block
{
- Dictionary<string, IKnownVariable> known_variables;
protected AnonymousMethodStorey am_storey;
public ExplicitBlock (Block parent, Location start, Location end)
this.Explicit = this;
}
- // <summary>
- // Marks a variable with name @name as being used in this or a child block.
- // If a variable name has been used in a child block, it's illegal to
- // declare a variable with the same name in the current block.
- // </summary>
- internal void AddKnownVariable (string name, IKnownVariable info)
- {
- if (known_variables == null)
- known_variables = new Dictionary<string, IKnownVariable> ();
+ #region Properties
- known_variables [name] = info;
+ public AnonymousMethodStorey AnonymousMethodStorey {
+ get {
+ return am_storey;
+ }
+ }
- if (Parent != null)
- Parent.Explicit.AddKnownVariable (name, info);
+ public bool HasCapturedThis {
+ set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
+ get {
+ return (flags & Flags.HasCapturedThis) != 0;
+ }
}
- public AnonymousMethodStorey AnonymousMethodStorey {
- get { return am_storey; }
+ public bool HasCapturedVariable {
+ set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
+ get {
+ return (flags & Flags.HasCapturedVariable) != 0;
+ }
}
+ #endregion
+
//
// Creates anonymous method storey in current block
//
public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
{
- //
- // When referencing a variable in iterator storey from children anonymous method
- //
- if (Toplevel.am_storey is IteratorStorey) {
- return Toplevel.am_storey;
- }
-
//
// An iterator has only 1 storey block
//
if (ec.CurrentIterator != null)
return ec.CurrentIterator.Storey;
+ //
+ // When referencing a variable in iterator storey from children anonymous method
+ //
+ if (ParametersBlock.am_storey is IteratorStorey) {
+ return ParametersBlock.am_storey;
+ }
+
if (am_storey == null) {
MemberBase mc = ec.MemberContext as MemberBase;
- GenericMethod gm = mc == null ? null : mc.GenericMethod;
//
// Creates anonymous method storey for this block
//
- am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
+ am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
}
return am_storey;
public override void Emit (EmitContext ec)
{
- if (am_storey != null)
- am_storey.EmitStoreyInstantiation (ec);
+ if (am_storey != null) {
+ DefineAnonymousStorey (ec);
+ am_storey.EmitStoreyInstantiation (ec, this);
+ }
bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
if (emit_debug_info)
ec.EndScope ();
}
- public override void EmitMeta (EmitContext ec)
+ void DefineAnonymousStorey (EmitContext ec)
{
//
// Creates anonymous method storey
//
- if (am_storey != null) {
- if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
+ if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
+ //
+ // Creates parent storey reference when hoisted this is accessible
+ //
+ if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
+ ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
+
//
- // Creates parent storey reference when hoisted this is accessible
+ // Hoisted this exists in top-level parent storey only
//
- if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
- ExplicitBlock parent = Toplevel.Parent.Explicit;
+ while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
+ parent = parent.Parent.Explicit;
- //
- // Hoisted this exists in top-level parent storey only
- //
- while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
- parent = parent.Parent.Explicit;
+ am_storey.AddParentStoreyReference (ec, parent.am_storey);
+ }
- am_storey.AddParentStoreyReference (ec, parent.am_storey);
- }
+ am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
+
+ // TODO MemberCache: Review
+ am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
+ }
- am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
+ am_storey.CreateType ();
+ if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
+ am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
- // TODO MemberCache: Review
- am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
- }
+ am_storey.DefineType ();
+ am_storey.ResolveTypeParameters ();
- am_storey.CreateType ();
- if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
- am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
-
- am_storey.DefineType ();
- am_storey.ResolveTypeParameters ();
- am_storey.Define ();
- am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
-
- var ref_blocks = am_storey.ReferencesFromChildrenBlock;
- if (ref_blocks != null) {
- foreach (ExplicitBlock ref_block in ref_blocks) {
- for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
- if (b.am_storey != null) {
- b.am_storey.AddParentStoreyReference (ec, am_storey);
-
- // Stop propagation inside same top block
- if (b.Toplevel == Toplevel)
- break;
-
- b = b.Toplevel;
- }
- b.HasCapturedVariable = true;
+ var ref_blocks = am_storey.ReferencesFromChildrenBlock;
+ if (ref_blocks != null) {
+ foreach (ExplicitBlock ref_block in ref_blocks) {
+ for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
+ if (b.am_storey != null) {
+ b.am_storey.AddParentStoreyReference (ec, am_storey);
+
+ // Stop propagation inside same top block
+ if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
+ break;
+
+ b = b.ParametersBlock;
}
+
+ b.HasCapturedVariable = true;
}
}
}
- base.EmitMeta (ec);
+ am_storey.Define ();
+ am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
}
- public IKnownVariable GetKnownVariable (string name)
+ public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
{
- if (known_variables == null)
- return null;
-
- IKnownVariable kw;
- known_variables.TryGetValue (name, out kw);
- return kw;
+ tryBlock.statements = statements;
+ statements = new List<Statement> (1);
+ statements.Add (tf);
}
+ }
- public bool HasCapturedThis
+ //
+ // ParametersBlock was introduced to support anonymous methods
+ // and lambda expressions
+ //
+ public class ParametersBlock : ExplicitBlock
+ {
+ public class ParameterInfo : INamedBlockVariable
{
- set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
- get { return (flags & Flags.HasCapturedThis) != 0; }
- }
+ readonly ParametersBlock block;
+ readonly int index;
+ public VariableInfo VariableInfo;
+ bool is_locked;
- public bool HasCapturedVariable
- {
- set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
- get { return (flags & Flags.HasCapturedVariable) != 0; }
- }
+ public ParameterInfo (ParametersBlock block, int index)
+ {
+ this.block = block;
+ this.index = index;
+ }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- ExplicitBlock target = (ExplicitBlock) t;
- target.known_variables = null;
- base.CloneTo (clonectx, t);
- }
- }
+ #region Properties
+
+ public Block Block {
+ get {
+ return block;
+ }
+ }
+
+ public bool IsDeclared {
+ get {
+ return true;
+ }
+ }
+
+ public bool IsLocked {
+ get {
+ return is_locked;
+ }
+ set {
+ is_locked = value;
+ }
+ }
- public class ToplevelParameterInfo : IKnownVariable {
- public readonly ToplevelBlock Block;
- public readonly int Index;
- public VariableInfo VariableInfo;
+ public Location Location {
+ get {
+ return Parameter.Location;
+ }
+ }
- Block IKnownVariable.Block {
- get { return Block; }
- }
- public Parameter Parameter {
- get { return Block.Parameters [Index]; }
- }
+ public Parameter Parameter {
+ get {
+ return block.Parameters [index];
+ }
+ }
- public TypeSpec ParameterType {
- get { return Block.Parameters.Types [Index]; }
- }
+ public TypeSpec ParameterType {
+ get {
+ return Parameter.Type;
+ }
+ }
- public Location Location {
- get { return Parameter.Location; }
- }
+ #endregion
- public ToplevelParameterInfo (ToplevelBlock block, int idx)
- {
- this.Block = block;
- this.Index = idx;
+ public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
+ {
+ return new ParameterReference (this, loc);
+ }
}
- }
- //
- // A toplevel block contains extra information, the split is done
- // only to separate information that would otherwise bloat the more
- // lightweight Block.
- //
- // In particular, this was introduced when the support for Anonymous
- // Methods was implemented.
- //
- public class ToplevelBlock : ExplicitBlock
- {
//
// Block is converted to an expression
//
sealed class BlockScopeExpression : Expression
{
Expression child;
- readonly ToplevelBlock block;
+ readonly ParametersBlock block;
- public BlockScopeExpression (Expression child, ToplevelBlock block)
+ public BlockScopeExpression (Expression child, ParametersBlock block)
{
this.child = child;
this.block = block;
{
if (child == null)
return null;
-
+
child = child.Resolve (ec);
if (child == null)
return null;
public override void Emit (EmitContext ec)
{
- block.EmitMeta (ec);
block.EmitScopeInitializers (ec);
child.Emit (ec);
}
}
protected ParametersCompiled parameters;
- protected ToplevelParameterInfo[] parameter_info;
- LocalInfo this_variable;
+ protected ParameterInfo[] parameter_info;
bool resolved;
- bool unreachable;
- CompilerContext compiler;
+ protected bool unreachable;
+ protected ToplevelBlock top_block;
- public HoistedVariable HoistedThisVariable;
+ public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
+ : base (parent, 0, start, start)
+ {
+ if (parameters == null)
+ throw new ArgumentNullException ("parameters");
- public bool Resolved {
+ this.parameters = parameters;
+ ParametersBlock = this;
+
+ this.top_block = parent.ParametersBlock.top_block;
+ ProcessParameters ();
+ }
+
+ protected ParametersBlock (ParametersCompiled parameters, Location start)
+ : base (null, 0, start, start)
+ {
+ if (parameters == null)
+ throw new ArgumentNullException ("parameters");
+
+ this.parameters = parameters;
+ ParametersBlock = this;
+ }
+
+ protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
+ : base (null, 0, source.StartLocation, source.EndLocation)
+ {
+ this.parameters = parameters;
+ this.statements = source.statements;
+ this.scope_initializers = source.scope_initializers;
+
+ this.resolved = true;
+ this.unreachable = source.unreachable;
+ this.am_storey = source.am_storey;
+
+ ParametersBlock = this;
+ }
+
+ #region Properties
+
+ //
+ // Block has been converted to expression tree
+ //
+ public bool IsExpressionTree {
get {
- return resolved;
+ return (flags & Flags.IsExpressionTree) != 0;
}
}
// The parameters for the block.
//
public ParametersCompiled Parameters {
- get { return parameters; }
+ get {
+ return parameters;
+ }
}
- public Report Report {
- get { return compiler.Report; }
+ public ToplevelBlock TopBlock {
+ get {
+ return top_block;
+ }
}
- public ToplevelBlock Container {
- get { return Parent == null ? null : Parent.Toplevel; }
+ public bool Resolved {
+ get {
+ return resolved;
+ }
}
- public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
- this (ctx, parent, (Flags) 0, parameters, start)
- {
- }
+ #endregion
- public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
- this (ctx, null, (Flags) 0, parameters, start)
+ // <summary>
+ // Check whether all `out' parameters have been assigned.
+ // </summary>
+ public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
{
+ if (vector.IsUnreachable)
+ return;
+
+ int n = parameter_info == null ? 0 : parameter_info.Length;
+
+ for (int i = 0; i < n; i++) {
+ VariableInfo var = parameter_info[i].VariableInfo;
+
+ if (var == null)
+ continue;
+
+ if (vector.IsAssigned (var, false))
+ continue;
+
+ TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
+ var.Name);
+ }
}
- ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
- this (ctx, null, flags, parameters, start)
+ public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (statements.Count == 1) {
+ Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
+ if (scope_initializers != null)
+ expr = new BlockScopeExpression (expr, this);
+
+ return expr;
+ }
+
+ return base.CreateExpressionTree (ec);
}
- // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
- // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
- public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
- base (null, flags, start, Location.Null)
+ public ParameterInfo GetParameterInfo (Parameter p)
{
- this.compiler = ctx;
- this.Toplevel = this;
-
- this.parameters = parameters;
- this.Parent = parent;
- if (parent != null)
- parent.AddAnonymousChild (this);
+ for (int i = 0; i < parameters.Count; ++i) {
+ if (parameters[i] == p)
+ return parameter_info[i];
+ }
- if (!this.parameters.IsEmpty)
- ProcessParameters ();
+ throw new ArgumentException ("Invalid parameter");
}
- public ToplevelBlock (CompilerContext ctx, Location loc)
- : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
+ public Expression GetParameterReference (int index, Location loc)
{
+ return new ParameterReference (parameter_info[index], loc);
}
- protected override void CloneTo (CloneContext clonectx, Statement t)
+ protected void ProcessParameters ()
{
- ToplevelBlock target = (ToplevelBlock) t;
- base.CloneTo (clonectx, t);
+ if (parameters.Count == 0)
+ return;
+
+ parameter_info = new ParameterInfo[parameters.Count];
+ for (int i = 0; i < parameter_info.Length; ++i) {
+ var p = parameters.FixedParameters[i];
+ if (p == null)
+ continue;
- if (parameters.Count != 0) {
- target.parameter_info = new ToplevelParameterInfo[parameters.Count];
- for (int i = 0; i < parameters.Count; ++i)
- target.parameter_info[i] = new ToplevelParameterInfo (target, i);
+ // TODO: Should use Parameter only and more block there
+ parameter_info[i] = new ParameterInfo (this, i);
+ AddLocalName (p.Name, parameter_info[i]);
}
}
- public bool CheckError158 (string name, Location loc)
+ public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
{
- if (AnonymousChildren != null) {
- foreach (ToplevelBlock child in AnonymousChildren) {
- if (!child.CheckError158 (name, loc))
+ if (resolved)
+ return true;
+
+ resolved = true;
+
+ if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
+ flags |= Flags.IsExpressionTree;
+
+ try {
+ ResolveMeta (rc);
+
+ using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
+ FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
+
+ if (!Resolve (rc))
return false;
+
+ unreachable = top_level.End ();
+ }
+ } catch (Exception e) {
+ if (e is CompletionResult || rc.Report.IsDisabled)
+ throw;
+
+ if (rc.CurrentBlock != null) {
+ rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
+ } else {
+ rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
}
+
+ if (Report.DebugFlags > 0)
+ throw;
}
- for (ToplevelBlock c = Container; c != null; c = c.Container) {
- if (!c.DoCheckError158 (name, loc))
+ if (rc.ReturnType != TypeManager.void_type && !unreachable) {
+ if (rc.CurrentAnonymousMethod == null) {
+ // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
+ if (md is IteratorMethod) {
+ unreachable = true;
+ } else {
+ rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
+ return false;
+ }
+ } else {
+ rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
+ rc.CurrentAnonymousMethod.GetSignatureForError ());
return false;
+ }
}
return true;
}
- void ProcessParameters ()
+ void ResolveMeta (BlockContext ec)
{
- int n = parameters.Count;
- parameter_info = new ToplevelParameterInfo [n];
- ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
- for (int i = 0; i < n; ++i) {
- parameter_info [i] = new ToplevelParameterInfo (this, i);
+ int orig_count = parameters.Count;
- Parameter p = parameters [i];
- if (p == null)
+ for (int i = 0; i < orig_count; ++i) {
+ Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
+
+ if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
continue;
- string name = p.Name;
- if (CheckParentConflictName (top_parent, name, loc))
- AddKnownVariable (name, parameter_info [i]);
+ VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
+ parameter_info[i].VariableInfo = vi;
+ ec.FlowOffset += vi.Length;
}
-
- // mark this block as "used" so that we create local declarations in a sub-block
- // FIXME: This appears to uncover a lot of bugs
- //this.Use ();
}
- bool DoCheckError158 (string name, Location loc)
+ public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
{
- LabeledStatement s = LookupLabel (name);
- if (s != null) {
- Report.SymbolRelatedToPreviousError (s.loc, s.Name);
- Error_158 (name, loc);
- return false;
- }
+ ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
+ pb.EndLocation = EndLocation;
+ pb.statements = statements;
- return true;
+ var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
+ am_storey = new IteratorStorey (iterator);
+
+ statements = new List<Statement> (1);
+ AddStatement (new Return (iterator, iterator.Location));
}
+ }
- public override Expression CreateExpressionTree (ResolveContext ec)
+ //
+ //
+ //
+ public class ToplevelBlock : ParametersBlock
+ {
+ LocalVariable this_variable;
+ CompilerContext compiler;
+ Dictionary<string, object> names;
+ Dictionary<string, object> labels;
+
+ public HoistedVariable HoistedThisVariable;
+
+ public Report Report {
+ get { return compiler.Report; }
+ }
+
+ public ToplevelBlock (CompilerContext ctx, Location loc)
+ : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
{
- if (statements.Count == 1) {
- Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
- if (scope_initializers != null)
- expr = new BlockScopeExpression (expr, this);
+ }
- return expr;
- }
+ public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
+ : base (parameters, start)
+ {
+ this.compiler = ctx;
+ top_block = this;
- return base.CreateExpressionTree (ec);
+ ProcessParameters ();
}
//
- // Reformats this block to be top-level iterator block
+ // Recreates a top level block from parameters block. Used for
+ // compiler generated methods where the original block comes from
+ // explicit child block. This works for already resolved blocks
+ // only to ensure we resolve them in the correct flow order
//
- public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
+ public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
+ : base (source, parameters)
{
- IsIterator = true;
+ this.compiler = source.TopBlock.compiler;
+ top_block = this;
+ }
+
+ public override void AddLocalName (string name, INamedBlockVariable li)
+ {
+ if (names == null)
+ names = new Dictionary<string, object> ();
+
+ object value;
+ if (!names.TryGetValue (name, out value)) {
+ names.Add (name, li);
+ return;
+ }
+
+ INamedBlockVariable existing = value as INamedBlockVariable;
+ List<INamedBlockVariable> existing_list;
+ if (existing != null) {
+ existing_list = new List<INamedBlockVariable> ();
+ existing_list.Add (existing);
+ names[name] = existing_list;
+ } else {
+ existing_list = (List<INamedBlockVariable>) value;
+ }
+
+ //
+ // A collision checking between local names
+ //
+ for (int i = 0; i < existing_list.Count; ++i) {
+ existing = existing_list[i];
+ Block b = existing.Block;
- // Creates block with original statements
- AddStatement (new IteratorStatement (iterator, new Block (this, source)));
+ // Collision at same level
+ if (li.Block == b) {
+ li.Block.Error_AlreadyDeclared (name, li);
+ break;
+ }
+
+ // Collision with parent
+ b = li.Block;
+ while ((b = b.Parent) != null) {
+ if (existing.Block == b) {
+ li.Block.Error_AlreadyDeclared (name, li, "parent or current");
+ i = existing_list.Count;
+ break;
+ }
+ }
- source.statements = new List<Statement> (1);
- source.AddStatement (new Return (iterator, iterator.Location));
- source.IsIterator = false;
+ // Collision with with children
+ b = existing.Block;
+ while ((b = b.Parent) != null) {
+ if (li.Block == b) {
+ li.Block.Error_AlreadyDeclared (name, li, "child");
+ i = existing_list.Count;
+ break;
+ }
+ }
+ }
- IteratorStorey iterator_storey = new IteratorStorey (iterator);
- source.am_storey = iterator_storey;
- return iterator_storey;
+ existing_list.Add (li);
}
- //
- // Returns a parameter reference expression for the given name,
- // or null if there is no such parameter
- //
- public Expression GetParameterReference (string name, Location loc)
+ public void AddLabel (string name, LabeledStatement label)
{
- for (ToplevelBlock t = this; t != null; t = t.Container) {
- if (t.parameters.IsEmpty)
- continue;
+ if (labels == null)
+ labels = new Dictionary<string, object> ();
- Expression expr = t.GetParameterReferenceExpression (name, loc);
- if (expr != null)
- return expr;
+ object value;
+ if (!labels.TryGetValue (name, out value)) {
+ labels.Add (name, label);
+ return;
}
- return null;
+ LabeledStatement existing = value as LabeledStatement;
+ List<LabeledStatement> existing_list;
+ if (existing != null) {
+ existing_list = new List<LabeledStatement> ();
+ existing_list.Add (existing);
+ labels[name] = existing_list;
+ } else {
+ existing_list = (List<LabeledStatement>) value;
+ }
+
+ //
+ // A collision checking between labels
+ //
+ for (int i = 0; i < existing_list.Count; ++i) {
+ existing = existing_list[i];
+ Block b = existing.Block;
+
+ // Collision at same level
+ if (label.Block == b) {
+ Report.SymbolRelatedToPreviousError (existing.loc, name);
+ Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
+ break;
+ }
+
+ // Collision with parent
+ b = label.Block;
+ while ((b = b.Parent) != null) {
+ if (existing.Block == b) {
+ Report.Error (158, label.loc,
+ "The label `{0}' shadows another label by the same name in a contained scope", name);
+ i = existing_list.Count;
+ break;
+ }
+ }
+
+ // Collision with with children
+ b = existing.Block;
+ while ((b = b.Parent) != null) {
+ if (label.Block == b) {
+ Report.Error (158, label.loc,
+ "The label `{0}' shadows another label by the same name in a contained scope", name);
+ i = existing_list.Count;
+ break;
+ }
+ }
+ }
+
+ existing_list.Add (label);
}
- protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
+ //
+ // Creates an arguments set from all parameters, useful for method proxy calls
+ //
+ public Arguments GetAllParametersArguments ()
{
- int idx = parameters.GetParameterIndexByName (name);
- return idx < 0 ?
- null : new ParameterReference (parameter_info [idx], loc);
+ int count = parameters.Count;
+ Arguments args = new Arguments (count);
+ for (int i = 0; i < count; ++i) {
+ var arg_expr = GetParameterReference (i, parameter_info[i].Location);
+ args.Add (new Argument (arg_expr));
+ }
+
+ return args;
}
- public ToplevelBlock CheckParameterNameConflict (string name)
+ //
+ // Lookup inside a block, the returned value can represent 3 states
+ //
+ // true+variable: A local name was found and it's valid
+ // false+variable: A local name was found in a child block only
+ // false+null: No local name was found
+ //
+ public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
{
- for (ToplevelBlock t = this; t != null; t = t.Container) {
- if (t.HasParameterWithName (name))
- return t;
+ if (names == null)
+ return false;
+
+ object value;
+ if (!names.TryGetValue (name, out value))
+ return false;
+
+ variable = value as INamedBlockVariable;
+ Block b = block;
+ if (variable != null) {
+ do {
+ if (variable.Block == b.Original)
+ return true;
+
+ b = b.Parent;
+ } while (b != null);
+
+ b = variable.Block;
+ do {
+ if (block == b)
+ return false;
+
+ b = b.Parent;
+ } while (b != null);
+ } else {
+ List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
+ for (int i = 0; i < list.Count; ++i) {
+ variable = list[i];
+ do {
+ if (variable.Block == b.Original)
+ return true;
+
+ b = b.Parent;
+ } while (b != null);
+
+ b = variable.Block;
+ do {
+ if (block == b)
+ return false;
+
+ b = b.Parent;
+ } while (b != null);
+
+ b = block;
+ }
}
- return null;
+ variable = null;
+ return false;
}
- protected virtual bool HasParameterWithName (string name)
+ public LabeledStatement GetLabel (string name, Block block)
{
- return parameters.GetParameterIndexByName (name) >= 0;
+ if (labels == null)
+ return null;
+
+ object value;
+ if (!labels.TryGetValue (name, out value)) {
+ return null;
+ }
+
+ var label = value as LabeledStatement;
+ Block b = block;
+ if (label != null) {
+ 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.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.Original)
+ return label;
+
+ // TODO: Temporary workaround for the switch block implicit label block
+ if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
+ return label;
+ }
+ }
+
+ return null;
}
// <summary>
// Returns the "this" instance variable of this block.
// See AddThisVariable() for more information.
// </summary>
- public LocalInfo ThisVariable {
+ public LocalVariable ThisVariable {
get { return this_variable; }
}
// analysis code to ensure that it's been fully initialized before control
// leaves the constructor.
// </summary>
- public LocalInfo AddThisVariable (TypeContainer ds, Location l)
+ public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
{
if (this_variable == null) {
- this_variable = new LocalInfo (ds, this, l);
- this_variable.Used = true;
- this_variable.IsThis = true;
-
- Variables.Add ("this", this_variable);
+ this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
+ this_variable.Type = ds.CurrentType;
+ this_variable.PrepareForFlowAnalysis (bc);
}
return this_variable;
set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
}
- //
- // Block has been converted to expression tree
- //
- public bool IsExpressionTree {
- get { return (flags & Flags.IsExpressionTree) != 0; }
- }
-
public bool IsThisAssigned (BlockContext ec)
{
return this_variable == null || this_variable.IsThisAssigned (ec, this);
}
- public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
- {
- if (resolved)
- return true;
-
- resolved = true;
-
- if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
- flags |= Flags.IsExpressionTree;
-
- try {
- if (!ResolveMeta (rc, ip))
- return false;
-
- using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
- FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
-
- if (!Resolve (rc))
- return false;
-
- unreachable = top_level.End ();
- }
- } catch (Exception e) {
- if (e is CompletionResult || rc.Report.IsDisabled)
- throw;
-
- if (rc.CurrentBlock != null) {
- rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
- } else {
- rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
- }
-
- if (Report.DebugFlags > 0)
- throw;
- }
-
- if (rc.ReturnType != TypeManager.void_type && !unreachable) {
- if (rc.CurrentAnonymousMethod == null) {
- rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
- return false;
- } else if (!rc.CurrentAnonymousMethod.IsIterator) {
- rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
- rc.CurrentAnonymousMethod.GetSignatureForError ());
- return false;
- }
- }
-
- return true;
- }
-
- bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
- {
- int errors = ec.Report.Errors;
- int orig_count = parameters.Count;
-
- if (ip != null)
- parameters = ip;
-
- // Assert: orig_count != parameter.Count => orig_count == 0
- if (orig_count != 0 && orig_count != parameters.Count)
- throw new InternalErrorException ("parameter information mismatch");
-
- int offset = Parent == null ? 0 : Parent.AssignableSlots;
-
- for (int i = 0; i < orig_count; ++i) {
- Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
-
- if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
- continue;
-
- VariableInfo vi = new VariableInfo (ip, i, offset);
- parameter_info [i].VariableInfo = vi;
- offset += vi.Length;
- }
-
- ResolveMeta (ec, offset);
-
- return ec.Report.Errors == errors;
- }
-
- // <summary>
- // Check whether all `out' parameters have been assigned.
- // </summary>
- public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
- {
- if (vector.IsUnreachable)
- return;
-
- int n = parameter_info == null ? 0 : parameter_info.Length;
-
- for (int i = 0; i < n; i++) {
- VariableInfo var = parameter_info [i].VariableInfo;
-
- if (var == null)
- continue;
-
- if (vector.IsAssigned (var, false))
- continue;
-
- Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
- var.Name);
- }
- }
-
public override void Emit (EmitContext ec)
{
if (Report.Errors > 0)
#if PRODUCTION
try {
#endif
- EmitMeta (ec);
-
if (ec.HasReturnLabel)
ec.ReturnLabel = ec.DefineLabel ();
#endif
}
- public override void EmitMeta (EmitContext ec)
- {
- // Avoid declaring an IL variable for this_variable since it is not accessed
- // from the generated IL
- if (this_variable != null)
- Variables.Remove ("this");
- base.EmitMeta (ec);
- }
-
protected override void EmitSymbolInfo (EmitContext ec)
{
AnonymousExpression ae = ec.CurrentAnonymousMethod;
public SwitchLabel Clone (CloneContext clonectx)
{
+ if (label == null)
+ return this;
+
return new SwitchLabel (label.Clone (clonectx), loc);
}
}
public class SwitchSection {
- // An array of SwitchLabels.
public readonly List<SwitchLabel> Labels;
public readonly Block Block;
{
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));
ExpressionStatement string_dictionary;
FieldExpr switch_cache_field;
static int unique_counter;
+ ExplicitBlock block;
//
// Nullable Types support
//
static TypeSpec [] allowed_types;
- public Switch (Expression e, List<SwitchSection> sects, Location l)
+ public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
{
Expr = e;
+ this.block = block;
Sections = sects;
loc = l;
}
+ public ExplicitBlock Block {
+ get {
+ return block;
+ }
+ }
+
public bool GotDefault {
get {
return default_section != null;
Arguments get_value_args = new Arguments (1);
get_value_args.Add (new Argument (value));
- Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args, loc), loc).Resolve (rc);
+ Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
if (get_item == null)
return;
protected override void DoEmit (EmitContext ec)
{
+ //
+ // Needed to emit anonymous storey initialization
+ // Otherwise it does not contain any statements for now
+ //
+ block.Emit (ec);
+
default_target = ec.DefineLabel ();
null_target = ec.DefineLabel ();
Iterator iter;
List<ResumableStatement> resume_points;
int first_resume_pc;
+ protected Statement stmt;
+ Label dispose_try_block;
+ bool prepared_for_dispose, emitted_dispose;
+
+ protected ExceptionStatement (Statement stmt, Location loc)
+ {
+ this.stmt = stmt;
+ this.loc = loc;
+ }
+
+ #region Properties
+
+ public Statement Statement {
+ get {
+ return stmt;
+ }
+ }
+
+ #endregion
protected abstract void EmitPreTryBody (EmitContext ec);
protected abstract void EmitTryBody (EmitContext ec);
Label [] labels = new Label [resume_points.Count];
for (int i = 0; i < resume_points.Count; ++i)
- labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
+ labels [i] = resume_points [i].PrepareForEmit (ec);
ec.Emit (OpCodes.Switch, labels);
}
resume_points.Add (stmt);
}
- Label dispose_try_block;
- bool prepared_for_dispose, emitted_dispose;
public override Label PrepareForDispose (EmitContext ec, Label end)
{
if (!prepared_for_dispose) {
public class Lock : ExceptionStatement {
Expression expr;
- public Statement Statement;
- TemporaryVariable temp;
+ TemporaryVariableReference expr_copy;
+ TemporaryVariableReference lock_taken;
- public Lock (Expression expr, Statement stmt, Location l)
+ public Lock (Expression expr, Statement stmt, Location loc)
+ : base (stmt, loc)
{
this.expr = expr;
- Statement = stmt;
- loc = l;
}
public override bool Resolve (BlockContext ec)
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;
+ }
- // Avoid creating libraries that reference the internal
- // mcs NullType:
- TypeSpec t = expr.Type;
- if (t == TypeManager.null_type)
- t = TypeManager.object_type;
-
- temp = new TemporaryVariable (t, 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)
Lock target = (Lock) t;
target.expr = expr.Clone (clonectx);
- target.Statement = Statement.Clone (clonectx);
+ target.stmt = Statement.Clone (clonectx);
}
}
//
// Fixed statement
//
- public class Fixed : Statement {
- Expression type;
- List<KeyValuePair<LocalInfo, Expression>> declarators;
- Statement statement;
- TypeSpec expr_type;
- Emitter[] data;
- bool has_ret;
-
- abstract class Emitter
+ public class Fixed : Statement
+ {
+ abstract class Emitter : ShimExpression
{
- protected LocalInfo vi;
- protected Expression converted;
+ protected LocalVariable vi;
- protected Emitter (Expression expr, LocalInfo li)
+ protected Emitter (Expression expr, LocalVariable li)
+ : base (expr)
{
- converted = expr;
vi = li;
}
- public abstract void Emit (EmitContext ec);
public abstract void EmitExit (EmitContext ec);
}
class ExpressionEmitter : Emitter {
- public ExpressionEmitter (Expression converted, LocalInfo li) :
+ public ExpressionEmitter (Expression converted, LocalVariable li) :
base (converted, li)
{
}
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ throw new NotImplementedException ();
+ }
+
public override void Emit (EmitContext ec) {
//
// Store pointer in pinned location
//
- converted.Emit (ec);
+ expr.Emit (ec);
vi.EmitAssign (ec);
}
class StringEmitter : Emitter
{
- LocalInfo pinned_string;
+ LocalVariable pinned_string;
- public StringEmitter (Expression expr, LocalInfo li, Location loc):
- base (expr, li)
+ public StringEmitter (Expression expr, LocalVariable li, Location loc)
+ : base (expr, li)
{
- pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
- pinned_string.Pinned = true;
}
- public StringEmitter Resolve (ResolveContext rc)
+ protected override Expression DoResolve (ResolveContext rc)
{
- pinned_string.Resolve (rc);
+ pinned_string = new LocalVariable (vi.Block, "$pinned",
+ LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
+ vi.Location);
+
+ 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;
+ type = TypeManager.int32_type;
return this;
}
public override void Emit (EmitContext ec)
{
- pinned_string.ResolveVariable (ec);
+ pinned_string.CreateBuilder (ec);
- converted.Emit (ec);
+ expr.Emit (ec);
pinned_string.EmitAssign (ec);
// TODO: Should use Binary::Add
}
}
- public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
- {
- this.type = type;
- declarators = decls;
- statement = stmt;
- loc = l;
- }
-
- public Statement Statement {
- get { return statement; }
- }
-
- public override bool Resolve (BlockContext ec)
+ public class VariableDeclaration : BlockVariableDeclaration
{
- if (!ec.IsUnsafe){
- Expression.UnsafeError (ec, loc);
- return false;
- }
-
- TypeExpr texpr = type.ResolveAsContextualType (ec, false);
- if (texpr == null) {
- if (type is VarExpr)
- ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
-
- return false;
+ public VariableDeclaration (FullNamedExpression type, LocalVariable li)
+ : base (type, li)
+ {
}
- expr_type = texpr.Type;
-
- data = new Emitter [declarators.Count];
-
- if (!expr_type.IsPointer){
- ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
- return false;
- }
-
- int i = 0;
- foreach (var p in declarators){
- LocalInfo vi = p.Key;
- Expression e = p.Value;
-
- vi.VariableInfo.SetAssigned (ec);
- vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
+ protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
+ {
+ if (!Variable.Type.IsPointer && li == Variable) {
+ bc.Report.Error (209, TypeExpression.Location,
+ "The type of locals declared in a fixed statement must be a pointer type");
+ return null;
+ }
//
// The rules for the possible declarators are pretty wise,
// is present, so we need to test for this particular case.
//
- if (e is Cast){
- ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
- return false;
+ 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;
}
- using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
- e = e.Resolve (ec);
- }
+ initializer = initializer.Resolve (bc);
- if (e == null)
- return false;
+ if (initializer == null)
+ return null;
//
- // Case 2: Array
+ // Case 1: Array
//
- if (e.Type.IsArray){
- TypeSpec array_type = TypeManager.GetElementType (e.Type);
-
+ if (initializer.Type.IsArray) {
+ TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
+
//
// Provided that array_type is unmanaged,
//
- if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
- return false;
+ if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
+ return null;
//
// and T* is implicitly convertible to the
// pointer type given in the fixed statement.
//
- ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
-
+ ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
+
Expression converted = Convert.ImplicitConversionRequired (
- ec, array_ptr, vi.VariableType, loc);
+ bc, array_ptr, li.Type, loc);
if (converted == null)
- return false;
-
+ return 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, e, new NullLiteral (loc), loc),
- new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
+ new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
+ new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
new NullPointer (loc),
converted, loc);
- converted = converted.Resolve (ec);
+ converted = converted.Resolve (bc);
- data [i] = new ExpressionEmitter (converted, vi);
- i++;
+ return new ExpressionEmitter (converted, li);
+ }
- continue;
+ //
+ // Case 2: string
+ //
+ if (initializer.Type == TypeManager.string_type) {
+ return new StringEmitter (initializer, li, loc).Resolve (bc);
+ }
+
+ // Case 3: fixed buffer
+ if (initializer is FixedBufferPtr) {
+ return new ExpressionEmitter (initializer, li);
}
//
- // Case 3: string
+ // Case 4: & object.
//
- if (e.Type == TypeManager.string_type){
- data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
- i++;
- continue;
+ 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;
+ }
}
- // Case 4: fixed buffer
- if (e is FixedBufferPtr) {
- data [i++] = new ExpressionEmitter (e, vi);
- continue;
+ if (already_fixed) {
+ bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
}
- //
- // Case 1: & object.
- //
- Unary u = e as Unary;
- if (u != null && u.Oper == Unary.Operator.AddressOf) {
- IVariableReference vr = u.Expr as IVariableReference;
- if (vr == null || !vr.IsFixed) {
- data [i] = new ExpressionEmitter (e, vi);
- }
- }
+ initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
+ return new ExpressionEmitter (initializer, li);
+ }
+ }
+
+
+ VariableDeclaration decl;
+ Statement statement;
+ bool has_ret;
+
+ public Fixed (VariableDeclaration decl, Statement stmt, Location l)
+ {
+ this.decl = decl;
+ statement = stmt;
+ loc = l;
+ }
+
+ #region Properties
+
+ public Statement Statement {
+ get {
+ return statement;
+ }
+ }
+
+ public BlockVariableDeclaration Variables {
+ get {
+ return decl;
+ }
+ }
- if (data [i++] == null)
- ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
+ #endregion
- e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
+ public override bool Resolve (BlockContext ec)
+ {
+ using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
+ if (!decl.Resolve (ec))
+ return false;
}
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
protected override void DoEmit (EmitContext ec)
{
- for (int i = 0; i < data.Length; i++) {
- data [i].Emit (ec);
+ decl.Variable.CreateBuilder (ec);
+ decl.Initializer.Emit (ec);
+ if (decl.Declarators != null) {
+ foreach (var d in decl.Declarators) {
+ d.Variable.CreateBuilder (ec);
+ d.Initializer.Emit (ec);
+ }
}
statement.Emit (ec);
//
// Clear the pinned variable
//
- for (int i = 0; i < data.Length; i++) {
- data [i].EmitExit (ec);
+ ((Emitter) decl.Initializer).EmitExit (ec);
+ if (decl.Declarators != null) {
+ foreach (var d in decl.Declarators) {
+ ((Emitter)d.Initializer).EmitExit (ec);
+ }
}
}
{
Fixed target = (Fixed) t;
- target.type = type.Clone (clonectx);
- target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
- foreach (var p in declarators) {
- target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
- clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
- }
-
+ target.decl = (VariableDeclaration) decl.Clone (clonectx);
target.statement = statement.Clone (clonectx);
}
}
-
- public class Catch : Statement {
- public readonly string Name;
- public Block Block;
- public Block VarBlock;
- Expression type_expr;
+ public class Catch : Statement
+ {
+ Block block;
+ LocalVariable li;
+ FullNamedExpression type_expr;
+ CompilerAssign assign;
TypeSpec type;
- public Catch (Expression type, string name, Block block, Block var_block, Location l)
+ public Catch (Block block, Location loc)
{
- type_expr = type;
- Name = name;
- Block = block;
- VarBlock = var_block;
- loc = l;
+ this.block = block;
+ this.loc = loc;
+ }
+
+ #region Properties
+
+ public Block Block {
+ get {
+ return block;
+ }
}
public TypeSpec CatchType {
}
}
+ public FullNamedExpression TypeExpression {
+ get {
+ return type_expr;
+ }
+ set {
+ type_expr = value;
+ }
+ }
+
+ public LocalVariable Variable {
+ get {
+ return li;
+ }
+ set {
+ li = value;
+ }
+ }
+
+ #endregion
+
protected override void DoEmit (EmitContext ec)
{
- if (CatchType != null)
- ec.BeginCatchBlock (CatchType);
- else
+ if (IsGeneral)
ec.BeginCatchBlock (TypeManager.object_type);
+ else
+ ec.BeginCatchBlock (CatchType);
- if (VarBlock != null)
- VarBlock.Emit (ec);
-
- if (Name != null) {
- // TODO: Move to resolve
- LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
- lvr.Resolve (new ResolveContext (ec.MemberContext));
-
- // Only to make verifier happy
- if (TypeManager.IsGenericParameter (lvr.Type))
- ec.Emit (OpCodes.Unbox_Any, lvr.Type);
+ if (li != null) {
+ li.CreateBuilder (ec);
- Expression source;
- if (lvr.IsHoisted) {
- LocalTemporary lt = new LocalTemporary (lvr.Type);
+ //
+ // Special case hoisted catch variable, we have to use a temporary variable
+ // to pass via anonymous storey initialization with the value still on top
+ // of the stack
+ //
+ if (li.HoistedVariant != null) {
+ LocalTemporary lt = new LocalTemporary (li.Type);
+ SymbolWriter.OpenCompilerGeneratedBlock (ec);
lt.Store (ec);
- source = lt;
- } else {
- // Variable is at the top of the stack
- source = EmptyExpression.Null;
- }
+ SymbolWriter.CloseCompilerGeneratedBlock (ec);
- lvr.EmitAssign (ec, source, false, false);
- } else
+ // switch to assigning from the temporary variable and not from top of the stack
+ assign.UpdateSource (lt);
+ }
+ } else {
+ SymbolWriter.OpenCompilerGeneratedBlock (ec);
ec.Emit (OpCodes.Pop);
+ SymbolWriter.CloseCompilerGeneratedBlock (ec);
+ }
Block.Emit (ec);
}
return false;
type = te.Type;
-
- if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
+ if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
- return false;
- }
- } else
- type = null;
+ } else if (li != null) {
+ li.Type = type;
+ li.PrepareForFlowAnalysis (ec);
- if (!Block.Resolve (ec))
- return false;
+ // source variable is at the top of the stack
+ Expression source = new EmptyExpression (li.Type);
+ if (li.Type.IsGenericParameter)
+ source = new UnboxCast (source, li.Type);
- // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
- // emit the "unused variable" warnings.
- if (VarBlock != null)
- return VarBlock.Resolve (ec);
+ assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
+ Block.AddScopeStatement (new StatementExpression (assign));
+ }
+ }
- return true;
+ return Block.Resolve (ec);
}
}
Catch target = (Catch) t;
if (type_expr != null)
- target.type_expr = type_expr.Clone (clonectx);
- if (VarBlock != null)
- target.VarBlock = clonectx.LookupBlock (VarBlock);
- target.Block = clonectx.LookupBlock (Block);
+ target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
+
+ target.block = clonectx.LookupBlock (block);
}
}
public class TryFinally : ExceptionStatement {
- Statement stmt;
Block fini;
- public TryFinally (Statement stmt, Block fini, Location l)
+ public TryFinally (Statement stmt, Block fini, Location loc)
+ : base (stmt, loc)
{
- this.stmt = stmt;
this.fini = fini;
- loc = l;
}
public override bool Resolve (BlockContext ec)
foreach (Catch c in Specific){
ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
- if (c.Name != null) {
- LocalInfo vi = c.Block.GetLocalInfo (c.Name);
- if (vi == null)
- throw new Exception ();
-
- vi.VariableInfo = null;
- }
-
if (!c.Resolve (ec)) {
ok = false;
continue;
TypeSpec resolved_type = c.CatchType;
for (int ii = 0; ii < last_index; ++ii) {
- if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
+ if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
ec.Report.Error (160, c.loc,
"A previous catch clause already catches all exceptions of this or a super type `{0}'",
TypeManager.CSharpName (prev_catches [ii]));
}
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 TemporaryVariable local_copy;
- Statement statement;
- Expression expr;
- protected Statement dispose_call;
-
- public UsingTemporary (Expression expr, Statement stmt, Location l)
+ public class VariableDeclaration : BlockVariableDeclaration
{
- this.expr = expr;
- statement = stmt;
- loc = l;
- }
+ Statement dispose_call;
- #region Properties
- public Expression Expression {
- get {
- return expr;
+ public VariableDeclaration (FullNamedExpression type, LocalVariable li)
+ : base (type, li)
+ {
}
- }
- public Statement Statement {
- get {
- return statement;
+ public VariableDeclaration (LocalVariable li, Location loc)
+ : base (li)
+ {
+ this.loc = loc;
}
- }
- #endregion
+ public VariableDeclaration (Expression expr)
+ : base (null)
+ {
+ loc = expr.Location;
+ Initializer = expr;
+ }
- protected virtual bool DoResolve (BlockContext ec)
- {
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
+ #region Properties
- if (!expr.Type.ImplementsInterface (TypeManager.idisposable_type) &&
- Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
- if (expr.Type != InternalType.Dynamic) {
- Using.Error_IsNotConvertibleToIDisposable (ec, expr);
- return false;
- }
+ public bool IsNested { get; private set; }
+
+ #endregion
- expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
+ public void EmitDispose (EmitContext ec)
+ {
+ dispose_call.Emit (ec);
}
- var expr_type = expr.Type;
+ public override bool Resolve (BlockContext bc)
+ {
+ if (IsNested)
+ return true;
+
+ return base.Resolve (bc);
+ }
- local_copy = new TemporaryVariable (expr_type, loc);
- local_copy.Resolve (ec);
+ public Expression ResolveExpression (BlockContext bc)
+ {
+ var e = Initializer.Resolve (bc);
+ if (e == null)
+ return null;
- if (TypeManager.void_dispose_void == null) {
- TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
- TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+ li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
+ Initializer = ResolveInitializer (bc, Variable, e);
+ return e;
}
- 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;
+ protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
+ {
+ if (li.Type == InternalType.Dynamic) {
+ initializer = initializer.Resolve (bc);
+ if (initializer == null)
+ return null;
+
+ // 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 = 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));
+ }
- dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
+ if (li == Variable) {
+ CheckIDiposableConversion (bc, li, initializer);
+ dispose_call = CreateDisposeCall (bc, li);
+ dispose_call.Resolve (bc);
+ }
- // 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 base.ResolveInitializer (bc, li, initializer);
+ }
- return dispose_call.Resolve (ec);
- }
+ protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
+ {
+ var type = li.Type;
- public override bool Resolve (BlockContext ec)
- {
- bool ok = DoResolve (ec);
+ if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
+ if (TypeManager.IsNullableType (type)) {
+ // it's handled in CreateDisposeCall
+ return;
+ }
- ec.StartFlowBranching (this);
+ 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 ());
- ok &= statement.Resolve (ec);
+ return;
+ }
+ }
- ec.EndFlowBranching ();
+ protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
+ {
+ var lvr = lv.CreateReferenceExpression (bc, lv.Location);
+ var type = lv.Type;
+ var loc = lv.Location;
- ok &= base.Resolve (ec);
+ if (TypeManager.void_dispose_void == null) {
+ TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
+ TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+ }
- return ok;
- }
+ 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;
- protected override void EmitPreTryBody (EmitContext ec)
- {
- local_copy.EmitAssign (ec, expr);
- }
+ Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
- protected override void EmitTryBody (EmitContext ec)
- {
- statement.Emit (ec);
- }
+ // 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);
- protected override void EmitFinallyBody (EmitContext ec)
- {
- dispose_call.Emit (ec);
- }
+ return dispose;
+ }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- UsingTemporary target = (UsingTemporary) t;
+ 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);
+ }
- target.expr = expr.Clone (clonectx);
- target.statement = statement.Clone (clonectx);
+ declarators = null;
+ return stmt;
+ }
}
- }
- public class Using : ExceptionStatement {
- Statement stmt;
- public Statement EmbeddedStatement {
- get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
+ VariableDeclaration decl;
+
+ public Using (VariableDeclaration decl, Statement stmt, Location loc)
+ : base (stmt, loc)
+ {
+ this.decl = decl;
}
- Expression var;
- Expression init;
+ public Using (Expression expr, Statement stmt, Location loc)
+ : base (stmt, loc)
+ {
+ this.decl = new VariableDeclaration (expr);
+ }
- ExpressionStatement assign;
+ #region Properties
- public Using (Expression var, Expression init, Statement stmt, Location l)
- {
- this.var = var;
- this.init = init;
- this.stmt = stmt;
- loc = l;
+ public Expression Expression {
+ get {
+ return decl.Variable == null ? decl.Initializer : null;
+ }
}
- static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
- {
- ec.Report.SymbolRelatedToPreviousError (expr.Type);
- ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
- TypeManager.CSharpName (expr.Type));
+ public BlockVariableDeclaration Variables {
+ get {
+ return decl;
+ }
}
+ #endregion
+
protected override void EmitPreTryBody (EmitContext ec)
{
- assign.EmitStatement (ec);
+ decl.Emit (ec);
}
protected override void EmitTryBody (EmitContext ec)
protected override void EmitFinallyBody (EmitContext ec)
{
- Label skip = ec.DefineLabel ();
-
- 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)
{
- if (!ResolveVariable (ec))
- return false;
-
- ec.StartFlowBranching (this);
-
- bool ok = stmt.Resolve (ec);
+ VariableReference vr;
+ bool vr_locked = false;
- ec.EndFlowBranching ();
+ using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
+ 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;
- ok &= base.Resolve (ec);
+ if (decl.Declarators != null) {
+ stmt = decl.RewriteForDeclarators (ec, stmt);
+ }
- if (TypeManager.void_dispose_void == null) {
- TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
- TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+ vr = null;
+ }
}
- return ok;
- }
+ ec.StartFlowBranching (this);
- bool ResolveVariable (BlockContext ec)
- {
- assign = new SimpleAssign (var, init, loc);
- assign = assign.ResolveStatement (ec);
- if (assign == null)
- return false;
+ stmt.Resolve (ec);
- if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
- return true;
- }
+ ec.EndFlowBranching ();
- Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
- if (e == null) {
- if (assign.Type == InternalType.Dynamic) {
- e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
- var = new TemporaryVariable (e.Type, loc);
- assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
- return true;
- }
+ if (vr != null)
+ vr.IsLockedByStatement = vr_locked;
- Error_IsNotConvertibleToIDisposable (ec, var);
- return false;
- }
+ base.Resolve (ec);
- throw new NotImplementedException ("covariance?");
+ return true;
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Using target = (Using) t;
- target.var = var.Clone (clonectx);
- target.init = init.Clone (clonectx);
+ target.decl = (VariableDeclaration) decl.Clone (clonectx);
target.stmt = stmt.Clone (clonectx);
}
}
sealed class ArrayForeach : Statement
{
- class ArrayCounter : TemporaryVariable
- {
- StatementExpression increment;
-
- public ArrayCounter (Location loc)
- : base (TypeManager.int32_type, loc)
- {
- }
-
- public void ResolveIncrement (BlockContext ec)
- {
- increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
- increment.Resolve (ec);
- }
-
- public void EmitIncrement (EmitContext ec)
- {
- increment.Emit (ec);
- }
- }
-
readonly Foreach for_each;
readonly Statement statement;
Expression conv;
- TemporaryVariable[] lengths;
+ TemporaryVariableReference[] lengths;
Expression [] length_exprs;
- ArrayCounter[] counter;
+ StatementExpression[] counter;
+ TemporaryVariableReference[] variables;
- TemporaryVariable copy;
+ TemporaryVariableReference copy;
Expression access;
+ LocalVariableReference variable;
public ArrayForeach (Foreach @foreach, int rank)
{
for_each = @foreach;
statement = for_each.statement;
loc = @foreach.loc;
+ variable = new LocalVariableReference (for_each.variable, loc);
- counter = new ArrayCounter [rank];
+ counter = new StatementExpression[rank];
+ variables = new TemporaryVariableReference[rank];
length_exprs = new Expression [rank];
//
// multi-dimensional arrays
//
if (rank > 1)
- lengths = new TemporaryVariable [rank];
+ lengths = new TemporaryVariableReference [rank];
}
protected override void CloneTo (CloneContext clonectx, Statement target)
public override bool Resolve (BlockContext ec)
{
- copy = new TemporaryVariable (for_each.expr.Type, loc);
+ Block variables_block = variable.local_info.Block;
+ copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
copy.Resolve (ec);
int rank = length_exprs.Length;
Arguments list = new Arguments (rank);
for (int i = 0; i < rank; i++) {
- counter [i] = new ArrayCounter (loc);
- counter [i].ResolveIncrement (ec);
+ var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
+ variables[i] = v;
+ counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
+ counter[i].Resolve (ec);
if (rank == 1) {
length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
} else {
- lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
- lengths [i].Resolve (ec);
+ lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
+ lengths[i].Resolve (ec);
Arguments args = new Arguments (1);
args.Add (new Argument (new IntConstant (i, loc)));
length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
}
- list.Add (new Argument (counter [i]));
+ list.Add (new Argument (v));
}
access = new ElementAccess (copy, list, loc).Resolve (ec);
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
ec.CurrentBranching.CreateSibling ();
- for_each.variable = for_each.variable.ResolveLValue (ec, conv);
- if (for_each.variable == null)
- ok = false;
+ variable.local_info.Type = conv.Type;
+ variable.Resolve (ec);
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
if (!statement.Resolve (ec))
IntConstant zero = new IntConstant (0, loc);
for (int i = 0; i < rank; i++) {
- counter [i].EmitAssign (ec, zero);
+ variables [i].EmitAssign (ec, zero);
ec.Emit (OpCodes.Br, test [i]);
ec.MarkLabel (loop [i]);
}
- ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
+ variable.local_info.CreateBuilder (ec);
+ variable.EmitAssign (ec, conv, false, false);
statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
for (int i = rank - 1; i >= 0; i--){
- counter [i].EmitIncrement (ec);
+ counter [i].Emit (ec);
ec.MarkLabel (test [i]);
- counter [i].Emit (ec);
+ variables [i].Emit (ec);
if (lengths != null)
lengths [i].Emit (ec);
}
}
- sealed class CollectionForeach : Statement, MethodGroupExpr.IErrorHandler
+ sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
{
class Body : Statement
{
TypeSpec type;
- Expression variable, current, conv;
+ LocalVariableReference variable;
+ Expression current, conv;
Statement statement;
- Assign assign;
- public Body (TypeSpec type, Expression variable,
+ public Body (TypeSpec type, LocalVariable variable,
Expression current, Statement statement,
Location loc)
{
this.type = type;
- this.variable = variable;
+ this.variable = new LocalVariableReference (variable, loc);
this.current = current;
this.statement = statement;
this.loc = loc;
if (conv == null)
return false;
- assign = new SimpleAssign (variable, conv, loc);
- if (assign.Resolve (ec) == null)
- return false;
+ variable.local_info.Type = conv.Type;
+ variable.Resolve (ec);
if (!statement.Resolve (ec))
return false;
protected override void DoEmit (EmitContext ec)
{
- assign.EmitStatement (ec);
+ variable.local_info.CreateBuilder (ec);
+ variable.EmitAssign (ec, conv, false, false);
+
statement.Emit (ec);
}
}
- class Dispose : UsingTemporary
+ class RuntimeDispose : Using.VariableDeclaration
{
- LocalTemporary dispose;
+ public RuntimeDispose (LocalVariable lv, Location loc)
+ : base (lv, loc)
+ {
+ }
- public Dispose (TemporaryVariable 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);
}
}
- Expression variable, expr;
+ LocalVariable variable;
+ Expression expr;
Statement statement;
Expression var_type;
ExpressionStatement init;
+ TemporaryVariableReference enumerator_variable;
+ bool ambiguous_getenumerator_name;
- public CollectionForeach (Expression var_type, Expression var,
- Expression expr, Statement stmt, Location l)
+ public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
{
this.var_type = var_type;
this.variable = var;
//
// Option 1: Try to match by name GetEnumerator first
//
- var mexpr = Expression.MemberLookup (rc.Compiler, rc.CurrentType, null, expr.Type, "GetEnumerator", -1,
- MemberKind.All, BindingRestriction.DefaultMemberLookup | BindingRestriction.AccessibleOnly, loc);
+ var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
+ "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
var mg = mexpr as MethodGroupExpr;
if (mg != null) {
mg.InstanceExpression = expr;
- mg.CustomErrorHandler = this;
Arguments args = new Arguments (0);
- mg = mg.OverloadResolve (rc, ref args, false, loc);
+ mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
+
+ // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
+ if (ambiguous_getenumerator_name)
+ mg = null;
if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
return mg;
// 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 (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
rc.Report.SymbolRelatedToPreviousError (expr.Type);
- rc.Report.Error(1640, loc,
+ rc.Report.Error (1640, loc,
"foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
}
}
}
- }
+
+ if (t.IsGenericParameter)
+ t = t.BaseType;
+ else
+ t = null;
+
+ } while (t != null);
if (iface_candidate == null) {
rc.Report.Error (1579, loc,
- "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is not accessible",
+ "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
expr.Type.GetSignatureForError (), "GetEnumerator");
return null;
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) {
}
var get_enumerator = get_enumerator_mg.BestCandidate;
- var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc);
- enumerator.Resolve (ec);
+ enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
+ enumerator_variable.Resolve (ec);
// Prepare bool MoveNext ()
var move_next_mg = ResolveMoveNext (ec, get_enumerator);
return false;
}
- move_next_mg.InstanceExpression = enumerator;
+ move_next_mg.InstanceExpression = enumerator_variable;
// Prepare ~T~ Current { get; }
var current_prop = ResolveCurrent (ec, get_enumerator);
return false;
}
- var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec);
+ var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
if (current_pe == null)
return false;
VarExpr ve = var_type as VarExpr;
if (ve != null) {
- // Infer implicitly typed local variable from foreach enumerable type
- var_type = new TypeExpression (current_pe.Type, var_type.Location);
+ if (is_dynamic) {
+ // Source type is dynamic, set element type to dynamic too
+ var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
+ } else {
+ // Infer implicitly typed local variable from foreach enumerable type
+ var_type = new TypeExpression (current_pe.Type, var_type.Location);
+ }
+ } else if (is_dynamic) {
+ // Explicit cast of dynamic collection elements has to be done at runtime
+ current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
}
var_type = var_type.ResolveAsTypeTerminal (ec, false);
if (var_type == null)
return false;
+ 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);
- var enum_type = enumerator.Type;
+ var enum_type = enumerator_variable.Type;
//
// Add Dispose method call when enumerator can be IDisposable
//
- if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) {
+ if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
//
// Runtime Dispose check
//
- var tv = new LocalTemporary (TypeManager.idisposable_type);
- statement = new Dispose (enumerator, 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
//
- this.init = new SimpleAssign (enumerator, init);
+ this.init = new SimpleAssign (enumerator_variable, init);
this.init.Resolve (ec);
}
} else {
//
// Static Dispose check
//
- statement = new Dispose (enumerator, 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);
protected override void DoEmit (EmitContext ec)
{
+ enumerator_variable.LocalInfo.CreateBuilder (ec);
+
if (init != null)
init.EmitStatement (ec);
#region IErrorHandler Members
- bool MethodGroupExpr.IErrorHandler.AmbiguousCall (ResolveContext ec, MethodGroupExpr mg, MethodSpec ambiguous)
+ bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
{
- ec.Report.SymbolRelatedToPreviousError (mg.BestCandidate);
+ ec.Report.SymbolRelatedToPreviousError (best);
ec.Report.Warning (278, 2, loc,
"`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
- mg.DeclaringType.GetSignatureForError (), "enumerable",
- mg.BestCandidate.GetSignatureForError (), ambiguous.GetSignatureForError ());
+ expr.Type.GetSignatureForError (), "enumerable",
+ best.GetSignatureForError (), ambiguous.GetSignatureForError ());
+ ambiguous_getenumerator_name = true;
return true;
}
- bool MethodGroupExpr.IErrorHandler.NoExactMatch (ResolveContext ec, MethodSpec method)
+ bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
+ {
+ return false;
+ }
+
+ bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
+ {
+ return false;
+ }
+
+ bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
{
return false;
}
}
Expression type;
- Expression variable;
+ LocalVariable variable;
Expression expr;
Statement statement;
- public Foreach (Expression type, LocalVariableReference var, Expression expr,
- Statement stmt, Location l)
+ public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
{
this.type = type;
this.variable = var;
Foreach target = (Foreach) t;
target.type = type.Clone (clonectx);
- target.variable = variable.Clone (clonectx);
target.expr = expr.Clone (clonectx);
target.statement = statement.Clone (clonectx);
}