Re: [fpc-pascal] Generic type conflicts

2019-11-10 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
So., 10. Nov. 2019, 02:11:

>
>
> > On Nov 7, 2019, at 12:28 PM, Ben Grasset via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
> >
> >   {$IF GetTypeKind(T) in [tkInteger, tkInt64, tkQWord]}
> > Result := A div B
> >   {$ELSEIF GetTypeKind(T) = tkFloat}
> > Result := A / B
> >   {$ELSE}
> > Result := Default(T);
> >   {$ENDIF}
>
> This is what I hinted in my post.  Has anything like this been considered
> before? It seems necessary to me but it looks like Sven had some fancy work
> around using pointers. Either way the compile time directive would be
> faster and avoid the conditional statements.
>

The token recorder does not support preprocessor conditions. Not to mention
that this mixes preprocessor and parser stuff. Generics are part of the
parser.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-09 Thread Ryan Joseph via fpc-pascal


> On Nov 7, 2019, at 12:28 PM, Ben Grasset via fpc-pascal 
>  wrote:
> 
>   {$IF GetTypeKind(T) in [tkInteger, tkInt64, tkQWord]}
> Result := A div B
>   {$ELSEIF GetTypeKind(T) = tkFloat}
> Result := A / B
>   {$ELSE}
> Result := Default(T);
>   {$ENDIF}

This is what I hinted in my post.  Has anything like this been considered 
before? It seems necessary to me but it looks like Sven had some fancy work 
around using pointers. Either way the compile time directive would be faster 
and avoid the conditional statements.
Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-08 Thread Sven Barth via fpc-pascal

Am 02.11.2019 um 15:55 schrieb Ryan Joseph via fpc-pascal:

I've wanted to make a generic version of a vector for a while but I always give 
up because it seems not very possible. It's probably not even a great idea 
because many methods don't translate between float and integer but I wanted to 
prevent other code duplication if possible.

Here's an example of how things break down. Are there any solutions for this 
currently? I feel like generics need to support some compiler directives so 
different blocks of code can specialize different depending on the type.

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_vector_2;
uses
   Math;

type
   generic TVec2 = record
 x, y: TScalar;
 function Normalize: TVec2;
   end;
   TVec2f = specialize TVec2;
   TVec2i = specialize TVec2;

function TVec2.Normalize: TVec2;
var
   fac: TScalar;
begin
   // Can't determine which overloaded function to call
   // Incompatible types: got "Extended" expected "LongInt"
   fac:=Sqrt(Sqr(x) + Sqr(y));
   if fac<>0.0 then begin
 // Incompatible types: got "Single" expected "LongInt"
 fac:=1.0/fac;
 result.x:=x*fac;
 result.y:=y*fac;
   end else begin
 result.x:=0;
 result.y:=0;
   end;
end;

begin
end.


First of Sqrt always returns a ValReal (aka the best precision floating 
point), thus you should declare fac as ValReal. Thus you should cast x 
and y to ValReal before passing them to Sqr to avoid overload troubles.


Then you only need to ensure that the adjusted vector components are 
passed correctly to the result which will lead to code like this:


=== code begin ===

program tgenvec;

{$mode objfpc}
{$modeswitch advancedrecords}

uses
  Math;

type
  generic TVec2 = record
  private type
    PScalar = ^TScalar;
  public
    x, y: TScalar;
    function Normalize: TVec2;
  end;

function TVec2.Normalize: TVec2;
var
  fac, tmpx, tmpy: ValReal;
begin
  fac := Sqrt(Sqr(ValReal(x)) + Sqr(ValReal(y)));
  if fac <> 0.0 then begin
    fac := 1.0 / fac;
    tmpx := x * fac;
    tmpy := y * fac;
    if GetTypeKind(TScalar) in [tkInteger, tkInt64, tkQWord] then begin
  Result.x := Round(tmpx);
  Result.y := Round(tmpy);
    end else if GetTypeKind(TScalar) = tkFloat then begin
  Result.x := PScalar(@tmpx)^;
  Result.y := PScalar(@tmpy)^;
    end;
  end else
    Result := Default(TVec2);
end;

type
  TVec2f = specialize TVec2;
  TVec2i = specialize TVec2;

var
  vf: TVec2f;
  vi: TVec2i;
begin
  vf.x := 2.5;
  vf.y := 3.5;
  vf := vf.Normalize;
  Writeln(vf.x, ' ', vf.y);
  // on Win64 this prints 5.8123819371909646E-001 8.1373347120673500E-001
  vi.x := 2;
  vi.y := 4;
  vi := vi.Normalize;
  Writeln(vi.x, ' ', vi.y);
  // on Win64 this prints 0 1
