From f91e28e3916f70a6b501536f14caa1c4250143f9 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 29 May 2026 18:26:46 +0200 Subject: [PATCH 1/4] Add failing tests for FS0027 on function parameters (issue #15803) Tests pin the desired behavior: FS0027 emitted on a parameter must not suggest the illegal 'let mutable x = expression' shadow. Local-let behavior preserved as regression sentinel. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AssignmentToParameterErrorTests.fs | 79 +++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 1 + 2 files changed, 80 insertions(+) create mode 100644 tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentToParameterErrorTests.fs diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentToParameterErrorTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentToParameterErrorTests.fs new file mode 100644 index 00000000000..c002454f3ab --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentToParameterErrorTests.fs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace ErrorMessages + +open Xunit +open FSharp.Test.Compiler + +module ``FS0027 on function parameters (issue 15803)`` = + + [] + [] + [] + [] + let ``FS0027 on parameter does not suggest illegal 'let mutable' shadow`` source = + FSharp source + |> typecheck + |> shouldFail + |> withErrorCode 27 + |> withDiagnosticMessageDoesntMatch "let mutable .* = expression" + + [] + let ``FS0027 on parameter still mentions mutability`` () = + FSharp "let f (x: int) = x <- 5" + |> typecheck + |> shouldFail + |> withErrorCode 27 + |> withDiagnosticMessageMatches "mutable" + + [] + let ``FS0027 on local let binding still suggests 'let mutable'`` () = + FSharp """ +let f () = + let x = 5 + x <- 10 +""" + |> typecheck + |> shouldFail + |> withErrorCode 27 + |> withDiagnosticMessageMatches "let mutable x = expression" + + [] + let ``FS0027 on constructor parameter does not suggest illegal 'let mutable' shadow`` () = + FSharp """ +type C(x: int) = + member _.M() = x <- 5 +""" + |> typecheck + |> shouldFail + |> withErrorCode 27 + |> withDiagnosticMessageDoesntMatch "let mutable .* = expression" + + [] + let ``FS0027 on closure-captured parameter does not suggest illegal 'let mutable' shadow`` () = + FSharp """ +let f (x: int) = + let inner () = x <- 5 + inner() +""" + |> typecheck + |> shouldFail + |> withErrorCode 27 + |> withDiagnosticMessageDoesntMatch "let mutable .* = expression" + + [] + let ``Mutable local does not trigger FS0027`` () = + FSharp """ +let f () = + let mutable x = 5 + x <- 10 + x +""" + |> typecheck + |> shouldSucceed + + [] + let ``Byref parameter does not trigger FS0027`` () = + FSharp "let f (x: byref) = x <- 5" + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index acfeca79f35..9e16fb98a0e 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -298,6 +298,7 @@ + From 8bb0b3fa5303c607334290d7a92cba7b911f2c4a Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 29 May 2026 19:05:18 +0200 Subject: [PATCH 2/4] Specialize FS0027 for function/method parameters (#15803) When the assignment target is a parameter, emit a message that suggests a legal remediation ('let mutable x = x' shadow, or byref<_>) instead of the illegal 'let mutable x = expression'. Local 'let' bindings retain the original message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Checking/CheckIncrementalClasses.fs | 8 ++++++++ src/Compiler/Driver/CompilerDiagnostics.fs | 10 +++++++++- src/Compiler/FSStrings.resx | 3 +++ src/Compiler/xlf/FSStrings.cs.xlf | 5 +++++ src/Compiler/xlf/FSStrings.de.xlf | 5 +++++ src/Compiler/xlf/FSStrings.es.xlf | 5 +++++ src/Compiler/xlf/FSStrings.fr.xlf | 5 +++++ src/Compiler/xlf/FSStrings.it.xlf | 5 +++++ src/Compiler/xlf/FSStrings.ja.xlf | 5 +++++ src/Compiler/xlf/FSStrings.ko.xlf | 5 +++++ src/Compiler/xlf/FSStrings.pl.xlf | 5 +++++ src/Compiler/xlf/FSStrings.pt-BR.xlf | 5 +++++ src/Compiler/xlf/FSStrings.ru.xlf | 5 +++++ src/Compiler/xlf/FSStrings.tr.xlf | 5 +++++ src/Compiler/xlf/FSStrings.zh-Hans.xlf | 5 +++++ src/Compiler/xlf/FSStrings.zh-Hant.xlf | 5 +++++ 16 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/CheckIncrementalClasses.fs b/src/Compiler/Checking/CheckIncrementalClasses.fs index 846aa5f9085..a7983727178 100644 --- a/src/Compiler/Checking/CheckIncrementalClasses.fs +++ b/src/Compiler/Checking/CheckIncrementalClasses.fs @@ -161,6 +161,14 @@ let TcImplicitCtorInfo_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, att // Put them in order let ctorArgs = List.map (fun v -> NameMap.find v vspecs) ctorArgNames + + // Mark constructor arguments as "displayed as parameters" so that diagnostics + // (e.g. FS0027 on assignment to a non-mutable parameter) can distinguish them + // from ordinary local bindings. + for v in ctorArgs do + if v.ArgReprInfoForDisplay.IsNone then + v.SetArgReprInfoForDisplay (Some (ValReprInfo.InferArgReprInfo v)) + let safeThisValOpt = MakeAndPublishSafeThisVal cenv env thisIdOpt thisTy // NOTE: the type scheme here is not complete!!! The ctorTy is more or less diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index 4ecbfc081ef..4632b067843 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -574,6 +574,7 @@ module OldStyleMessages = let RuleNeverMatchedE () = Message("RuleNeverMatched", "") let EnumMatchIncomplete1E () = Message("EnumMatchIncomplete1", "") let ValNotMutableE () = Message("ValNotMutable", "%s") + let ValNotMutableParameterE () = Message("ValNotMutableParameter", "%s%s%s") let ValNotLocalE () = Message("ValNotLocal", "") let Obsolete1E () = Message("Obsolete1", "") let Obsolete2E () = Message("Obsolete2", "%s") @@ -1838,7 +1839,14 @@ type Exception with | PatternMatchCompilation.RuleNeverMatched _ -> os.AppendString(RuleNeverMatchedE().Format) - | ValNotMutable(_, vref, _) -> os.AppendString(ValNotMutableE().Format(vref.DisplayName)) + | ValNotMutable(_, vref, _) -> + let name = vref.DisplayName + let msg = + if vref.Deref.ArgReprInfoForDisplay.IsSome then + ValNotMutableParameterE().Format name name name + else + ValNotMutableE().Format name + os.AppendString msg | ValNotLocal _ -> os.AppendString(ValNotLocalE().Format) diff --git a/src/Compiler/FSStrings.resx b/src/Compiler/FSStrings.resx index 920cc48a52a..d05b6129e32 100644 --- a/src/Compiler/FSStrings.resx +++ b/src/Compiler/FSStrings.resx @@ -1041,6 +1041,9 @@ This value is not mutable. Consider using the mutable keyword, e.g. 'let mutable {0} = expression'. + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + This value is not local diff --git a/src/Compiler/xlf/FSStrings.cs.xlf b/src/Compiler/xlf/FSStrings.cs.xlf index a28784716d6..337197f86c4 100644 --- a/src/Compiler/xlf/FSStrings.cs.xlf +++ b/src/Compiler/xlf/FSStrings.cs.xlf @@ -292,6 +292,11 @@ Pole {0} a {1} jsou odlišného typu. + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Omezení hodnoty: Hodnota {0} má odvozený obecný typ\n {1}\nNicméně hodnoty nemohou mít proměnné obecného typu jako '_a v "let x: '_a". Můžete provést jednu z následujících akcí:\n- Definujte jej jako jednoduchý datový termín, například celočíselný literál, řetězcový literál nebo případ sjednocení, například let x = 1\n- Přidejte explicitní typovou anotaci, například let x : int\n- Použijte hodnotu jako negenerický typ v pozdějším kódu pro typovou inferenci, jako například do x\nebo pokud přesto chcete typové výsledky, můžete místo toho {2} definovat jako funkci, a to buď:\n- Přidejte jednotkový parametr, jako například let x()\n- Napište explicitní typové parametry, jako například let x<'a>.\nTato chyba je způsobena tím, že vazba let bez parametrů definuje hodnotu, nikoli funkci. Hodnoty nemůžou být generické, protože se předpokládá, že výsledkem čtení hodnoty bude všude totéž, ale generické typové parametry mohou tento předpoklad zrušit tím, že umožní získat výsledky závislé na typu. diff --git a/src/Compiler/xlf/FSStrings.de.xlf b/src/Compiler/xlf/FSStrings.de.xlf index 73299495a4b..6e192cc8ed9 100644 --- a/src/Compiler/xlf/FSStrings.de.xlf +++ b/src/Compiler/xlf/FSStrings.de.xlf @@ -292,6 +292,11 @@ Die Felder "{0}" und "{1}" stammen aus unterschiedlichen Typen. + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Werteinschränkung: Der Wert "{0}" weist einen abgeleiteten generischen Typ auf.\n {1}\nWerte dürfen jedoch keine generischen Typvariablen wie "_a" in "let x: "_a" aufweisen. Sie können eine der folgenden Aktionen ausführen:\n– Definieren Sie ihn als einfachen Datenausdruck wie ein ganzzahliges Literal, ein Zeichenfolgenliteral oder einen Union-Fall wie "let x = 1"\n– Fügen Sie eine explizite Typanmerkung wie "let x : int" hinzu.\n– Verwenden Sie den Wert als nicht generischen Typ im späteren Code für Typrückschlüsse wie "do x"\noder wenn Sie weiterhin typabhängige Ergebnisse wünschen, können Sie "{2}" stattdessen als Funktion definieren, indem Sie einen der folgenden Schritte ausführen:\n– Fügen Sie einen Einheitenparameter wie "let x()" hinzu.\n– Schreiben Sie explizite Typparameter wie "let x<'a>".\nDieser Fehler liegt daran, dass eine let-Bindung ohne Parameter einen Wert definiert, keine Funktion. Werte können nicht generisch sein, da davon ausgegangen wird, dass das Lesen eines Werts überall zum selben Ergebnis führt, generische Typparameter diese Annahme jedoch möglicherweise ungültig machen, indem typabhängige Ergebnisse aktiviert werden. diff --git a/src/Compiler/xlf/FSStrings.es.xlf b/src/Compiler/xlf/FSStrings.es.xlf index e73ce8e4291..b7e4198460a 100644 --- a/src/Compiler/xlf/FSStrings.es.xlf +++ b/src/Compiler/xlf/FSStrings.es.xlf @@ -292,6 +292,11 @@ Los campos '{0}' y '{1}' son de tipos diferentes. + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Restricción de valor: el valor '{0}' tiene un tipo genérico inferido\n {1}\nSin embargo, los valores no pueden tener variables de tipo genérico como '_a en "let x: '_a". Puede realizar una de las siguientes acciones:\n- Definirlo como un término de datos simple como un literal entero, un literal de cadena o un caso de unión como "let x = 1"\n- Agregar una anotación de tipo explícito como "let x : int"\n- Usar el valor como un tipo no genérico en código posterior para la inferencia de tipos como "do x"\no si aún desea resultados dependientes de tipos, puede definir '{2}' como una función en su lugar haciendo lo siguiente:\n- Agregar un parámetro de unidad como "let x()"\n- Escribir parámetros de tipo explícito como "let x<'a>".\nEste error se debe a que un enlace let sin parámetros define un valor, no una función. Los valores no pueden ser genéricos porque se supone que la lectura de un valor da como resultado lo mismo en todas partes, pero los parámetros de tipo genérico pueden invalidar esta suposición habilitando resultados dependientes de tipos. diff --git a/src/Compiler/xlf/FSStrings.fr.xlf b/src/Compiler/xlf/FSStrings.fr.xlf index cb784fdab67..5dc02b7ddf7 100644 --- a/src/Compiler/xlf/FSStrings.fr.xlf +++ b/src/Compiler/xlf/FSStrings.fr.xlf @@ -292,6 +292,11 @@ Les champs '{0}' et '{1}' sont de types différents + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Restriction de valeur : La valeur '{0}' a un type générique déduit\n {1}\nCependant, les valeurs ne peuvent pas avoir de variables de type générique comme '_a dans "let x: '_a". Vous pouvez effectuer l'une des opérations suivantes :\n- Le définir comme un terme de données simple comme un littéral entier, un littéral de chaîne ou un cas d'union comme "let x = 1"\n- Ajouter une annotation de type explicite comme "let x : int"\n- Utilisez la valeur comme type non générique dans le code ultérieur pour l'inférence de type comme "do x"\ni si vous souhaitez toujours des résultats dépendants du type, vous pouvez définir '{2}' comme fonction à la place en faisant soit :\n- Ajoutez un paramètre d'unité comme "let x()"\n- Écrivez des paramètres de type explicites comme "let x<'a>".\nCette erreur est due au fait qu'une liaison let sans paramètres définit une valeur, pas une fonction. Les valeurs ne peuvent pas être génériques car la lecture d'une valeur est supposée donner le même résultat partout, mais les paramètres de type génériques peuvent invalider cette hypothèse en permettant des résultats dépendants du type. diff --git a/src/Compiler/xlf/FSStrings.it.xlf b/src/Compiler/xlf/FSStrings.it.xlf index de6c619667a..6333f03308b 100644 --- a/src/Compiler/xlf/FSStrings.it.xlf +++ b/src/Compiler/xlf/FSStrings.it.xlf @@ -292,6 +292,11 @@ I campi '{0}' e '{1}' sono di tipi diversi + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Restrizione del valore: il valore '{0}' ha un tipo generico dedotta\n {1}\nTuttavia, i valori non possono avere variabili di tipo generico come '_a in "let x: '_a". È possibile eseguire una delle operazioni seguenti:\n- Definirlo come termine di dati semplice come un valore letterale integer, un valore letterale stringa o un case di unione come "let x = 1"\n- Aggiungere un'annotazione di tipo esplicito come "let x : int"\n- Usare il valore come tipo non generico nel codice successivo per l'inferenza del tipo come "do x"\noppure se si vogliono ancora risultati dipendenti dal tipo, è possibile definire '{2}' come funzione eseguendo una delle operazioni seguenti:\n- Aggiungere un parametro di unità come "let x()"\n- Scrivere parametri di tipo esplicito come "let x<'a>".\nQuesto errore è dovuto al fatto che un'associazione let senza parametri definisce un valore, non una funzione. I valori non possono essere generici, perché si presuppone che la lettura di un valore restituisca lo stesso risultato ovunque, ma i parametri di tipo generico possono invalidare questa ipotesi, consentendo risultati dipendenti dal tipo. diff --git a/src/Compiler/xlf/FSStrings.ja.xlf b/src/Compiler/xlf/FSStrings.ja.xlf index 23154e99892..608c8e747cd 100644 --- a/src/Compiler/xlf/FSStrings.ja.xlf +++ b/src/Compiler/xlf/FSStrings.ja.xlf @@ -292,6 +292,11 @@ フィールド '{0}' と '{1}' は異なる型です + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. 値の制限: 値 '{0}' は推論されたジェネリック関数 \n {1}\n を持ちますが、 "let x: '_a" で '_a のようなジェネリック型変数を持つことはできません。次のいずれかを実行できます。①整数リテラル、文字列リテラル、または "let x = 1"\n- のような共用体ケースを簡易データ項目として定義する。②"let x : int"\n- のような明示的な型の注釈を追加する。③型依存型の結果が必要な場合は、"do x"\nor などの型の推定のためにジェネリック型以外として後のコードで使用して、:\n- のいずれかを実行する代わりに関数として '{2}' を定義できます。④"let x()"\n- のような単位パラメーターを追加する。⑤"let x<'a>".\n のような明示的な型パラメーターを記述する。このエラーは、\nパラメーターのない let バインディングが関数ではなく値を定義するため発生します。値を読み取るとどこでも同じ結果になると想定されるため、値をジェネリックにすることはできませんが、ジェネリック型パラメーターは型依存の結果を有効にすることで、この想定が無効になる可能性があります。 diff --git a/src/Compiler/xlf/FSStrings.ko.xlf b/src/Compiler/xlf/FSStrings.ko.xlf index 8b2e15d5d5f..f2b1359930a 100644 --- a/src/Compiler/xlf/FSStrings.ko.xlf +++ b/src/Compiler/xlf/FSStrings.ko.xlf @@ -292,6 +292,11 @@ {0}' 필드와 '{1}' 필드의 소스 형식이 서로 다릅니다. + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. 값 제한: 값 '{0}'에 유추된 제네릭 형식이 있습니다.\n {1}\n하지만 값은 "let x: '_a"의 '_a와 같은 제네릭 형식 변수를 가질 수 없습니다. 다음 중 하나를 수행할 수 있습니다.\n- 정수 리터럴, 문자열 리터럴 또는 "let x = 1"과 같은 공용 구조체 대/소문자와 같은 간단한 데이터 용어로 정의합니다.\n- "let x : int"와 같은 명시적 형식 주석을 추가합니다.\n- "do x"와 같은 형식 유추를 위해 이후 코드에서 값을 제네릭이 아닌 형식으로 사용합니다.\n또는 여전히 형식 종속 결과를 원할 경우 다음 중 하나를 수행하여 '{2}'을(를) 함수로 정의할 수 있습니다.\n- "let x()"와 같은 단위 매개 변수를 추가합니다.\n- "let x<'a>"와 같은 명시적 형식 매개 변수를 작성합니다.\n이 오류는 매개 변수가 없는 let 바인딩이 함수가 아니라 값을 정의하기 때문입니다. 값을 읽으면 모든 위치에서 동일한 결과가 발생하는 것으로 가정되지만 제네릭 형식 매개 변수가 형식 종속 결과를 사용하여 이 가정을 무효화할 수 있기 때문에 값은 제네릭일 수 없습니다. diff --git a/src/Compiler/xlf/FSStrings.pl.xlf b/src/Compiler/xlf/FSStrings.pl.xlf index 7ab2fe2d494..8aafa27d58f 100644 --- a/src/Compiler/xlf/FSStrings.pl.xlf +++ b/src/Compiler/xlf/FSStrings.pl.xlf @@ -292,6 +292,11 @@ Pola „{0}” i „{1}” są polami różnego typu + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Ograniczenie wartości: wartość „{0}” ma wywnioskowany typ ogólny\n {1}\nWartości nie mogą mieć zmiennych typu ogólnego, takich jak „_a w „let x: „_a”. Możesz wykonać jedną z następujących czynności:\n- Zdefiniuj go jako prosty termin danych, taki jak literał liczby całkowitej, literał ciągu lub przypadek unii, taki jak „let x = 1”\n- Dodaj jawną adnotację typu, taką jak „let x : int”\n- Użyj wartości jako typu nie generycznego w późniejszym kodzie dla wnioskowania typu, takiego jak „do x”,\n jeśli nadal chcesz uzyskać wyniki zależne od typu, Można zdefiniować „{2}” jako funkcję, wykonując jedną z następujących czynności:\n- Dodaj parametr jednostkowy, taki jak „let x()”\n- Zapisz jawne parametry typu, takie jak „let x<'a>”.\nTen błąd jest spowodowany tym, że powiązanie let bez parametrów definiuje wartość, a nie funkcję. Wartości nie mogą być ogólne, ponieważ zakłada się, że odczytanie wartości skutkuje tym samym wszędzie, ale parametry typu ogólnego mogą unieważnić to założenie, umożliwiając uzyskanie wyników zależnych od typu. diff --git a/src/Compiler/xlf/FSStrings.pt-BR.xlf b/src/Compiler/xlf/FSStrings.pt-BR.xlf index f5e8bc53ca4..64f06366737 100644 --- a/src/Compiler/xlf/FSStrings.pt-BR.xlf +++ b/src/Compiler/xlf/FSStrings.pt-BR.xlf @@ -292,6 +292,11 @@ Os campos '{0}' e '{1}' são de tipos diferentes + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Restrição de valor: o valor "{0}" tem um tipo genérico inferido\n {1}\nEntretanto, os valores não podem ter variáveis de tipo genérico como '_a em "let x: '_a". Você pode fazer o seguinte:\n- Defina-o como um termo de dados simples, como um literal inteiro, um literal de cadeia de caracteres ou um caso de união como "let x = 1"\n- Adicionar uma anotação de tipo explícita como "let x : int"\n- Use o valor como um tipo não genérico em código posterior para inferência de tipo como "do x"\ne se você ainda quiser resultados dependentes de tipo, você pode definir "{2}" como uma função fazendo:\n- Adicione um parâmetro de unidade como "let x()"\n- Escreva parâmetros de tipo explícitos como "let x<'a>".\nEsse erro ocorre porque uma ligação let sem parâmetros define um valor, não uma função. Os valores não podem ser genéricos porque se supõe que a leitura de um valor resulte no mesmo em todos os lugares, mas parâmetros de tipo genérico podem invalidar essa suposição habilitando resultados dependentes de tipo. diff --git a/src/Compiler/xlf/FSStrings.ru.xlf b/src/Compiler/xlf/FSStrings.ru.xlf index 117c522a187..e4b209e4f0a 100644 --- a/src/Compiler/xlf/FSStrings.ru.xlf +++ b/src/Compiler/xlf/FSStrings.ru.xlf @@ -292,6 +292,11 @@ Поля "{0}" и "{1}" принадлежат различным типам + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Ограничение на значение: значение '{0}' имеет предполагаемый универсальный тип\n {1}\nОднако значения не могут иметь переменные универсального типа, такие как '_a в "let x: '_a". Вы можете сделать одно из следующих действий:\n- Определите его как простой термин данных, например целочисленный литерал, строковый литерал или случай объединения, например "let x = 1".\n- Добавьте явную аннотацию типа, например "let x: int"\n- Используйте значение как необобщенный тип в более позднем коде для вывода типа, например "do x"\nили, если вам все еще нужны результаты, зависящие от типа, вы можете вместо этого определить '{2}' как функцию, выполнив одно из следующих действий:\n- Добавьте параметр модуля, например "let x()"\n- Запишите явные параметры типа, например "let x<'a>".\nЭта ошибка связана с тем, что привязка let без параметров определяет значение, а не функцию. Значения не могут быть универсальными, поскольку предполагается, что чтение значения везде приводит к одному и тому же результату, но параметры универсального типа могут сделать это предположение недействительным, включив результаты, зависящие от типа. diff --git a/src/Compiler/xlf/FSStrings.tr.xlf b/src/Compiler/xlf/FSStrings.tr.xlf index 6483eba2da0..fc33819c0f0 100644 --- a/src/Compiler/xlf/FSStrings.tr.xlf +++ b/src/Compiler/xlf/FSStrings.tr.xlf @@ -292,6 +292,11 @@ {0}' ve '{1}' alanları farklı türlerde + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. Değer kısıtlaması: '{0}' değeri, çıkarsanan bir genel türe sahip\n {1}\nAncak değerlerin "let f: '_a" içinde '_a gibi genel tür değişkenleri olamaz. Şunlardan birini yapabilirsiniz:\n- Tamsayı sabit değeri, dize sabit değeri veya birleşim durumu gibi basit bir veri terimi olarak tanımlayın, örneğin "let x = 1"\n- Açık bir tür ek açıklaması ekleyin, örneğin "let x : int" \n- Tür çıkarımı için değeri sonraki kodda genel olmayan bir tür olarak kullanın, örneğin "do x" \nveya yine de türe bağımlı sonuçlar istiyorsanız, şunlardan birini yaparak '{2}' öğesini işlev olarak tanımlayın:\n- Bir birim parametresi ekleyin, örneğin "let x()"\n- Açık tür parametreleri yazın, örneğin "let x<'a>".\nBu hatanın nedeni parametre içermeyen bir let bağlamasının bir işlevi değil bir değeri tanımlamasıdır. Bir değerin okunmasının her yerde aynı şekilde sonuçlanacağı varsayıldığından değerler genel olamaz, ancak genel tür parametreleri türe bağımlı sonuçları etkinleştirerek bu varsayımı geçersiz yapabilir. diff --git a/src/Compiler/xlf/FSStrings.zh-Hans.xlf b/src/Compiler/xlf/FSStrings.zh-Hans.xlf index be48009ccde..e32a0b91b0b 100644 --- a/src/Compiler/xlf/FSStrings.zh-Hans.xlf +++ b/src/Compiler/xlf/FSStrings.zh-Hans.xlf @@ -292,6 +292,11 @@ 字段“{0}”和“{1}”来自不同的类型 + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. 值限制: 值“{0}”具有推断的泛型类型\n {1}\n但是,值不能具有泛型类型变量,如“let x: '_a”中的 '_a。可以执行下列操作之一:\n- 将其定义为简单数据词,如整数文本、字符串文本或联合大小写(如“let x = 1”)\n- 添加显式类型批注(如“let x : int”)\n- 在后面的代码中将该值用作非泛型类型,以进行类型推理(如“do x”)\n如果仍需要依赖类型的结果,可以改为通过执行以下任一操作来定义“{2}”:\n- 添加单元参数(如“let x()”)\n- 编写显式类型参数,如“let x<'a>”。\n此错误是因为没有参数的 let 绑定定义了一个值,而不是函数。值不能是泛型的,因为会假定读取值会导致所有位置都相同,但泛型类型参数可能通过启用依赖类型的结果来使此假设失效。 diff --git a/src/Compiler/xlf/FSStrings.zh-Hant.xlf b/src/Compiler/xlf/FSStrings.zh-Hant.xlf index 45cae27f445..fdbca717ff8 100644 --- a/src/Compiler/xlf/FSStrings.zh-Hant.xlf +++ b/src/Compiler/xlf/FSStrings.zh-Hant.xlf @@ -292,6 +292,11 @@ 欄位 '{0}' 和 '{1}' 來自不同類型 + + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + The parameter '{0}' is not mutable. Either shadow it with a mutable binding (e.g. 'let mutable {1} = {2}'), or change its type to 'byref<_>'. + + Value restriction: The value '{0}' has an inferred generic type\n {1}\nHowever, values cannot have generic type variables like '_a in "let x: '_a". You can do one of the following:\n- Define it as a simple data term like an integer literal, a string literal or a union case like "let x = 1"\n- Add an explicit type annotation like "let x : int"\n- Use the value as a non-generic type in later code for type inference like "do x"\nor if you still want type-dependent results, you can define '{2}' as a function instead by doing either:\n- Add a unit parameter like "let x()"\n- Write explicit type parameters like "let x<'a>".\nThis error is because a let binding without parameters defines a value, not a function. Values cannot be generic because reading a value is assumed to result in the same everywhere but generic type parameters may invalidate this assumption by enabling type-dependent results. 值限制: 值 '{0}' 具有推斷的函式類型\n {1}\n不過,值不能有泛型類型變數,例如 "let x: '_a" 中的 '_a。您可以執行下列其中一項操作:\n- 將其定義為簡單資料項,例如整數常值、字串常值或聯集,例如 "let x = 1"\n- 新增明確類型註釋,例如 "let x : int"\n- 在稍後的程式碼中使用該值作為非泛型類型,以用於類型推斷,例如 "do x"\n,或者如果您仍然想要類型相依結果,可以改為將 '{2}' 定義為函式,方法是執行以下兩個動作之一:\n- 新增單位參數,例如 "let x()"\n- 寫入明確類型參數,例如 "let x<'a>"。\n此錯誤是因為 let 繫結沒有定義值的參數,而不是函式。值不能是泛型值,因為讀取值會假設在相同位置產生,但泛型類型參數可能透過啟用類型相依結果,使此假設失效。 From e03da54dd68ba3286fdc2151e1111d4774ebcfa3 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 29 May 2026 20:10:06 +0200 Subject: [PATCH 3/4] Format and validate FS0027 parameter message fix (#15803) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 1 + src/Compiler/Driver/CompilerDiagnostics.fs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 43601b6e34c..0403a8bd7a0 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,5 +1,6 @@ ### Fixed +* Diagnostic FS0027 now emits a parameter-specific message (suggesting a `let mutable x = x` shadow or `byref<_>`) instead of the illegal `let mutable x = expression` shadow when the assignment target is a function or method parameter. ([Issue #15803](https://github.com/dotnet/fsharp/issues/15803)) * Reject non-function bindings for single-case and partial active pattern names with FS1209, matching the existing multi-case behavior. ([PR #19763](https://github.com/dotnet/fsharp/pull/19763)) * Fix FS0421 "The address of the variable cannot be used at this point" incorrectly raised for the discard pattern `let _ = &expr` when `let x = &expr` compiles. ([Issue #18841](https://github.com/dotnet/fsharp/issues/18841), [PR #19811](https://github.com/dotnet/fsharp/pull/19811)) * Honor `--nowarn` and `--warnaserror` for warnings emitted during command-line option parsing ([Issue #19576](https://github.com/dotnet/fsharp/issues/19576), [PR #19776](https://github.com/dotnet/fsharp/pull/19776)) diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index 4632b067843..c7c44dbf671 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -1841,11 +1841,13 @@ type Exception with | ValNotMutable(_, vref, _) -> let name = vref.DisplayName + let msg = if vref.Deref.ArgReprInfoForDisplay.IsSome then ValNotMutableParameterE().Format name name name else ValNotMutableE().Format name + os.AppendString msg | ValNotLocal _ -> os.AppendString(ValNotLocalE().Format) From b7cc31a17fb3e650c57e51365ad7e282e20b8ab0 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 29 May 2026 20:55:21 +0200 Subject: [PATCH 4/4] Add PR reference to release notes entry for #15803 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 0403a8bd7a0..43dbbe2705d 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,6 +1,6 @@ ### Fixed -* Diagnostic FS0027 now emits a parameter-specific message (suggesting a `let mutable x = x` shadow or `byref<_>`) instead of the illegal `let mutable x = expression` shadow when the assignment target is a function or method parameter. ([Issue #15803](https://github.com/dotnet/fsharp/issues/15803)) +* Diagnostic FS0027 now emits a parameter-specific message (suggesting a `let mutable x = x` shadow or `byref<_>`) instead of the illegal `let mutable x = expression` shadow when the assignment target is a function or method parameter. ([Issue #15803](https://github.com/dotnet/fsharp/issues/15803), [PR #19866](https://github.com/dotnet/fsharp/pull/19866)) * Reject non-function bindings for single-case and partial active pattern names with FS1209, matching the existing multi-case behavior. ([PR #19763](https://github.com/dotnet/fsharp/pull/19763)) * Fix FS0421 "The address of the variable cannot be used at this point" incorrectly raised for the discard pattern `let _ = &expr` when `let x = &expr` compiles. ([Issue #18841](https://github.com/dotnet/fsharp/issues/18841), [PR #19811](https://github.com/dotnet/fsharp/pull/19811)) * Honor `--nowarn` and `--warnaserror` for warnings emitted during command-line option parsing ([Issue #19576](https://github.com/dotnet/fsharp/issues/19576), [PR #19776](https://github.com/dotnet/fsharp/pull/19776))