Filtered try/with in seq { } emits int 0 where seq<'T> is expected for the unmatched case

Ouverte Adaptée aux débutants
#20,040 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 2 jours

Personne n'a encore pris cette issue.

Évaluation

Difficulté
2/5
Temps estimé
1-3 heures
Accessibilité débutants
78/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Calme
Stack technique
fsharp
Domaine
compilers

Piste de recherche

Commencez dans CheckSequenceExpressions.fs, au niveau de la compilation du handler autour des lignes 356-360, puis comparez-la avec TcExprTryWith dans CheckExpressions.fs autour des lignes 6511-6512 et avec les actions d’échec dans PatternMatchCompilation.fs. Reproduisez la séquence try/with filtrée et inspectez son arbre typé. C’est terminé lorsque la branche du handler sans correspondance est un rethrow plutôt que int 0, tandis que le filtre utilise toujours FailFilter.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

Bug Needs-Triage

This affects F# Compiler Services (FCS). We hit it in the Fable compiler, which builds on FCS and consumes the typed tree to transpile F# to other languages. The mistyped node breaks Fable's statically-typed targets.

A try/with inside a sequence expression (seq { }) that uses a filtered handler — a type test (with :? SomeExn ->) or a when guard — is lowered to RuntimeHelpers.EnumerateTryWith. In CheckSequenceExpressions.fs the handler clauses are compiled with FailFilter as the match-failure action, so the unmatched fallback branch is emitted as a literal 0 of type int in a position where the handler must return seq<'T>. A normal (non-sequence) try/with compiles its handler with Rethrow, which is correctly typed. The wrong type produces invalid output on downstream consumers that rely on the typed tree.

Repro steps

  1. Compile a sequence expression containing a try/with with a filtered handler:
    seq {
        try raise (System.InvalidOperationException "boom")
        with :? System.ArgumentException -> yield 0
    } |> Seq.toList |> ignore
    
  2. Inspect the typed tree for the RuntimeHelpers.EnumerateTryWith handler lambda and look at the branch taken when the exception does not match the filter.

Expected behavior

The unmatched/fallback case of the handler should be compiled the same way as a normal try/with — as a rethrow (reraise) of the caught exception. That expression is bottom-typed and therefore compatible with the seq<'T> result type, and it is semantically correct (a non-matching exception should re-propagate).

In CheckExpressions.fs, TcExprTryWith already does this — the handler is compiled with Rethrow:

// CheckExpressions.fs:6511-6512
let v1, filterExpr = CompilePatternForMatchClauses cenv env mWithToLast mWithToLast true FailFilter None g.exn_ty g.int_ty checkedFilterClauses
let v2, handlerExpr = CompilePatternForMatchClauses cenv env mWithToLast mWithToLast true Rethrow None g.exn_ty overallTy.Commit checkedHandlerClauses

CheckSequenceExpressions.fs should use Rethrow for the handler in the same way.

Actual behavior

CheckSequenceExpressions.fs compiles the handler with FailFilter instead of Rethrow:

// CheckSequenceExpressions.fs:356-360
let v1, filterExpr =
    CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses
    // correct: int

let v2, handlerExpr =
    CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers
    // BUG: should be Rethrow

FailFilter lowers the fallback leaf to a literal int 0:

// PatternMatchCompilation.fs:1029-1035
| FailFilter -> mkInt g mMatch 0            // returns 0 (int) — wrong when result type is seq<'T>
| Rethrow    -> mkReraise mMatch resultTy   // bottom-typed — what's needed here

So the handler's unmatched fallback is int 0 where genOuterTy (seq<'T>) is expected. The branch is unreachable at runtime (the handler only runs after the filter matches), so it is harmless on .NET, but the mistyped node is invalid for consumers of the typed tree that require correct types on every branch. In Fable this breaks compilation of the statically-typed targets.

Suggested fix: in CheckSequenceExpressions.fs (line 360), change the handler's ActionOnFailure from FailFilter to Rethrow, mirroring CheckExpressions.fs:6512.

Known workarounds

In the Fable compiler we post-process the EnumerateTryWith handler lambda and rewrite the mistyped int 0 fallback leaf into a rethrow of the caught exception (walking through if/then/else, decision trees, and let bindings to reach the leaf).

Related information

  • Affected component: F# Compiler Services (FCS) — CheckSequenceExpressions.fs; surfaced via the Fable compiler
  • Operating system: macOS (Darwin), cross-platform
  • .NET Runtime kind: .NET (Core), SDK 10+
  • Editing Tools: N/A — reproduces via the compiler directly (F# Compiler Services)
Langage dominant
F#
Étoiles
4.3k
Forks
883
Merge moyen
3 j 39 min
PR mergées (30 j)
83

Préparer son environnement

Ouvrir dans Codespaces

Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de dotnet/fsharp

Toutes les issues de dotnet/fsharp

Issues similaires

Plus d'issues Compilers

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.