• Unsafe code blocks

    From Simon Clubley@clubley@remove_me.eisner.decus.org-Earth.UFP to comp.os.vms on Thu Nov 13 14:04:50 2025
    From Newsgroup: comp.os.vms

    On 2025-11-12, Arne Vajhoj <arne@vajhoej.dk> wrote:

    using System;

    namespace AAX
    {
    public class Program
    {
    public static void Main(string[] args)
    {
    int[] a = new int[4];
    unsafe
    {
    fixed (int* hack = &a[0])
    {
    for(int i = 0; i < 5; i++)
    {
    hack[i] = i;
    Console.WriteLine(i);
    }
    }
    }
    }
    }
    }

    0
    1
    2
    3
    4

    unsafe { } clearly reveal that something fishy is going on.


    The unsafe keyword is a hack implemented in languages that have not been designed correctly. The Ada approach, of disabling checks on a specific reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe things with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-3.html

    Simon.
    --
    Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
    Walking destinations on a map are further away than they appear.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Thu Nov 13 15:44:06 2025
    From Newsgroup: comp.os.vms

    On 11/13/2025 9:04 AM, Simon Clubley wrote:
    On 2025-11-12, Arne Vajh|+j <arne@vajhoej.dk> wrote:
    int[] a = new int[4];
    unsafe
    {
    fixed (int* hack = &a[0])
    {
    for(int i = 0; i < 5; i++)
    {
    hack[i] = i;
    Console.WriteLine(i);
    }
    }
    }

    0
    1
    2
    3
    4

    unsafe { } clearly reveal that something fishy is going on.

    The unsafe keyword is a hack implemented in languages that have not been designed correctly. The Ada approach, of disabling checks on a specific reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe things with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-3.html

    In principle I totally agree:

    scope of unsafe is operation
    is better than
    scope of unsafe is block
    is better than
    scope of unsafe is function
    is better than
    scope of unsafe is file

    That said then a C# unsafe block are almost always:
    - very short
    - only have one pointer variable benefiting from
    unsafe
    so more principle than practical difference.

    (C# unsafe blocks are also very rare - my guess is that >95%
    of C# developers have never used one and may not even know they
    exist)

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Marc Van Dyck@marc.gr.vandyck@invalid.skynet.be to comp.os.vms on Fri Nov 14 12:14:24 2025
    From Newsgroup: comp.os.vms

    Simon Clubley wrote :
    On 2025-11-12, Arne Vajhoj <arne@vajhoej.dk> wrote:

    using System;

    namespace AAX
    {
    public class Program
    {
    public static void Main(string[] args)
    {
    int[] a = new int[4];
    unsafe
    {
    fixed (int* hack = &a[0])
    {
    for(int i = 0; i < 5; i++)
    {
    hack[i] = i;
    Console.WriteLine(i);
    }
    }
    }
    }
    }
    }

    0
    1
    2
    3
    4

    unsafe { } clearly reveal that something fishy is going on.


    The unsafe keyword is a hack implemented in languages that have not been designed correctly. The Ada approach, of disabling checks on a specific reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe things with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-3.html

    Simon.

    IS this the same as the [unsafe] attribute in OpenVMS Pascal ?
    --
    Marc Van Dyck
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From John Reagan@johnrreagan@earthlink.net to comp.os.vms on Fri Nov 14 11:47:25 2025
    From Newsgroup: comp.os.vms

    On 11/14/2025 6:14 AM, Marc Van Dyck wrote:
    Simon Clubley wrote :
    On 2025-11-12, Arne Vajh|+j <arne@vajhoej.dk> wrote:

    using System;

    namespace AAX
    {
    -a-a-a-a public class Program
    -a-a-a-a {
    -a-a-a-a-a-a-a-a public static void Main(string[] args)
    -a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a int[] a = new int[4];
    -a-a-a-a-a-a-a-a-a-a-a-a unsafe
    -a-a-a-a-a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a fixed (int* hack = &a[0])
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a for(int i = 0; i < 5; i++)
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a hack[i] = i;
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a Console.WriteLine(i);
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
    -a-a-a-a-a-a-a-a-a-a-a-a }
    -a-a-a-a-a-a-a-a }
    -a-a-a-a }
    }

    0
    1
    2
    3
    4

    unsafe { } clearly reveal that something fishy is going on.


    The unsafe keyword is a hack implemented in languages that have not been
    designed correctly. The Ada approach, of disabling checks on a specific
    reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe
    things
    with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-3.html

    Simon.

    IS this the same as the [unsafe] attribute in OpenVMS Pascal ?

    Sorta. Pascal's [UNSAFE] is mostly used in writing generic routines
    that can accept many different "shapes" of a type. It also helps on
    RECORDs by giving you an invisible/free union of bytes around the entire record. I would say between Pascal's [UNSAFE] and the type-cast "::" operator, you can get Ada's unchecked_conversion. The nice thing about
    Ada is that unchecked_conversion is easy to search for and jumps out at
    you. Pascal's "::" operator is harder for a human to see. C's
    type-cast operator is essentially invisible.

    Languages like Rust and C# with "unsafe" routines/blocks, turns those
    into "collection of bugs"
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Fri Nov 14 13:55:23 2025
    From Newsgroup: comp.os.vms

    On 11/14/2025 6:14 AM, Marc Van Dyck wrote:
    Simon Clubley wrote :
    On 2025-11-12, Arne Vajh|+j <arne@vajhoej.dk> wrote:
    -a-a-a-a-a-a-a-a-a-a-a-a int[] a = new int[4];
    -a-a-a-a-a-a-a-a-a-a-a-a unsafe
    -a-a-a-a-a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a fixed (int* hack = &a[0])
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a for(int i = 0; i < 5; i++)
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a {
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a hack[i] = i;
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a Console.WriteLine(i);
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
    -a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a }
    -a-a-a-a-a-a-a-a-a-a-a-a }

    unsafe { } clearly reveal that something fishy is going on.

    The unsafe keyword is a hack implemented in languages that have not been
    designed correctly. The Ada approach, of disabling checks on a specific
    reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe
    things
    with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-3.html

    IS this the same as the [unsafe] attribute in OpenVMS Pascal ?

    If not the same then at least similar.

    It disables some checks.

    But it seems to do a little more.

    Example based on example in the docs:

    $ type dirty.pas
    program dirty(input,output);

    procedure hexdump(v : [unsafe] packed array [b1..b2:integer] of char);

    var
    i : integer;

    begin
    for i := b1 to b2 do begin
    write(' ', hex(ord(v[i]), 2));
    end;
    writeln;
    end;

    type
    r = packed record
    iv : integer;
    xv : real;
    sv : packed array [1..4] of char;
    end;

    var
    v : r value [iv: 257; xv: 1.0; sv: 'ABCD' ];

    begin
    hexdump(257);
    hexdump(1.0);
    hexdump('ABCD');
    hexdump(v);
    end.
    $ pas dirty
    $ link dirty
    $ run dirty
    01 01 00 00
    00 00 80 3F
    41 42 43 44
    01 01 00 00 00 00 80 3F 41 42 43 44

    Cute!

    Arne


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Fri Nov 14 14:02:15 2025
    From Newsgroup: comp.os.vms

    On 11/14/2025 11:47 AM, John Reagan wrote:
    On 11/14/2025 6:14 AM, Marc Van Dyck wrote:
    Simon Clubley wrote :
    On 2025-11-12, Arne Vajh|+j <arne@vajhoej.dk> wrote:
    unsafe { } clearly reveal that something fishy is going on.

    The unsafe keyword is a hack implemented in languages that have not been >>> designed correctly. The Ada approach, of disabling checks on a specific
    reference to a variable instead of disabling checks within a whole block >>> of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that
    what
    is in the variable after the unsafe conversion is actually a valid
    value.

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe
    things
    with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-3.html

    Simon.

    IS this the same as the [unsafe] attribute in OpenVMS Pascal ?

    Sorta.-a Pascal's [UNSAFE] is mostly used in writing generic routines
    that can accept many different "shapes" of a type.-a It also helps on RECORDs by giving you an invisible/free union of bytes around the entire record.-a I would say between Pascal's [UNSAFE] and the type-cast "::" operator, you can get Ada's unchecked_conversion.-a The nice thing about
    Ada is that unchecked_conversion is easy to search for and jumps out at you.-a Pascal's "::" operator is harder for a human to see.-a C's type-
    cast operator is essentially invisible.

    Languages like Rust and C# with "unsafe" routines/blocks, turns those
    into "collection of bugs"

    The important distinction is between:
    can use unsafe constructs anywhere without any special syntax
    and:
    can use unsafe constructs when using a syntax that clearly identifies something as unsafe

    So for VMS Pascal:
    * search for "::"
    * search for "UNSAFE"
    * multi line regex search for "RECORD.*CASE.*END"
    and setup extra code reviews for all hits.

    Anything else to look for?

    Ada is clearer, but ...

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.os.vms on Fri Nov 14 19:32:55 2025
    From Newsgroup: comp.os.vms

    On Fri, 14 Nov 2025 11:47:25 -0500, John Reagan wrote:

    C's type-cast operator is essentially invisible.

    Time for C to adopt C++-style explicit-casting constructs?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Fri Nov 14 20:00:25 2025
    From Newsgroup: comp.os.vms

    On 11/14/2025 1:55 PM, Arne Vajh|+j wrote:
    On 11/14/2025 6:14 AM, Marc Van Dyck wrote:
    Simon Clubley wrote :
    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-1.html

    IS this the same as the [unsafe] attribute in OpenVMS Pascal ?

    If not the same then at least similar.

    It disables some checks.

    But it seems to do a little more.

    Example based on example in the docs:

    procedure hexdump(v : [unsafe] packed array [b1..b2:integer] of char);

    -a-a hexdump(257);
    -a-a hexdump(1.0);
    -a-a hexdump('ABCD');
    -a-a hexdump(v);

    -a01 01 00 00
    -a00 00 80 3F
    -a41 42 43 44
    -a01 01 00 00 00 00 80 3F 41 42 43 44

    Cute!

    An example where it is closer to Ada:

    $ type saf.pas
    program saf(input,output);

    var
    c : char;

    begin
    c := 65;
    writeln(c);
    end.
    $ pas saf

    c := 65;
    .....^
    %PASCAL-E-OPNDASSCOM, Operands are not assignment compatible
    at line number 7 in file DKA0:[arne]saf.pas;1
    %PASCAL-E-ENDDIAGS, PASCAL completed with 1 diagnostic
    $ type usaf.pas
    program usaf(input,output);

    var
    c : [unsafe] char;

    begin
    c := 65;
    writeln(c);
    end.
    $ pas usaf
    $ link usaf
    $ run usaf
    A

    and:

    $ type saf.adb
    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure Saf is

    c : character;

    begin
    c := 65;
    Put(c);
    New_Line;
    end Saf;
    $ gnat make saf
    gcc -c saf.adb
    saf.adb:10:10: expected type "Standard.Character"
    saf.adb:10:10: found type universal integer
    gnatmake: "saf.adb" compilation error
    $ type usaf.adb
    with Unchecked_Conversion;

    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure USaf is

    function i2c is new Unchecked_Conversion(Source => integer, Target => character);

    c : character;

    begin
    c := i2c(65);
    Put(c);
    New_Line;
    end USaf;
    $ gnat make usaf
    gcc -c usaf.adb
    usaf.adb:9:01: warning: types for unchecked conversion have different sizes gnatbind -x usaf.ali
    gnatlink usaf.ali
    $ run usaf
    A

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Sun Nov 16 02:16:49 2025
    From Newsgroup: comp.os.vms

    In article <10f4oi1$25lkk$2@dont-email.me>,
    Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> wrote:
    On 2025-11-12, Arne Vajhoj <arne@vajhoej.dk> wrote:

    using System;

    namespace AAX
    {
    public class Program
    {
    public static void Main(string[] args)
    {
    int[] a = new int[4];
    unsafe
    {
    fixed (int* hack = &a[0])
    {
    for(int i = 0; i < 5; i++)
    {
    hack[i] = i;
    Console.WriteLine(i);
    }
    }
    }
    }
    }
    }

    0
    1
    2
    3
    4

    unsafe { } clearly reveal that something fishy is going on.

    The unsafe keyword is a hack implemented in languages that have not been >designed correctly.

    Wait, Simon, tell me how you really feel.

    The Ada approach, of disabling checks on a specific
    reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    It's unclear to me how this is materially different.

    In Rust, blocks return the value of the last expression they
    contain, so an `unsafe` block may refer to a single expression.
    However, I disagree with the above in the sense that the ability
    to introduce scope when doing something `unsafe` can be
    incredibly useful. But what if the expression is really a
    statement?

    For example:

    ```rust
    unsafe {
    use core::intrinsics::volatile_copy_memory;
    let src = entry.virt_page_addr() as *const arch::Page;
    volatile_copy_memory(page, src, 1);
    }
    ```

    There's no need to leak the existence of `src` or that one is
    using the volatile memcpy intrinsic here. I suppose could could
    also write this as,

    ```rust
    {
    use core::intrinsics::volatile_copy_memory;
    let src = entry.virt_page_addr() as *const arch::Page;
    unsafe {
    volatile_copy_memory(page, src, 1);
    }
    }
    ```

    etc.


    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html

    This example seems equivalent to an `unsafe fn` in Rust. An
    unsafe conversion of this nature might use `std::mem::transmute`
    in that world.

    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Sum types make this trivial:

    impl SomeType {
    fn try_from(i: i32) -> Option<Self> {
    // if valid, return `Some(whatever`),
    // else return `None`.
    }
    }

    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe things >with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-3.html

    This seems like too coarse of a tool that's overloaded for too
    many things. When will Ada grow up and become an actually safe
    language? :-D

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Marc Van Dyck@marc.gr.vandyck@invalid.skynet.be to comp.os.vms on Mon Nov 17 09:25:20 2025
    From Newsgroup: comp.os.vms

    Arne Vajhoj wrote on 14/11/2025 :
    On 11/14/2025 11:47 AM, John Reagan wrote:
    On 11/14/2025 6:14 AM, Marc Van Dyck wrote:
    Simon Clubley wrote :
    On 2025-11-12, Arne Vajhoj <arne@vajhoej.dk> wrote:
    unsafe { } clearly reveal that something fishy is going on.

    The unsafe keyword is a hack implemented in languages that have not been >>>> designed correctly. The Ada approach, of disabling checks on a specific >>>> reference to a variable instead of disabling checks within a whole block >>>> of code, is far superior.

    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-1.html

    Also note the availability of the 'Valid attribute to make sure that what >>>> is in the variable after the unsafe conversion is actually a valid value. >>>>
    Likewise, 'Unchecked_Access is a way that Ada allows you to do unsafe >>>> things
    with pointers:

    https://www.adaic.org/resources/add_content/docs/95style/html/
    sec_5/5-9-3.html

    Simon.

    IS this the same as the [unsafe] attribute in OpenVMS Pascal ?

    Sorta.a Pascal's [UNSAFE] is mostly used in writing generic routines that >> can accept many different "shapes" of a type.a It also helps on RECORDs by >> giving you an invisible/free union of bytes around the entire record.a I
    would say between Pascal's [UNSAFE] and the type-cast "::" operator, you
    can get Ada's unchecked_conversion.a The nice thing about Ada is that
    unchecked_conversion is easy to search for and jumps out at you.a Pascal's >> "::" operator is harder for a human to see.a C's type- cast operator is
    essentially invisible.

    Languages like Rust and C# with "unsafe" routines/blocks, turns those into >> "collection of bugs"

    The important distinction is between:
    can use unsafe constructs anywhere without any special syntax
    and:
    can use unsafe constructs when using a syntax that clearly identifies something as unsafe

    So for VMS Pascal:
    * search for "::"
    * search for "UNSAFE"
    * multi line regex search for "RECORD.*CASE.*END"
    and setup extra code reviews for all hits.

    Anything else to look for?

    Ada is clearer, but ...

    Arne

    For languages that are supported, would VAXset's Source Code Analyzer
    be
    of any help to identify such potential weaknesses ? I have been using
    it regularly, but never for that...
    --
    Marc Van Dyck
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Simon Clubley@clubley@remove_me.eisner.decus.org-Earth.UFP to comp.os.vms on Mon Nov 17 18:56:39 2025
    From Newsgroup: comp.os.vms

    On 2025-11-14, Arne Vajhoj <arne@vajhoej.dk> wrote:

    These two examples _appear_ to do different things.

    $ type usaf.pas
    program usaf(input,output);

    var
    c : [unsafe] char;

    begin
    c := 65;
    writeln(c);
    end.
    $ pas usaf
    $ link usaf
    $ run usaf
    A

    and:


    This one appears to say that _all_ writes to the variable "c" are not
    checked.

    [snip]

    $ type usaf.adb
    with Unchecked_Conversion;

    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure USaf is

    function i2c is new Unchecked_Conversion(Source => integer, Target => character);

    c : character;

    begin
    c := i2c(65);
    Put(c);
    New_Line;
    end USaf;
    $ gnat make usaf
    gcc -c usaf.adb
    usaf.adb:9:01: warning: types for unchecked conversion have different sizes gnatbind -x usaf.ali
    gnatlink usaf.ali
    $ run usaf
    A


    However, with this one, you get to choose which writes to "c" are checked
    by Ada. You could have Ada apply full checking to all assignments apart from one specific problem assignment, but if I understand the above VMS Pascal
    code correctly, then it's an all or nothing situation.

    Have I understood the Pascal code correctly or is there something
    I am missing ?

    Simon.
    --
    Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
    Walking destinations on a map are further away than they appear.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Simon Clubley@clubley@remove_me.eisner.decus.org-Earth.UFP to comp.os.vms on Mon Nov 17 19:22:07 2025
    From Newsgroup: comp.os.vms

    On 2025-11-15, Dan Cross <cross@spitfire.i.gajendra.net> wrote:
    In article <10f4oi1$25lkk$2@dont-email.me>,
    Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> wrote:

    The unsafe keyword is a hack implemented in languages that have not been >>designed correctly.

    Wait, Simon, tell me how you really feel.


    Well, Simon is known to have opinions. :-)

    The Ada approach, of disabling checks on a specific
    reference to a variable instead of disabling checks within a whole block
    of code, is far superior.

    It's unclear to me how this is materially different.

    In Rust, blocks return the value of the last expression they
    contain, so an `unsafe` block may refer to a single expression.
    However, I disagree with the above in the sense that the ability
    to introduce scope when doing something `unsafe` can be
    incredibly useful. But what if the expression is really a
    statement?

    For example:

    ```rust
    unsafe {
    use core::intrinsics::volatile_copy_memory;
    let src = entry.virt_page_addr() as *const arch::Page;
    volatile_copy_memory(page, src, 1);
    }
    ```

    And that is exactly the kind of thing I am talking about. :-)

    Of those three statements, only the last one appears to be the unsafe one.

    It is also the kind of thing I was reacting to when I saw Arne's
    example and went YUCK!, YUCK!, YUCK! :-)


    There's no need to leak the existence of `src` or that one is
    using the volatile memcpy intrinsic here. I suppose could could
    also write this as,


    If that is important, I would have placed the whole sequence inside a
    local block as you do below.

    ```rust
    {
    use core::intrinsics::volatile_copy_memory;
    let src = entry.virt_page_addr() as *const arch::Page;
    unsafe {
    volatile_copy_memory(page, src, 1);
    }
    }
    ```

    And this example is much closer to the Ada way. Related statements still
    get checked and only one specific statement, the actual statement that is unsafe, has checks disabled.


    etc.


    For example, this is how you do an unsafe conversion in Ada:

    https://adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html

    This example seems equivalent to an `unsafe fn` in Rust. An
    unsafe conversion of this nature might use `std::mem::transmute`
    in that world.


    The Ada example localises the unsafe bit down to a specific operation
    within a statement, not a full statement, and certainly not a full function.

    Simon.
    --
    Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
    Walking destinations on a map are further away than they appear.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Mon Nov 17 14:22:34 2025
    From Newsgroup: comp.os.vms

    On 11/17/2025 1:56 PM, Simon Clubley wrote:
    On 2025-11-14, Arne Vajh|+j <arne@vajhoej.dk> wrote:
    These two examples _appear_ to do different things.

    They both stuff an integer into a char aka circumvent
    strict typing.

    They do it differently.

    $ type usaf.pas

    var
    c : [unsafe] char;

    c := 65;

    This one appears to say that _all_ writes to the variable "c" are not checked.

    Yes.

    $ type usaf.adb
    with Unchecked_Conversion;

    function i2c is new Unchecked_Conversion(Source => integer, Target =>
    character);

    c : character;

    c := i2c(65);
    Put(c);

    However, with this one, you get to choose which writes to "c" are checked
    by Ada.

    Yes.

    The Pascal example is by variable. The Ada example is by assignment.

    More granular control in Ada.

    Have I understood the Pascal code correctly or is there something
    I am missing ?

    No.

    Only thing is that [unsafe] can be used for other things - see
    the hexdump example.

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Mon Nov 17 14:55:42 2025
    From Newsgroup: comp.os.vms

    Ada is generally pretty good.

    But I don't know if you noticed that the example gave a
    warning.

    The warning did not have an impact.

    But the same warning in another context can indicate
    huge problem.

    And I was surprised.

    $ type Simon1.adb
    with Unchecked_Conversion;
    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure Simon1 is

    type Rt is new Integer range 1..6;

    function I2R is new Unchecked_Conversion(Source => Integer, Target => Rt);

    V : Rt;
    I : Integer;

    begin
    i := 9;
    V := I2R(I);
    if V'Valid then
    Put(Integer(V));
    else
    Put("Not valid");
    end if;
    New_Line;
    end Simon1;
    $ gnat make Simon1
    gcc -c simon1.adb
    simon1.adb:10:01: warning: types for unchecked conversion have different
    sizes
    gnatbind -x simon1.ali
    gnatlink simon1.ali
    $ run Simon1
    1
    $ type Simon2.adb
    with Unchecked_Conversion;
    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure Simon2 is

    type Rt is new Integer range 1..6;
    for Rt'Size use Integer'Size;

    function I2R is new Unchecked_Conversion(Source => Integer, Target => Rt);

    V : Rt;
    I : Integer;

    begin
    i := 9;
    V := I2R(I);
    if V'Valid then
    Put(Integer(V));
    else
    Put("Not valid");
    end if;
    New_Line;
    end Simon2;
    $ gnat make Simon2
    gcc -c simon2.adb
    gnatbind -x simon2.ali
    gnatlink simon2.ali
    $ run Simon2
    Not valid

    That was not what I expected from Ada.

    And it is not due to VMS Alpha GNAT being an old 3.12p.

    C:\Work\Ada>gnat -v
    GNAT Community 2021 (20210519-103)
    Copyright 1996-2021, Free Software Foundation, Inc.
    ...

    C:\Work\Ada>type Simon1.adb
    with Unchecked_Conversion;
    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure Simon1 is

    type Rt is new Integer range 1..6;

    function I2R is new Unchecked_Conversion(Source => Integer, Target => Rt);

    V : Rt;
    I : Integer;

    begin
    i := 9;
    V := I2R(I);
    if V'Valid then
    Put(Integer(V));
    else
    Put("Not valid");
    end if;
    New_Line;
    end Simon1;

    C:\Work\Ada>gnat make Simon1
    gcc -c simon1.adb
    simon1.adb:10:01: warning: types for unchecked conversion have different
    sizes [-gnatwz]
    gnatbind -x simon1.ali
    gnatlink simon1.ali

    C:\Work\Ada>Simon1
    1

    C:\Work\Ada>type Simon2.adb
    with Unchecked_Conversion;
    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    procedure Simon2 is

    type Rt is new Integer range 1..6;
    for Rt'Size use Integer'Size;

    function I2R is new Unchecked_Conversion(Source => Integer, Target => Rt);

    V : Rt;
    I : Integer;

    begin
    i := 9;
    V := I2R(I);
    if V'Valid then
    Put(Integer(V));
    else
    Put("Not valid");
    end if;
    New_Line;
    end Simon2;

    C:\Work\Ada>gnat make Simon2
    gcc -c simon2.adb
    gnatbind -x simon2.ali
    gnatlink simon2.ali

    C:\Work\Ada>Simon2
    Not valid

    Arne
















































































































































































































































































































    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Mon Nov 17 14:58:06 2025
    From Newsgroup: comp.os.vms

    On 11/17/2025 3:25 AM, Marc Van Dyck wrote:
    Arne Vajh|+j wrote on 14/11/2025 :
    So for VMS Pascal:
    * search for "::"
    * search for "UNSAFE"
    * multi line regex search for "RECORD.*CASE.*END"
    and setup extra code reviews for all hits.

    Anything else to look for?

    For languages that are supported, would VAXset's Source Code Analyzer be
    of any help to identify such potential weaknesses ? I have been using
    it regularly, but never for that...

    It do not see anything in the SCA manual.

    And when SCA was created the focus was on other things.

    But I have never used SCA myself, so maybe there is something.

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris Townley@news@cct-net.co.uk to comp.os.vms on Mon Nov 17 20:11:53 2025
    From Newsgroup: comp.os.vms

    On 17/11/2025 19:58, Arne Vajh|+j wrote:
    On 11/17/2025 3:25 AM, Marc Van Dyck wrote:
    Arne Vajh|+j wrote on 14/11/2025 :
    So for VMS Pascal:
    * search for "::"
    * search for "UNSAFE"
    * multi line regex search for "RECORD.*CASE.*END"
    and setup extra code reviews for all hits.

    Anything else to look for?

    For languages that are supported, would VAXset's Source Code Analyzer be
    of any help to identify such potential weaknesses ? I have been using
    it regularly, but never for that...

    It do not see anything in the SCA manual.

    And when SCA was created the focus was on other things.

    But I have never used SCA myself, so maybe there is something.

    Arne


    I used SCA a bit in the past, and I never saw, or read about anything
    like that. However I may missed it...
    --
    Chris
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Simon Clubley@clubley@remove_me.eisner.decus.org-Earth.UFP to comp.os.vms on Mon Nov 17 20:33:37 2025
    From Newsgroup: comp.os.vms

    On 2025-11-17, Arne Vajhoj <arne@vajhoej.dk> wrote:
    Ada is generally pretty good.

    But I don't know if you noticed that the example gave a
    warning.


    I noticed the warning, but I let it slide because I didn't want to
    start _yet_ another argument about compiler flags and warnings, plus
    the fact I have several other discussions going on at the moment. :-)

    The warning did not have an impact.

    But the same warning in another context can indicate
    huge problem.

    And I was surprised.


    Interesting, but I've never encountered this because I never allow
    compiler warnings to stand. They _always_ get fixed regardless of
    language, even for own-time personal projects. In this case, I would
    have noted the difference in sizes, gone "Simon, you silly twit!"
    and fixed up the sizes. (It's only supposed to used in conversions
    of the same size).

    BTW, compilers which support it, regardless of language, always get
    used by me in a mode which converts warnings to fatal errors.

    The GNAT RM has more information:

    https://docs.adacore.com/gnat_rm-docs/html/gnat_rm/gnat_rm/standard_library_routines.html

    Search for "Ada.Unchecked_Conversion (13.9)". It also tells you what happens, at least in the case of GNAT, if you do try to use different sizes.

    Once you read that, you will see where the value "1" is coming from.

    Simon.
    --
    Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
    Walking destinations on a map are further away than they appear.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Mon Nov 17 15:47:57 2025
    From Newsgroup: comp.os.vms

    On 11/17/2025 3:11 PM, Chris Townley wrote:
    On 17/11/2025 19:58, Arne Vajh|+j wrote:
    On 11/17/2025 3:25 AM, Marc Van Dyck wrote:
    Arne Vajh|+j wrote on 14/11/2025 :
    So for VMS Pascal:
    * search for "::"
    * search for "UNSAFE"
    * multi line regex search for "RECORD.*CASE.*END"
    and setup extra code reviews for all hits.

    Anything else to look for?

    For languages that are supported, would VAXset's Source Code Analyzer be >>> of any help to identify such potential weaknesses ? I have been using
    it regularly, but never for that...

    It do not see anything in the SCA manual.

    And when SCA was created the focus was on other things.

    But I have never used SCA myself, so maybe there is something.

    I used SCA a bit in the past, and I never saw, or read about anything
    like that. However I may missed it...

    Different time.

    If one had 100000 lines of Pascal/Basic/Fortran and 5000 lines
    of Macro-32, then identifying 100 lines of Pascal/Basic/Fortran
    doing unsafe things was a bit pointless.

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Mon Nov 17 15:55:10 2025
    From Newsgroup: comp.os.vms

    On 11/17/2025 3:33 PM, Simon Clubley wrote:
    On 2025-11-17, Arne Vajh|+j <arne@vajhoej.dk> wrote:
    But the same warning in another context can indicate
    huge problem.

    And I was surprised.

    Interesting, but I've never encountered this because I never allow
    compiler warnings to stand.

    Good practice.

    The GNAT RM has more information:

    https://docs.adacore.com/gnat_rm-docs/html/gnat_rm/gnat_rm/standard_library_routines.html

    Search for "Ada.Unchecked_Conversion (13.9)". It also tells you what happens, at least in the case of GNAT, if you do try to use different sizes.

    Once you read that, you will see where the value "1" is coming from.

    Truncating when moving from bigger data to smaller data is not so
    surprising.

    But truncating to max bits needed to represent logical data instead
    of truncating to bytes used in memory surprises me.

    And it is important to realize that Valid checks if resulting
    data is valid not whether the original data was valid for
    conversion.

    Arne



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Tue Nov 18 12:54:48 2025
    From Newsgroup: comp.os.vms

    In article <10ffsku$1233b$2@dont-email.me>,
    Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> wrote:
    On 2025-11-15, Dan Cross <cross@spitfire.i.gajendra.net> wrote:
    In article <10f4oi1$25lkk$2@dont-email.me>,
    Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> wrote:

    The unsafe keyword is a hack implemented in languages that have not been >>>designed correctly.

    Wait, Simon, tell me how you really feel.


    Well, Simon is known to have opinions. :-)

    :-D

    The Ada approach, of disabling checks on a specific
    reference to a variable instead of disabling checks within a whole block >>>of code, is far superior.

    It's unclear to me how this is materially different.

    In Rust, blocks return the value of the last expression they
    contain, so an `unsafe` block may refer to a single expression.
    However, I disagree with the above in the sense that the ability
    to introduce scope when doing something `unsafe` can be
    incredibly useful. But what if the expression is really a
    statement?

    For example:

    ```rust
    unsafe {
    use core::intrinsics::volatile_copy_memory;
    let src = entry.virt_page_addr() as *const arch::Page;
    volatile_copy_memory(page, src, 1);
    }
    ```

    And that is exactly the kind of thing I am talking about. :-)

    Of those three statements, only the last one appears to be the unsafe one.

    This is true, but also the sort of thing where I want to
    localize the scope of both the use statement, as well as `src`.

    It is also the kind of thing I was reacting to when I saw Arne's
    example and went YUCK!, YUCK!, YUCK! :-)

    Heh.

    There's no need to leak the existence of `src` or that one is
    using the volatile memcpy intrinsic here. I suppose could could
    also write this as,

    If that is important, I would have placed the whole sequence inside a
    local block as you do below.

    ```rust
    {
    use core::intrinsics::volatile_copy_memory;
    let src = entry.virt_page_addr() as *const arch::Page;
    unsafe {
    volatile_copy_memory(page, src, 1);
    }
    }
    ```

    And this example is much closer to the Ada way. Related statements still
    get checked and only one specific statement, the actual statement that is >unsafe, has checks disabled.

    Let me be clear: _all_ of those expressions get checked. The
    `unsafe` superset just lets you do a few additional things; the
    contract is that you, the programmer, have some external domain
    knowledge that the compiler cannot derive from the code, and you
    are asserting that you have verified that the language's
    requirements vis its semantics hold, since the compiler cannot
    check that for you. `unsafe` does not mean you get to violate
    the language's rules; it just means that you're taking
    responsibility for them when the compiler would ordinarily do
    that for you.

    etc.


    For example, this is how you do an unsafe conversion in Ada:
    https://adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html >>
    This example seems equivalent to an `unsafe fn` in Rust. An
    unsafe conversion of this nature might use `std::mem::transmute`
    in that world.

    The Ada example localises the unsafe bit down to a specific operation
    within a statement, not a full statement, and certainly not a full function.

    Ah, I'm being unclear, then. In Rust, `unsafe fn` means that
    the function must be _called_ from an `unsafe` block. It does
    not mean that the statements inside the function are implicitly
    wrapped in an `unsafe` block (though in fairness it used to;
    this was changed in, I believe, the 2024 edition of the
    language).

    Anyway, I think it's unfair to say that `unsafe` blocks are a
    misfeature, or a sign of poor design. In expression-oriented
    languages, they're sort of size-to-fit.

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Tue Nov 18 13:10:29 2025
    From Newsgroup: comp.os.vms

    In article <10ffuoe$11uj0$2@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/17/2025 3:25 AM, Marc Van Dyck wrote:
    Arne Vajh|+j wrote on 14/11/2025 :
    So for VMS Pascal:
    * search for "::"
    * search for "UNSAFE"
    * multi line regex search for "RECORD.*CASE.*END"
    and setup extra code reviews for all hits.

    Anything else to look for?

    For languages that are supported, would VAXset's Source Code Analyzer be
    of any help to identify such potential weaknesses ? I have been using
    it regularly, but never for that...

    It do not see anything in the SCA manual.

    And when SCA was created the focus was on other things.

    But I have never used SCA myself, so maybe there is something.

    This is an area where modern LSP integration between editors and
    other tooling really shines: again, only using Rust as an
    example, one can configure the popular editors to render
    `unsafe` code in a different font/color/etc, which can be a huge
    help. Of course, language servers for other languages can be
    configured similarly, and since there's a clear separation
    between language server and editor, this technique can be
    employed across many editor/language combinations (provided that
    somebody does the work).

    It would be interesting to do an LSP implementation, for, say,
    VSI Pascal: one could imagine using e.g. VSCode with a local
    proxy agent talking to a language server running on VMS.

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Tue Nov 18 14:04:56 2025
    From Newsgroup: comp.os.vms

    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure that what
    is in the variable after the unsafe conversion is actually a valid value.

    Sum types make this trivial:

    impl SomeType {
    fn try_from(i: i32) -> Option<Self> {
    // if valid, return `Some(whatever`),
    // else return `None`.
    }
    }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Tue Nov 18 14:07:16 2025
    From Newsgroup: comp.os.vms

    On 11/18/2025 2:04 PM, Arne Vajh|+j wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure that
    what
    is in the variable after the unsafe conversion is actually a valid
    value.

    Sum types make this trivial:

    -a-a-a-a impl SomeType {
    -a-a-a-a-a-a-a-a fn try_from(i: i32) -> Option<Self> {
    -a-a-a-a-a-a-a-a-a-a-a-a // if valid, return `Some(whatever`),
    -a-a-a-a-a-a-a-a-a-a-a-a // else return `None`.
    -a-a-a-a-a-a-a-a }
    -a-a-a-a }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    Option is in fashion in recent years.

    There are not that many languages on VMS supporting
    Option.

    Scala does - Option[T].

    And Java 8 Optional<T> is almost the same.

    Arne
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Wed Nov 19 16:02:22 2025
    From Newsgroup: comp.os.vms

    In article <10fig0p$1n41a$2@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure that what >>> is in the variable after the unsafe conversion is actually a valid value. >>
    Sum types make this trivial:

    impl SomeType {
    fn try_from(i: i32) -> Option<Self> {
    // if valid, return `Some(whatever`),
    // else return `None`.
    }
    }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    I'm afraid this misses the point.

    If a language supports sum types, then general solutions like
    `Option` or `Result` types can be employed to represent the
    return type of the conversion operation. If the conversion is
    invalid, then one returns `None` (or, perhaps, the `Err` variant
    of a `Result` if one wants to capture what the actual failure
    was). That is, the conversion operation itself subsumes the
    functionality of the valid attribute, and both the value and
    whether the conversion was valid are represented in the return
    type.

    Again, taking Rust just as an example, there is a standard way
    to represent such conversions; the `TryFrom` trait (and it's
    dual, `TryInto`). Consider this silly example problem: I have a
    type representing colors; perhaps the only colors I care about
    are Red, Green, and Blue. I can label these with integers, say
    1, 2 and 3, and I can represent this in Rust using an
    enumeration.

    Now further suppose that I have a file of 8-bit bytes that
    contain the (raw, integral) representation of these color
    values; I'd like to convert these into proper objects of the
    enumeration type, but of course, not all 8-bit byte values
    represent valid colors, so the proper tool in this case is
    `TryFrom`. Note, however, that converting back, from a color to
    its 8-bit value, is _always_ valid, so I just use `From` in that
    case (which also, conveniently, gives me `into`).

    Here's the code:

    ```rust
    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    enum Colors {
    Red = 1,
    Green = 2,
    Blue = 3,
    }

    impl TryFrom<u8> for Colors {
    type Error = String;
    fn try_from(raw: u8) -> Result<Self, Self::Error> {
    match raw {
    1 => Ok(Self::Red),
    2 => Ok(Self::Green),
    3 => Ok(Self::Blue),
    _ => Err(format!("unsupported value '{raw}'")),
    }
    }
    }

    impl From<Colors> for u8 {
    fn from(color: Colors) -> u8 {
    color as u8
    }
    }

    #[cfg(test)]
    mod tests {
    use super::*;

    #[test]
    fn try_from_raw() {
    assert_eq!(Colors::try_from(1), Ok(Colors::Red));
    assert_eq!(Colors::try_from(2), Ok(Colors::Green));
    assert_eq!(Colors::try_from(3), Ok(Colors::Blue));
    assert_eq!(Colors::try_from(0), Err("unsupported value '0'".into()));
    }

    #[test]
    fn from_clor() {
    assert_eq!(u8::from(Colors::Red), 1);
    assert_eq!(u8::from(Colors::Green), 2);
    assert_eq!(u8::from(Colors::Blue), 3);
    }
    }
    ```

    Note the unit tests. Note also, that, as a result of the way
    that the conversion is structured, as aided by the return type,
    the conversion operation itself is safe.

    Of course, this example is trivial, but the same principle
    applies for more comple conversions as well, including unsafe
    conversions.

    Further, by localizing the unsafety inside of the conversion,
    and using a return type that can represent failure, one can
    construct a _safe_ interface to do _unsafe_ conversions.
    Consider a hypothetical in-place conversion from an array of
    bytes to a string type that is guaranteed to contain only valid
    UTF-8 sequences, such as a Rust `&str`. This is an `unsafe`
    operation, since it relies on the program to assert that the
    byte array contents are valid UTF-8. But, my conversion
    function can probe the input byte sequence for validity, and
    return `Err` if an invalid sequence is found, otherwise it can
    do an (unsafe) conversion and return that in an `Ok`. This is
    fine, since the conversion function itself enforces the
    invariants required by the type.

    In contrast, `Valid` is easy to misuse, primarily by not using
    it at all, in which case you run the risk of raising a runtime
    exception, or just having an incorrect program. With either
    `Option` or `Result`, you are forced to contend with the error
    case. The programmer can't forget to check.

    Finally, `Valid` is very limited in its applicability: it can
    only be used with scalar types. At least in Rust, `Option` and
    `Result` are generic over essentially arbitrary types.

    Put another way, if the language supports something like an
    `Option` type, then there is no need for a special-case facility
    like Ada's `Valid` attribute.

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Wed Nov 19 16:12:32 2025
    From Newsgroup: comp.os.vms

    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/18/2025 2:04 PM, Arne Vajh|+j wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure that
    what
    is in the variable after the unsafe conversion is actually a valid
    value.

    Sum types make this trivial:

    -a-a-a-a impl SomeType {
    -a-a-a-a-a-a-a-a fn try_from(i: i32) -> Option<Self> {
    -a-a-a-a-a-a-a-a-a-a-a-a // if valid, return `Some(whatever`),
    -a-a-a-a-a-a-a-a-a-a-a-a // else return `None`.
    -a-a-a-a-a-a-a-a }
    -a-a-a-a }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s, which as
    far as all things fashion goes, is a pretty good run;
    particularly considering some of the things that were popular in
    that decade.

    There are not that many languages on VMS supporting
    Option.

    Scala does - Option[T].

    And Java 8 Optional<T> is almost the same.

    More's the pity.

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.os.vms on Wed Nov 19 19:29:53 2025
    From Newsgroup: comp.os.vms

    On Wed, 19 Nov 2025 16:12:32 -0000 (UTC)
    cross@spitfire.i.gajendra.net (Dan Cross) wrote:
    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh_+j <arne@vajhoej.dk> wrote:
    On 11/18/2025 2:04 PM, Arne Vajh_+j wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure
    that what
    is in the variable after the unsafe conversion is actually a
    valid value.

    Sum types make this trivial:

    _a_a_a_a impl SomeType {
    _a_a_a_a_a_a_a_a fn try_from(i: i32) -> Option<Self> {
    _a_a_a_a_a_a_a_a_a_a_a_a // if valid, return `Some(whatever`),
    _a_a_a_a_a_a_a_a_a_a_a_a // else return `None`.
    _a_a_a_a_a_a_a_a }
    _a_a_a_a }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s,
    That does not follow.
    Something existing for a long time is not the same as being fashionable
    for a long time.
    Options became fashionable relatively recently, as result of growing dissatisfaction with previous fashionable item - exceptions.
    Parts of programming community that are less concerned with fashion of
    the day ignored both of this things. They use error codes, either in
    crude form (like C) or more refined (like Go).
    which as
    far as all things fashion goes, is a pretty good run;
    particularly considering some of the things that were popular in
    that decade.

    There are not that many languages on VMS supporting
    Option.

    Scala does - Option[T].

    And Java 8 Optional<T> is almost the same.

    More's the pity.

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Wed Nov 19 12:41:15 2025
    From Newsgroup: comp.os.vms

    On 11/19/2025 11:12 AM, Dan Cross wrote:
    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s, which as
    far as all things fashion goes, is a pretty good run;
    particularly considering some of the things that were popular in
    that decade.

    I would not consider Haskell, OCaml to ever have been in fashion.

    Scala 2.0 in 2006, Rust in 2012, Swift in 2014, Java 8 in 2014,
    Python 3.5 in 2015 (only type hint!), C++ 17 are more mainstream
    and more recent.

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Wed Nov 19 18:19:13 2025
    From Newsgroup: comp.os.vms

    In article <10fkvfr$2d2kr$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 11:12 AM, Dan Cross wrote:
    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s, which as
    far as all things fashion goes, is a pretty good run;
    particularly considering some of the things that were popular in
    that decade.

    I would not consider Haskell, OCaml to ever have been in fashion.

    In your world of business software programming? That's probably
    true. In the world of research and systems? Definitely not
    true.

    But notably, Haskell and OCaml both significantly post date SML.

    Scala 2.0 in 2006, Rust in 2012, Swift in 2014, Java 8 in 2014,
    Python 3.5 in 2015 (only type hint!), C++ 17 are more mainstream
    and more recent.

    ...and where do you think they got the idea from? ;-}

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Wed Nov 19 19:00:48 2025
    From Newsgroup: comp.os.vms

    In article <20251119192953.00005981@yahoo.com>,
    Michael S <already5chosen@yahoo.com> wrote:
    On Wed, 19 Nov 2025 16:12:32 -0000 (UTC)
    cross@spitfire.i.gajendra.net (Dan Cross) wrote:

    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh_+j <arne@vajhoej.dk> wrote:
    On 11/18/2025 2:04 PM, Arne Vajh_+j wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure
    that what
    is in the variable after the unsafe conversion is actually a
    valid value.

    Sum types make this trivial:

    _a_a_a_a impl SomeType {
    _a_a_a_a_a_a_a_a fn try_from(i: i32) -> Option<Self> {
    _a_a_a_a_a_a_a_a_a_a_a_a // if valid, return `Some(whatever`),
    _a_a_a_a_a_a_a_a_a_a_a_a // else return `None`.
    _a_a_a_a_a_a_a_a }
    _a_a_a_a }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s,

    That does not follow.
    Something existing for a long time is not the same as being fashionable
    for a long time.

    That's a fair point, but it's also context dependent. ADTs have
    been fashionable for a while in some communities, though perhaps
    not in others. It depends on what kind of work you're doing and
    what specific technologies you work with.

    Options became fashionable relatively recently, as result of growing >dissatisfaction with previous fashionable item - exceptions.

    Eh, now _this_ does not follow. Options and exceptions are
    orthogonal, and many languages that support one also support the
    other.

    Parts of programming community that are less concerned with fashion of
    the day ignored both of this things. They use error codes, either in
    crude form (like C) or more refined (like Go).

    C predates ADTs, of course, and likely wouldn't have adopted
    them even if it didn't because of the constraints of its early
    environment.

    The situation with Go is a bit more complex, and in part comes
    from not designing the language with support for generics from
    the start (to really make effective use of ADTs, you pretty much
    need parametric polymorphism over data types), and generics were
    omitted because they weren't initially sure how to make them
    work well with interfaces, which they were pretty bullish on.

    But I would argue that the Go convention of returning a tuple
    with an explicit `error` element and allowing a simple statement
    to be nested beween the `if` and boolean expression, is closer
    to a Maybe monad than C's overloading an `int`, or using one a
    return value in combination with an out parameter or something
    similar.

    - Dna C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Wed Nov 19 14:21:11 2025
    From Newsgroup: comp.os.vms

    On 11/19/2025 1:19 PM, Dan Cross wrote:
    In article <10fkvfr$2d2kr$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 11:12 AM, Dan Cross wrote:
    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s, which as
    far as all things fashion goes, is a pretty good run;
    particularly considering some of the things that were popular in
    that decade.

    I would not consider Haskell, OCaml to ever have been in fashion.

    In your world of business software programming? That's probably
    true. In the world of research and systems? Definitely not
    true.

    Hmm.

    I would say that the main area for Haskell and OCampl outside of
    university CS departments is in finance, which is business software.

    Just still just a niche in those companies overall IT landscape.

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Wed Nov 19 19:49:34 2025
    From Newsgroup: comp.os.vms

    In article <10fl5b7$2d2kq$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 1:19 PM, Dan Cross wrote:
    In article <10fkvfr$2d2kr$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 11:12 AM, Dan Cross wrote:
    In article <10fig54$1n41a$3@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    Option is in fashion in recent years.

    Algebraic data types have been used in functional languages
    since the 1970s, starting with the "Hope" language from
    Edinburgh. ML took them from Hope and popularized them, and
    they leaked into Miranda, Haskell, and OCaml from there. Now
    many languages support them; even C++ (`std::optional`).

    Hope was first described in a paper in 1980, but the work of
    course predated that.

    That makes them about as old as VMS, and older than Ada. In
    other words, they've been "In Fashion" since the 70s, which as
    far as all things fashion goes, is a pretty good run;
    particularly considering some of the things that were popular in
    that decade.

    I would not consider Haskell, OCaml to ever have been in fashion.

    In your world of business software programming? That's probably
    true. In the world of research and systems? Definitely not
    true.

    Hmm.

    I would say that the main area for Haskell and OCampl outside of
    university CS departments is in finance, which is business software.

    Funny, I would say compiler development and hardware design (e.g
    Bluespec). The only finance folks I know of using e.g. OCaml
    are Jane St; dunno about Haskell in that world, though Meta
    was using it for their PHP compiler at one point.

    Perhaps I should have said "enterprise software." The point is
    that you aren't going to see a lot of IT folks who are used to
    Java or C# or COBOL or something similar slinging OCaml or
    Haskell around. Not many of them are writing compilers either,
    though.

    Just still just a niche in those companies overall IT landscape.

    I mean, Jane St uses OCaml for pretty much everything, but ok. https://www.janestreet.com/tech-talks/ocaml-all-the-way-down/

    Anyway, how about getting back to the point re: Ada and `Valid`?

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Wed Nov 19 20:26:54 2025
    From Newsgroup: comp.os.vms

    On 11/19/2025 11:02 AM, Dan Cross wrote:
    In article <10fig0p$1n41a$2@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure that what >>>> is in the variable after the unsafe conversion is actually a valid value. >>>
    Sum types make this trivial:

    impl SomeType {
    fn try_from(i: i32) -> Option<Self> {
    // if valid, return `Some(whatever`),
    // else return `None`.
    }
    }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    I'm afraid this misses the point.

    If a language supports sum types, then general solutions like
    `Option` or `Result` types can be employed to represent the
    return type of the conversion operation. If the conversion is
    invalid, then one returns `None` (or, perhaps, the `Err` variant
    of a `Result` if one wants to capture what the actual failure
    was). That is, the conversion operation itself subsumes the
    functionality of the valid attribute, and both the value and
    whether the conversion was valid are represented in the return
    type.

    Again, taking Rust just as an example, there is a standard way
    to represent such conversions; the `TryFrom` trait (and it's
    dual, `TryInto`). Consider this silly example problem: I have a
    type representing colors; perhaps the only colors I care about
    are Red, Green, and Blue. I can label these with integers, say
    1, 2 and 3, and I can represent this in Rust using an
    enumeration.

    Now further suppose that I have a file of 8-bit bytes that
    contain the (raw, integral) representation of these color
    values; I'd like to convert these into proper objects of the
    enumeration type, but of course, not all 8-bit byte values
    represent valid colors, so the proper tool in this case is
    `TryFrom`. Note, however, that converting back, from a color to
    its 8-bit value, is _always_ valid, so I just use `From` in that
    case (which also, conveniently, gives me `into`).

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    enum Colors {
    Red = 1,
    Green = 2,
    Blue = 3,
    }

    impl TryFrom<u8> for Colors {
    type Error = String;
    fn try_from(raw: u8) -> Result<Self, Self::Error> {
    match raw {
    1 => Ok(Self::Red),
    2 => Ok(Self::Green),
    3 => Ok(Self::Blue),
    _ => Err(format!("unsupported value '{raw}'")),
    }
    }
    }

    Note the unit tests. Note also, that, as a result of the way
    that the conversion is structured, as aided by the return type,
    the conversion operation itself is safe.

    Of course, this example is trivial, but the same principle
    applies for more comple conversions as well, including unsafe
    conversions.

    Further, by localizing the unsafety inside of the conversion,
    and using a return type that can represent failure, one can
    construct a _safe_ interface to do _unsafe_ conversions.

    In contrast, `Valid` is easy to misuse, primarily by not using
    it at all, in which case you run the risk of raising a runtime
    exception, or just having an incorrect program. With either
    `Option` or `Result`, you are forced to contend with the error
    case. The programmer can't forget to check.

    Finally, `Valid` is very limited in its applicability: it can
    only be used with scalar types. At least in Rust, `Option` and
    `Result` are generic over essentially arbitrary types.

    Put another way, if the language supports something like an
    `Option` type, then there is no need for a special-case facility
    like Ada's `Valid` attribute.

    All fine.

    But you can do the same in Ada.

    Both doing similar to above and by using Unchecked_Conversion
    and Valid.

    with Unchecked_Conversion;

    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    Procedure ColorFul is

    type Color is (Red, Green, Blue);
    for Color use (Red => 1, Green => 2, Blue => 3);
    for Color'Size use Integer'Size;

    procedure Put(col : Color) is

    begin
    case col is
    when Red => Put("Red");
    when Green => Put("Green");
    when Blue => Put("Blue");
    end case;
    end Put;

    type ColorOption(Valid : Boolean := False) is record
    case Valid is
    when False =>
    null;
    when True =>
    Value : Color;
    end case;
    end record;

    function Integer2ColorNice(I : Integer) return ColorOption is

    res : ColorOption;

    begin
    case I is
    when 1 => res := (Valid => True, Value => Red);
    when 2 => res := (Valid => True, Value => Green);
    when 3 => res := (Valid => True, Value => Blue);
    when others => res := (Valid => False);
    end case;
    return res;
    end Integer2ColorNice;

    function Integer2ColorTricky(I : Integer) return ColorOption is

    function BitI2C is new Unchecked_Conversion(Source => Integer, Target => Color);

    temp : Color;
    res : ColorOption;

    begin
    temp := BitI2C(I);
    if temp'Valid then
    res := (Valid => True, Value => temp);
    else
    res := (Valid => False);
    end if;
    return res;
    end Integer2ColorTricky;

    col : ColorOption;

    begin
    for I in 0..4 loop
    col := Integer2ColorNice(I);
    Put(I, Width => 1);
    Put(" is ");
    if col.Valid then
    Put(col.Value);
    else
    Put("not a valid color");
    end if;
    New_Line;
    end loop;
    for I in 0..4 loop
    col := Integer2ColorTricky(I);
    Put(I, Width => 1);
    Put(" is ");
    if col.Valid then
    Put(col.Value);
    else
    Put("not a valid color");
    end if;
    New_Line;
    end loop;
    end ColorFul;

    A bit verbose. But if one want short code, then
    Ada is not the right language choice.

    I am printing instead of asserting, because assertions was first
    to Ada 20 years ago and the VMS GNAT I have is over 25 years old.

    Arne


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Wed Nov 19 20:31:03 2025
    From Newsgroup: comp.os.vms

    On 11/19/2025 8:26 PM, Arne Vajh|+j wrote:
    -a-a temp := BitI2C(I);
    -a-a if temp'Valid then
    -a-a-a-a-a-a res := (Valid => True, Value => temp);
    -a-a else
    -a-a-a-a-a-a res := (Valid => False);
    -a-a end if;
    -a-a return res;

    And in case someone was thinking:

    temp := BitI2C(I);
    res := (Valid => Temp'Valid, Value => temp);
    return res;

    No - that does not compile.

    Arne


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Wed Nov 19 21:32:48 2025
    From Newsgroup: comp.os.vms

    On 11/19/2025 8:26 PM, Arne Vajh|+j wrote:
    But you can do the same in Ada.

    Both doing similar to above and by using Unchecked_Conversion
    and Valid.

    Procedure ColorFul is

    Maybe this variant is more Option'esque:

    with Unchecked_Conversion;

    with Ada.Text_IO, Ada.Integer_Text_IO;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    Procedure ColorFul2 is

    type Color is (Red, Green, Blue);
    for Color use (Red => 1, Green => 2, Blue => 3);
    for Color'Size use Integer'Size;

    procedure Put(col : Color) is

    begin
    case col is
    when Red => Put("Red");
    when Green => Put("Green");
    when Blue => Put("Blue");
    end case;
    end Put;

    type ColorOption(Valid : Boolean := False) is record
    case Valid is
    when False =>
    null;
    when True =>
    Value : Color;
    end case;
    end record;

    function ColorSome(col : Color) return ColorOption is

    res : ColorOption;

    begin
    res := (Valid => True, Value => col);
    return res;
    end ColorSome;

    function ColorNone return ColorOption is

    res : ColorOption;

    begin
    res := (Valid => False);
    return res;
    end ColorNone;

    function Integer2ColorNice(I : Integer) return ColorOption is

    res : ColorOption;

    begin
    case I is
    when 1 => res := ColorSome(Red);
    when 2 => res := ColorSome(Green);
    when 3 => res := ColorSome(Blue);
    when others => res := ColorNone;
    end case;
    return res;
    end Integer2ColorNice;

    function Integer2ColorTricky(I : Integer) return ColorOption is

    function BitI2C is new Unchecked_Conversion(Source => Integer, Target => Color);

    temp : Color;
    res : ColorOption;

    begin
    temp := BitI2C(I);
    if temp'Valid then
    res := ColorSome(temp);
    else
    res := ColorNone;
    end if;
    return res;
    end Integer2ColorTricky;

    col : ColorOption;

    begin
    for I in 0..4 loop
    col := Integer2ColorNice(I);
    Put(I, Width => 1);
    Put(" is ");
    if col.Valid then
    Put(col.Value);
    else
    Put("not a valid color");
    end if;
    New_Line;
    end loop;
    for I in 0..4 loop
    col := Integer2ColorTricky(I);
    Put(I, Width => 1);
    Put(" is ");
    if col.Valid then
    Put(col.Value);
    else
    Put("not a valid color");
    end if;
    New_Line;
    end loop;
    end ColorFul2;

    Arne

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Thu Nov 20 11:54:33 2025
    From Newsgroup: comp.os.vms

    In article <10flqou$2l1qc$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 11:02 AM, Dan Cross wrote:
    In article <10fig0p$1n41a$2@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/15/2025 9:16 PM, Dan Cross wrote:
    Also note the availability of the 'Valid attribute to make sure that what >>>>> is in the variable after the unsafe conversion is actually a valid value. >>>>
    Sum types make this trivial:

    impl SomeType {
    fn try_from(i: i32) -> Option<Self> {
    // if valid, return `Some(whatever`),
    // else return `None`.
    }
    }
    (assuming Option in Rust is what it is in other languages)

    Option and Ada Valid are somewhat different.

    Option is a way for a function/method to either return
    a value or return the fact that there is no value.

    A much better way to do that than traditional
    return null or -1 or whatever to indicate there
    is no value.

    Ada valid attribute is a runtime check on the result from
    an unsafe conversion to see if it meets the constraints
    of the data type.

    I'm afraid this misses the point.

    If a language supports sum types, then general solutions like
    `Option` or `Result` types can be employed to represent the
    return type of the conversion operation. If the conversion is
    invalid, then one returns `None` (or, perhaps, the `Err` variant
    of a `Result` if one wants to capture what the actual failure
    was). That is, the conversion operation itself subsumes the
    functionality of the valid attribute, and both the value and
    whether the conversion was valid are represented in the return
    type.

    Again, taking Rust just as an example, there is a standard way
    to represent such conversions; the `TryFrom` trait (and it's
    dual, `TryInto`). Consider this silly example problem: I have a
    type representing colors; perhaps the only colors I care about
    are Red, Green, and Blue. I can label these with integers, say
    1, 2 and 3, and I can represent this in Rust using an
    enumeration.

    Now further suppose that I have a file of 8-bit bytes that
    contain the (raw, integral) representation of these color
    values; I'd like to convert these into proper objects of the
    enumeration type, but of course, not all 8-bit byte values
    represent valid colors, so the proper tool in this case is
    `TryFrom`. Note, however, that converting back, from a color to
    its 8-bit value, is _always_ valid, so I just use `From` in that
    case (which also, conveniently, gives me `into`).

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    enum Colors {
    Red = 1,
    Green = 2,
    Blue = 3,
    }

    impl TryFrom<u8> for Colors {
    type Error = String;
    fn try_from(raw: u8) -> Result<Self, Self::Error> {
    match raw {
    1 => Ok(Self::Red),
    2 => Ok(Self::Green),
    3 => Ok(Self::Blue),
    _ => Err(format!("unsupported value '{raw}'")),
    }
    }
    }

    Note the unit tests. Note also, that, as a result of the way
    that the conversion is structured, as aided by the return type,
    the conversion operation itself is safe.

    Of course, this example is trivial, but the same principle
    applies for more comple conversions as well, including unsafe
    conversions.

    Further, by localizing the unsafety inside of the conversion,
    and using a return type that can represent failure, one can
    construct a _safe_ interface to do _unsafe_ conversions.

    In contrast, `Valid` is easy to misuse, primarily by not using
    it at all, in which case you run the risk of raising a runtime
    exception, or just having an incorrect program. With either
    `Option` or `Result`, you are forced to contend with the error
    case. The programmer can't forget to check.

    Finally, `Valid` is very limited in its applicability: it can
    only be used with scalar types. At least in Rust, `Option` and
    `Result` are generic over essentially arbitrary types.

    Put another way, if the language supports something like an
    `Option` type, then there is no need for a special-case facility
    like Ada's `Valid` attribute.

    All fine.

    But you can do the same in Ada.

    Indeed, you can, but I never said you couldn't. My point was
    that Ada's `Valid` attribute is overly specific, underpowered,
    and ultimately unnecessary in a language that supports proper
    sum types over generics. Ada's `Valid` attribute is neither as
    general nor as robust as using an ADT like
    Option/Result/Maybe/whatever it's called in any given langauge.

    Both doing similar to above and by using Unchecked_Conversion
    and Valid.

    [snip code segment for brevity]

    A bit verbose. But if one want short code, then
    Ada is not the right language choice.

    Also not as general. Note that in the Rust example, `Option` is
    generic over some type `T`: your example is concrete over the
    color enum, and just colocates it with a flag and a convenience
    method that returns null if the flag is false (but now all
    accesses are via reference). As a simulacrum, it will serve,
    but one could do that in any number of languages; even C with a
    couple of accessor functions can do something similar.

    But Ada supports generics (recall that Stepanov did the first
    implementation of the STL in Ada). You could use that to build
    an actual `Option` type that would closer to the Rust version.
    It would probably not be as convenient to use because Ada lacks
    Rust-style pattern matching and destructuring, but it would more
    or less obviate the need for `Valid`.

    I am printing instead of asserting, because assertions was first
    to Ada 20 years ago and the VMS GNAT I have is over 25 years old.

    Fortunately, Generics have been in the language since the 80s.
    :-D

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@arne@vajhoej.dk to comp.os.vms on Thu Nov 20 19:18:52 2025
    From Newsgroup: comp.os.vms

    On 11/20/2025 6:54 AM, Dan Cross wrote:
    In article <10flqou$2l1qc$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 11:02 AM, Dan Cross wrote:
    Further, by localizing the unsafety inside of the conversion,
    and using a return type that can represent failure, one can
    construct a _safe_ interface to do _unsafe_ conversions.

    In contrast, `Valid` is easy to misuse, primarily by not using
    it at all, in which case you run the risk of raising a runtime
    exception, or just having an incorrect program. With either
    `Option` or `Result`, you are forced to contend with the error
    case. The programmer can't forget to check.

    Finally, `Valid` is very limited in its applicability: it can
    only be used with scalar types. At least in Rust, `Option` and
    `Result` are generic over essentially arbitrary types.

    Put another way, if the language supports something like an
    `Option` type, then there is no need for a special-case facility
    like Ada's `Valid` attribute.

    All fine.

    But you can do the same in Ada.

    Indeed, you can, but I never said you couldn't. My point was
    that Ada's `Valid` attribute is overly specific, underpowered,
    and ultimately unnecessary in a language that supports proper
    sum types over generics. Ada's `Valid` attribute is neither as
    general nor as robust as using an ADT like
    Option/Result/Maybe/whatever it's called in any given langauge.

    Ada's valid does exactly what it is supposed to do: check if the
    output from an Unchecked_Conversion meet the constraints of the
    data type.

    It is not a replacement for Option/Result.

    It can be used to implement Option/Result for those
    types that can be set with Unchecked_Conversion, but obviously
    not for other data types.

    A screwdriver is not good for hammering nails into wood,
    but that does not mean that a screwdriver is a bad tool - it
    is just not intended for that usage.

    Regarding the risk of not being checked, then it is inherent
    in the problem - no matter the mechanism used then the developer
    can chose not to do it right, because "there will always be
    a valid value here".

    Also not as general. Note that in the Rust example, `Option` is
    generic over some type `T`: your example is concrete over the
    color enum, and just colocates it with a flag and a convenience
    method that returns null if the flag is false (but now all
    accesses are via reference).

    But Ada supports generics (recall that Stepanov did the first
    implementation of the STL in Ada). You could use that to build
    an actual `Option` type that would closer to the Rust version.

    Generics is one of those Ada features that I don't like to use.

    But a generic version is attached below.

    It would probably not be as convenient to use because Ada lacks
    Rust-style pattern matching and destructuring, but it would more
    or less obviate the need for `Valid`.
    Valid is still a way - probably the best way - to produce
    the Option.

    Arne

    ++++

    $ type OptionPackage.ads
    generic
    type T is private;

    package OptionPackage is

    type Option(Valid : Boolean := False) is record
    case Valid is
    when False =>
    null;
    when True =>
    Value : T;
    end case;
    end record;

    function SomeValue(O : T) return Option;

    function None return Option;

    end OptionPackage;
    $ type OptionPackage.adb
    package body OptionPackage is

    function SomeValue(O : T) return Option is

    res : Option;

    begin
    res := (Valid => True, Value => o);
    return res;
    end SomeValue;

    function None return Option is

    res : Option;

    begin
    res := (Valid => False);
    return res;
    end None;

    end OptionPackage;
    $ type ColorFul3.adb
    with Unchecked_Conversion;

    with Ada.Text_IO, Ada.Integer_Text_IO;

    with OptionPackage;

    use Ada.Text_IO, Ada.Integer_Text_IO;

    Procedure ColorFul3 is

    type Color is (Red, Green, Blue);
    for Color use (Red => 1, Green => 2, Blue => 3);
    for Color'Size use Integer'Size;

    procedure Put(col : Color) is

    begin
    case col is
    when Red => Put("Red");
    when Green => Put("Green");
    when Blue => Put("Blue");
    end case;
    end Put;

    package ColorOption is new OptionPackage(T => Color);

    function Integer2ColorNice(I : Integer) return ColorOption.Option is

    res : ColorOption.Option;

    begin
    case I is
    when 1 => res := ColorOption.SomeValue(Red);
    when 2 => res := ColorOption.SomeValue(Green);
    when 3 => res := ColorOption.SomeValue(Blue);
    when others => res := ColorOption.None;
    end case;
    return res;
    end Integer2ColorNice;

    function Integer2ColorTricky(I : Integer) return ColorOption.Option is

    function BitI2C is new Unchecked_Conversion(Source => Integer, Target => Color);

    temp : Color;
    res : ColorOption.Option;

    begin
    temp := BitI2C(I);
    if temp'Valid then
    res := ColorOption.SomeValue(temp);
    else
    res := ColorOption.None;
    end if;
    return res;
    end Integer2ColorTricky;

    col : ColorOption.Option;

    begin
    for I in 0..4 loop
    col := Integer2ColorNice(I);
    Put(I, Width => 1);
    Put(" is ");
    if col.Valid then
    Put(col.Value);
    else
    Put("not a valid color");
    end if;
    New_Line;
    end loop;
    for I in 0..4 loop
    col := Integer2ColorTricky(I);
    Put(I, Width => 1);
    Put(" is ");
    if col.Valid then
    Put(col.Value);
    else
    Put("not a valid color");
    end if;
    New_Line;
    end loop;
    end ColorFul3;

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.os.vms on Fri Nov 21 03:03:25 2025
    From Newsgroup: comp.os.vms

    In article <10fob5c$3aj3s$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/20/2025 6:54 AM, Dan Cross wrote:
    In article <10flqou$2l1qc$1@dont-email.me>,
    Arne Vajh|+j <arne@vajhoej.dk> wrote:
    On 11/19/2025 11:02 AM, Dan Cross wrote:
    Further, by localizing the unsafety inside of the conversion,
    and using a return type that can represent failure, one can
    construct a _safe_ interface to do _unsafe_ conversions.

    In contrast, `Valid` is easy to misuse, primarily by not using
    it at all, in which case you run the risk of raising a runtime
    exception, or just having an incorrect program. With either
    `Option` or `Result`, you are forced to contend with the error
    case. The programmer can't forget to check.

    Finally, `Valid` is very limited in its applicability: it can
    only be used with scalar types. At least in Rust, `Option` and
    `Result` are generic over essentially arbitrary types.

    Put another way, if the language supports something like an
    `Option` type, then there is no need for a special-case facility
    like Ada's `Valid` attribute.

    All fine.

    But you can do the same in Ada.

    Indeed, you can, but I never said you couldn't. My point was
    that Ada's `Valid` attribute is overly specific, underpowered,
    and ultimately unnecessary in a language that supports proper
    sum types over generics. Ada's `Valid` attribute is neither as
    general nor as robust as using an ADT like
    Option/Result/Maybe/whatever it's called in any given langauge.

    Ada's valid does exactly what it is supposed to do: check if the
    output from an Unchecked_Conversion meet the constraints of the
    data type.

    Again. Ada's `Valid` attribute is overly specific (it cannot be
    repurposed for other things), underpowered (it only applies to
    scalars and, critically, can be ignored) and unnecessary if a
    language has an `Option` (`Maybe`) or `Result` (`Either`) type.

    It is not a replacement for Option/Result.

    No, it is not. Indeed, it _cannot_ be since it is a strictly
    weaker construct in all respects.

    However, `Option` or `Result`, in conjunction with something
    like `TryFrom`, _can_ be a replacement for `Valid`, because
    things like `TryFrom` and `Option`/`Result` are more general and
    more powerful than `Valid`.

    It can be used to implement Option/Result for those
    types that can be set with Unchecked_Conversion, but obviously
    not for other data types.

    It could be, but it doesn't _need_ to be.

    I can see how `Valid` might be _useful_. Unfortunately, the
    construct seems overly rooted in some fundamental assumptions
    that Ada made about the world back in the 70s and 80s. It's
    just been passed by by the state of the art since then, as has
    Ada more generally. That's a pity; Ada isn't a _bad_ language,
    but we have better alternatives now.

    A screwdriver is not good for hammering nails into wood,
    but that does not mean that a screwdriver is a bad tool - it
    is just not intended for that usage.

    Non sequitur. You seem to be arguing something nobody was
    saying. What I am saying is that `Valid` isn't a great tool for
    its intended purpose. It's like trying to turn a phillips-head
    screw with a flat blade driver by sorta twisting it to the
    side....

    Regarding the risk of not being checked, then it is inherent
    in the problem - no matter the mechanism used then the developer
    can chose not to do it right, because "there will always be
    a valid value here".

    Nope, this is wrong. The whole point of a Rust-style `Option`
    or `Result` is that you are no longer working in terms of the
    generic type T, but rather a wrapper around that type; if that
    wrapper has the value `None` then there is no T, so the
    statement, 'because "there will always be a valid value here"'
    is, by definition, incorrect. The programmer is forced to
    contend with this possibility because because the programmer is
    working in terms of the wrapper type (the `Option` itself, say)
    and not the underlying value type.

    That said, Cloudflare had a recent production outage because
    somebody called `unwrap` on a `Result` that was `Err`. Yeah,
    that was unwise. But note that this is qualitatively different:
    here, the programmer _chose_ to ignore the safety guidelines and
    unwrap something that wasn't known to be `Ok(...)`; this was an
    _active_ thing. That's different than omitting a check against
    the valid attribute, through carelessness or ignorance; that's a
    _passive_ thing..

    Also not as general. Note that in the Rust example, `Option` is
    generic over some type `T`: your example is concrete over the
    color enum, and just colocates it with a flag and a convenience
    method that returns null if the flag is false (but now all
    accesses are via reference).

    But Ada supports generics (recall that Stepanov did the first
    implementation of the STL in Ada). You could use that to build
    an actual `Option` type that would closer to the Rust version.

    Generics is one of those Ada features that I don't like to use.

    That's silly. It's a powerful construct that _should_ be used,
    though of course it has its costs (monomorphization on concrete
    types, primarily).

    But a generic version is attached below.

    It would probably not be as convenient to use because Ada lacks
    Rust-style pattern matching and destructuring, but it would more
    or less obviate the need for `Valid`.

    Valid is still a way - probably the best way - to produce
    the Option.

    Well, perhaps, provided that the concrete type embedded in the
    `Option` will work with `Valid`; recall that `Valid` applies
    only to scalars. If the `Option` wraps a product type (records,
    etc) then `Valid` won't work, as it's not applicable in that
    context.

    But for scalars, whether it's the "best" way or not seems highly
    subjective. There are probably categories of scalar values that
    are difficult to represent using the language's type constraints
    and so for which `Valid` would not meanigfully apply, but
    something like, `TryFrom` might. For example, one might define
    a newtype over integers that represents primes; the invariant is
    that if an instance of that type exists at all, then it is
    guaranteed that the contained number must be prime. This is the
    sort of thing you _can_ check with an explicit conversion
    function, but _not_ with `Valid`.

    A nice property of using an `Valid` in your example `Option`
    type, however is that because the check for validity is
    encapsulated in the machinery built into the `Option` itself;
    the programmer cannot misuse it because its use is hidden from
    the programmer.

    [snip]

    - Dan C.

    --- Synchronet 3.21a-Linux NewsLink 1.2