end.

=== code end ===

The path not taken for the GetTypeKind inside the TVec<>.Normalize will 
be optimized away. The pointer conversion is needed, because a floating 
point type can not be assigned to an integer type. Sadly the compiler 
does not realize that it does not really need to take the address there, 
so that will stay even in O4 for the non-floating point case. But hey, 
you've got a generic Normalize then...


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-08 Thread Ben Grasset via fpc-pascal
On Fri, Nov 8, 2019 at 11:18 AM Ben Grasset  wrote:

> I know what you mean, and I'm aware, but you actually kind of can to the
> fairly straightforward extent that I'm concerned about with Ryan's patch
> (i.e. simply passing constant results as constraints rather than function
> parameters.)
>

Also, if you were referring specifically to the "making the choice part",
looking at the PDQSort C++ code again, all it was actually doing is
evaluating the *types* involved, not the comparison function itself. Which
would be fairly straightforward to replicate in FPC using something like a
constant TTypeKind constraint.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-08 Thread Jonas Maebe
On 08/11/2019 17:18, Ben Grasset via fpc-pascal wrote:
> On Fri, Nov 8, 2019 at 11:04 AM Jonas Maebe  > wrote:
> 
> You can't. It's the main difference between C++ templates, which is a
> Turing-complete programming language, and generics, which is simply a
> parametrising mechanic.
> 
> I know what you mean, and I'm aware, but you actually kind of can to the
> fairly straightforward extent that I'm concerned about with Ryan's patch
> (i.e. simply passing constant results as constraints rather than
> function parameters.) 

It does explain why templates can contain invalid C++: it's because the
invalid C++ gets discarded before it is (semantically) presented to the
C++ parser. This does not work with generics, because generics are
Pascal themselves.


Jonas
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-08 Thread Ben Grasset via fpc-pascal
On Fri, Nov 8, 2019 at 11:04 AM Jonas Maebe  wrote:

> You can't. It's the main difference between C++ templates, which is a
> Turing-complete programming language, and generics, which is simply a
> parametrising mechanic.
>

I know what you mean, and I'm aware, but you actually kind of can to the
fairly straightforward extent that I'm concerned about with Ryan's patch
(i.e. simply passing constant results as constraints rather than function
parameters.)
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-08 Thread Jonas Maebe
On 08/11/2019 17:02, Ben Grasset via fpc-pascal wrote:
> The original C++ version used "constexpr" template parameters to
> determine at compile time whether or not the user-provided comparison
> function was one for which it should use the "branchless" version of its
> code for item partitioning or not, and based on that passed the boolean
> "true" or "false" result as another constant template parameter.
> 
> The end result of course being that the compiled code is always tuned
> precisely to the given comparison function, without any kind of runtime
> selection based on non-constant boolean function parameters (which is
> what I've currently had to write the "branchless" choice as, defaulting
> to "true" unless the user specifies otherwise.)
> 
> With Ryan's patch, I'll at least be able to write that choice as a
> constant parameter instead, although I'm unaware of any way to replicate
> actually having the compiler *make* the choice.

You can't. It's the main difference between C++ templates, which is a
Turing-complete programming language, and generics, which is simply a
parametrising mechanic.


Jonas
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-08 Thread Ben Grasset via fpc-pascal
On Fri, Nov 8, 2019 at 1:33 AM Michael Van Canneyt 
wrote:

> As an aside:
> In my opinion (keep in mind I am not a big fan of generics) the above code
> would be a prime example where one should not be using generics, but
> simple overloads.
> If you need to use GetTypeKind in a generic, I think you're on the wrong
> path.
> Use of IsManagedType() in a generic is stretching it, but GetTypeKind() is
> over the line.
>

I agree to an extent that it's not a *huge* problem and that there are
workarounds in many cases, however there's definitely certain things that
simply cannot be achieved in an equally performant way via any alternative
such as normal overloading.

That said, Ryan's currently-in-limbo patch for constant generic parameters
actually itself in theory already provides proper solutions for quite a few
aspects of the general problem, so even as it stands right now we're at
least making progress towards improving the situation I'd say.

For example, I recently did a translation of the fairly well-known
"PDQSort" algorithm from C++ to Pascal:

https://github.com/Akira13641/PasPDQSort

