diff --git a/compiler/src/main/pascal/uSemantic.pas b/compiler/src/main/pascal/uSemantic.pas index a2c8e8e..31775d4 100644 --- a/compiler/src/main/pascal/uSemantic.pas +++ b/compiler/src/main/pascal/uSemantic.pas @@ -8000,6 +8000,15 @@ begin TArrayLiteralExpr(Arg).IsConstArray := True; Arg.ResolvedType := Par.ResolvedType; end; + { Empty bracket literal [] bound to a plain open-array formal: pin its type + to the formal so codegen emits a zero-length open array (data=nil, high=-1) + of the right element type. The untyped [] would otherwise reach codegen + with no ResolvedType. } + if (Par.ResolvedType <> nil) and (Par.ResolvedType.Kind = tyOpenArray) and + (Arg is TArrayLiteralExpr) and + (TArrayLiteralExpr(Arg).Elements.Count = 0) and + (Arg.ResolvedType = nil) then + Arg.ResolvedType := Par.ResolvedType; end; end; @@ -8073,6 +8082,17 @@ begin Result := 2; Exit; end; + { An EMPTY bracket literal [] passed to a plain open-array parameter (array of + T) is a valid empty open array of any element type — it has no elements to + infer a type from, so AnalyseArrayLiteralExpr left it untyped (nil). Match + it here, before the nil-arg bail; AnalyseProcCall re-types it to the formal's + open-array type so codegen emits a zero-length (data=nil, high=-1) array. } + if (AParam.Kind = tyOpenArray) and (AArgExpr is TArrayLiteralExpr) and + (TArrayLiteralExpr(AArgExpr).Elements.Count = 0) then + begin + Result := 2; + Exit; + end; if AArg = nil then Exit; { Integer literal (untyped constant) matches any integer type exactly — mirrors Pascal's treatment of untyped integer constants. Floating-point diff --git a/compiler/src/test/pascal/cp.test.e2e.openarray.pas b/compiler/src/test/pascal/cp.test.e2e.openarray.pas index 26abddf..23c14fe 100644 --- a/compiler/src/test/pascal/cp.test.e2e.openarray.pas +++ b/compiler/src/test/pascal/cp.test.e2e.openarray.pas @@ -52,6 +52,10 @@ type procedure TestRun_DynToOpen_Empty; procedure TestRun_DynToOpen_OfString; procedure TestRun_DynToOpen_PassToNested; + + { Empty bracket literal [] as an open-array argument } + procedure TestRun_EmptyLiteral_ToOpenArray; + procedure TestRun_EmptyLiteral_OverloadDisambiguation; end; implementation @@ -484,6 +488,54 @@ begin AssertRunsOnAll(SrcDynNested, '100' + #10, 0); end; +procedure TE2EOpenArrayTests.TestRun_EmptyLiteral_ToOpenArray; +{ An empty bracket literal [] passed to a plain open-array parameter — both for a + string and an integer open array — is a valid zero-length open array. This + previously failed overload resolution ("No matching overload"). } +const Src = ''' + program P; + procedure ShowS(const A: array of string); + begin WriteLn(Length(A)) end; + procedure ShowI(const A: array of Integer); + var i, s: Integer; + begin + s := 0; + for i := 0 to Length(A) - 1 do s := s + A[i]; + WriteLn(Length(A), ' ', s) + end; + begin + ShowS([]); + ShowS(['a', 'b']); + ShowI([]); + ShowI([10, 20, 30]) + end. + '''; +begin + if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end; + AssertRunsOnAll(Src, + '0' + #10 + '2' + #10 + '0 0' + #10 + '3 60' + #10, 0); +end; + +procedure TE2EOpenArrayTests.TestRun_EmptyLiteral_OverloadDisambiguation; +{ [] selects the open-array overload over a non-array one; a non-bracket + argument still picks the scalar overload. } +const Src = ''' + program P; + procedure F(X: Integer); overload; + begin WriteLn('int') end; + procedure F(const A: array of string); overload; + begin WriteLn('oa ', Length(A)) end; + begin + F([]); + F(['x']); + F(5) + end. + '''; +begin + if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end; + AssertRunsOnAll(Src, 'oa 0' + #10 + 'oa 1' + #10 + 'int' + #10, 0); +end; + initialization RegisterTest(TE2EOpenArrayTests);