sealed class ThisInitializer : Statement
{
readonly HoistedThis hoisted_this;
+ readonly AnonymousMethodStorey parent;
- public ThisInitializer (HoistedThis hoisted_this)
+ public ThisInitializer (HoistedThis hoisted_this, AnonymousMethodStorey parent)
{
this.hoisted_this = hoisted_this;
+ this.parent = parent;
}
protected override void DoEmit (EmitContext ec)
{
- hoisted_this.EmitAssign (ec, new CompilerGeneratedThis (ec.CurrentType, loc), false, false);
+ Expression source;
+
+ if (parent == null)
+ source = new CompilerGeneratedThis (ec.CurrentType, loc);
+ else {
+ source = new FieldExpr (parent.HoistedThis.Field, Location.Null) {
+ InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location.Null)
+ };
+ }
+
+ hoisted_this.EmitAssign (ec, source, false, false);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
}
protected override void CloneTo (CloneContext clonectx, Statement target)
public Expression Instance;
bool initialize_hoisted_this;
+ AnonymousMethodStorey hoisted_this_parent;
public AnonymousMethodStorey (ExplicitBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind)
- : base (parent, MakeMemberName (host, name, parent.Module.CounterAnonymousContainers, tparams, block.StartLocation),
+ : base (parent, MakeMemberName (host, name, parent.PartialContainer.CounterAnonymousContainers, tparams, block.StartLocation),
tparams, 0, kind)
{
OriginalSourceBlock = block;
- ID = parent.Module.CounterAnonymousContainers++;
+ ID = parent.PartialContainer.CounterAnonymousContainers++;
}
- public void AddCapturedThisField (EmitContext ec)
+ public void AddCapturedThisField (EmitContext ec, AnonymousMethodStorey parent)
{
TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location);
Field f = AddCompilerGeneratedField ("$this", type_expr);
hoisted_this = new HoistedThis (this, f);
initialize_hoisted_this = true;
+ hoisted_this_parent = parent;
}
public Field AddCapturedVariable (string name, TypeSpec type)
var hoisted = localVariable.HoistedVariant;
if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) {
- // TODO: It's too late the field is defined in HoistedLocalVariable ctor
+ //
+ // Variable is already hoisted but we need it in storey which can be shared
+ //
hoisted.Storey.hoisted_locals.Remove (hoisted);
+ hoisted.Storey.Members.Remove (hoisted.Field);
hoisted = null;
}
// referenced indirectly
//
if (initialize_hoisted_this) {
- rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this));
+ rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this, hoisted_this_parent));
}
//
this.field = field;
}
+ public Field Field {
+ get {
+ return field;
+ }
+ }
+
public AnonymousMethodStorey Storey {
get {
return storey;
#region Properties
- public Field Field {
- get {
- return field;
- }
- }
-
public bool IsAssigned { get; set; }
public ParameterReference Parameter {
: base (storey, field)
{
}
-
- public Field Field {
- get {
- return field;
- }
- }
}
//
}
}
+ public override bool IsSideEffectFree {
+ get {
+ return true;
+ }
+ }
+
public ParametersCompiled Parameters {
get {
return Block.Parameters;
return null;
}
- protected bool VerifyExplicitParameters (ResolveContext ec, TypeSpec delegate_type, AParametersCollection parameters)
+ protected bool VerifyExplicitParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection parameters)
{
- if (VerifyParameterCompatibility (ec, delegate_type, parameters, ec.IsInProbingMode))
+ if (VerifyParameterCompatibility (ec, tic, delegate_type, parameters, ec.IsInProbingMode))
return true;
if (!ec.IsInProbingMode)
return false;
}
- protected bool VerifyParameterCompatibility (ResolveContext ec, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
+ protected bool VerifyParameterCompatibility (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
{
if (Parameters.Count != invoke_pd.Count) {
if (ignore_errors)
return false;
if (p_mod == Parameter.Modifier.NONE)
- ec.Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
- (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.FixedParameters [i].ModFlags));
+ ec.Report.Error (1677, Parameters[i].Location, "Parameter `{0}' should not be declared with the `{1}' keyword",
+ (i + 1).ToString (), Parameter.GetModifierSignature (Parameters [i].ModFlags));
else
- ec.Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
+ ec.Report.Error (1676, Parameters[i].Location, "Parameter `{0}' must be declared with the `{1}' keyword",
(i+1).ToString (), Parameter.GetModifierSignature (p_mod));
error = true;
}
continue;
TypeSpec type = invoke_pd.Types [i];
+
+ if (tic != null)
+ type = tic.InflateGenericArgument (ec, type);
- // We assume that generic parameters are always inflated
- if (TypeManager.IsGenericParameter (type))
- continue;
-
- if (TypeManager.HasElementType (type) && TypeManager.IsGenericParameter (TypeManager.GetElementType (type)))
- continue;
-
- if (!TypeSpecComparer.IsEqual (invoke_pd.Types [i], Parameters.Types [i])) {
+ if (!TypeSpecComparer.IsEqual (type, Parameters.Types [i])) {
if (ignore_errors)
return false;
- ec.Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
+ ec.Report.Error (1678, Parameters [i].Location, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
(i+1).ToString (),
Parameters.Types [i].GetSignatureForError (),
invoke_pd.Types [i].GetSignatureForError ());
//
// Infers type arguments based on explicit arguments
//
- public bool ExplicitTypeInference (ResolveContext ec, TypeInferenceContext type_inference, TypeSpec delegate_type)
+ public bool ExplicitTypeInference (TypeInferenceContext type_inference, TypeSpec delegate_type)
{
if (!HasExplicitParameters)
return false;
throw new InternalErrorException (e, loc);
}
- if (!ec.IsInProbingMode) {
+ if (!ec.IsInProbingMode && !etree_conversion) {
compatibles.Add (type, am ?? EmptyExpression.Null);
}
return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types);
}
- if (!VerifyExplicitParameters (ec, delegate_type, delegate_parameters)) {
+ if (!VerifyExplicitParameters (ec, tic, delegate_type, delegate_parameters)) {
return null;
}
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;
}
}
var bc = ec as BlockContext;
- if (bc != null)
- aec.FlowOffset = bc.FlowOffset;
+
+ if (bc != null) {
+ aec.AssignmentInfoOffset = bc.AssignmentInfoOffset;
+ aec.EnclosingLoop = bc.EnclosingLoop;
+ aec.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch;
+ aec.Switch = bc.Switch;
+ }
var errors = ec.Report.Errors;
- bool res = Block.Resolve (ec.CurrentBranching, aec, null);
+ bool res = Block.Resolve (aec);
+
+ if (res && errors == ec.Report.Errors) {
+ MarkReachable (new Reachability ());
+
+ if (!CheckReachableExit (ec.Report)) {
+ return null;
+ }
+
+ if (bc != null)
+ bc.AssignmentInfoOffset = aec.AssignmentInfoOffset;
+ }
if (am != null && am.ReturnTypeInference != null) {
am.ReturnTypeInference.FixAllTypes (ec);
//
// If e is synchronous the inferred return type is T
- // If e is asynchronous the inferred return type is Task<T>
+ // If e is asynchronous and the body of F is either an expression classified as nothing
+ // or a statement block where no return statements have expressions, the inferred return type is Task
+ // If e is async and has an inferred result type T, the inferred return type is Task<T>
//
if (block.IsAsync && ReturnType != null) {
- ReturnType = ec.Module.PredefinedTypes.TaskGeneric.TypeSpec.MakeGenericType (ec, new [] { ReturnType });
+ ReturnType = ReturnType.Kind == MemberKind.Void ?
+ ec.Module.PredefinedTypes.Task.TypeSpec :
+ ec.Module.PredefinedTypes.TaskGeneric.TypeSpec.MakeGenericType (ec, new [] { ReturnType });
}
}
return false;
}
+ bool CheckReachableExit (Report report)
+ {
+ if (block.HasReachableClosingBrace && ReturnType.Kind != MemberKind.Void) {
+ // FIXME: Flow-analysis on MoveNext generated code
+ if (!IsIterator) {
+ report.Error (1643, StartLocation,
+ "Not all code paths return a value in anonymous method of type `{0}'", GetSignatureForError ());
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ // We are reachable, mark block body reachable too
+ MarkReachable (new Reachability ());
+
+ CheckReachableExit (fc.Report);
+
+ var das = fc.BranchDefiniteAssignment ();
+ var prev_pb = fc.ParametersBlock;
+ fc.ParametersBlock = Block;
+ var da_ontrue = fc.DefiniteAssignmentOnTrue;
+ var da_onfalse = fc.DefiniteAssignmentOnFalse;
+
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
+ block.FlowAnalysis (fc);
+
+ fc.ParametersBlock = prev_pb;
+ fc.DefiniteAssignment = das;
+ fc.DefiniteAssignmentOnTrue = da_ontrue;
+ fc.DefiniteAssignmentOnFalse = da_onfalse;
+ }
+
+ public override void MarkReachable (Reachability rc)
+ {
+ block.MarkReachable (rc);
+ }
+
public void SetHasThisAccess ()
{
ExplicitBlock b = block;
Modifiers modifiers;
TypeDefinition parent = null;
+ TypeParameters hoisted_tparams = null;
var src_block = Block.Original.Explicit;
if (src_block.HasCapturedVariable || src_block.HasCapturedThis) {
if (src_block.HasCapturedThis) {
//
// Remove hoisted 'this' request when simple instance method is
- // enough (no hoisted variables only 'this')
+ // enough. No hoisted variables only 'this' and don't need to
+ // propagate this to value type state machine.
//
- if (src_block.ParametersBlock.StateMachine == null)
+ StateMachine sm_parent;
+ var pb = src_block.ParametersBlock;
+ do {
+ sm_parent = pb.StateMachine;
+ pb = pb.Parent == null ? null : pb.Parent.ParametersBlock;
+ } while (sm_parent == null && pb != null);
+
+ if (sm_parent == null) {
top_block.RemoveThisReferenceFromChildrenBlock (src_block);
-
- //
- // Special case where parent class is used to emit instance method
- // because currect storey is of value type (async host). We cannot
- // use ldftn on non-boxed instances either to share mutated state
- //
- if (sm != null && sm.Kind == MemberKind.Struct) {
- parent = sm.Parent.PartialContainer;
+ } else if (sm_parent.Kind == MemberKind.Struct) {
+ //
+ // Special case where parent class is used to emit instance method
+ // because currect storey is of value type (async host) and we cannot
+ // use ldftn on non-boxed instances either to share mutated state
+ //
+ parent = sm_parent.Parent.PartialContainer;
+ hoisted_tparams = sm_parent.OriginalTypeParameters;
+ } else if (sm is IteratorStorey) {
+ //
+ // For iterators we can host everything in one class
+ //
+ parent = storey = sm;
}
}
-
- //
- // For iterators we can host everything in one class
- //
- if (sm is IteratorStorey)
- parent = storey = sm;
}
modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE;
modifiers = Modifiers.STATIC | Modifiers.PRIVATE;
}
+ if (storey == null && hoisted_tparams == null)
+ hoisted_tparams = ec.CurrentTypeParameters;
+
if (parent == null)
parent = ec.CurrentTypeDefinition.Parent.PartialContainer;
string name = CompilerGeneratedContainer.MakeName (parent != storey ? block_name : null,
- "m", null, ec.Module.CounterAnonymousMethods++);
+ "m", null, parent.PartialContainer.CounterAnonymousMethods++);
MemberName member_name;
- if (storey == null && ec.CurrentTypeParameters != null) {
-
- var hoisted_tparams = ec.CurrentTypeParameters;
+ if (hoisted_tparams != null) {
var type_params = new TypeParameters (hoisted_tparams.Count);
for (int i = 0; i < hoisted_tparams.Count; ++i) {
type_params.Add (hoisted_tparams[i].CreateHoistedCopy (null));
// Special case for value type storey where this is not lifted but
// droped off to parent class
//
- for (var b = Block.Parent; b != null; b = b.Parent) {
- if (b.ParametersBlock.StateMachine != null) {
- ec.Emit (OpCodes.Ldfld, b.ParametersBlock.StateMachine.HoistedThis.Field.Spec);
- break;
- }
- }
+ if (ec.CurrentAnonymousMethod != null && ec.AsyncTaskStorey != null)
+ ec.Emit (OpCodes.Ldfld, ec.AsyncTaskStorey.HoistedThis.Field.Spec);
}
var delegate_method = method.Spec;
ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ()));
} else {
- if (delegate_method.IsGeneric)
- delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, method.TypeParameters);
+ if (delegate_method.IsGeneric) {
+ TypeParameterSpec[] tparams;
+ var sm = ec.CurrentAnonymousMethod == null ? null : ec.CurrentAnonymousMethod.Storey as StateMachine;
+ if (sm != null && sm.OriginalTypeParameters != null) {
+ tparams = sm.CurrentTypeParameters.Types;
+ } else {
+ tparams = method.TypeParameters;
+ }
+
+ delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, tparams);
+ }
ec.Emit (OpCodes.Ldftn, delegate_method);
}
Method tostring = new Method (this, new TypeExpression (Compiler.BuiltinTypes.String, loc),
Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc),
- Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
+ ParametersCompiled.EmptyReadOnlyParameters, null);
ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc);
equals.Block = equals_block;
equals.Define ();
- equals.PrepareEmit ();
Members.Add (equals);
//
Method hashcode = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Int, loc),
Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN,
new MemberName ("GetHashCode", loc),
- Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
+ ParametersCompiled.EmptyReadOnlyParameters, null);
//
// Modified FNV with good avalanche behavior and uniform
hashcode_block.AddStatement (new Return (hash_variable, loc));
hashcode.Block = hashcode_top;
hashcode.Define ();
- hashcode.PrepareEmit ();
Members.Add (hashcode);
//
tostring_block.AddStatement (new Return (string_concat, loc));
tostring.Block = tostring_block;
tostring.Define ();
- tostring.PrepareEmit ();
Members.Add (tostring);
return true;