Skip to content

Report FS0039 only once for undefined type in inherit clause#19862

Open
T-Gro wants to merge 12 commits into
mainfrom
fix/issue-16432
Open

Report FS0039 only once for undefined type in inherit clause#19862
T-Gro wants to merge 12 commits into
mainfrom
fix/issue-16432

Conversation

@T-Gro

@T-Gro T-Gro commented May 29, 2026

Copy link
Copy Markdown
Member

Fixes #16432

inherit T() with undefined T emitted FS0039 three times — one per
type-checking pass over the inherit clause. Now emitted once.

Copilot and others added 5 commits May 29, 2026 13:15
…f unknown type

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multiple typecheck passes (EstablishTypeDefinitionCores FirstPass and SecondPass, plus Phase2AInherit's TcType/TcNewExpr) each emit the same FS0039 for an undefined base type in an 'inherit'/'interface inherit' clause, surfacing three identical diagnostics. Introduce a DedupInheritDiagnosticsLogger overlay scoped via UseTransformedDiagnosticsLogger to TcMutRecDefinitions. The dedup key is the formatted message of UndefinedName so duplicate FS0039s for the same identifier collapse to one while unrelated diagnostics, including FS0039 for distinct identifiers, pass through unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a cenv-scoped `(range, idText)` dedup of `UndefinedName` diagnostics applied around Phase 1F inherit type checking and Phase 2A `Phase2AInherit` processing in CheckDeclarations.fs. Resolves the triple-reporting in #16432 without changing the surface API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch reportedUndefinedNames to ConcurrentDictionary<struct (range * string), unit> for consistency with the sibling argInfoCache field and to be defensive against any future parallel cenv use.

- Forward CheckForRealErrorsIgnoringWarnings to the wrapped logger so the InheritDedupDiagnosticsLogger is a faithful pass-through for everything other than the intended UndefinedName dedup.

- Document the rationale for unwrapping only WrappedError in isDuplicateUndefinedName.

Expert reviewer item 1 (merging suggestions from later UndefinedName into the first) intentionally not applied: in the inherit-clause case the colliding diagnostics carry identical suggestion sets, and merging would couple this site to NameResolution.AddResults internals.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented May 29, 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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Compiler/Checking/CheckDeclarations.fs Outdated
@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label May 29, 2026
@github-actions github-actions Bot added the AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed label Jun 2, 2026
@T-Gro T-Gro requested a review from abonie June 3, 2026 10:14
T-Gro and others added 5 commits June 5, 2026 12:21
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the InheritDedupDiagnosticsLogger sink-wrapper (PR #19862) with a
per-cenv 'inheritResolutionFailed' marker. Phase 1D records (tycon.Stamp,
synBaseTy.Range) when name resolution fails with UndefinedName. Phase 1F
and Phase 2A consult the marker and skip re-resolution entirely, so the
diagnostic is emitted once and the redundant work is eliminated. Non-
UndefinedName failures (constraint, type-provider, etc.) continue to flow
through normal recovery in every pass.

Addresses review comment 3324874944 on PR #19862.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Extend isUndefinedNameFailure to also unwrap the constraint-solver wrappers
  (ErrorFromAddingTypeEquation, ErrorFromAddingConstraint, ErrorFromApplyingDefault)
  so the marker is set when UndefinedName surfaces from the CheckCxs second pass
  via a generic inherit-clause type-argument failure.
- Remove a misplaced /// doc comment from 'type cenv = TcFileState'.
- Collapse three duplicated InheritsDeclarations regression Fact methods into a
  single Theory + InlineData driving an assertSingleFS39 helper.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Delete .tools/ralph/research-notes.md (agent scratchpad, not for repo).
- Replace isUndefinedNameFailure helper + XML doc with active pattern
  (|UndefinedNameError|_|) co-located with the catch.
- Extract tryResolveInheritType local in EstablishTypeDefinitionCores so
  the call site reads as a single mapFold.
- Drop narrative block comments at both Phase2A and Phase 1D/F sites; the
  function name and pattern carry the intent.
- Trim 5-line XML doc on inheritResolutionFailed to a one-liner.
- Rewrite tests with strict withDiagnostics asserting exact code, range,
  and message for the full diagnostic set (no per-code filtering).
@T-Gro T-Gro changed the title Fix #16432: Report FS0039 only once for undefined type in inherit clause Report FS0039 only once for undefined type in inherit clause Jun 9, 2026
@T-Gro T-Gro requested a review from auduchinok June 9, 2026 14:06
@T-Gro T-Gro enabled auto-merge (squash) June 9, 2026 14:06

@auduchinok auduchinok 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.

The active pattern for undefined name errors captures unrelated errors which is wrong. The whole approach seems to still do the same analysis multiple times and these changes just try to hide this problem.


argInfoCache: ConcurrentDictionary<string * range, ArgReprInfo>

/// Inherit clauses whose type already failed UndefinedName; skip re-resolution to avoid duplicate FS0039.

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.

The comment duplicates the signature.

| UndefinedName _ -> Some ()
| WrappedError(inner, _)
| ErrorFromAddingTypeEquation(error = inner)
| ErrorFromAddingConstraint(error = inner)

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 is definitely not a undefined name error.

Comment on lines +1379 to +1381
if cenv.inheritResolutionFailed.ContainsKey(struct (tcref.Stamp, synBaseTy.Range)) then
mkUnit g m, tpenv
else

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.

I'm not sure I'm following the idea. If there was an error previously then we skip the analysis and if there wasn't we still do it the second time? Why do we need to resolve the same identifiers more than once at all?

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Unresolved name error in 'inherit' member reported multiple times

3 participants