The original C++ version used "constexpr" template parameters to determine
at compile time whether or not the user-provided comparison function was
one for which it should use the "branchless" version of its code for item
partitioning or not, and based on that passed the boolean "true" or "false"
result as another constant template parameter.

The end result of course being that the compiled code is always tuned
precisely to the given comparison function, without any kind of runtime
selection based on non-constant boolean function parameters (which is what
I've currently had to write the "branchless" choice as, defaulting to
"true" unless the user specifies otherwise.)

With Ryan's patch, I'll at least be able to write that choice as a constant
parameter instead, although I'm unaware of any way to replicate actually
having the compiler *make* the choice.

So perhaps what's needed is not even necessarily something like "static if"
support, but rather just more compiler intrinsics along the lines of
GetTypeKind / IsManagedType that evaluate different things and return
constant values, which in combination with Ryan's patch will go a long way
as far as increasing the granularity of control available to users of FPC
as far as this kind of stuff.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-07 Thread Michael Van Canneyt



On Thu, 7 Nov 2019, Ben Grasset via fpc-pascal wrote:


On Thu, Nov 7, 2019 at 10:23 AM Michael Van Canneyt 
wrote:


If I understood Sven's example correct, then the compiler does exactly this
already.



It does in the sense of *code generation* for things that are specifically
compiler intrinsics (which I was aware of), but not in a sense that makes
the issue Ryan was posting about here avoidable. Maybe this will explain
what I'm trying to get at a bit better:

program Example;

{$mode ObjFPC}

// Without actually specializing and using GDiv,
// this program compiles fine.

generic function GDiv(const A, B: T): T; inline;
begin
 if GetTypeKind(T) in [tkInteger, tkInt64, tkQWord] then
   Result := A div B
 else if GetTypeKind(T) = tkFloat then
   Result := A / B
 else
   Result := Default(T);
end;

// However, once we do specialize it...

procedure UseGDiv;
begin
 // Example.pas(13,17) Error: Incompatible types: got "Double" expected
"Int64"
 WriteLn(specialize GDiv(1, 2));
 // Example.pas(11,17) Error: Operator is not overloaded: "Double" div
"Double"
 WriteLn(specialize GDiv(1, 2));
end;


Thanks for the explanation. All is clear.

As an aside:
In my opinion (keep in mind I am not a big fan of generics) the above code
would be a prime example where one should not be using generics, but simple 
overloads.
If you need to use GetTypeKind in a generic, I think you're on the wrong path. 
Use of IsManagedType() in a generic is stretching it, but GetTypeKind() is over the line.


Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-07 Thread Ben Grasset via fpc-pascal
On Thu, Nov 7, 2019 at 10:23 AM Michael Van Canneyt 
wrote:

> If I understood Sven's example correct, then the compiler does exactly this
> already.
>

It does in the sense of *code generation* for things that are specifically
compiler intrinsics (which I was aware of), but not in a sense that makes
the issue Ryan was posting about here avoidable. Maybe this will explain
what I'm trying to get at a bit better:

program Example;

{$mode ObjFPC}

// Without actually specializing and using GDiv,
// this program compiles fine.

generic function GDiv(const A, B: T): T; inline;
begin
  if GetTypeKind(T) in [tkInteger, tkInt64, tkQWord] then
Result := A div B
  else if GetTypeKind(T) = tkFloat then
Result := A / B
  else
Result := Default(T);
end;

// However, once we do specialize it...

procedure UseGDiv;
begin
  // Example.pas(13,17) Error: Incompatible types: got "Double" expected
"Int64"
  WriteLn(specialize GDiv(1, 2));
  // Example.pas(11,17) Error: Operator is not overloaded: "Double" div
"Double"
  WriteLn(specialize GDiv(1, 2));
end;

// Note: I'm fully aware that having the *normal* if-statement work in such
a way that the
// the above would compile is neither feasible or a good idea. However,
imagine something
// like the following hypothetical version of GDiv, which would rely on the
conditional
// compilation aspect of the scanner to be aware of more than it currently
is, and also
// for the conditional compilation syntax to be somewhat more advanced:

generic function GDiv(const A, B: T): T; inline;
begin
  // Here, we're doing precisely the same kind of thing that is currently
  // possible with SizeOf, Declared, Defined, and so on.
  {$IF GetTypeKind(T) in [tkInteger, tkInt64, tkQWord]}
Result := A div B
  {$ELSEIF GetTypeKind(T) = tkFloat}
Result := A / B
  {$ELSE}
Result := Default(T);
  {$ENDIF}
end;

begin
end.

