Skip to content

Feat: add known error sets - #3319

Open
iwami4438 wants to merge 9 commits into
zigtools:masterfrom
iwami4438:feature/add-known-error-sets
Open

iwami4438 wants to merge 9 commits into
zigtools:masterfrom
iwami4438:feature/add-known-error-sets

Conversation

@iwami4438

Copy link
Copy Markdown

Ref: #3311

Before↓

const ErrorSet = error{A} || error{B}
(type = error{A,B})

const ErrorSetWithUnknown = error{A} || undefined.Unknown
(unknown)

const ErrorSetWithUnknown2 = undefined.Unknown || error{A}
(unknown)

const ErrorSetWithUnknown3 = undefined.Unknown || error{A} || error{B}
(unknown)

const AllUnknown = undefined.Unknown || undefined.Unknown
(unknown)

After: ↓

const ErrorSet = error{A} || error{B}
(type = error{A,B})

const ErrorSetWithUnknown = error{A} || undefined.Unknown
(type = error{A, ... })

const ErrorSetWithUnknown2 = undefined.Unknown || error{B}
(type = error{B, ... })

const ErrorSetWithUnknown3 = undefined.Unknown || error{A} || error{B}
(type = error{A,B, ... })

const AllUnknown = undefined.Unknown || undefined.Unknown
(type = error{ ... })

- Modified the logic for determining `has_unknown`.
- Updated the branching logic in `merge_error_sets` to return
  `unknown_type` instead of `null`.
- Removed debugging code.
- Configured `has_unknown` to be set to `bool_false` when encountering
  `error_set_type` or `error_union` (as these should not contain an
  `unknown` state).
- Added a `has_unknown` check within `eql` (previously, it defaulted to
  `bool_false` during `get` operations).
- Updated the tests.
- Updated evaluation to use `isUnknownDeep` instead of `isUnknown` (this
  ensures that `error_set` is handled correctly when encountered).
- Added logic to `isUnknownDeep` to check whether `has_unknown` is set
  to `bool_true` when evaluating an `error_set`.
- Added tests; applied some formatting adjustments.
@iwami4438

Copy link
Copy Markdown
Author

There was an error in the test (it has been fixed—the number of ^ symbols was insufficient). Could you please re-run the CI?

@neoto neoto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't comment on the implementation since I'm not familiar with the codebase, but I tested the PR with these test cases and they all crash the server:

const AnyerrorUnknown = anyerror || Unknown;

const UnknownAnyerror = Unknown || anyerror;

const AnyerrorPartiallyUnknown = anyerror || error{A} || Unknown;

At the very least, I think this should now be lifted up from the else branch to the start of the function:

        // Anything merged with anyerror is anyerror.
        if (a_ty == .anyerror_type or b_ty == .anyerror_type) {
            return .anyerror_type;
        }

Comment thread src/analyser/InternPool.zig Resolved
Comment thread tests/analysis/error_union.zig Resolved
@iwami4438

iwami4438 commented Nov 9, 2026 •

Copy link
Copy Markdown
Author

I won't comment on the implementation since I'm not familiar with the codebase, but I tested the PR with these test cases and they all crash the server:

const AnyerrorUnknown = anyerror || Unknown;

const UnknownAnyerror = Unknown || anyerror;

const AnyerrorPartiallyUnknown = anyerror || error{A} || Unknown;

At the very least, I think this should now be lifted up from the else branch to the start of the function:

        // Anything merged with anyerror is anyerror.
        if (a_ty == .anyerror_type or b_ty == .anyerror_type) {
            return .anyerror_type;
        }

Thank you for your feedback.
anyerror was overlooked—my apologies.
I will add a test.

@techatrix

Copy link
Copy Markdown
Member

@iwami4438 Did use LLMs or AI to author anything here? Let it be the source code, commit descriptions or your comments/replies. Also counting AI translation tools.

- And added tests.
- The initial value was incorrectly set to `bool_type`; it has been
corrected to `bool_false`.
- This ensures the default value is `bool_false`, which aligns with the
return value of `errorSetMerge` and allows the tests to pass.
@iwami4438

Copy link
Copy Markdown
Author

@iwami4438 Did use LLMs or AI to author anything here? Let it be the source code, commit descriptions or your comments/replies. Also counting AI translation tools.

I am using Google Translate for the English text; please excuse any awkward phrasing. I have not used any AI.

@iwami4438

Copy link
Copy Markdown
Author

All tests passed in my environment; could you please run the CI? Thank you.

@techatrix techatrix left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this. The implementation appears to handle all cases correctly although the code could still be improved to avoid unnecessary or redundant work:

  • Avoid calling isUnknownDeep. We can use isUnknown instead or since we still assume that the arguments are guaranteed to be either an unknown type or an error set, ty == .unknown_type.
  • In the case of error{Foo} || Unknown, the error names are unnecessary placed in a set, sorted and interned again. The interned error names could be reused instead.

Here is a suggestion for how the code could be restructured:

    if (a_ty == .unknown_type and b_ty == .unknown_type) {
        // return error set with `.names = .empty` and set has_unknown
    }

    if (a_ty == .unknown_type) {
        // return error set where the error names are taken from `b_ty` and set has_unknown
    }

    if (b_ty == .unknown_type) {
        // return error set where the error names are taken from `a_ty` and set has_unknown
    }

    // Merge error sets as usual but make sure to resolve `has_unknown` as well.

Also, the resolvePeerErrorSets function in analysis.zig should be adjusted to handle unknown types as well. Also note this review comment in case it's overlooked.

@iwami4438

Copy link
Copy Markdown
Author

@techatrix
Thank you for your suggestion. I understand that isUnknownDeep should not be used. For this particular use case, I have determined that using isUnknown is more appropriate than comparing via .unknown_type, so I will proceed with that approach.
Also, thank you for your suggestion regarding has_unknown.
I will give that a try.

- Modified `ErrorSet` to define `flags` and reference `has_unknown`
  through it.
- Removed the implementation previously added to
  `isUnknownDeepInternal`.
- Updated the logic to determine `has_unknown` via `ip.isUnknown`.
- Adjusted the code to reflect the renaming of `UnknownType` in the
tests.
@iwami4438
iwami4438 force-pushed the feature/add-known-error-sets branch from 74b6b5d to 2cdb34e Compare November 14, 2026 17:11
@iwami4438

Copy link
Copy Markdown
Author

I have incorporated the suggested changes. I tested them in my environment, and no errors occurred. Please review.

@iwami4438

Copy link
Copy Markdown
Author

CI failed. Merging #3330 into ZLS, then merging those changes here and pushing them, followed by re-running the CI, should resolve the issue.

@techatrix techatrix left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for following up on the review. I have some comments on further refactors but otherwise looks ready to go.

});
ip.string_pool.sortStrings(ip.io, set.keys());

const a_or_b_has_unknown = ip.indexToKey(a_ty).error_set_type.flags.has_unknown or ip.indexToKey(b_ty).error_set_type.flags.has_unknown;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is best to avoid unnecessarily calling indexToKey. There is a previous call to ip.indexToKey(a_ty) and ip.indexToKey(b_ty) that should be reused.

}

if (a_ty == b_ty) return a_ty;
if (has_unknown) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is unnecessary and can be safely removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants