Skip to content

Fix enum constraints in recursive groups and improve constraint diagnostics (#14580) - #20454

Open
edgarfgp wants to merge 5 commits into
dotnet:mainfrom
edgarfgp:fix/14580-enum-constraint-diagnostics
Open

Fix enum constraints in recursive groups and improve constraint diagnostics (#14580)#20454
edgarfgp wants to merge 5 commits into
dotnet:mainfrom
edgarfgp:fix/14580-enum-constraint-diagnostics

Conversation

@edgarfgp

@edgarfgp edgarfgp commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Problem

Two problems with enum constraints. A bare enum constraint gets a cryptic error that leaks an internal marker, and an enum<...> constraint checked against an enum of the same recursive group crashes with an internal error.

Fixes #14580.

Before

type I<'T when 'T : enum> = interface end
// error FS0571: Unexpected identifier: 'enum (4)'

type J<'T when 'T : whatever> = interface end
// error FS0571: Unexpected identifier: 'whatever (4)'
module rec MyModule

type MyEnum =
    | Alpha = 1
    | Beta = 2

type MyInter<'TEnum when 'TEnum : enum<int>> = interface end

type MyAlias = MyInter<MyEnum>
// error FS0073: internal error: no 'value__' field found for enumeration type MyEnum

After

type I<'T when 'T : enum> = interface end
// error FS0699: An 'enum' constraint must be of the form 'enum<type>'

type J<'T when 'T : whatever> = interface end
// error FS0571: Unexpected identifier: 'whatever'

The recursive group compiles, and a wrong underlying type is still reported:

type MyInter<'TEnum when 'TEnum : enum<int64>> = interface end
type MyAlias = MyInter<MyEnum>
// error FS0043: The type 'int64' does not match the type 'int'

Cause

The suffixes (2)/ (3)/ (4) are in the parsUnexpectedIdentifier arguments in pars.fsy to tell the rules apart, and they ship in the message. Bare enum falls into the same catch-all as any unknown identifier, so the parser now uses FS0699, which the checker already has for this.

The internal error is a phase ordering problem. Phase1B gives an enum its kind with an empty field table, so isEnumTy is true, but the value__ field holding the underlying type is only added in Phase1G. Constraints checked in between, for abbreviations in Phase1E and inheritance in Phase1F, ask for an underlying type that does not exist yet. let bindings were unaffected because they are checked after Phase1G.

SolveTypeIsEnum now uses tryUnderlyingTypeOfEnumTy, which returns ValueNone while the representation is being established, and re-queues the solve on the existing post inference check list. Nothing is skipped, so a wrong underlying type is still reported, in implementation and signature files.

Making a bare enum valid would be a language change and needs an fslang suggestion first, so this PR keeps rejecting it and only fixes the message.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

✅ No release notes required

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Sep 5, 2026
@edgarfgp edgarfgp changed the title Report a proper error for a bare 'enum' constraint (#14580) Fix enum constraints in recursive groups and improve constraint diagnostics (#14580) Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

internal error: no 'value__' field found for enumeration type when rec module & enum constraint & type alias

1 participant