Of course, implementing that kind of thing in the scanner would likely be
far more difficult than implementing it as something that happens in
"normal" code, via some syntax or perhaps intrinsic that makes it
distinctively a fully-statically-considered conditional, as opposed to a
partially-statically considered one as is the case with normal "if".
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-07 Thread Sven Barth via fpc-pascal
Michael Van Canneyt  schrieb am Do., 7. Nov. 2019,
16:23:

>
>
> On Thu, 7 Nov 2019, Ben Grasset via fpc-pascal wrote:
>
> > On Thu, Nov 7, 2019 at 3:03 AM Sven Barth via fpc-pascal <
> > fpc-pascal@lists.freepascal.org> wrote:
> >
> >> If there is no type checking, then it is *not* verified by the compiler.
> >>
> >
> > Perhaps "no type checking" was not the write way to put it. A better way
> to
> > describe it might be:
> >
>
> [snip]
>
> > E.G. it could still do the full checking, as the only thing that really
> > matters is that it does not actively raise error messages only relevant
> for
> > Integer under code blocks only entered for tkFloat.
>
> If I understood Sven's example correct, then the compiler does exactly this
> already.
>

Not quite. If the generic parameter T is a LongInt, then the branch for
tkFloat *must* also be valid for LongInts. Because the type checking is
done *before* the branch is discarded.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-07 Thread Michael Van Canneyt



On Thu, 7 Nov 2019, Ben Grasset via fpc-pascal wrote:


On Thu, Nov 7, 2019 at 3:03 AM Sven Barth via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:


If there is no type checking, then it is *not* verified by the compiler.



Perhaps "no type checking" was not the write way to put it. A better way to
describe it might be:



[snip]


E.G. it could still do the full checking, as the only thing that really
matters is that it does not actively raise error messages only relevant for
Integer under code blocks only entered for tkFloat.


If I understood Sven's example correct, then the compiler does exactly this
already.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-07 Thread Ben Grasset via fpc-pascal
On Thu, Nov 7, 2019 at 3:03 AM Sven Barth via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:

> If there is no type checking, then it is *not* verified by the compiler.
>

Perhaps "no type checking" was not the write way to put it. A better way to
describe it might be:

Since the compiler *always* knows exactly which type a generic type
constraint currently amounts to, it is clearly capable of properly choosing
specific code paths based on that, if given some kind of
constant-evaluatable boolean condition that is specifically based on
TTypeKind.

In general, it's *exactly* the same concept as something like using SizeOf
in an {$IF} / {$ELSEIF} directive pair (where the compiler does indeed
completely ignore everything in the "false" section):

program Example;

begin
  // You can in fact put stuff that isn't even close to valid code in
whichever block is false for you, below.
  {$IF SizeOf(Pointer) = 4}
WriteLn('The size of a pointer is 4 bytes.');
  {$ELSEIF SizeOf(Pointer) = 8}
WriteLn('The size of a pointer is 8 bytes.');
  {$ENDIF}
end.

Not that it has to do precisely the same thing for a hypothetical
TTypeKind-based choice.

E.G. it could still do the full checking, as the only thing that really
matters is that it does not actively raise error messages only relevant for
Integer under code blocks only entered for tkFloat.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-07 Thread Sven Barth via fpc-pascal

Am 07.11.2019 um 01:33 schrieb Ben Grasset via fpc-pascal:
On Wed, Nov 6, 2019 at 12:44 PM Sven Barth via fpc-pascal 
> wrote:


Pascal has a strong type safety, thus something like the
if-expression won't be used/allowed to weaken that.

If that means that some things can't be implemented in generics
the "easy" way, then so be it.


I agree that Pascal has strong type safety.

IMO conditional branching based on compile-time constant evaluation, 
that cannot fail, does not do anything resembling weakening it, however.


It strengthens it, and has no downsides, because it's verified by the 
compiler.


Encouraging typecasting (which cares only about the sizes of the types 
involved, nothing else) at the programmer level is far more 
error-prone in a variety of ways.
If there is no type checking, then it is *not* verified by the compiler. 
If I have an if-expression with an always true if-clause I could write 
any syntactically correct garbage in the else-clause if type checking 
would be disabled, because the compiler would not verify it. And this is 
not how Pascal works. And also not how FPC's parser works. We're taking 
huge care that generics are correctly type checked and that as many 
errors as possible are caught when writing the generic (instead of when 
specializing), so we're not going to introduce something like this. The 
if-expression is intended to be like C/C++'s ternary operator nothing 
more, nothing less cause that is what most people want to use it for.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-06 Thread Sven Barth via fpc-pascal

