From 0f4e7b65b2596af6cb37c785b4c7a5e7139f17dd Mon Sep 17 00:00:00 2001
From: marco <marco@freepascal.org>
Date: Tue, 5 Nov 2019 15:35:13 +0000
Subject: [PATCH] # revisions: 41843,41844,42700

git-svn-id: branches/fixes_3_2@43396 -
---
 .gitattributes          |   4 ++
 compiler/defcmp.pas     |   5 ++
 compiler/ncnv.pas       |  39 ++++++++---
 tests/test/tarray18.pp  | 149 ++++++++++++++++++++++++++++++++++++++++
 tests/test/tarray19.pp  |  22 ++++++
 tests/test/tarray20.pp  |  21 ++++++
 tests/webtbs/tw35955.pp |  18 +++++
 7 files changed, 248 insertions(+), 10 deletions(-)
 create mode 100644 tests/test/tarray18.pp
 create mode 100644 tests/test/tarray19.pp
 create mode 100644 tests/test/tarray20.pp
 create mode 100644 tests/webtbs/tw35955.pp

diff --git a/.gitattributes b/.gitattributes
index a6133088aa..f11cf64422 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12694,7 +12694,10 @@ tests/test/tarray14.pp svneol=native#text/pascal
 tests/test/tarray15.pp svneol=native#text/pascal
 tests/test/tarray16.pp svneol=native#text/pascal
 tests/test/tarray17.pp svneol=native#text/pascal
+tests/test/tarray18.pp svneol=native#text/pascal
+tests/test/tarray19.pp svneol=native#text/pascal
 tests/test/tarray2.pp svneol=native#text/plain
+tests/test/tarray20.pp svneol=native#text/pascal
 tests/test/tarray3.pp svneol=native#text/plain
 tests/test/tarray4.pp svneol=native#text/plain
 tests/test/tarray5.pp svneol=native#text/plain
@@ -16435,6 +16438,7 @@ tests/webtbs/tw35862.pp svneol=native#text/pascal
 tests/webtbs/tw3589.pp svneol=native#text/plain
 tests/webtbs/tw3594.pp svneol=native#text/plain
 tests/webtbs/tw3595.pp svneol=native#text/plain
+tests/webtbs/tw35955.pp svneol=native#text/pascal
 tests/webtbs/tw3612.pp svneol=native#text/plain
 tests/webtbs/tw3617.pp svneol=native#text/plain
 tests/webtbs/tw3619.pp svneol=native#text/plain
diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 9b159d8021..21dd36fd16 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -1009,6 +1009,11 @@ implementation
                                       end
                                     else if subeq>te_convert_l6 then
                                       eq:=pred(subeq)
+                                    else if subeq=te_convert_operator then
+                                      { the operater needs to be applied by element, so we tell
+                                        the caller that it's some unpreffered conversion and let
+                                        it handle the per-element stuff }
+                                      eq:=te_convert_l6
                                     else
                                       eq:=subeq;
                                    doconv:=tc_arrayconstructor_2_dynarray;
diff --git a/compiler/ncnv.pas b/compiler/ncnv.pas
index 3a0b1ca57f..9cdbe428ab 100644
--- a/compiler/ncnv.pas
+++ b/compiler/ncnv.pas
@@ -1657,7 +1657,25 @@ implementation
         left:=nil;
         { create a set constructor tree }
         arrayconstructor_to_set(hp);
-        result:=hp;
+        if is_emptyset(hp) then
+          begin
+            { enforce the result type for an empty set }
+            hp.resultdef:=resultdef;
+            result:=hp;
+          end
+        else if hp.resultdef<>resultdef then
+          begin
+            { the set might contain a subrange element (e.g. through a variable),
+              thus we need to insert another type conversion }
+            if nf_explicit in flags then
+              result:=ctypeconvnode.create_explicit(hp,totypedef)
+            else if nf_internal in flags then
+              result:=ctypeconvnode.create_internal(hp,totypedef)
+            else
+              result:=ctypeconvnode.create(hp,totypedef);
+          end
+        else
+          result:=hp;
       end;
 
 
