This commit is contained in:
Ivan Bondarev 2021-10-10 14:01:37 +02:00
parent 0923a10a68
commit 05f94b9dde
4 changed files with 131 additions and 29 deletions

View file

@ -9023,7 +9023,59 @@ namespace PascalABCCompiler.NETGenerator
else if (real_parameters[0].type.is_nullable_type && real_parameters[1].type.is_nullable_type)
{
MethodInfo mi_left = null;
MethodInfo mi_right = null;
TypeInfo ti_left = helper.GetTypeReference(real_parameters[0].type);
TypeInfo ti_right = helper.GetTypeReference(real_parameters[1].type);
Label lb_false = il.DefineLabel();
Label lb_true = il.DefineLabel();
Label lb_end = il.DefineLabel();
Label lb_common = il.DefineLabel();
if (!(real_parameters[0] is IDefaultOperatorNode) && !(real_parameters[1] is IDefaultOperatorNode))
{
is_dot_expr = true;
real_parameters[0].visit(this);
if (real_parameters[0].type is IGenericTypeInstance)
mi_left = TypeBuilder.GetMethod(ti_left.tp, typeof(Nullable<>).GetMethod("get_HasValue", new Type[] { }));
else
mi_left = ti_left.tp.GetMethod("get_HasValue", new Type[] { });
il.Emit(OpCodes.Call, mi_left);
LocalBuilder tmp_lb = il.DeclareLocal(TypeFactory.BoolType);
il.Emit(OpCodes.Stloc, tmp_lb);
il.Emit(OpCodes.Ldloc, tmp_lb);
is_dot_expr = true;
real_parameters[1].visit(this);
if (real_parameters[1].type is IGenericTypeInstance)
mi_right = TypeBuilder.GetMethod(ti_right.tp, typeof(Nullable<>).GetMethod("get_HasValue", new Type[] { }));
else
mi_right = ti_right.tp.GetMethod("get_HasValue", new Type[] { });
il.Emit(OpCodes.Call, mi_right);
if (value.basic_function.basic_function_type == basic_function_type.objnoteq)
{
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Brtrue, lb_true);
il.Emit(OpCodes.Br, lb_common);
il.MarkLabel(lb_true);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Br, lb_end);
}
else
{
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Brfalse, lb_false);
il.Emit(OpCodes.Ldloc, tmp_lb);
il.Emit(OpCodes.Brtrue, lb_common);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Br, lb_end);
il.MarkLabel(lb_false);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Br, lb_end);
}
}
il.MarkLabel(lb_common);
if (real_parameters[0] is IDefaultOperatorNode)
il.Emit(OpCodes.Ldc_I4_0);
else
@ -9045,8 +9097,8 @@ namespace PascalABCCompiler.NETGenerator
}
MethodInfo mi_right = null;
TypeInfo ti_right = helper.GetTypeReference(real_parameters[1].type);
if (real_parameters[1] is IDefaultOperatorNode)
il.Emit(OpCodes.Ldc_I4_0);
else
@ -9067,6 +9119,7 @@ namespace PascalABCCompiler.NETGenerator
}
}
if (!(real_parameters[0] is IDefaultOperatorNode))
{
is_dot_expr = true;
@ -9079,38 +9132,40 @@ namespace PascalABCCompiler.NETGenerator
real_parameters[1].visit(this);
il.Emit(OpCodes.Call, mi_right);
}
EmitOperator(value);
if (!(real_parameters[0] is IDefaultOperatorNode) && !(real_parameters[1] is IDefaultOperatorNode))
MethodInfo eq_mi = null;
if (real_parameters[0].type is IGenericTypeInstance)
{
is_dot_expr = true;
real_parameters[0].visit(this);
if (real_parameters[0].type is IGenericTypeInstance)
mi_left = TypeBuilder.GetMethod(ti_right.tp, typeof(Nullable<>).GetMethod("get_HasValue", new Type[] { }));
else
mi_left = ti_left.tp.GetMethod("get_HasValue", new Type[] { });
il.Emit(OpCodes.Call, mi_left);
is_dot_expr = true;
real_parameters[1].visit(this);
if (real_parameters[1].type is IGenericTypeInstance)
mi_right = TypeBuilder.GetMethod(ti_right.tp, typeof(Nullable<>).GetMethod("get_HasValue", new Type[] { }));
else
mi_right = ti_right.tp.GetMethod("get_HasValue", new Type[] { });
var ctn = (real_parameters[0].type as IGenericTypeInstance).generic_parameters[0] as ICommonTypeNode;
il.Emit(OpCodes.Call, mi_right);
if (value.basic_function.basic_function_type == basic_function_type.objnoteq)
if (ctn != null)
{
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Or);
}
else
{
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.And);
foreach (ICommonMethodNode cmn in ctn.methods)
if ((value.basic_function.basic_function_type == basic_function_type.objnoteq? cmn.name == "op_Inequality" || cmn.name == "<>":cmn.name == "op_Equality" || cmn.name == "=") && cmn.parameters.Length == 2 && cmn.parameters[0].type == ctn && cmn.parameters[1].type == ctn)
{
eq_mi = helper.GetMethod(cmn).mi;
break;
}
}
}
else if (real_parameters[0].type is ICompiledTypeNode)
{
var t = (real_parameters[0].type as ICompiledTypeNode).compiled_type.GetGenericArguments()[0];
foreach (MethodInfo mi in t.GetMethods(BindingFlags.Public | BindingFlags.Static))
{
if ((value.basic_function.basic_function_type == basic_function_type.objnoteq ? mi.Name == "op_Inequality" : mi.Name == "op_Equality") && mi.GetParameters().Length == 2 && mi.GetParameters()[0].ParameterType == t && mi.GetParameters()[1].ParameterType == t)
{
eq_mi = mi;
break;
}
}
}
if (eq_mi != null)
il.Emit(OpCodes.Call, eq_mi);
else
EmitOperator(value);
il.MarkLabel(lb_end);
is_dot_expr = tmp_dot;
if (tmp_dot)
{

19
TestSuite/nullable7.pas Normal file
View file

@ -0,0 +1,19 @@
var i: integer;
type
r1 = record
static function operator=(a,b: r1): boolean;
begin
i := 1;
Result := false;
end;
static function operator<>(a,b: r1) := false;
end;
begin
var n1, n2: r1?;
assert(n1 = n2); // Должно быть true, без вызва r1.operator=
n1 := new r1;
assert(not (n1 <> n1));
assert(not (n1 = n1));
assert(i = 1);
end.

15
TestSuite/nullable8.pas Normal file
View file

@ -0,0 +1,15 @@
type
r1 = record end;
begin
var n1, n2: r1?;
assert(n1 = n2);
assert(n1 = nil);
assert(not (n1<>n2));
n1 := new r1;
assert(n1 = n1);
n2 := new r1;
assert(n1 = n2);
n2 := nil;
assert(n1 <> n2);
end.

13
TestSuite/nullable9.pas Normal file
View file

@ -0,0 +1,13 @@

begin
var n1, n2: System.TimeSpan?;
assert(n1=n2);
assert(n1=nil);
n1 := System.TimeSpan.Zero;
n2 := System.TimeSpan.Zero;
assert(n1=n2);
assert(not (n1<>n2));
n2 := nil;
assert(n1<>n2);
assert(not (n1=n2));
end.