Am 07.11.2019 um 01:42 schrieb Ben Grasset via fpc-pascal:
On Wed, Nov 6, 2019 at 7:33 PM Ben Grasset > wrote:


Encouraging typecasting (which cares only about the sizes of the
types involved, nothing else) at the programmer level is far more
error-prone in a variety of ways.


Also: it's slower in many cases, because it tends to involve "if" 
statements that *remain* as if statements in the final generated 
assembly code, whereas a static check would allow for simply 
generating *only* the code for the path that's actually taken.
This is not true. If the compiler can prove at compile time that the 
if-condition is constant then the branch that is not taken is removed at 
the node level. It doesn't even remotely reach the code generation 
phase. (I just checked that yesterday when I implemented IsManagedType() 
and wondered about missing "unreachable code" warnings).


Take a look at the test for IsManagedType():

=== code begin ===

program tismngd1;

{$mode objfpc}
{$modeswitch advancedrecords}

uses
  TypInfo;

var
  gError: LongInt = 0;

function NextErrorCode: LongInt; inline;
begin
  Inc(gError);
  Result := gError;
end;

generic procedure TestType(aIsMngd: Boolean); inline;
begin
  if IsManagedType(T) <> aIsMngd then begin
    Writeln('IsManagedType(', PTypeInfo(TypeInfo(T))^.Name, ') failure; 
expected: ', aIsMngd, ', got: ', IsManagedType(T));

    Halt(NextErrorCode);
  end;
  NextErrorCode;
end;

type
  TTestLongInt = record
    a: LongInt;
  end;

  TTestAnsiString = record
    a: AnsiString;
  end;

  TTestManaged = record
    a: LongInt;
    class operator Initialize(var aTestManaged: TTestManaged);
  end;

  TTestObj = object
    a: LongInt;
  end;

  TTestObjAnsiString = object
    a: AnsiString;
  end;

class operator TTestManaged.Initialize(var aTestManaged: TTestManaged);
begin
  aTestManaged.a := 42;
end;

type
  TProcVar = procedure;
  TMethodVar = procedure of object;

  TDynArrayLongInt = array of LongInt;
  TStaticArrayLongInt = array[0..4] of LongInt;
  TStaticArrayAnsiString = array[0..4] of AnsiString;

  TEnum = (eOne, eTwo, eThree);
  TSet = set of (sOne, sTwo, sThree);

begin
  specialize TestType(False);
  specialize TestType(False);
  specialize TestType(False);
  specialize TestType(True);
  specialize TestType(True);
  specialize TestType(True);
  specialize TestType(False);
  specialize TestType(False);
  specialize TestType(False);
  specialize TestType(False);
  specialize TestType(True);
  specialize TestType(False);
  specialize TestType(False);
  specialize TestType(True);
  specialize TestType(True);
  specialize TestType(False);
  specialize TestType(True);
  specialize TestType(True);
  specialize TestType(False);
  specialize TestType(True);
  specialize TestType(False);
  specialize TestType(False);
  Writeln('Ok');
end.

=== code end ===

Thanks to the node level optimization I mentioned the assembly code of 
the main function will look like this (in this case x86_64-win64):


=== code begin ===

.section .text.n_main,"ax"
    .balign 16,0x90
.globl    main
main:
.globl    PASCALMAIN
PASCALMAIN:
.Lc122:
.Lc123:
# Temps allocated between rbp-8 and rbp+0
.seh_proc main
# [62] begin
    pushq    %rbp
.seh_pushreg %rbp
.Lc124:
.Lc125:
    movq    %rsp,%rbp
.Lc126:
    leaq    -48(%rsp),%rsp
.seh_stackalloc 48
    movq    %rbx,-8(%rbp)
.seh_savereg %rbx, 40
.seh_endprologue
    call    fpc_initializeunits
# [63] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [64] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [65] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [66] specialize TestType(True);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [67] specialize TestType(True);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [68] specialize TestType(True);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [69] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [70] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [71] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [72] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [73] specialize TestType(True);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [74] specialize TestType(False);
    addl    $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
    movl    TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# 

Re: [fpc-pascal] Generic type conflicts

2019-11-06 Thread Ben Grasset via fpc-pascal
On Wed, Nov 6, 2019 at 7:33 PM Ben Grasset  wrote:

> Encouraging typecasting (which cares only about the sizes of the types
> involved, nothing else) at the programmer level is far more error-prone in
> a variety of ways.
>

Also: it's slower in many cases, because it tends to involve "if"
statements that *remain* as if statements in the final generated assembly
code, whereas a static check would allow for simply generating *only* the
code for the path that's actually taken.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-06 Thread Ben Grasset via fpc-pascal
On Wed, Nov 6, 2019 at 12:44 PM Sven Barth via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:

> Pascal has a strong type safety, thus something like the if-expression
> won't be used/allowed to weaken that.
>
> If that means that some things can't be implemented in generics the "easy"
> way, then so be it.
>

I agree that Pascal has strong type safety.

IMO conditional branching based on compile-time constant evaluation, that
cannot fail, does not do anything resembling weakening it, however.

It strengthens it, and has no downsides, because it's verified by the
compiler.

Encouraging typecasting (which cares only about the sizes of the types
involved, nothing else) at the programmer level is far more error-prone in
a variety of ways.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-06 Thread Sven Barth via fpc-pascal
Ben Grasset via fpc-pascal  schrieb am
Mi., 6. Nov. 2019, 15:43:

> On Wed, Nov 6, 2019 at 2:01 AM Sven Barth via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
>
>> A normal if-statements has the same non-evaluation.
>>
>
> Not in the way I meant, though.
>
> Like, I thought the difference between "normal if" and "ternary if" was
> supposed to be the same as the difference between the existing IfThen()
> function and the intrinsic version of IfThen(), on which you based the
> "if-then-else" syntax I think.
>

No. The difference between the currently provided IfThen<> generic is the
same in regards to evaluation to the rejected IfThen intrinsic, an
if-expression and an if-statement: All four typecheck their branches, but
only the later three don't execute the statements in the branch that is not
taken (and also have a branch optimised away if the condition is const).
The only difference between the if-expression and the if-statement is that
the former can be used as an expression (duh!).

The point being that the type-checking is neither useful or necessary in
> scenarios where the branch being evaluated is statically known to be
> unreachable ahead of time.
>

Pascal has a strong type safety, thus something like the if-expression
won't be used/allowed to weaken that.

If that means that some things can't be implemented in generics the "easy"
way, then so be it.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-06 Thread Ben Grasset via fpc-pascal
On Wed, Nov 6, 2019 at 2:01 AM Sven Barth via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:

> A normal if-statements has the same non-evaluation.
>

Not in the way I meant, though.

Like, I thought the difference between "normal if" and "ternary if" was
supposed to be the same as the difference between the existing IfThen()
function and the intrinsic version of IfThen(), on which you based the
"if-then-else" syntax I think.

By which I mean, something similar to the difference between "if" and
"static if" in the D programming language (at least when given things that
are possible to evaluate at compile time, like GetTypeKind is in Pascal).
For example:

import std.stdio;
import std.traits;

// Would not compile, because everything is evaluated fully,
// and in this case the parameters we pass aren't
// compatible with all branches.
void PrintSomething(T)(T value) {
  if (isFloatingPoint!(T)) {
writefln("Floating point value: %f", value * value);
  } else if (isIntegral!(T)) {
writefln("Integral value: %d", value * value);
  } else if (isSomeString!(T)) {
writefln("String value: %s", value ~ value);
  }
}

// Compiles fine, as only the relevant branch for
// a given parameter is evaluated.
void StaticPrintSomething(T)(T value) {
  static if (isFloatingPoint!(T)) {
writefln("Floating point value: %f", value * value);
  }
  else static if (isIntegral!(T)) {
writefln("Integral value: %d", value * value);
  }
  else static if (isSomeString!(T)) {
// "~" does string concatenation in D
writefln("String value: %s", value ~ value ~ value);
  }
}

void main() {
  StaticPrintSomething(1);
  StaticPrintSomething(1.0);
  StaticPrintSomething("hey");
}

The point being that the type-checking is neither useful or necessary in
scenarios where the branch being evaluated is statically known to be
unreachable ahead of time.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-05 Thread Sven Barth via fpc-pascal
Ben Grasset via fpc-pascal  schrieb am
Mi., 6. Nov. 2019, 04:49:

> On Tue, Nov 5, 2019 at 5:24 PM Sven Barth via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
>
>> Does this really work? Cause the compiler should nevertheless typecheck
>> the code in the other branch and thus without casts that shouldn't compile.
>> Also if it should indeed work, it would also work without the
>> if-expression, but with an if-statement.
>>
>
> Hm, it actually doesn't quite for that particular example, testing it for
> real. It definitely did for a few other generic things I experimented with
> though (that were impossible otherwise.) I'll see if I can find the source
> files anywhere.
>
> Why would it work the same way as a normal if statement, though? Isn't the
> non-evaluation of the false branch pretty much the only point of the
> ternary "form"? E.G. you'd specifically use it when you had two branches
> that you knew would never *both* be compilable.
> You'd be able to do the same thing with an {$IFDEF}, for example, if
> {$IFDEFS} specifically carried through across generic specializations.
>

A normal if-statements has the same non-evaluation. However that it might
not be evaluated does not mean that it is not type checked. Especially as
the type of the expression is taken from the if-branch. Imagine this:

=== code begin ===

SomeLongint := if SomethingFalse then SomeOtherLongint else SomeString;

=== code end ===

The if-branch is obviously not taken here. But nevertheless the type of the
whole condition is LongInt, because the type of the if-branch is LongInt.
Thus there will be a type error in the else-branch even before the compiler
determines that the if-condition is constant and False.

And the if-expression behaves like an if-clause, because the compiler
transforms it as such. The above example becomes (in the node tree)
essentially the following (general case without further optimizations):

=== code begin ===

if SomethingFalse then
  tmpLongInt := SomeOtherLongint
else
  tmpLongInt := SomeString;
SomeLongint := tmpLongInt;

===code end ===

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-05 Thread Ben Grasset via fpc-pascal
On Tue, Nov 5, 2019 at 5:24 PM Sven Barth via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:

> Does this really work? Cause the compiler should nevertheless typecheck
> the code in the other branch and thus without casts that shouldn't compile.
> Also if it should indeed work, it would also work without the
> if-expression, but with an if-statement.
>

Hm, it actually doesn't quite for that particular example, testing it for
real. It definitely did for a few other generic things I experimented with
though (that were impossible otherwise.) I'll see if I can find the source
files anywhere.

Why would it work the same way as a normal if statement, though? Isn't the
non-evaluation of the false branch pretty much the only point of the
ternary "form"? E.G. you'd specifically use it when you had two branches
that you knew would never *both* be compilable.
You'd be able to do the same thing with an {$IFDEF}, for example, if
{$IFDEFS} specifically carried through across generic specializations.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-05 Thread Sven Barth via fpc-pascal
Ben Grasset via fpc-pascal  schrieb am
Di., 5. Nov. 2019, 19:11:

> On Sat, Nov 2, 2019 at 11:51 AM Ryan Joseph via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
>
>> Are there any solutions for this currently? I feel like generics need to
>> support some compiler directives so different blocks of code can specialize
>> different depending on the type.
>>
>
> There's one, that works *exactly* like you want (although it's not
> currently present in trunk FPC) which is to apply the (still-working) patch
> Sven posted in this mailing list thread a few years ago:
> https://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg41336.html
>
> It introduces a const-evaluated "ternary if" of the form "Variable := if
> Expression else Expression;" where only the true branch is taken into
> consideration by the compiler at all.
>
> Combining it with handy compiler intrinsics like GetTypeKind makes stuff
> like the following modified version of your code possible:
>
> {$mode objfpc}
> {$modeswitch advancedrecords}
>
> program generic_vector_2;
>
> type
>   generic TVec2 = record
> X, Y: TScalar;
> class function Create(const AX, AY: TScalar): TVec2; static; inline;
> class operator / (const Left, Right: TVec2): TVec2; inline;
>   end;
>
>   class function TVec2.Create(const AX, AY: TScalar): TVec2;
>   begin
> with Result do begin
>   X := AX;
>   Y := AY;
> end;
>   end;
>
>   class operator TVec2./(const Left, Right: TVec2): TVec2;
>   begin
> // GetTypeKind is evaluated at compile time, so the following works
> perfectly with "ternary if".
> Result :=
>   if GetTypeKind(TScalar) = tkFloat then
> TVec2.Create(Left.X / Right.X, Left.Y / Right.Y)
>   else
> TVec2.Create(Left.X div Right.X, Left.Y div Right.Y);
>   end;
>
> type
>   TVec2f = specialize TVec2;
>   TVec2i = specialize TVec2;
>
> begin
> end.
>