@@ -2387,15 +2405,6 @@ implementation
            not(resultdef.typ in [procvardef,recorddef,setdef]) then
           maybe_call_procvar(left,true);
 
-        { convert array constructors to sets, because there is no conversion
-          possible for array constructors }
-        if (resultdef.typ<>arraydef) and
-           is_array_constructor(left.resultdef) then
-          begin
-            arrayconstructor_to_set(left);
-            typecheckpass(left);
-          end;
-
         if target_specific_general_typeconv then
           exit;
 
@@ -2484,6 +2493,16 @@ implementation
 
               te_incompatible :
                 begin
+                  { convert an array constructor to a set so that we still get
+                    the error "set of Y incompatible to Z" instead of "array of
+                    X incompatible to Z" }
+                  if (resultdef.typ<>arraydef) and
+                     is_array_constructor(left.resultdef) then
+                    begin
+                      arrayconstructor_to_set(left);
+                      typecheckpass(left);
+                    end;
+
                   { Procedures have a resultdef of voiddef and functions of their
                     own resultdef. They will therefore always be incompatible with
                     a procvar. Because isconvertable cannot check for procedures we
diff --git a/tests/test/tarray18.pp b/tests/test/tarray18.pp
new file mode 100644
index 0000000000..a167c1984f
--- /dev/null
+++ b/tests/test/tarray18.pp
@@ -0,0 +1,149 @@
+program tarray18;
+
+{$mode objfpc}
+{$modeswitch advancedrecords}
+
+function CheckArray(aArr, aExpected: array of LongInt): Boolean;
+var
+  i: LongInt;
+begin
+  if Length(aArr) <> Length(aExpected) then
+    Exit(False);
+  for i := Low(aArr) to High(aArr) do
+    if aArr[i] <> aExpected[i] then
+      Exit(False);
+  Result := True;
+end;
+
+type
+  TTest1 = record
+    f: array of LongInt;
+    class operator := (a: array of LongInt): TTest1;
+  end;
+
+  TTest2 = record
+    f: array of LongInt;
+    class operator Explicit(a: array of LongInt): TTest2;
+  end;
+
+  TTest3 = record
+    f: array of LongInt;
+  end;
+
+  TTest4 = record
+    f: array of LongInt;
+  end;
+
+function AssignArray(a: array of LongInt): specialize TArray<LongInt>;
+var
+  i: LongInt;
+begin
+  SetLength(Result, Length(a));
+  for i := 0 to High(a) do
+    Result[i] := a[i];
+end;
+
+class operator TTest1.:=(a: array of LongInt): TTest1;
+begin
+  Result.f := AssignArray(a);
+end;
+
+class operator TTest2.Explicit(a: array of LongInt): TTest2;
+begin
+  Result.f := AssignArray(a);
+end;
+
+operator :=(a: array of LongInt): TTest3;
+begin
+  Result.f := AssignArray(a);
+end;
+
+operator :=(a: array of LongInt): TTest4;
+begin
+  Result.f := AssignArray(a);
+end;
+
+procedure Test1(aRec: TTest1; a: array of LongInt; aCode: LongInt);
+begin
+  if not CheckArray(aRec.f, a) then
+    Halt(aCode);
+end;
+
+procedure Test2(aRec: TTest2; a: array of LongInt; aCode: LongInt);
+begin
+  if not CheckArray(aRec.f, a) then
+    Halt(aCode);
+end;
+
+procedure Test3(aRec: TTest3; a: array of LongInt; aCode: LongInt);
+begin
+  if not CheckArray(aRec.f, a) then
+    Halt(aCode);
+end;
+
+procedure Test4(aRec: TTest4; a: array of LongInt; aCode: LongInt);
+begin
+  if not CheckArray(aRec.f, a) then
+    Halt(aCode);
+end;
+
+var
+  t1: TTest1;
+  t2: TTest2;
+  t3: TTest3;
+  t4: TTest4;
+begin
+  t1 := [];
+  if not CheckArray(t1.f, []) then
+    Halt(1);
+  t1 := [2, 4];
+  if not CheckArray(t1.f, [2, 4]) then
+    Halt(2);
+  t1 := TTest1([]);
+  if not CheckArray(t1.f, []) then
+    Halt(3);
+  t1 := TTest1([2, 4]);
+  if not CheckArray(t1.f, [2, 4]) then
+    Halt(4);
+
+  t2 := TTest2([]);
+  if not CheckArray(t2.f, []) then
+    Halt(5);
+  t2 := TTest2([2, 4]);
+  if not CheckArray(t2.f, [2, 4]) then
+    Halt(6);
+
+  t3 := [];
+  if not CheckArray(t3.f, []) then
+    Halt(7);
+  t3 := [2, 4];
+  if not CheckArray(t3.f, [2, 4]) then
+    Halt(8);
+  t3 := TTest3([]);
+  if not CheckArray(t3.f, []) then
+    Halt(9);
+  t3 := TTest3([2, 4]);
+  if not CheckArray(t3.f, [2, 4]) then
+    Halt(10);
+
+  t4 := TTest4([]);
+  if not CheckArray(t4.f, []) then
+    Halt(11);
+  t4 := TTest4([2, 4]);
+  if not CheckArray(t4.f, [2, 4]) then
+    Halt(12);
+
+  Test1([], [], 13);
+  Test1([2, 4], [2, 4], 14);
+
+  Test2(TTest2([]), [], 15);
+  Test2(TTest2([2, 4]), [2, 4], 16);
+
+  Test3([], [], 17);
+  Test3([2, 4], [2, 4], 18);
+
+  Test4(TTest4([]), [], 19);
+  Test4(TTest4([2, 4]), [2, 4], 20);
+
+  Writeln('ok');
+end.
diff --git a/tests/test/tarray19.pp b/tests/test/tarray19.pp
new file mode 100644
index 0000000000..5e8431a7e4
--- /dev/null
+++ b/tests/test/tarray19.pp
@@ -0,0 +1,22 @@
+{ %FAIL }
+
+program tarray19;
+
+{$mode objfpc}
+{$modeswitch advancedrecords}
+
+type
+  TTest = record
+    class operator Explicit(a: array of LongInt): TTest;
+  end;
+
+class operator TTest.Explicit(a: array of LongInt): TTest;
+begin
+
+end;
+
+var
+  t: TTest;
+begin
+  t := [21, 42];
+end.
diff --git a/tests/test/tarray20.pp b/tests/test/tarray20.pp
new file mode 100644
index 0000000000..4c013b061f
--- /dev/null
+++ b/tests/test/tarray20.pp
@@ -0,0 +1,21 @@
+{ %FAIL }
+
+program tarray20;
+
+{$mode objfpc}
+{$modeswitch advancedrecords}
+
+type
+  TTest = record
+  end;
+
+operator Explicit(a: array of LongInt): TTest;
+begin
+
+end;
+
+var
+  t: TTest;
+begin
+  t := [21, 42];
+end.
diff --git a/tests/webtbs/tw35955.pp b/tests/webtbs/tw35955.pp
new file mode 100644
index 0000000000..3b473bdcd9
--- /dev/null
+++ b/tests/webtbs/tw35955.pp
@@ -0,0 +1,18 @@
+{ %NORUN }
+
+program tw35955;
+
+{$mode delphi}
+
+type
+  TVariantArray = array of Variant;
+
+var
+  S: string;
+  A: TVariantArray;
+begin
+  S := 'xyz';
+  A := [S]; // << project1.lpr(13,8) Error: Compilation raised exception internally
+  Writeln(A[0]);
+  Readln;
+end.