~sircmpwn/hare-dev

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
9 5

[RFC PATCH harelang.org v2] Add page explaining various error conditions

Details
Message ID
<20231220221545.1600647-1-sir@cmpwn.com>
DKIM signature
missing
Download raw message
Patch: +243 -0
To be added to harec to add helpful links alongside error messages.
---
v2 moves it to a shorter URL, https://harelang.org/errors

 assets/main.scss                  |   4 +
 content/errors.md                 | 231 ++++++++++++++++++++++++++++++
 layouts/codetutorials/single.html |   8 ++
 3 files changed, 243 insertions(+)
 create mode 100644 content/errors.md

diff --git a/assets/main.scss b/assets/main.scss
index 28d7c15..666d440 100644
--- a/assets/main.scss
+++ b/assets/main.scss
@@ -327,6 +327,10 @@ ol li {
    margin: 0 auto;
  }

  li .highlight {
    margin-bottom: 1rem;
  }

  section {
    display: grid;
    grid-template-rows: 1fr;
diff --git a/content/errors.md b/content/errors.md
new file mode 100644
index 0000000..92e77e6
--- /dev/null
+++ b/content/errors.md
@@ -0,0 +1,231 @@
---
# vim: ts=2 sw=2 tw=80 et :
title: Error message details
type: codetutorials
summary: |
  This page summarizes conditions from which various errors can arise when
  working on Hare projects.

  Each error includes a description of the problem, a sample of the kind of code
  which can create such a scenario, and suggested options for resolving the
  situation.
sections:
- section: Runtime assertions
- title: Slice or array out of bounds
  id: fa-0
  sample: |
      export fn main() void = {
      	let x: [3]int = [1, 2, 3];
      	x[0]; // okay
      	x[1]; // okay
      	x[2]; // okay
      	x[3]; // abort
      
      	x[0..2];  // okay
      	x[0..10]; // abort
      };
  details: |
    The "slice or array out of bounds" message is a **runtime assertion** which
    occurs when accessing an index of a slice or array which falls outside of
    its defined length. This may occur when using an indexing expression
    (`x[4]`), a slicing expression (`x[2..4]`), or a slice assignment expression
    <br />(`x[..] = y[..]`).

    This message indicates that the programmer has made a design error.

    **Recommended solutions**

    - Test that your index is less than the length of the object before
      accessing a value at that index:

      ```hare
      let x: [3]int;
      let ix = 10z;
      if (ix < len(x)) {
              x[ix]; // okay
      };
      ```
    - Ensure that you are computing indicies correctly. For example, look for
      [off-by-one errors](https://en.wikipedia.org/wiki/Off-by-one_error).
      Note that slices and arrays in Hare are zero-indexed, so the first item is
      at `0` and the last item is at `len(x) - 1`.

    *Further reading: [Arrays and slices](https://harelang.org/tutorials/introduction/#arrays-and-slices),
    [Working with slices](https://harelang.org/tutorials/introduction/#working-with-slices)*
- title: Type assertion failed
  id: fa-1
  sample: |
      export fn main() void = {
      	let x: (int | uint) = 10i;  // value is of type int
      	x as int;                   // okay
      	x as uint;                  // abort
      };
  details: |
    The "type assertion failed" message is a **runtime assertion** which occurs
    when the `as` operator is used on a tagged union value which is storing a
    value of a type which differs from the right-hand side of the `as`
    operation.

    This message indicates that the programmer has made a design error.

    **Recommended solutions**

    - Determine why the value was of a different type than the programmer
      expected at this point.
    - Replace the `as` expression with a match expression which handles more
      possible cases.

      ```hare
      // Before:
      let x: (int | uint) = 10i;
      x as uint; // Will fail if x is an int

      // After:
      let x: (int | uint) = 10i;
      match (x) {
      case let x: int =>
              // x is an int
      case let x: uint =>
              // x is a uint
      };
      ```

    *Further reading: [Casting & type assertions](https://harelang.org/tutorials/introduction/#casting--type-assertions)*
- title: Out of memory
  id: fa-2
  sample: |
      export fn main() void = {
      	let x: *int = alloc(42);         // might fail
      	let x: []int = alloc([1, 2, 3]); // might fail
      	append(x, 42);                   // might fail
      	insert(x[0], 42);                // might fail
      };
  details: |
    The "out of memory" message is a **runtime assertion** which occurs when a
    memory allocation fails without being handled by the programmer.

    **Recommended solutions**

    - If using the `alloc` expression, use a *nullable* pointer type. In the
      event of an allocation failure, the value will be null and this runtime
      assertion will not occur.

    <div class="alert">
      <strong>Note:</strong> Improving support for managing out-of-memory
      conditions in Hare programs is a design area planned for future
      improvement.
    </div>
- title: Static insert/append exceeds slice capacity
  id: fa-3
  sample: |
      export fn main() void = {
      	let x: [4]int = [0...];
      	let y: []int = x[..0];
      	static append(x, 1);
      	static append(x, 2);
      	static append(x, 3);
      	static append(x, 4);
      	static append(x, 5);  // abort
      };
  details: |
    The "static insert/append exceeds slice capacity" message is a **runtime
    assertion** which occurs when a `static append` or `static insert` operation
    would exceed the capacity of the slice. When using static insert and append,
    the programmer must ensure that their program will not exceed the amount of
    space allocated for the underlying slice.

    This message indicates that the programmer has made a design error.

    **Recommended solutions**

    - Test that the capacity is sufficient before performing an insert or
      append, and handle the case where there is insufficient space.
    - Increase the size of the underlying array.
    - If better suited to your use-case, switch to dynamically allocated slices
      (i.e. drop the "static" keyword).

    *Further reading: [Static slice operations](https://harelang.org/tutorials/introduction/#static-slice-operations)*
- title: Execution reached unreachable code (compiler bug)
  id: fa-4
  sample: |
      export fn main() void = {
      	let x: (u8 | u16 | u32 | u64) = 0u64;
      	match (x) {
      	case u8 => // ...
      	case u16 => // ...
      	case u32 => // ...
      	};
      };
  details: |
    The "execution reached unreachable code (compiler bug)" message is a
    **runtime assertion** which occurs when the program reaches an area in the
    generated code which was thought to be impossible by the compiler designers.
    This is a safeguard put in place by the compiler to avoid introducing
    vulnerabilities or unexpected behavior if such situations were to arise
    anyway.

    This message indicates that a bug is present in the Hare compiler. Please
    report it to the [hare-users] mailing list. (*See note below -- do not
    report cases with respect to inexhaustive match expressions*)

    [hare-users]: https://lists.sr.ht/~sircmpwn/hare-users

    <div class="alert">
      <strong>Note:</strong> The compiler does not currently implement
      exhaustivity analysis for match expressions. The most common cause of this
      assertion occuring in Hare programs today is when a match expression does
      not have enough branches for every possible type of its input. Presently
      this is enforced at runtime through this runtime assertion, but in the
      future the sample code at the right will produce a compiler error. To
      address the problem in your own code, double check that your match
      expression includes all possible cases.
    </div>
- title: Slice allocation capacity smaller than initializer
  id: fa-5
  sample: |
      export fn main() void = {
      	let capacity = 3z;
      	let x: []int = alloc([1, 2, 3, 4, 5], capacity); // abort
      };
  details: |
    The "slice allocation capacity smaller than initializer" message is a
    **runtime assertion** which occurs when allocating a slice with both an
    initializer and a desired capacity using an `alloc` expression if the
    provided initializer has a greater length than the desired capacity.

    This message indicates that the programmer has made a design error.

    **Recommended solutions**

    - Ensure that the initializer has no excess elements
    - Increase the requested capacity
- title: Error occured
  id: fa-7
  sample: |
      use fmt;
      
      // Use `hare run main.ha >/dev/full` to simulate this error
      export fn main() void = {
        fmt::println("Hello world!")!; // abort
      };
  details: |
    The "error occured" message is a **runtime assertion** which occurs when
    the error assertion operator (`!`) is used and an error occured at runtime.

    This message indicates that the programmer has made a design error.

    **Recommended solutions**

    - Determine why an error occured where one was not expected
    - Use a match expression to handle the possible error cases explicitly
    - Use the error propagation operator (`?`) and update the return type to
      pass the error to the function which called the relevant code

    <div class="alert">
      <strong>Tip:</strong> Using haredoc to look up the function which
      produced the error is a good way to quickly find out what possible
      outcomes you might need to handle.
    </div>

    *Further reading: [Handling errors](https://harelang.org/tutorials/introduction/#handling-errors)*
---
diff --git a/layouts/codetutorials/single.html b/layouts/codetutorials/single.html
index 975a846..6475b4f 100644
--- a/layouts/codetutorials/single.html
+++ b/layouts/codetutorials/single.html
@@ -34,7 +34,11 @@

        {{ else }}
        <li>
          {{ if .id }}
          <a href="#{{ .id }}">{{ .title }}</a>
          {{ else }}
          <a href="#{{ .title | anchorize }}">{{ .title }}</a>
          {{ end }}
        </li>
        {{ end }}

@@ -53,7 +57,11 @@
  <h2 id="{{ .section | anchorize }}">{{.section}}</h2>
  {{ else }}
  <section>
    {{ if .id }}
    <h3 id="{{ .id }}">{{.title}}</h3>
    {{ else }}
    <h3 id="{{ .title | anchorize }}">{{.title}}</h3>
    {{ end }}
    {{ if .sample }}
    {{ highlight .sample "hare" "" }}
    {{ end }}
-- 
2.43.0

[harelang.org/patches/.build.yml] build success

builds.sr.ht <builds@sr.ht>
Details
Message ID
<CXTIBA2FWUOC.2IS72PVSCHU50@cirno2>
In-Reply-To
<20231220221545.1600647-1-sir@cmpwn.com> (view parent)
DKIM signature
missing
Download raw message
harelang.org/patches/.build.yml: SUCCESS in 26s

[Add page explaining various error conditions][0] v2 from [Drew DeVault][1]

[0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/47913
[1]: sir@cmpwn.com

✓ #1118662 SUCCESS harelang.org/patches/.build.yml https://builds.sr.ht/~sircmpwn/job/1118662
Details
Message ID
<CXTIGKPRVF9T.1E18A3A2VYPK0@d2evs.net>
In-Reply-To
<20231220221545.1600647-1-sir@cmpwn.com> (view parent)
DKIM signature
missing
Download raw message
+1

On Wed Dec 20, 2023 at 10:15 PM UTC, Drew DeVault wrote:
> diff --git a/content/errors.md b/content/errors.md
> @@ -0,0 +1,231 @@
-%<-
> +    - Ensure that you are computing indicies correctly. For example, look for

indices*. should probably run this through a spellchecker more broadly

> +- title: Execution reached unreachable code (compiler bug)

i think we should remove the "(compiler bug)" from this message (both
here and in harec) until we implement match exhaustivity checking

> +  details: |
> +    The "execution reached unreachable code (compiler bug)" message is a
> +    **runtime assertion** which occurs when the program reaches an area in the
> +    generated code which was thought to be impossible by the compiler designers.
> +    This is a safeguard put in place by the compiler to avoid introducing
> +    vulnerabilities or unexpected behavior if such situations were to arise
> +    anyway.
> +
> +    This message indicates that a bug is present in the Hare compiler. Please
> +    report it to the [hare-users] mailing list. (*See note below -- do not
> +    report cases with respect to inexhaustive match expressions*)

similarly, i think we should put the note about non-exhaustive matches
before mentioning that it might be a compiler bug

> +    [hare-users]: https://lists.sr.ht/~sircmpwn/hare-users
> +
> +    <div class="alert">
> +      <strong>Note:</strong> The compiler does not currently implement
> +      exhaustivity analysis for match expressions. The most common cause of this
> +      assertion occuring in Hare programs today is when a match expression does
> +      not have enough branches for every possible type of its input. Presently
> +      this is enforced at runtime through this runtime assertion, but in the
> +      future the sample code at the right will produce a compiler error. To
> +      address the problem in your own code, double check that your match
> +      expression includes all possible cases.
> +    </div>
Details
Message ID
<CXTJJGR92DDJ.197GI1J0MHQ11@notmylaptop>
In-Reply-To
<20231220221545.1600647-1-sir@cmpwn.com> (view parent)
DKIM signature
missing
Download raw message
+1

On Wed Dec 20, 2023 at 5:15 PM EST, Drew DeVault wrote:
> +- section: Runtime assertions
> +- title: Slice or array out of bounds
> +  id: fa-0
> +  sample: |
> +      export fn main() void = {
> +      	let x: [3]int = [1, 2, 3];
> +      	x[0]; // okay
> +      	x[1]; // okay
> +      	x[2]; // okay
> +      	x[3]; // abort
> +      
> +      	x[0..2];  // okay
> +      	x[0..10]; // abort
> +      };
> +  details: |
> +    The "slice or array out of bounds" message is a **runtime assertion** which
> +    occurs when accessing an index of a slice or array which falls outside of
> +    its defined length. This may occur when using an indexing expression
> +    (`x[4]`), a slicing expression (`x[2..4]`), or a slice assignment expression
> +    <br />(`x[..] = y[..]`).

harec recognizes out-of-bounds indexing of arrays at compile-time, so
this runtime assert only applies for slices.

Also, small nit: this can also happen in insert/delete expressions, but
notably, for insert expressions, the bounds check is different, since
`insert(x[len(x)], 0]` is legal, and equivalent to append. This is
enough of an edge-case that it may not be worth bringing it up here, but
I figured I'd at least mention it.

> +    This message indicates that the programmer has made a design error.
> +
> +    **Recommended solutions**
> +
> +    - Test that your index is less than the length of the object before
> +      accessing a value at that index:
> +
> +      ```hare
> +      let x: [3]int;

Syntax error: variable never initialized.

> +      let ix = 10z;
> +      if (ix < len(x)) {
> +              x[ix]; // okay
> +      };
> +      ```

Maybe there could also be an example which uses &&, to demonstrate
short-circuiting behavior?

	let x: []int;
	let ix = 10z;
	if (ix < len(x) && x[ix] == 0) { // short-circuiting, okay
		// ...
	};

> +    The "execution reached unreachable code (compiler bug)" message is a
> +    **runtime assertion** which occurs when the program reaches an area in the
> +    generated code which was thought to be impossible by the compiler designers.
> +    This is a safeguard put in place by the compiler to avoid introducing
> +    vulnerabilities or unexpected behavior if such situations were to arise
> +    anyway.
> +
> +    This message indicates that a bug is present in the Hare compiler. Please
> +    report it to the [hare-users] mailing list. (*See note below -- do not
> +    report cases with respect to inexhaustive match expressions*)

This isn't always true. Notably, with the current implementation of
enums, it's trivial to cause this with a switch expression:

	type foo = enum { A, B };
	let x = foo::A | foo::B;
	switch (x) {
	case foo::A => void;
	case foo::B => void;
	};

This isn't a compiler bug. Even when we improve the situation with
enums, it'll still be possible to cast an integer to an enum.

This can also occur as a result of something like stack corruption.

The spec even requires the unreachable code abort, in addition to
requiring exhaustivity:

6.7.44.7:
>> If the value of the switching expression doesn't compare equal to any
>> of the switch-cases, a diagnostic message shall be printed and the
>> execution environment shall abort.
>> 
>> (informative) This isn't possible under normal circumstances, but
>> certain operations such as invalid casts can cause this to occur.

6.7.45.10:
>> If the type of the matching expression isn't accounted for by any of
>> the match-cases, a diagnostic message shall be printed and the
>> execution environment shall abort.
>> 
>> (informative) This isn't possible under normal circumstances, but
>> certain operations such as invalid casts can cause this to occur.

> +- title: Slice allocation capacity smaller than initializer
> +  id: fa-5
> +  sample: |
> +      export fn main() void = {
> +      	let capacity = 3z;
> +      	let x: []int = alloc([1, 2, 3, 4, 5], capacity); // abort
> +      };
> +  details: |
> +    The "slice allocation capacity smaller than initializer" message is a
> +    **runtime assertion** which occurs when allocating a slice with both an
> +    initializer and a desired capacity using an `alloc` expression if the
> +    provided initializer has a greater length than the desired capacity.

As of now, this commonly will happen when using an expandable array and
a capacity of zero:

	let capacity = 0z;
	let x: []int = alloc([0...], capacity); // abort

If we implement [...] and change the behavior to fill zero or more
values, this will stop being an issue, but for now, it might be good to
add this as an example, and recommend that for now a workaround like
`if (capacity == 0) [] else alloc([0...], capacity)` is used instead.
Details
Message ID
<CXTJK9WZPZ3E.2HKRX0H9EDY7C@d2evs.net>
In-Reply-To
<CXTJJGR92DDJ.197GI1J0MHQ11@notmylaptop> (view parent)
DKIM signature
missing
Download raw message
On Wed Dec 20, 2023 at 11:13 PM UTC, Sebastian wrote:
> harec recognizes out-of-bounds indexing of arrays at compile-time, so
> this runtime assert only applies for slices.

fwiw i kinda want to remove these
Details
Message ID
<CXTYTT25J0HM.1QCL78R9A6JON@taiga>
In-Reply-To
<CXTJK9WZPZ3E.2HKRX0H9EDY7C@d2evs.net> (view parent)
DKIM signature
missing
Download raw message
On Thu Dec 21, 2023 at 12:15 AM CET, Ember Sawady wrote:
> On Wed Dec 20, 2023 at 11:13 PM UTC, Sebastian wrote:
> > harec recognizes out-of-bounds indexing of arrays at compile-time, so
> > this runtime assert only applies for slices.
>
> fwiw i kinda want to remove these

NACK
Details
Message ID
<CXUROYLIP9I2.2I8W38M9SKW4G@taiga>
In-Reply-To
<CXTIGKPRVF9T.1E18A3A2VYPK0@d2evs.net> (view parent)
DKIM signature
missing
Download raw message
On Wed Dec 20, 2023 at 11:23 PM CET, Ember Sawady wrote:
> > +- title: Execution reached unreachable code (compiler bug)
>
> i think we should remove the "(compiler bug)" from this message (both
> here and in harec) until we implement match exhaustivity checking

+1

> > +  details: |
> > +    The "execution reached unreachable code (compiler bug)" message is a
> > +    **runtime assertion** which occurs when the program reaches an area in the
> > +    generated code which was thought to be impossible by the compiler designers.
> > +    This is a safeguard put in place by the compiler to avoid introducing
> > +    vulnerabilities or unexpected behavior if such situations were to arise
> > +    anyway.
> > +
> > +    This message indicates that a bug is present in the Hare compiler. Please
> > +    report it to the [hare-users] mailing list. (*See note below -- do not
> > +    report cases with respect to inexhaustive match expressions*)
>
> similarly, i think we should put the note about non-exhaustive matches
> before mentioning that it might be a compiler bug

aight
Details
Message ID
<CXUT73Z1UDCS.3OW75NUADQQSW@taiga>
In-Reply-To
<CXTJJGR92DDJ.197GI1J0MHQ11@notmylaptop> (view parent)
DKIM signature
missing
Download raw message
On Thu Dec 21, 2023 at 12:13 AM CET, Sebastian wrote:
> harec recognizes out-of-bounds indexing of arrays at compile-time, so
> this runtime assert only applies for slices.

Not always true. First, the compile-time test is optional per the spec,
so another compiler could not perform it and raise this message.

But also, this supposition is trivially disproven:

use fmt;

export fn main() void = {
	let arr: [3]int = [0...];
	let ix = 42;
	fmt::println(arr[ix])!;
};

The compile-time test only works if the index is translation-compatible.

> Maybe there could also be an example which uses &&, to demonstrate
> short-circuiting behavior?

nah

> As of now, this commonly will happen when using an expandable array and
> a capacity of zero:
>
> 	let capacity = 0z;
> 	let x: []int = alloc([0...], capacity); // abort
>
> If we implement [...] and change the behavior to fill zero or more
> values, this will stop being an issue, but for now, it might be good to
> add this as an example, and recommend that for now a workaround like
> `if (capacity == 0) [] else alloc([0...], capacity)` is used instead.

Actually, filling in 0 or more values sounds like a good idea. I'd
rather just implement that than document the workaround.
Details
Message ID
<CZ02ZA7LWP5L.UL68RC9RFHUI@attila>
In-Reply-To
<CXTYTT25J0HM.1QCL78R9A6JON@taiga> (view parent)
DKIM signature
pass
Download raw message
On Thu Dec 21, 2023 at 12:12 PM CET, Drew DeVault wrote:
> On Thu Dec 21, 2023 at 12:15 AM CET, Ember Sawady wrote:
> > On Wed Dec 20, 2023 at 11:13 PM UTC, Sebastian wrote:
> > > harec recognizes out-of-bounds indexing of arrays at compile-time, so
> > > this runtime assert only applies for slices.
> >
> > fwiw i kinda want to remove these
>
> NACK

Any particular reason? Apologies if there was already some more discussion that
I missed. I liked those at first too, but they're kinda against the principle
of not being too smart about runtime values at compile time.
Details
Message ID
<CZ0DYD8OY7YN.Z45VORXISJUG@taiga>
In-Reply-To
<CZ02ZA7LWP5L.UL68RC9RFHUI@attila> (view parent)
DKIM signature
pass
Download raw message
On Fri Feb 9, 2024 at 12:19 AM CET, Bor Grošelj Simić wrote:
> Any particular reason? Apologies if there was already some more discussion that
> I missed. I liked those at first too, but they're kinda against the principle
> of not being too smart about runtime values at compile time.

I don't think we really have such a principle. I've always been in favor
of the compiler being reasonably smart where it does not interfere with
the program's correctness, though I've erred on the side of "it's not
worth adding too much smarts to the bootstrap compiler when we will have
to add the smarts all over again to the hosted compiler". But when some
reasonably well-justified smarts appears in the patch queue for the
bootstrap compiler I'm not likely to turn it down.
Reply to thread Export thread (mbox)