Does this really work? Cause the compiler should nevertheless typecheck the
code in the other branch and thus without casts that shouldn't compile.
Also if it should indeed work, it would also work without the
if-expression, but with an if-statement.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-05 Thread Ben Grasset via fpc-pascal
On Sat, Nov 2, 2019 at 11:51 AM Ryan Joseph via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:

> Are there any solutions for this currently? I feel like generics need to
> support some compiler directives so different blocks of code can specialize
> different depending on the type.
>

There's one, that works *exactly* like you want (although it's not
currently present in trunk FPC) which is to apply the (still-working) patch
Sven posted in this mailing list thread a few years ago:
https://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg41336.html

It introduces a const-evaluated "ternary if" of the form "Variable := if
Expression else Expression;" where only the true branch is taken into
consideration by the compiler at all.

Combining it with handy compiler intrinsics like GetTypeKind makes stuff
like the following modified version of your code possible:

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_vector_2;

type
  generic TVec2 = record
X, Y: TScalar;
class function Create(const AX, AY: TScalar): TVec2; static; inline;
class operator / (const Left, Right: TVec2): TVec2; inline;
  end;

  class function TVec2.Create(const AX, AY: TScalar): TVec2;
  begin
with Result do begin
  X := AX;
  Y := AY;
end;
  end;

  class operator TVec2./(const Left, Right: TVec2): TVec2;
  begin
