--- /dev/null
+// CS0023: The `?' operator cannot be applied to operand of type `int'
+// Line: 11
+
+using System;
+
+class C
+{
+ static void Main()
+ {
+ int k = 0;
+ var r = k?.ToString ();
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS1061: Type `int' does not contain a definition for `GetValueOrDefault' and no extension method `GetValueOrDefault' of type `int' could be found. Are you missing an assembly reference?
+// Line: 9
+
+class C
+{
+ static void Main ()
+ {
+ int? i = 4;
+ var m = i?.GetValueOrDefault ();
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS1644: Feature `null propagating operator' cannot be used because it is not part of the C# 5.0 language specification
+// Line: 10
+// Compiler options: -langversion:5
+
+class C
+{
+ static void Main ()
+ {
+ object o = null;
+ string s = o?.ToString ();
+ }
+}
\ No newline at end of file
args.Add (new Argument (awaiter, Argument.AType.Ref));
args.Add (new Argument (new CompilerGeneratedThis (CurrentType, Location), Argument.AType.Ref));
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- mg.EmitCall (ec, args);
+ mg.EmitCall (ec, args, true);
}
}
args.Add (new Argument (exceptionVariable));
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- mg.EmitCall (ec, args);
+ mg.EmitCall (ec, args, true);
}
}
}
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- mg.EmitCall (ec, args);
+ mg.EmitCall (ec, args, true);
}
}
public Expression InstanceExpression;
//
- // When set leaves an extra copy of all arguments on the stack
+ // When call has to leave an extra copy of all arguments on the stack
//
public bool DuplicateArguments;
//
public bool HasAwaitArguments;
+ public bool NullShortCircuit;
+
//
// When dealing with await arguments the original arguments are converted
// into a new set with hoisted stack results
//
public Arguments EmittedArguments;
+ public Label NullOperatorLabel;
+
public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
{
- EmitPredefined (ec, method, Arguments, loc);
+ EmitPredefined (ec, method, Arguments, false, loc);
+ }
+
+ public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
+ {
+ EmitPredefined (ec, method, Arguments, true, loc);
}
- public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, Location? loc = null)
+ public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
{
Expression instance_copy = null;
OpCode call_op;
LocalTemporary lt = null;
+ InstanceEmitter ie = new InstanceEmitter ();
if (method.IsStatic) {
call_op = OpCodes.Call;
if (HasAwaitArguments) {
instance_copy = InstanceExpression.EmitToField (ec);
- if (Arguments == null)
- EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
+ ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+
+ if (Arguments == null) {
+ ie.EmitLoad (ec);
+ }
} else if (!InstanceExpressionOnStack) {
- var instance_on_stack_type = EmitCallInstance (ec, InstanceExpression, method.DeclaringType, call_op);
+ ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
+ ie.NullShortCircuit = NullShortCircuit;
+ ie.Emit (ec);
+
+ if (NullShortCircuit) {
+ NullOperatorLabel = ie.NullOperatorLabel;
+ }
if (DuplicateArguments) {
ec.Emit (OpCodes.Dup);
if (Arguments != null && Arguments.Count != 0) {
- lt = new LocalTemporary (instance_on_stack_type);
+ lt = new LocalTemporary (ie.GetStackType (ec));
lt.Store (ec);
instance_copy = lt;
}
EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
if (EmittedArguments != null) {
if (instance_copy != null) {
- EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
+ ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+ ie.Emit (ec);
if (lt != null)
lt.Release (ec);
if (method.Parameters.HasArglist) {
var varargs_types = GetVarargsTypes (method, Arguments);
ec.Emit (call_op, method, varargs_types);
- return;
- }
-
- //
- // If you have:
- // this.DoFoo ();
- // and DoFoo is not virtual, you can omit the callvirt,
- // because you don't need the null checking behavior.
- //
- ec.Emit (call_op, method);
- }
-
- static TypeSpec EmitCallInstance (EmitContext ec, Expression instance, TypeSpec declaringType, OpCode callOpcode)
- {
- var instance_type = instance.Type;
-
- //
- // Push the instance expression
- //
- if ((instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
- instance_type.IsGenericParameter || declaringType.IsNullableType) {
+ } else {
//
- // If the expression implements IMemoryLocation, then
- // we can optimize and use AddressOf on the
- // return.
+ // If you have:
+ // this.DoFoo ();
+ // and DoFoo is not virtual, you can omit the callvirt,
+ // because you don't need the null checking behavior.
//
- // If not we have to use some temporary storage for
- // it.
- var iml = instance as IMemoryLocation;
- if (iml != null) {
- iml.AddressOf (ec, AddressOp.Load);
- } else {
- LocalTemporary temp = new LocalTemporary (instance_type);
- instance.Emit (ec);
- temp.Store (ec);
- temp.AddressOf (ec, AddressOp.Load);
- }
-
- return ReferenceContainer.MakeType (ec.Module, instance_type);
+ ec.Emit (call_op, method);
}
- if (instance_type.IsStructOrEnum) {
- instance.Emit (ec);
- ec.Emit (OpCodes.Box, instance_type);
- return ec.BuiltinTypes.Object;
- }
+ //
+ // Pop the return value if there is one and stack should be empty
+ //
+ if (statement && method.ReturnType.Kind != MemberKind.Void)
+ ec.Emit (OpCodes.Pop);
- instance.Emit (ec);
- return instance_type;
+ if (NullShortCircuit && !DuplicateArguments) {
+ ie.EmitResultLift (ec, method.ReturnType, statement);
+ }
}
static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
return true;
}
+
+ static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
+ {
+ var instance_type = instance.Type;
+ return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
+ instance_type.IsGenericParameter || declaringType.IsNullableType;
+ }
+ }
+
+ public struct InstanceEmitter
+ {
+ readonly Expression instance;
+ readonly bool addressRequired;
+ bool value_on_stack;
+
+ public bool NullShortCircuit;
+ public Label NullOperatorLabel;
+
+ public InstanceEmitter (Expression instance, bool addressLoad)
+ {
+ this.instance = instance;
+ this.addressRequired = addressLoad;
+ NullShortCircuit = false;
+ NullOperatorLabel = new Label ();
+ }
+
+ public void Emit (EmitContext ec)
+ {
+ Nullable.Unwrap unwrap;
+
+ if (NullShortCircuit) {
+ NullOperatorLabel = ec.DefineLabel ();
+ unwrap = instance as Nullable.Unwrap;
+ } else {
+ unwrap = null;
+ }
+
+ if (unwrap != null) {
+ unwrap.Store (ec);
+ unwrap.EmitCheck (ec);
+ ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
+ unwrap.Emit (ec);
+ var tmp = ec.GetTemporaryLocal (unwrap.Type);
+ ec.Emit (OpCodes.Stloc, tmp);
+ ec.Emit (OpCodes.Ldloca, tmp);
+ ec.FreeTemporaryLocal (tmp, unwrap.Type);
+ return;
+ }
+
+ EmitLoad (ec);
+
+ if (NullShortCircuit) {
+ ec.Emit (OpCodes.Dup);
+ ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
+ }
+
+ value_on_stack = true;
+ }
+
+ public void EmitLoad (EmitContext ec)
+ {
+ var instance_type = instance.Type;
+
+ //
+ // Push the instance expression
+ //
+ if (addressRequired) {
+ //
+ // If the expression implements IMemoryLocation, then
+ // we can optimize and use AddressOf on the
+ // return.
+ //
+ // If not we have to use some temporary storage for
+ // it.
+ var iml = instance as IMemoryLocation;
+ if (iml != null) {
+ iml.AddressOf (ec, AddressOp.Load);
+ } else {
+ LocalTemporary temp = new LocalTemporary (instance_type);
+ instance.Emit (ec);
+ temp.Store (ec);
+ temp.AddressOf (ec, AddressOp.Load);
+ }
+
+ return;
+ }
+
+ instance.Emit (ec);
+
+ // Only to make verifier happy
+ if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) {
+ ec.Emit (OpCodes.Box, instance_type);
+ } else if (instance_type.IsStructOrEnum) {
+ ec.Emit (OpCodes.Box, instance_type);
+ }
+ }
+
+ public void EmitResultLift (EmitContext ec, TypeSpec type, bool statement)
+ {
+ if (!NullShortCircuit)
+ throw new InternalErrorException ();
+
+ bool value_rt = TypeSpec.IsValueType (type);
+ TypeSpec lifted;
+ if (value_rt) {
+ if (type.IsNullableType)
+ lifted = type;
+ else {
+ lifted = Nullable.NullableInfo.MakeType (ec.Module, type);
+ ec.Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (lifted));
+ }
+ } else {
+ lifted = null;
+ }
+
+ var end = ec.DefineLabel ();
+ if (value_on_stack || !statement) {
+ ec.Emit (OpCodes.Br_S, end);
+ }
+
+ ec.MarkLabel (NullOperatorLabel);
+
+ if (value_on_stack)
+ ec.Emit (OpCodes.Pop);
+
+ if (!statement) {
+ if (value_rt)
+ Nullable.LiftedNull.Create (lifted, Location.Null).Emit (ec);
+ else
+ ec.EmitNull ();
+ }
+
+ ec.MarkLabel (end);
+ }
+
+ public TypeSpec GetStackType (EmitContext ec)
+ {
+ var instance_type = instance.Type;
+
+ if (addressRequired)
+ return ReferenceContainer.MakeType (ec.Module, instance_type);
+
+ if (instance_type.IsStructOrEnum)
+ return ec.Module.Compiler.BuiltinTypes.Object;
+
+ return instance_type;
+ }
}
}
%token MAKEREF
%token ASYNC
%token AWAIT
+%token INTERR_OPERATOR
/* C# keywords which are not really keywords */
%token GET
$$ = new ParenthesizedExpression ((Expression) $2, GetLocation ($1));
}
;
-
+
member_access
: primary_expression DOT identifier_inside_body opt_type_argument_list
{
$$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location);
lbag.AddLocation ($$, GetLocation ($2));
}
+ | primary_expression INTERR_OPERATOR DOT identifier_inside_body opt_type_argument_list
+ {
+ if (lang_version < LanguageVersion.V_6)
+ FeatureIsNotAvailable (GetLocation ($2), "null propagating operator");
+
+ var lt = (LocatedToken) $4;
+ $$ = new NullMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location);
+ lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3));
+ }
| builtin_types DOT identifier_inside_body opt_type_argument_list
{
var lt = (LocatedToken) $3;
$$ = new ElementAccess ((Expression) $1, (Arguments) $3, GetLocation ($2));
lbag.AddLocation ($$, GetLocation ($4));
}
+ | primary_expression INTERR_OPERATOR OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET
+ {
+ if (lang_version < LanguageVersion.V_6)
+ FeatureIsNotAvailable (GetLocation ($2), "null propagating operator");
+
+ throw new NotImplementedException ();
+// $$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3));
+// lbag.AddLocation ($$, GetLocation ($4));
+ }
| primary_expression OPEN_BRACKET_EXPR expression_list_arguments error
{
Error_SyntaxError (yyToken);
return Token.OP_COALESCING;
}
+ if (d == '.') {
+ return Token.INTERR_OPERATOR;
+ }
+
switch (current_token) {
case Token.CLOSE_PARENS:
case Token.TRUE:
int parens = 0;
switch (xtoken ()) {
+ case Token.DOT:
+ case Token.OPEN_BRACKET_EXPR:
+ next_token = Token.INTERR_OPERATOR;
+ break;
case Token.LITERAL:
case Token.TRUE:
case Token.FALSE:
public override void Emit (EmitContext ec)
{
- if (method_group.InstanceExpression == null)
+ InstanceEmitter ie;
+ if (method_group.InstanceExpression == null) {
+ ie = new InstanceEmitter ();
ec.EmitNull ();
- else
- method_group.InstanceExpression.Emit (ec);
+ } else {
+ ie = new InstanceEmitter (method_group.InstanceExpression, false);
+ ie.NullShortCircuit = method_group.NullShortCircuit;
+ ie.Emit (ec);
+ }
var delegate_method = method_group.BestCandidate;
}
ec.Emit (OpCodes.Newobj, constructor_method);
+
+ if (method_group.NullShortCircuit) {
+ ie.EmitResultLift (ec, type, false);
+ }
}
public override void FlowAnalysis (FlowAnalysisContext fc) {
//
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpr;
- call.EmitPredefined (ec, method, arguments, loc);
+ call.Emit (ec, method, arguments, loc);
}
public override void EmitStatement (EmitContext ec)
{
- Emit (ec);
- //
- // Pop the return value if there is one
- //
- if (type.Kind != MemberKind.Void)
- ec.Emit (OpCodes.Pop);
+ var call = new CallEmitter ();
+ call.InstanceExpression = InstanceExpr;
+ call.EmitStatement (ec, method, arguments, loc);
}
public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
get;
}
+ public bool NullShortCircuit { get; set; }
+
protected abstract TypeSpec DeclaringType {
get;
}
InstanceExpression.FlowAnalysis (fc);
}
+ protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type)
+ {
+ return TypeSpec.IsValueType (type) && !type.IsNullableType ?
+ Nullable.NullableInfo.MakeType (rc.Module, type) :
+ type;
+ }
+
public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs)
{
if (!ResolveInstanceExpressionCore (rc, rhs))
public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original)
{
- if (left != null && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
+ if (left != null && !NullShortCircuit && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
ec.Report.Warning (1720, 1, left.Location,
"Expression will always cause a `{0}'", "System.NullReferenceException");
}
return this;
}
- protected void EmitInstance (EmitContext ec, bool prepare_for_load)
+ protected InstanceEmitter EmitInstance (EmitContext ec, bool prepare_for_load)
{
- TypeSpec instance_type = InstanceExpression.Type;
- if (TypeSpec.IsValueType (instance_type)) {
- if (InstanceExpression is IMemoryLocation) {
- ((IMemoryLocation) InstanceExpression).AddressOf (ec, AddressOp.Load);
- } else {
- // Cannot release the temporary variable when its address
- // is required to be on stack for any parent
- LocalTemporary t = new LocalTemporary (instance_type);
- InstanceExpression.Emit (ec);
- t.Store (ec);
- t.AddressOf (ec, AddressOp.Store);
- }
- } else {
- InstanceExpression.Emit (ec);
-
- // Only to make verifier happy
- if (instance_type.IsGenericParameter && !(InstanceExpression is This) && TypeSpec.IsReferenceType (instance_type))
- ec.Emit (OpCodes.Box, instance_type);
- }
+ var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type));
+ inst.NullShortCircuit = NullShortCircuit;
+ inst.Emit (ec);
if (prepare_for_load)
ec.Emit (OpCodes.Dup);
+
+ return inst;
}
public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
throw new NotSupportedException ();
}
- public void EmitCall (EmitContext ec, Arguments arguments)
+ public void EmitCall (EmitContext ec, Arguments arguments, bool statement)
{
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
- call.Emit (ec, best_candidate, arguments, loc);
+ call.NullShortCircuit = NullShortCircuit;
+ if (statement)
+ call.EmitStatement (ec, best_candidate, arguments, loc);
+ else
+ call.Emit (ec, best_candidate, arguments, loc);
}
public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
if (best_candidate_return.Kind == MemberKind.Void && best_candidate.IsConditionallyExcluded (ec))
Methods = Excluded;
+ if (NullShortCircuit)
+ best_candidate_return = LiftMemberType (ec, best_candidate_return);
+
return this;
}
variable_info = var.VariableInfo.GetStructFieldInfo (Name);
}
+ if (NullShortCircuit) {
+ type = LiftMemberType (ec, type);
+
+ if (InstanceExpression.IsNull)
+ return Constant.CreateConstantFromValue (type, null, loc);
+ }
+
eclass = ExprClass.Variable;
return this;
}
ec.Emit (OpCodes.Ldsfld, spec);
} else {
+ InstanceEmitter ie;
if (!prepared)
- EmitInstance (ec, false);
+ ie = EmitInstance (ec, false);
+ else
+ ie = new InstanceEmitter ();
// Optimization for build-in types
if (type.IsStruct && type == ec.CurrentType && InstanceExpression.Type == type) {
ec.Emit (OpCodes.Volatile);
ec.Emit (OpCodes.Ldfld, spec);
+
+ if (NullShortCircuit) {
+ ie.EmitResultLift (ec, spec.MemberType, false);
+ }
}
}
}
}
if (IsInstance) {
+ if (NullShortCircuit)
+ throw new NotImplementedException ("null operator assignment");
+
if (has_await_source)
source = source.EmitToField (ec);
// Special case: length of single dimension array property is turned into ldlen
//
if (IsSingleDimensionalArrayLength ()) {
- EmitInstance (ec, false);
+ var inst = EmitInstance (ec, false);
+
ec.Emit (OpCodes.Ldlen);
ec.Emit (OpCodes.Conv_I4);
+
+ if (NullShortCircuit)
+ inst.EmitResultLift (ec, ec.BuiltinTypes.Int, false);
+
return;
}
call.InstanceExpression = InstanceExpression;
if (args == null)
call.InstanceExpressionOnStack = true;
+ if (NullShortCircuit) {
+ call.NullShortCircuit = true;
+ call.NullOperatorLabel = null_operator_label;
+ }
- call.Emit (ec, Setter, args, loc);
+ if (leave_copy)
+ call.Emit (ec, Setter, args, loc);
+ else
+ call.EmitStatement (ec, Setter, args, loc);
if (temp != null) {
temp.Emit (ec);
protected LocalTemporary temp;
protected bool emitting_compound_assignment;
protected bool has_await_arguments;
+ protected Label null_operator_label;
protected PropertyOrIndexerExpr (Location l)
{
if (expr == null)
return null;
+ if (NullShortCircuit && !ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
+ type = LiftMemberType (ec, type);
+ }
+
if (expr != this)
return expr.Resolve (ec);
}
if (!ResolveSetter (ec))
return null;
+ if (NullShortCircuit && ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
+ var lifted_type = LiftMemberType (ec, type);
+ if (type != lifted_type) {
+ // TODO: Workaround to disable codegen for now
+ return Nullable.Wrap.Create (this, lifted_type);
+ }
+ }
+
return this;
}
public virtual void Emit (EmitContext ec, bool leave_copy)
{
var call = new CallEmitter ();
+ call.NullShortCircuit = NullShortCircuit;
call.InstanceExpression = InstanceExpression;
if (has_await_arguments)
call.HasAwaitArguments = true;
has_await_arguments = true;
}
+ if (NullShortCircuit && emitting_compound_assignment)
+ null_operator_label = call.NullOperatorLabel;
+
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temp = new LocalTemporary (Type);
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
- call.Emit (ec, op, args, loc);
+ call.NullShortCircuit = NullShortCircuit;
+ call.EmitStatement (ec, op, args, loc);
}
#endregion
public override void Emit (EmitContext ec)
{
var call = new CallEmitter ();
- call.EmitPredefined (ec, oper, arguments, loc);
+ call.Emit (ec, oper, arguments, loc);
}
public override void FlowAnalysis (FlowAnalysisContext fc)
var method = res.ResolveMember<MethodSpec> (new ResolveContext (ec.MemberContext), ref arguments);
if (method != null) {
var call = new CallEmitter ();
- call.EmitPredefined (ec, method, arguments);
+ call.EmitPredefined (ec, method, arguments, false);
}
}
fc.SetVariableAssigned (variable_info);
}
}
-
+
/// <summary>
/// Invocation of methods or delegates.
/// </summary>
if (mg.IsConditionallyExcluded)
return;
- mg.EmitCall (ec, arguments);
+ mg.EmitCall (ec, arguments, false);
}
public override void EmitStatement (EmitContext ec)
{
- Emit (ec);
+ if (mg.IsConditionallyExcluded)
+ return;
- //
- // Pop the return value if there is one
- //
- if (type.Kind != MemberKind.Void)
- ec.Emit (OpCodes.Pop);
+ mg.EmitCall (ec, arguments, true);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
return (type.Kind & dot_kinds) != 0 || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
}
+ static bool IsNullPropagatingValid (TypeSpec type)
+ {
+ return TypeSpec.IsReferenceType (type) || type.IsNullableType;
+ }
+
public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
{
var sn = expr as SimpleName;
return null;
}
+ if (this is NullMemberAccess) {
+ if (!IsNullPropagatingValid (expr.Type))
+ expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type);
+
+ if (expr_type.IsNullableType) {
+ expr = Nullable.Unwrap.Create (expr, true).Resolve (rc);
+ expr_type = expr.Type;
+ }
+ }
+
var lookup_arity = Arity;
bool errorMode = false;
Expression member_lookup;
sn = null;
}
+ if (this is NullMemberAccess) {
+ me.NullShortCircuit = true;
+ }
+
me = me.ResolveMemberAccess (rc, expr, sn);
if (Arity > 0) {
}
}
+ public class NullMemberAccess : MemberAccess
+ {
+ public NullMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
+ : base (expr, identifier, args, loc)
+ {
+ }
+ }
+
/// <summary>
/// Implements checked expressions
/// </summary>
var call = new CallEmitter ();
call.InstanceExpression = new CompilerGeneratedThis (type, loc);
- call.EmitPredefined (ec, base_ctor, argument_list);
+ call.EmitPredefined (ec, base_ctor, argument_list, false);
}
public override void EmitStatement (EmitContext ec)
}
public static TypeSpec GetEnumUnderlyingType (ModuleContainer module, TypeSpec nullableEnum)
+ {
+ return MakeType (module, EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum)));
+ }
+
+ public static TypeSpec MakeType (ModuleContainer module, TypeSpec underlyingType)
{
return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module,
- new[] { EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum)) });
+ new[] { underlyingType });
+
}
}
}
if (!type.IsNullableType)
- type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { type });
+ type = NullableInfo.MakeType (rc.Module, type);
return Wrap.Create (expr, type);
}
var ce = new CallEmitter ();
ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
- ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
+ ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
} else {
EmitFinallyBody (ec);
}
if (finally_host != null) {
var ce = new CallEmitter ();
ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
- ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
+ ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
} else {
EmitFinallyBody (ec);
}
--- /dev/null
+using System;
+
+struct S
+{
+ public int Prop { get; set; }
+}
+
+interface I
+{
+ int Method ();
+}
+
+class CI : I
+{
+ public int Method ()
+ {
+ return 33;
+ }
+
+ public int Prop { get; set; }
+}
+
+class C
+{
+ static int prop_calls;
+ static string Prop {
+ get {
+ ++prop_calls;
+ return null;
+ }
+ }
+
+ static int TestArray ()
+ {
+ int[] k = null;
+ var t1 = k?.ToString ();
+ if (t1 != null)
+ return 1;
+
+ var t2 = k?.GetLength (0);
+ if (t2 != null)
+ return 2;
+
+ var t3 = k?.Length;
+ if (t3 != null)
+ return 3;
+
+ k = new int[] { 3 };
+ var t11 = k?.ToString ();
+ if (t11.GetType () != typeof (string))
+ return 10;
+
+ var t12 = k?.GetLength (0);
+ if (t12.GetType () != typeof (int))
+ return 11;
+
+ var t13 = k?.Length;
+ if (t13.GetType () != typeof (int))
+ return 12;
+
+ return 0;
+ }
+
+ static int TestReferenceType ()
+ {
+ string s = null;
+ var t1 = s?.Split ();
+ if (t1 != null)
+ return 1;
+
+ var t2 = s?.Length;
+ if (t2 != null)
+ return 2;
+
+ var t3 = Prop?.Length;
+ if (t3 != null)
+ return 3;
+ if (prop_calls != 1)
+ return 4;
+
+ var t4 = Prop?.Split ();
+ if (t4 != null)
+ return 5;
+ if (prop_calls != 2)
+ return 6;
+
+ return 0;
+ }
+
+ static int TestGeneric<T> (T t) where T : class, I
+ {
+ // FIXME:
+ //var t1 = t?.Method ();
+ //if (t1 != null)
+ // return 1;
+
+ T[] at = null;
+ var t2 = at?.Length;
+ if (t2 != null)
+ return 2;
+
+ return 0;
+ }
+
+ static int TestNullable ()
+ {
+ int? i = 4;
+ var m = i?.CompareTo (3);
+ if (m.GetType () != typeof (int))
+ return 1;
+
+ if (m != 1)
+ return 2;
+
+ DateTime? dt = null;
+ dt?.ToString ();
+ if (dt?.ToString () != null)
+ return 3;
+
+ byte? b = 0;
+ if (b?.ToString () != "0")
+ return 4;
+
+ S? s = null;
+ var p1 = s?.Prop;
+ if (p1 != null)
+ return 5;
+
+ return 0;
+ }
+
+ static int Main ()
+ {
+ int res;
+ res = TestNullable ();
+ if (res != 0)
+ return 100 + res;
+
+ res = TestArray ();
+ if (res != 0)
+ return 200 + res;
+
+ res = TestReferenceType ();
+ if (res != 0)
+ return 300 + res;
+
+ CI ci = null;
+ res = TestGeneric<CI> (ci);
+ if (res != 0)
+ return 400 + res;
+
+ Console.WriteLine ("ok");
+ return 0;
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+class CI
+{
+ public long Field;
+ public sbyte? FieldNullable;
+ public object FieldReference;
+
+ public int Prop { get; set; }
+ public byte? PropNullable { get; set; }
+ public string PropReference { get; set; }
+
+ public event Action ev1;
+
+ public int this [bool arg] { get { return 4; } }
+ public int? this [int? arg] { get { return arg; } }
+ public string this [string arg] { get { return arg; } }
+
+/*
+ void Foo ()
+ {
+ var l = this?.Field;
+ }
+*/
+}
+
+class C
+{
+ /*
+ static int TestProperty ()
+ {
+ CI ci = null;
+ var m1 = ci?.Prop;
+ var m2 = ci?.PropNullable;
+ var m3 = ci?.PropReference;
+
+ ci?.Prop = 6;
+
+ ci = new CI ();
+ m1 = ci?.Prop;
+ m2 = ci?.PropNullable;
+ m3 = ci?.PropReference;
+
+ ci?.Prop = 5;
+ if (ci.Prop != 5)
+ return 1;
+
+// TODO: It's not allowed for now
+// ci?.Prop += 4;
+// var pp1 = ci?.Prop = 4;
+// var pp2 = ci?.Prop += 4;
+
+ return 0;
+ }
+
+ static int TestField ()
+ {
+ CI ci = null;
+ var m1 = ci?.Field;
+ var m2 = ci?.FieldNullable;
+ var m3 = ci?.FieldReference;
+
+// ci?.Field = 6;
+
+ ci = new CI ();
+ m1 = ci?.Field;
+ m2 = ci?.FieldNullable;
+ m3 = ci?.FieldReference;
+
+// ci?.Field = 5;
+// if (ci.Field != 5)
+// return 1;
+
+// TODO: It's not allowed for now
+// ci?.Field += 4;
+// var pp1 = ci?.Field = 4;
+// var pp2 = ci?.Field += 4;
+
+ return 0;
+ }
+
+ static int TestEvent ()
+ {
+ CI ci = null;
+ ci?.ev1 += null;
+
+ ci = new CI ();
+ ci?.ev1 += null;
+
+ return 0;
+ }
+*/
+/*
+ static int TestIndexer ()
+ {
+ CI ci = null;
+ var m1 = ci?[0];
+ var m2 = ci?[(int?) 1];
+ var m3 = ci?[""];
+ }
+*/
+ static int Main ()
+ {
+ int res;
+/*
+ res = TestProperty ();
+ if (res != 0)
+ return 10 + res;
+
+ res = TestField ();
+ if (res != 0)
+ return 20 + res;
+
+ res = TestEvent ();
+ if (res != 0)
+ return 30 + res;
+*/
+
+ Console.WriteLine ("ok");
+ return 0;
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+class C
+{
+ int field;
+
+ int Test1 ()
+ {
+ var x = this?.field;
+ if (x == null)
+ return 1;
+
+ // TODO: Should it really be of int? type
+
+ return 0;
+ }
+
+ static int Main ()
+ {
+ var c = new C ();
+ c.Test1 ();
+
+ const C c2 = null;
+ var res = c2?.field;
+ if (res != null)
+ return 1;
+
+ Console.WriteLine ("ok");
+ return 0;
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+public class D
+{
+ void Foo ()
+ {
+ }
+
+ public static void Main()
+ {
+ D d = null;
+ Action a = d?.Foo;
+ }
+}
</method>\r
</type>\r
</test>\r
+ <test name="test-null-operator-01.cs">\r
+ <type name="S">\r
+ <method name="Int32 get_Prop()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_Prop(Int32)" attrs="2182">\r
+ <size>8</size>\r
+ </method>\r
+ </type>\r
+ <type name="CI">\r
+ <method name="Int32 Method()" attrs="486">\r
+ <size>11</size>\r
+ </method>\r
+ <method name="Int32 get_Prop()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_Prop(Int32)" attrs="2182">\r
+ <size>8</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ <type name="C">\r
+ <method name="System.String get_Prop()" attrs="2193">\r
+ <size>22</size>\r
+ </method>\r
+ <method name="Int32 TestArray()" attrs="145">\r
+ <size>349</size>\r
+ </method>\r
+ <method name="Int32 TestReferenceType()" attrs="145">\r
+ <size>231</size>\r
+ </method>\r
+ <method name="Int32 TestGeneric[T](T)" attrs="145">\r
+ <size>58</size>\r
+ </method>\r
+ <method name="Int32 TestNullable()" attrs="145">\r
+ <size>384</size>\r
+ </method>\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>120</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
+ <test name="test-null-operator-02.cs">\r
+ <type name="CI">\r
+ <method name="Int32 get_Prop()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_Prop(Int32)" attrs="2182">\r
+ <size>8</size>\r
+ </method>\r
+ <method name="System.Nullable`1[System.Byte] get_PropNullable()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_PropNullable(System.Nullable`1[System.Byte])" attrs="2182">\r
+ <size>8</size>\r
+ </method>\r
+ <method name="System.String get_PropReference()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_PropReference(System.String)" attrs="2182">\r
+ <size>8</size>\r
+ </method>\r
+ <method name="Void add_ev1(System.Action)" attrs="2182">\r
+ <size>42</size>\r
+ </method>\r
+ <method name="Void remove_ev1(System.Action)" attrs="2182">\r
+ <size>42</size>\r
+ </method>\r
+ <method name="Int32 get_Item(Boolean)" attrs="2182">\r
+ <size>10</size>\r
+ </method>\r
+ <method name="System.Nullable`1[System.Int32] get_Item(System.Nullable`1[System.Int32])" attrs="2182">\r
+ <size>10</size>\r
+ </method>\r
+ <method name="System.String get_Item(System.String)" attrs="2182">\r
+ <size>10</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ <type name="C">\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>20</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
+ <test name="test-null-operator-03.cs">\r
+ <type name="C">\r
+ <method name="Int32 Test1()" attrs="129">\r
+ <size>62</size>\r
+ </method>\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>62</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
+ <test name="test-null-operator-04.cs">\r
+ <type name="D">\r
+ <method name="Void Foo()" attrs="129">\r
+ <size>2</size>\r
+ </method>\r
+ <method name="Void Main()" attrs="150">\r
+ <size>27</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-partial-01.cs">\r
<type name="Foo.Hello">\r
<method name="Void .ctor()" attrs="6278">\r