Skip to content

Report a proper error for a bare 'enum' constraint (#14580) - #20454

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

Report a proper error for a bare 'enum' constraint (#14580)#20454
edgarfgp wants to merge 3 commits into
dotnet:mainfrom
edgarfgp:fix/14580-enum-constraint-diagnostics

Conversation

@edgarfgp

@edgarfgp edgarfgp commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Problem

A bare enum constraint gets a cryptic error that leaks an internal marker, instead of saying what the constraint should look like.

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)'

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'

Cause

Two small defects in the constraint rules of pars.fsy:

  • The parsUnexpectedIdentifier arguments have (2)/ (3)/ (4) suffixes to tell the rules apart, and they end up in the user-facing message.
  • Bare enum falls into the same catch-all as any unknown identifier. The checker already has the right message for this (FS0699, localized), so the parser now uses it.

About the original report: the repro there uses a bare 'T : enum, which since F# 8 is a parse error in every context (module rec makes no difference), so that spelling stops before type checking and this PR makes the rejection say why:

module rec MyModule

type MyEnum =
  | Alpha = 1
  | Beta = 2
type MyInter<'TEnum when 'TEnum : enum> = interface end

type MyAlias = MyInter
// F# 7:          error FS0073: internal error: no 'value__' field found for enumeration type MyEnum
// F# 8 to 10:    error FS0571: Unexpected identifier: 'enum (4)'
// this PR:       error FS0699: An 'enum' constraint must be of the form 'enum<type>'

The internal error itself is not fixed here. With the valid enum<int> spelling it still happens on main, as @T-Gro reported in #14580 (comment):

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

An and group behaves the same way, and removing the recursion compiles. Phase1B gives an enum its TFSharpEnum kind with an empty field table, so isEnumTy is already true while value__ is only added by Phase1G, and a constraint checked in between (abbreviations in Phase1E) asks for an underlying type that does not exist yet. So #14580 should stay open after this PR, which only covers the diagnostics.

Accepting bare enum again, as F# 7 accidentally did via the self-constraint fallback, would be a language change and needs an fslang suggestion first.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Sep 5, 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