Skip to content

Address signature generation bugs#19586

Open
T-Gro wants to merge 13 commits intomainfrom
fix/sig-file-gen
Open

Address signature generation bugs#19586
T-Gro wants to merge 13 commits intomainfrom
fix/sig-file-gen

Conversation

@T-Gro
Copy link
Copy Markdown
Member

@T-Gro T-Gro commented Apr 14, 2026

Summary

Fixes 6 signature generation bugs in FSharpCheckFileResults.GenerateSignature. All fixes ensure generated signatures roundtrip-compile with their implementations.

Fixes

Design notes

Literal attribute arg recovery: TcVal inlines literal vals to Expr.Const, losing the original identifier. The fix recovers literal ValRef from the syntax tree via range matching and stores it as Expr.Val in AttribExpr.source for display. This is an in-memory-only optimization — p_attrib_expr normalizes back to Expr.Const before pickling, so the on-disk format is unchanged.

Struct NoComparison/NoEquality: When a struct type's fields prevent comparison/equality augmentation, synthetic attributes are injected in the display layer (NicePrint) only.

[] emission: The printVerboseSignatures gate was removed; class kind is now always tracked. The needsStartEnd heuristic determines whether [<Class>] or class...end is needed based on visible constructors, fields, and members.

Binary compatibility

Zero cross-version regression. The pickle normalization in p_attrib_expr ensures that DLLs compiled with this change produce identical metadata to before. Old compilers reading new DLLs see Expr.Const in all positions — no behavioral change.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 14, 2026

✅ No release notes required

@T-Gro T-Gro force-pushed the fix/sig-file-gen branch 2 times, most recently from 103245e to 6a5aab8 Compare April 15, 2026 08:21
T-Gro and others added 13 commits April 16, 2026 07:56
…nature

Filter out compiler-generated vals with '@' in their LogicalName from
inferred signature printing. In 'module rec' contexts, 'do ()' bindings
are compiled as TMDefRec with vals named like 'doval@3' that leaked
into generated signatures.

Fixes #13832

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When generating signatures, attribute arguments that reference [<Literal>]
values now preserve the literal identifier name instead of showing the
evaluated constant value. For example, [<Category(A)>] is preserved
instead of being reduced to [<Category("A")>].

The fix works by recovering literal val references from the syntax tree
during attribute checking: TcVal inlines literals to Expr.Const, but we
now look up the original identifier in the name resolution environment
and store Expr.Val as the source expression in AttribExpr. The Expr.Val
case is then handled in layoutAttribArg in NicePrint.fs to display the
literal name.

Fixes #13810

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When generating signatures for struct types whose fields prevent
comparison/equality augmentation (e.g., fields of type obj), the
generated signature now includes [<NoComparison>] and [<NoEquality>]
attributes. Without these, the signature fails to compile with FS0293.

The fix detects when a struct type is a candidate for comparison/equality
augmentation but augmentation was not generated (GeneratedCompareToValues
or GeneratedHashAndEqualsValues is None), and injects synthetic attributes
into the signature output.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change single-backtick checks to double-backtick checks so identifiers
containing backticks in their names get properly escaped with `` `` ``
delimiters during signature generation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tures (#15560)

Move layoutAccessibility call after layoutTyparDecls so that 'private'
is placed before the entire ''a P' construct rather than between the
type parameter and name (producing 'type 'a private P' instead of
the correct 'type private 'a P').

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

When generating signatures for class types whose constructors are not
visible (e.g., private constructors), the [<Class>] attribute was not
emitted. This caused the generated signatures to fail compilation with
error FS0938 because the compiler cannot infer that the type is a class
from the signature alone.

The fix:
1. Always set start=Some "class" for class types regardless of
   printVerboseSignatures setting (was gated behind it before)
2. Suppress [<Class>] when allDecls is empty since the repr layout
   already produces 'class end' for empty classes
3. Remove commented-out conditions that were never active

Fixes #16531

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Test multiple literal args in tuple attribute position (names recovered)
- Test qualified literal reference limitation (shows constant value)
- Add CompilerCompat test: type with literal-based attribute arg exercises
  Expr.Val in AttribExpr.source pickling across compiler versions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Struct with explicit [<NoComparison>] does not get duplicate attribute
- Struct with [<CustomComparison>] does not get [<NoComparison>] injected
- Empty class with private constructor uses 'class end' repr
- AbstractClass with private constructor roundtrips correctly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Only replace Expr.Const with Expr.Val in AttribExpr.source when the
literal val reference is local (VRefLocal). Cross-assembly literals
(VRefNonLocal from 'open ExternalLib') keep Expr.Const to avoid
creating unresolvable transitive assembly dependencies in pickled
metadata — consumers of the DLL may not reference the external assembly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The literal name recovery stores Expr.Val in AttribExpr.source as an
in-memory optimization for signature generation display. However, this
must NOT change the pickle format:

- Old compilers reading Expr.Val in AttribExpr.source would display
  '(* unsupported attribute argument *)' instead of the constant value
  in tooltips and signatures — a cross-version display regression.
- Old compilers' CheckDeclarations.fs pattern-matches on the source
  field and would miss the AssemblyVersion format warning.

By normalizing Expr.Val(literal_vref) back to Expr.Const(literal_value)
before pickling, the on-disk format is identical to before this PR.
The literal name display only works during the current compilation
(the signature generation use case), which is the correct scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two fixes from adversarial cross-model review:

1. Add SynExpr.App traversal to literal ident collector so named
   attribute arguments like [<Attr(Name = MyLiteral)>] also get
   literal name recovery. Previously only positional args worked.

2. Replace IsCompilerGeneratedName (any '@' in name) with targeted
   StartsWithOrdinal("doval@") in filterVal. The broad '@' check
   would incorrectly drop backtick-escaped user identifiers containing
   '@' from module rec signatures. The doval@ prefix is the only
   compiler-generated name pattern that leaks through TMDefRec bindings,
   matching the same pattern used in SignatureHash.fs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The PR unconditionally set start=Some "class" for class types, causing
[<Class>] to appear in FSI output. Gate on denv.showAttributes instead
of the removed printVerboseSignatures check:
- FSI (showAttributes=false): no [<Class>] emitted (matches old behavior)
- GenerateSignature (showAttributes=true): [<Class>] emitted when needed
- --sig flag (showAttributes=true): [<Class>] emitted when needed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@T-Gro T-Gro force-pushed the fix/sig-file-gen branch from 6a5aab8 to abe9691 Compare April 16, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

1 participant