set { type = value; }
}
- public Location Location {
- get { return loc; }
+ public virtual bool IsSideEffectFree {
+ get {
+ return false;
+ }
}
- public virtual string GetSignatureForError ()
- {
- return type.GetDefinition ().GetSignatureForError ();
+ public Location Location {
+ get { return loc; }
}
public virtual bool IsNull {
}
}
+ //
+ // Returns true when the expression during Emit phase breaks stack
+ // by using await expression
+ //
+ public virtual bool ContainsEmitWithAwait ()
+ {
+ return false;
+ }
+
/// <summary>
/// Performs semantic analysis on the Expression
/// </summary>
}
}
}
+
+ public virtual string GetSignatureForError ()
+ {
+ return type.GetDefinition ().GetSignatureForError ();
+ }
/// <summary>
/// Resolves an expression and performs semantic analysis on it.
ec.Emit (OpCodes.Pop);
}
+ //
+ // Emits the expression into temporary field variable. The method
+ // should be used for await expressions only
+ //
+ public virtual Expression EmitToField (EmitContext ec)
+ {
+ //
+ // This is the await prepare Emit method. When emitting code like
+ // a + b we emit code like
+ //
+ // a.Emit ()
+ // b.Emit ()
+ // Opcodes.Add
+ //
+ // For await a + await b we have to interfere the flow to keep the
+ // stack clean because await yields from the expression. The emit
+ // then changes to
+ //
+ // a = a.EmitToField () // a is changed to temporary field access
+ // b = b.EmitToField ()
+ // a.Emit ()
+ // b.Emit ()
+ // Opcodes.Add
+ //
+ //
+ // The idea is to emit expression and leave the stack empty with
+ // result value still available.
+ //
+ // Expressions should override this default implementation when
+ // optimized version can be provided (e.g. FieldExpr)
+ //
+ //
+ // We can optimize for side-effect free expressions, they can be
+ // emitted out of order
+ //
+ if (IsSideEffectFree)
+ return this;
+
+ bool needs_temporary = ContainsEmitWithAwait ();
+ if (!needs_temporary)
+ ec.EmitThis ();
+
+ // Emit original code
+ EmitToFieldSource (ec);
+
+ //
+ // Store the result to temporary field when we
+ // cannot load this directly
+ //
+ var field = ec.GetTemporaryField (type);
+ if (needs_temporary) {
+ //
+ // Create temporary local (we cannot load this before Emit)
+ //
+ var temp = ec.GetTemporaryLocal (type);
+ ec.Emit (OpCodes.Stloc, temp);
+
+ ec.EmitThis ();
+ ec.Emit (OpCodes.Ldloc, temp);
+ field.EmitAssignFromStack (ec);
+
+ ec.FreeTemporaryLocal (temp, type);
+ } else {
+ field.EmitAssignFromStack (ec);
+ }
+
+ return field;
+ }
+
+ protected virtual void EmitToFieldSource (EmitContext ec)
+ {
+ //
+ // Default implementation calls Emit method
+ //
+ Emit (ec);
+ }
+
+ protected static void EmitExpressionsList (EmitContext ec, List<Expression> expressions)
+ {
+ if (ec.HasSet (BuilderContext.Options.AsyncBody)) {
+ bool contains_await = false;
+
+ for (int i = 1; i < expressions.Count; ++i) {
+ if (expressions[i].ContainsEmitWithAwait ()) {
+ contains_await = true;
+ break;
+ }
+ }
+
+ if (contains_await) {
+ for (int i = 0; i < expressions.Count; ++i) {
+ expressions[i] = expressions[i].EmitToField (ec);
+ }
+ }
+ }
+
+ for (int i = 0; i < expressions.Count; ++i) {
+ expressions[i].Emit (ec);
+ }
+ }
+
/// <summary>
/// Protected constructor. Only derivate types should
/// be able to be created
return null;
}
- protected static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc)
+ public static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc)
{
var ctors = MemberCache.FindMembers (type, Constructor.ConstructorName, true);
if (ctors == null) {
continue;
}
- if (non_method == null || member is MethodSpec) {
+ if (non_method == null || member is MethodSpec || non_method.IsNotCSharpCompatible) {
non_method = member;
- } else if (!errorMode) {
+ } else if (!errorMode && !member.IsNotCSharpCompatible) {
ambig_non_method = member;
}
}
}
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return child.ContainsEmitWithAwait ();
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
get { return child.IsOneInteger; }
}
+ public override bool IsSideEffectFree {
+ get {
+ return child.IsSideEffectFree;
+ }
+ }
+
public override bool IsZeroInteger {
get { return child.IsZeroInteger; }
}
}
}
+ public override bool IsSideEffectFree {
+ get {
+ return Child.IsSideEffectFree;
+ }
+ }
+
public override bool IsZeroInteger {
get { return Child.IsZeroInteger; }
}
this.loc = orig.Location;
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return stm.ContainsEmitWithAwait ();
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
return orig_expr.CreateExpressionTree (ec);
#endregion
+ public override bool ContainsEmitWithAwait ()
+ {
+ return expr.ContainsEmitWithAwait ();
+ }
+
//
// Creates fully resolved expression switcher
//
this.loc = expr.Location;
}
- public override Expression CreateExpressionTree (ResolveContext ec)
+ public override bool ContainsEmitWithAwait ()
+ {
+ return expr.ContainsEmitWithAwait ();
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext rc)
{
- return expr.CreateExpressionTree (ec);
+ return expr.CreateExpressionTree (rc);
}
public Expression Child {
get { return expr; }
}
- protected override Expression DoResolve (ResolveContext ec)
+ protected override Expression DoResolve (ResolveContext rc)
{
- expr = expr.Resolve (ec);
+ expr = expr.Resolve (rc);
if (expr != null) {
type = expr.Type;
eclass = expr.eclass;
this.expr = expr;
}
+ public Expression Expr {
+ get {
+ return expr;
+ }
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
if (expr == null)
target.expr = expr.Clone (clonectx);
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return expr.ContainsEmitWithAwait ();
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ("ET");
throw new InternalErrorException ("Missing Resolve call");
}
- public Expression Expr {
- get { return expr; }
- }
}
//
}
}
- // MSAF
var retval = ctx.LookupNamespaceOrType (Name, Arity, LookupMode.IgnoreAccessibility, loc);
if (retval != null) {
ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (retval.Type);
// resolved to different type
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return false;
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ("ET");
get;
}
- // TODO: Not needed
protected abstract TypeSpec DeclaringType {
get;
}
}
}
+ public override bool ContainsEmitWithAwait ()
+ {
+ return InstanceExpression != null && InstanceExpression.ContainsEmitWithAwait ();
+ }
+
static bool IsSameOrBaseQualifier (TypeSpec type, TypeSpec qtype)
{
do {
InstanceExpression.Emit (ec);
t.Store (ec);
t.AddressOf (ec, AddressOp.Store);
+ t.Release (ec);
}
} else {
InstanceExpression.Emit (ec);
public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
}
+ public class ExtensionMethodCandidates
+ {
+ NamespaceContainer container;
+ Namespace ns;
+ IList<MethodSpec> methods;
+
+ public ExtensionMethodCandidates (IList<MethodSpec> methods, NamespaceContainer nsContainer)
+ : this (methods, nsContainer, null)
+ {
+ }
+
+ public ExtensionMethodCandidates (IList<MethodSpec> methods, NamespaceContainer nsContainer, Namespace ns)
+ {
+ this.methods = methods;
+ this.container = nsContainer;
+ this.ns = ns;
+ }
+
+ public NamespaceContainer Container {
+ get {
+ return container;
+ }
+ }
+
+ public bool HasUninspectedMembers { get; set; }
+
+ public Namespace Namespace {
+ get {
+ return ns;
+ }
+ }
+
+ public IList<MethodSpec> Methods {
+ get {
+ return methods;
+ }
+ }
+ }
+
//
// Represents a group of extension method candidates for whole namespace
//
class ExtensionMethodGroupExpr : MethodGroupExpr, OverloadResolver.IErrorHandler
{
- NamespaceContainer namespace_entry;
+ ExtensionMethodCandidates candidates;
public readonly Expression ExtensionExpression;
- public ExtensionMethodGroupExpr (IList<MethodSpec> list, NamespaceContainer n, Expression extensionExpr, Location l)
- : base (list.Cast<MemberSpec>().ToList (), extensionExpr.Type, l)
+ public ExtensionMethodGroupExpr (ExtensionMethodCandidates candidates, Expression extensionExpr, Location loc)
+ : base (candidates.Methods.Cast<MemberSpec>().ToList (), extensionExpr.Type, loc)
{
- this.namespace_entry = n;
+ this.candidates = candidates;
this.ExtensionExpression = extensionExpr;
}
get { return true; }
}
+ //
+ // For extension methodgroup we are not looking for base members but parent
+ // namespace extension methods
+ //
public override IList<MemberSpec> GetBaseMembers (TypeSpec baseType)
{
- if (namespace_entry == null)
+ // TODO: candidates are null only when doing error reporting, that's
+ // incorrect. We have to discover same extension methods in error mode
+ if (candidates == null)
return null;
+ int arity = type_arguments == null ? 0 : type_arguments.Count;
+
//
- // For extension methodgroup we are not looking for base members but parent
- // namespace extension methods
+ // Here we try to resume the search for extension method at the point
+ // where the last bunch of candidates was found. It's more tricky than
+ // it seems as we have to check both namespace containers and namespace
+ // in correct order.
//
- int arity = type_arguments == null ? 0 : type_arguments.Count;
- var found = namespace_entry.LookupExtensionMethod (DeclaringType, Name, arity, ref namespace_entry);
- if (found == null)
+ // Consider:
+ //
+ // namespace A {
+ // using N1;
+ // namespace B.C.D {
+ // <our first search found candidates in A.B.C.D
+ // }
+ // }
+ //
+ // In the example above namespace A.B.C.D, A.B.C and A.B have to be
+ // checked before we hit A.N1 using
+ //
+ if (candidates.Namespace == null) {
+ Namespace scope;
+ var methods = candidates.Container.NS.LookupExtensionMethod (candidates.Container, ExtensionExpression.Type, Name, arity, out scope);
+ if (methods != null) {
+ candidates = new ExtensionMethodCandidates (null, candidates.Container, scope);
+ return methods.Cast<MemberSpec> ().ToList ();
+ }
+ }
+
+ var ns_container = candidates.HasUninspectedMembers ? candidates.Container : candidates.Container.Parent;
+ if (ns_container == null)
+ return null;
+
+ candidates = ns_container.LookupExtensionMethod (ExtensionExpression.Type, Name, arity);
+ if (candidates == null)
return null;
- return found.Cast<MemberSpec> ().ToList ();
+ return candidates.Methods.Cast<MemberSpec> ().ToList ();
}
public override MethodGroupExpr LookupExtensionMethod (ResolveContext rc)
public void EmitCall (EmitContext ec, Arguments arguments)
{
- Invocation.EmitCall (ec, InstanceExpression, best_candidate, arguments, loc);
+ var call = new CallEmitter ();
+ call.InstanceExpression = InstanceExpression;
+ call.Emit (ec, best_candidate, arguments, loc);
}
public override void Error_ValueCannotBeConverted (ResolveContext ec, Location loc, TypeSpec target, bool expl)
return null;
int arity = type_arguments == null ? 0 : type_arguments.Count;
- NamespaceContainer methods_scope = null;
- var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity, ref methods_scope);
+ var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity);
if (methods == null)
return null;
- var emg = new ExtensionMethodGroupExpr (methods, methods_scope, InstanceExpression, loc);
+ var emg = new ExtensionMethodGroupExpr (methods, InstanceExpression, loc);
emg.SetTypeArguments (rc, type_arguments);
return emg;
}
if (q.Kind == MemberKind.Void) {
return p.Kind != MemberKind.Void ? 1: 0;
}
+
+ //
+ // When anonymous method is an asynchronous, and P has a return type Task<Y1>, and Q has a return type Task<Y2>
+ // better conversion is performed between underlying types Y1 and Y2
+ //
+ if (p.IsGenericTask || q.IsGenericTask) {
+ if (p.IsGenericTask != q.IsGenericTask) {
+ return 0;
+ }
+
+ var async_am = a.Expr as AnonymousMethodExpression;
+ if (async_am == null || !async_am.IsAsync)
+ return 0;
+
+ q = q.TypeArguments[0];
+ p = p.TypeArguments[0];
+ }
+
+ //
+ // The parameters are identicial and return type is not void, use better type conversion
+ // on return type to determine better one
+ //
} else {
if (argument_type == p)
return 1;
}
}
- /// <summary>
- /// Fully resolved expression that evaluates to a Field
- /// </summary>
- public class FieldExpr : MemberExpr, IDynamicAssign, IMemoryLocation, IVariableReference {
+ //
+ // Fully resolved expression that references a Field
+ //
+ public class FieldExpr : MemberExpr, IDynamicAssign, IMemoryLocation, IVariableReference
+ {
protected FieldSpec spec;
VariableInfo variable_info;
} else if (var != null && var.IsHoisted) {
AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, var, loc);
}
-
+
return new FixedBufferPtr (this, fb.ElementType, loc).Resolve (ec);
}
}
}
- 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)
{
- prepared = prepare_for_load && !(source is DynamicExpressionStatement);
- if (IsInstance)
+ bool has_await_source = ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait ();
+ if (isCompound && !(source is DynamicExpressionStatement)) {
+ if (has_await_source) {
+ if (IsInstance)
+ InstanceExpression = InstanceExpression.EmitToField (ec);
+ } else {
+ prepared = true;
+ }
+ }
+
+ if (IsInstance) {
+ if (has_await_source)
+ source = source.EmitToField (ec);
+
EmitInstance (ec, prepared);
+ }
source.Emit (ec);
+
if (leave_copy) {
ec.Emit (OpCodes.Dup);
if (!IsStatic) {
}
}
+ //
+ // Emits store to field with prepared values on stack
+ //
+ public void EmitAssignFromStack (EmitContext ec)
+ {
+ if (IsStatic) {
+ ec.Emit (OpCodes.Stsfld, spec);
+ } else {
+ ec.Emit (OpCodes.Stfld, spec);
+ }
+ }
+
public override void Emit (EmitContext ec)
{
Emit (ec, false);
} else
need_copy = false;
- if (need_copy){
- LocalBuilder local;
+ if (need_copy) {
Emit (ec);
- local = ec.DeclareLocal (type, false);
- ec.Emit (OpCodes.Stloc, local);
- ec.Emit (OpCodes.Ldloca, local);
+ var temp = ec.GetTemporaryLocal (type);
+ ec.Emit (OpCodes.Stloc, temp);
+ ec.Emit (OpCodes.Ldloca, temp);
+ ec.FreeTemporaryLocal (temp, type);
return;
}
}
- /// <summary>
- /// Expression that evaluates to a Property. The Assign class
- /// might set the `Value' expression if we are in an assignment.
- ///
- /// This is not an LValue because we need to re-write the expression, we
- /// can not take data from the stack and store it.
- /// </summary>
- class PropertyExpr : PropertyOrIndexerExpr<PropertySpec>
+ //
+ // Expression that evaluates to a Property.
+ //
+ // This is not an LValue because we need to re-write the expression. We
+ // can not take data from the stack and store it.
+ //
+ sealed class PropertyExpr : PropertyOrIndexerExpr<PropertySpec>
{
public PropertyExpr (PropertySpec spec, Location l)
: base (l)
#region Properties
+ protected override Arguments Arguments {
+ get {
+ return null;
+ }
+ set {
+ }
+ }
+
protected override TypeSpec DeclaringType {
get {
return best_candidate.DeclaringType;
// Special case: length of single dimension array property is turned into ldlen
//
if (IsSingleDimensionalArrayLength ()) {
- if (!prepared)
- EmitInstance (ec, false);
+ EmitInstance (ec, false);
ec.Emit (OpCodes.Ldlen);
ec.Emit (OpCodes.Conv_I4);
return;
}
- Invocation.EmitCall (ec, InstanceExpression, Getter, null, loc, prepared, false);
-
- if (leave_copy) {
- ec.Emit (OpCodes.Dup);
- if (!IsStatic) {
- temp = new LocalTemporary (this.Type);
- temp.Store (ec);
- }
- }
+ base.Emit (ec, leave_copy);
}
- public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+ public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
Arguments args;
+ LocalTemporary await_source_arg = null;
- if (prepare_for_load && !(source is DynamicExpressionStatement)) {
- args = new Arguments (0);
- prepared = true;
+ if (isCompound && !(source is DynamicExpressionStatement)) {
+ emitting_compound_assignment = true;
source.Emit (ec);
-
- if (leave_copy) {
- ec.Emit (OpCodes.Dup);
- if (!IsStatic) {
+
+ if (has_await_arguments) {
+ await_source_arg = new LocalTemporary (Type);
+ await_source_arg.Store (ec);
+
+ args = new Arguments (1);
+ args.Add (new Argument (await_source_arg));
+
+ if (leave_copy) {
+ temp = await_source_arg;
+ }
+
+ has_await_arguments = false;
+ } else {
+ args = null;
+
+ if (leave_copy) {
+ ec.Emit (OpCodes.Dup);
temp = new LocalTemporary (this.Type);
temp.Store (ec);
}
}
}
- Invocation.EmitCall (ec, InstanceExpression, Setter, args, loc, false, prepared);
-
+ emitting_compound_assignment = false;
+
+ var call = new CallEmitter ();
+ call.InstanceExpression = InstanceExpression;
+ if (args == null)
+ call.InstanceExpressionOnStack = true;
+
+ call.Emit (ec, Setter, args, loc);
+
if (temp != null) {
temp.Emit (ec);
temp.Release (ec);
}
+
+ if (await_source_arg != null) {
+ await_source_arg.Release (ec);
+ }
}
protected override Expression OverloadResolve (ResolveContext rc, Expression right_side)
{
eclass = ExprClass.PropertyAccess;
- if (best_candidate.IsNotRealProperty) {
+ if (best_candidate.IsNotCSharpCompatible) {
Error_PropertyNotValid (rc);
}
protected T best_candidate;
protected LocalTemporary temp;
- protected bool prepared;
+ protected bool emitting_compound_assignment;
+ protected bool has_await_arguments;
protected PropertyOrIndexerExpr (Location l)
{
#region Properties
+ protected abstract Arguments Arguments { get; set; }
+
public MethodSpec Getter {
get {
return getter;
//
// Implements the IAssignMethod interface for assignments
//
- public abstract void Emit (EmitContext ec, bool leave_copy);
- public abstract void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
+ public virtual void Emit (EmitContext ec, bool leave_copy)
+ {
+ var call = new CallEmitter ();
+ call.InstanceExpression = InstanceExpression;
+ if (has_await_arguments)
+ call.HasAwaitArguments = true;
+ else
+ call.DuplicateArguments = emitting_compound_assignment;
+
+ call.Emit (ec, Getter, Arguments, loc);
+
+ if (call.HasAwaitArguments) {
+ InstanceExpression = call.InstanceExpression;
+ Arguments = call.EmittedArguments;
+ has_await_arguments = true;
+ }
+
+ if (leave_copy) {
+ ec.Emit (OpCodes.Dup);
+ temp = new LocalTemporary (Type);
+ temp.Store (ec);
+ }
+ }
+
+ public abstract void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound);
public override void Emit (EmitContext ec)
{
Emit (ec, false);
}
+ protected override void EmitToFieldSource (EmitContext ec)
+ {
+ has_await_arguments = true;
+ Emit (ec, false);
+ }
+
public abstract SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source);
protected abstract Expression OverloadResolve (ResolveContext rc, Expression right_side);
throw new NotImplementedException ();
}
- 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)
{
- if (leave_copy || !prepare_for_load)
+ if (leave_copy || !isCompound)
throw new NotImplementedException ("EventExpr::EmitAssign");
Arguments args = new Arguments (1);
args.Add (new Argument (source));
- Invocation.EmitCall (ec, InstanceExpression, op, args, loc);
+
+ var call = new CallEmitter ();
+ call.InstanceExpression = InstanceExpression;
+ call.Emit (ec, op, args, loc);
}
#endregion