// GetTypeKind is evaluated at compile time, so the following works
perfectly with "ternary if".
Result :=
  if GetTypeKind(TScalar) = tkFloat then
TVec2.Create(Left.X / Right.X, Left.Y / Right.Y)
  else
TVec2.Create(Left.X div Right.X, Left.Y div Right.Y);
  end;

type
  TVec2f = specialize TVec2;
  TVec2i = specialize TVec2;

begin
end.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-02 Thread Ryan Joseph via fpc-pascal


> On Nov 2, 2019, at 11:06 AM, Cyrax via fpc-pascal 
>  wrote:
> 
> You need to do a explicit typecasting.
> 
> ...
> fac:=Sqrt(Sqr(TScalar(x)) + Sqr(TScalar(y)));
> ...
> fac:=1.0/TScalar(fac);

Doesn't work. Try running the example.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Generic type conflicts

2019-11-02 Thread Cyrax via fpc-pascal

On 02/11/2019 16:55, Ryan Joseph via fpc-pascal wrote:

I've wanted to make a generic version of a vector for a while but I always give 
up because it seems not very possible. It's probably not even a great idea 
because many methods don't translate between float and integer but I wanted to 
prevent other code duplication if possible.

Here's an example of how things break down. Are there any solutions for this 
currently? I feel like generics need to support some compiler directives so 
different blocks of code can specialize different depending on the type.

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_vector_2;
uses
   Math;

