protected HoistedThis hoisted_this;
// Local variable which holds this storey instance
- public LocalTemporary Instance;
+ public Expression Instance;
public AnonymousMethodStorey (Block block, TypeContainer parent, MemberBase host, TypeParameter[] tparams, string name)
: base (parent, MakeMemberName (host, name, unique_id, tparams, block.StartLocation),
protected Field AddCompilerGeneratedField (string name, FullNamedExpression type)
{
- const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
+ return AddCompilerGeneratedField (name, type, false);
+ }
+
+ protected Field AddCompilerGeneratedField (string name, FullNamedExpression type, bool privateAccess)
+ {
+ Modifiers mod = Modifiers.COMPILER_GENERATED | (privateAccess ? Modifiers.PRIVATE : Modifiers.INTERNAL);
Field f = new Field (this, type, mod, new MemberName (name, Location), null);
AddField (f);
return f;
SymbolWriter.OpenCompilerGeneratedBlock (ec);
//
- // Create an instance of a storey
+ // Create an instance of this storey
//
- var storey_type_expr = CreateStoreyTypeExpression (ec);
-
ResolveContext rc = new ResolveContext (ec.MemberContext);
rc.CurrentBlock = block;
- Expression e = new New (storey_type_expr, null, Location).Resolve (rc);
- e.Emit (ec);
- Instance = new LocalTemporary (storey_type_expr.Type);
- Instance.Store (ec);
+ var storey_type_expr = CreateStoreyTypeExpression (ec);
+ var source = new New (storey_type_expr, null, Location).Resolve (rc);
+
+ //
+ // When the current context is async (or iterator) lift local storey
+ // instantiation to the currect storey
+ //
+ if (ec.CurrentAnonymousMethod is StateMachineInitializer) {
+ //
+ // Unfortunately, normal capture mechanism could not be used because we are
+ // too late in the pipeline and standart assign cannot be used either due to
+ // recursive nature of GetStoreyInstanceExpression
+ //
+ var field = ec.CurrentAnonymousMethod.Storey.AddCompilerGeneratedField (
+ LocalVariable.GetCompilerGeneratedName (block), storey_type_expr, true);
+
+ field.Define ();
+ field.Emit ();
+
+ var fexpr = new FieldExpr (field, Location);
+ fexpr.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location);
+ fexpr.EmitAssign (ec, source, false, false);
+
+ Instance = fexpr;
+ } else {
+ var local = TemporaryVariableReference.Create (source.Type, block, Location);
+ local.EmitAssign (ec, source);
+
+ Instance = local;
+ }
EmitHoistedFieldsInitialization (rc, ec);
- SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
+ // TODO: Implement properly
+ //SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
+
SymbolWriter.CloseCompilerGeneratedBlock (ec);
}
if (f == null) {
if (am.Storey == this) {
//
- // Access inside of same storey (S -> S)
+ // Access from inside of same storey (S -> S)
//
return new CompilerGeneratedThis (CurrentType, Location);
}
+
//
// External field access
//
this.hv = hv;
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return false;
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
return hv.CreateExpressionTree ();
GetFieldExpression (ec).Emit (ec);
}
+ public Expression EmitToField (EmitContext ec)
+ {
+ return GetFieldExpression (ec);
+ }
+
//
// Creates field access expression for hoisted variable
//
GetFieldExpression (ec).Emit (ec, leave_copy);
}
- public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false);
}
{
readonly string name;
- public HoistedLocalVariable (AnonymousMethodStorey scope, LocalVariable local, string name)
- : base (scope, name, local.Type)
+ public HoistedLocalVariable (AnonymousMethodStorey storey, LocalVariable local, string name)
+ : base (storey, name, local.Type)
{
this.name = local.Name;
}
+ //
+ // For compiler generated local variables
+ //
+ public HoistedLocalVariable (AnonymousMethodStorey storey, Field field)
+ : base (storey, field)
+ {
+ }
+
public override void EmitSymbolInfo ()
{
SymbolWriter.DefineCapturedLocal (storey.ID, name, field.Name);
}
}
- Dictionary<TypeSpec, Expression> compatibles;
+ readonly Dictionary<TypeSpec, Expression> compatibles;
+ readonly bool is_async;
+
public ParametersBlock Block;
- public AnonymousMethodExpression (Location loc)
+ public AnonymousMethodExpression (bool isAsync, Location loc)
{
+ this.is_async = isAsync;
this.loc = loc;
this.compatibles = new Dictionary<TypeSpec, Expression> ();
}
+ #region Properties
+
public override string ExprClassName {
get {
return "anonymous method";
return Parameters != ParametersCompiled.Undefined;
}
}
-
+
+ public bool IsAsync {
+ get {
+ return is_async;
+ }
+ }
+
public ParametersCompiled Parameters {
- get { return Block.Parameters; }
+ get {
+ return Block.Parameters;
+ }
}
+ #endregion
+
//
// Returns true if the body of lambda expression can be implicitly
// converted to the delegate of type `delegate_type'
}
using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) {
- am = CompatibleMethodBody (ec, tic, InternalType.Arglist, delegate_type);
- if (am != null)
- am = am.Compatible (ec);
+ var body = CompatibleMethodBody (ec, tic, InternalType.Arglist, delegate_type);
+ if (body != null) {
+ if (is_async) {
+ AsyncInitializer.Create (ec, body.Block, body.Parameters, ec.CurrentMemberDefinition.Parent, null, loc);
+ }
+
+ am = body.Compatible (ec, body, is_async);
+ } else {
+ am = null;
+ }
}
if (am == null)
return am.ReturnType;
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return false;
+ }
+
//
// Returns AnonymousMethod container if this anonymous method
// expression can be implicitly converted to the delegate type `delegate_type'
// lambda, this also means no variable capturing between this
// and parent scope
//
- am = body.Compatible (ec, ec.CurrentAnonymousMethod);
+ am = body.Compatible (ec, ec.CurrentAnonymousMethod, is_async);
//
// Quote nested expression tree
am = CreateExpressionTree (ec, delegate_type);
}
} else {
+ if (is_async) {
+ AsyncInitializer.Create (ec, body.Block, body.Parameters, ec.CurrentMemberDefinition.Parent, body.ReturnType, loc);
+ }
+
am = body.Compatible (ec);
}
} catch (CompletionResult) {
if (!DoResolveParameters (ec))
return null;
+#if !STATIC
// FIXME: The emitted code isn't very careful about reachability
// so, ensure we have a 'ret' at the end
BlockContext bc = ec as BlockContext;
if (bc != null && bc.CurrentBranching != null && bc.CurrentBranching.CurrentUsageVector.IsUnreachable)
bc.NeedReturnLabel ();
-
+#endif
return this;
}
ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block;
return CompatibleMethodFactory (return_type, delegate_type, p, b);
-
}
protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b)
//
// Abstract expression for any block which requires variables hoisting
//
- public abstract class AnonymousExpression : Expression
+ public abstract class AnonymousExpression : ExpressionStatement
{
protected class AnonymousMethodMethod : Method
{
{
EmitContext ec = new EmitContext (this, ig, ReturnType);
ec.CurrentAnonymousMethod = AnonymousMethod;
- if (AnonymousMethod.return_label != null) {
- ec.HasReturnLabel = true;
- ec.ReturnLabel = (Label) AnonymousMethod.return_label;
- }
-
return ec;
}
public TypeSpec ReturnType;
- object return_label;
-
protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc)
{
this.ReturnType = return_type;
public AnonymousExpression Compatible (ResolveContext ec)
{
- return Compatible (ec, this);
+ return Compatible (ec, this, false);
}
- public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae)
+ public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae, bool isAsync)
{
if (block.Resolved)
return this;
bool res = Block.Resolve (ec.CurrentBranching, aec, null);
- if (aec.HasReturnLabel)
- return_label = aec.ReturnLabel;
-
if (am != null && am.ReturnTypeInference != null) {
am.ReturnTypeInference.FixAllTypes (ec);
ReturnType = am.ReturnTypeInference.InferredTypeArguments [0];
am.ReturnTypeInference = null;
+
+ //
+ // If e is synchronous the inferred return type is T
+ // If e is asynchronous the inferred return type is Task<T>
+ //
+ if (isAsync && ReturnType != null) {
+ ReturnType = ec.Module.PredefinedTypes.TaskGeneric.TypeSpec.MakeGenericType (ec, new [] { ReturnType });
+ }
}
if (res && errors != ec.Report.Errors)
return res ? this : null;
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return false;
+ }
+
public void SetHasThisAccess ()
{
ExplicitBlock b = block;
}
public override bool IsIterator {
- get { return false; }
+ get {
+ return false;
+ }
+ }
+
+ public ParametersCompiled Parameters {
+ get {
+ return parameters;
+ }
}
public TypeInferenceContext ReturnTypeInference {
//
if (is_static) {
- ec.Emit (OpCodes.Ldnull);
+ ec.EmitNull ();
} else if (storey != null) {
Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext));
if (e != null)
e.Emit (ec);
} else {
- ec.Emit (OpCodes.Ldarg_0);
+ ec.EmitThis ();
}
var delegate_method = method.Spec;
}
}
+ public override void EmitStatement (EmitContext ec)
+ {
+ throw new NotImplementedException ();
+ }
+
//
// Look for the best storey for this anonymous method
//