type
   generic TVec2 = record
 x, y: TScalar;
 function Normalize: TVec2;
   end;
   TVec2f = specialize TVec2;
   TVec2i = specialize TVec2;

function TVec2.Normalize: TVec2;
var
   fac: TScalar;
begin
   // Can't determine which overloaded function to call
   // Incompatible types: got "Extended" expected "LongInt"
   fac:=Sqrt(Sqr(x) + Sqr(y));
   if fac<>0.0 then begin
 // Incompatible types: got "Single" expected "LongInt"
 fac:=1.0/fac;
 result.x:=x*fac;
 result.y:=y*fac;
   end else begin
 result.x:=0;
 result.y:=0;
   end;
end;

begin
end.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal



You need to do a explicit typecasting.

...
fac:=Sqrt(Sqr(TScalar(x)) + Sqr(TScalar(y)));
...
fac:=1.0/TScalar(fac);

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


[fpc-pascal] Generic type conflicts

2019-11-02 Thread Ryan Joseph via fpc-pascal
I've wanted to make a generic version of a vector for a while but I always give 
up because it seems not very possible. It's probably not even a great idea 
because many methods don't translate between float and integer but I wanted to 
prevent other code duplication if possible.

Here's an example of how things break down. Are there any solutions for this 
currently? I feel like generics need to support some compiler directives so 
different blocks of code can specialize different depending on the type.

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_vector_2;
uses
  Math;

type
  generic TVec2 = record
x, y: TScalar;
function Normalize: TVec2;
  end;
  TVec2f = specialize TVec2;
  TVec2i = specialize TVec2;

function TVec2.Normalize: TVec2;
var
  fac: TScalar;
begin
  // Can't determine which overloaded function to call
  // Incompatible types: got "Extended" expected "LongInt"
  fac:=Sqrt(Sqr(x) + Sqr(y));
  if fac<>0.0 then begin
// Incompatible types: got "Single" expected "LongInt"
fac:=1.0/fac;
result.x:=x*fac;
result.y:=y*fac;
  end else begin
result.x:=0;
result.y:=0;
  end;
end;

begin
end.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal