Skip to content

Commit 7af5178

Browse files
authored
Fix 11873: Name is bound multiple times is not reported in 'as' pattern (#18984)
1 parent 38e7655 commit 7af5178

File tree

8 files changed

+331
-34
lines changed

8 files changed

+331
-34
lines changed

docs/release-notes/.FSharp.Compiler.Service/11.0.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* Scripts: Fix resolving the dotnet host path when an SDK directory is specified. ([PR #18960](https://github.com/dotnet/fsharp/pull/18960))
44
* Fix excessive StackGuard thread jumping ([PR #18971](https://github.com/dotnet/fsharp/pull/18971))
5+
* Fix name is bound multiple times is not reported in 'as' pattern ([PR #18984](https://github.com/dotnet/fsharp/pull/18984))
56

67
### Added
78

src/Compiler/Checking/CheckBasics.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ type TcFileState =
319319
TcPat: WarnOnUpperFlag -> TcFileState -> TcEnv -> PrelimValReprInfo option -> TcPatValFlags -> TcPatLinearEnv -> TType -> SynPat -> (TcPatPhase2Input -> Pattern) * TcPatLinearEnv
320320

321321
// forward call
322-
TcSimplePats: TcFileState -> bool -> CheckConstraints -> TType -> TcEnv -> TcPatLinearEnv -> SynSimplePats -> string list * TcPatLinearEnv
322+
TcSimplePats: TcFileState -> bool -> CheckConstraints -> TType -> TcEnv -> TcPatLinearEnv -> SynSimplePats -> SynPat list * bool -> string list * TcPatLinearEnv
323323

324324
// forward call
325325
TcSequenceExpressionEntry: TcFileState -> TcEnv -> OverallTy -> UnscopedTyparEnv -> bool * SynExpr -> range -> Expr * UnscopedTyparEnv

src/Compiler/Checking/CheckBasics.fsi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ type TcFileState =
295295
-> TcEnv
296296
-> TcPatLinearEnv
297297
-> SynSimplePats
298+
// SynPat list: Represents parsed patterns,
299+
// bool: Indicates if this is the first pattern in a sequence of patterns
300+
-> SynPat list * bool
298301
-> string list * TcPatLinearEnv
299302

300303
// forward call
@@ -345,6 +348,7 @@ type TcFileState =
345348
-> TcEnv
346349
-> TcPatLinearEnv
347350
-> SynSimplePats
351+
-> SynPat list * bool
348352
-> string list * TcPatLinearEnv) *
349353
tcSequenceExpressionEntry:
350354
(TcFileState -> TcEnv -> OverallTy -> UnscopedTyparEnv -> bool * SynExpr -> range -> Expr * UnscopedTyparEnv) *

src/Compiler/Checking/CheckPatterns.fs

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -145,47 +145,84 @@ and ValidateOptArgOrder (synSimplePats: SynSimplePats) =
145145
List.iter (fun pat -> if isOptArg pat then hitOptArg <- true elif hitOptArg then error(Error(FSComp.SR.tcOptionalArgsMustComeAfterNonOptionalArgs(), m))) pats
146146

147147

148-
/// Bind the patterns used in argument position for a function, method or lambda.
149-
and TcSimplePats (cenv: cenv) optionalArgsOK checkConstraints ty env patEnv synSimplePats =
148+
/// Bind the patterns used in the argument position for a function, method or lambda.
149+
and TcSimplePats (cenv: cenv) optionalArgsOK checkConstraints ty env patEnv synSimplePats (parsedPatterns: SynPat list * bool) =
150150

151-
let g = cenv.g
152-
let (TcPatLinearEnv(tpenv, names, takenNames)) = patEnv
153-
154-
// validate optional argument declaration
151+
let rec collectBoundIdTextsFromPat (acc: string list) (p: SynPat) : string list =
152+
match p with
153+
| SynPat.FromParseError(p, _)
154+
| SynPat.Paren(p, _) -> collectBoundIdTextsFromPat acc p
155+
| SynPat.Tuple(_, ps, _, _)
156+
| SynPat.ArrayOrList(_, ps, _) -> List.fold collectBoundIdTextsFromPat acc ps
157+
| SynPat.As(lhs, rhs, _) -> collectBoundIdTextsFromPat (collectBoundIdTextsFromPat acc lhs) rhs
158+
| SynPat.Named(SynIdent(id, _), _, _, _)
159+
| SynPat.OptionalVal(id, _) -> id.idText :: acc
160+
| SynPat.LongIdent(argPats = SynArgPats.Pats ps) -> List.fold collectBoundIdTextsFromPat acc ps
161+
| SynPat.Or(p1, p2, _, _) -> collectBoundIdTextsFromPat (collectBoundIdTextsFromPat acc p1) p2
162+
| SynPat.Ands(pats, _) -> List.fold collectBoundIdTextsFromPat acc pats
163+
| SynPat.Record(fieldPats = fields) ->
164+
(acc, fields)
165+
||> List.fold (fun acc (NamePatPairField(_, _, _, pat, _)) -> collectBoundIdTextsFromPat acc pat)
166+
| SynPat.ListCons(lhsPat = l; rhsPat = r) -> collectBoundIdTextsFromPat (collectBoundIdTextsFromPat acc l) r
167+
| _ -> acc
168+
169+
let augmentTakenNamesFromFirstGroup (parsedData: SynPat list * bool) (patEnvOut: TcPatLinearEnv) : TcPatLinearEnv =
170+
match parsedData, patEnvOut with
171+
| (pats ,true), TcPatLinearEnv(tpenvR, namesR, takenNamesR) ->
172+
match pats with
173+
| pat :: _ ->
174+
let extra = collectBoundIdTextsFromPat [] pat |> Set.ofList
175+
TcPatLinearEnv(tpenvR, namesR, Set.union takenNamesR extra)
176+
| _ -> patEnvOut
177+
| _ -> patEnvOut
178+
179+
let bindCurriedGroup (synSimplePats: SynSimplePats) : string list * TcPatLinearEnv =
180+
let g = cenv.g
181+
let (TcPatLinearEnv(tpenv, names, takenNames)) = patEnv
182+
match synSimplePats with
183+
| SynSimplePats.SimplePats ([], _, m) ->
184+
// Unit "()" patterns in argument position become SynSimplePats.SimplePats([], _) in the
185+
// syntactic translation when building bindings. This is done because the
186+
// use of "()" has special significance for arity analysis and argument counting.
187+
//
188+
// Here we give a name to the single argument implied by those patterns.
189+
// This is a little awkward since it would be nice if this was
190+
// uniform with the process where we give names to other (more complex)
191+
// patterns used in argument position, e.g. "let f (D(x)) = ..."
192+
let id = ident("unitVar" + string takenNames.Count, m)
193+
UnifyTypes cenv env m ty g.unit_ty
194+
let vFlags = TcPatValFlags (ValInline.Optional, permitInferTypars, noArgOrRetAttribs, false, None, true)
195+
let _, namesR, takenNamesR = TcPatBindingName cenv env id ty false None None vFlags (names, takenNames)
196+
[ id.idText ], TcPatLinearEnv(tpenv, namesR, takenNamesR)
197+
| SynSimplePats.SimplePats ([sp], _, _) ->
198+
// Single parameter: no tuple splitting, check directly
199+
let v, patEnv' = TcSimplePat optionalArgsOK checkConstraints cenv ty env patEnv sp []
200+
[ v ], patEnv'
201+
| SynSimplePats.SimplePats (ps, _, m) ->
202+
// Multiple parameters: treat a domain type as a ref-tuple and map each simple pat
203+
let ptys = UnifyRefTupleType env.eContextInfo cenv env.DisplayEnv m ty ps
204+
let namesOut, patEnvR =
205+
(patEnv, List.zip ptys ps)
206+
||> List.mapFold (fun penv (pty, sp) -> TcSimplePat optionalArgsOK checkConstraints cenv pty env penv sp [])
207+
namesOut, patEnvR
208+
209+
// 1) validate optional-arg ordering
155210
ValidateOptArgOrder synSimplePats
156211

157-
match synSimplePats with
158-
| SynSimplePats.SimplePats ([],_, m) ->
159-
// Unit "()" patterns in argument position become SynSimplePats.SimplePats([], _) in the
160-
// syntactic translation when building bindings. This is done because the
161-
// use of "()" has special significance for arity analysis and argument counting.
162-
//
163-
// Here we give a name to the single argument implied by those patterns.
164-
// This is a little awkward since it would be nice if this was
165-
// uniform with the process where we give names to other (more complex)
166-
// patterns used in argument position, e.g. "let f (D(x)) = ..."
167-
let id = ident("unitVar" + string takenNames.Count, m)
168-
UnifyTypes cenv env m ty g.unit_ty
169-
let vFlags = TcPatValFlags (ValInline.Optional, permitInferTypars, noArgOrRetAttribs, false, None, true)
170-
let _, namesR, takenNamesR = TcPatBindingName cenv env id ty false None None vFlags (names, takenNames)
171-
let patEnvR = TcPatLinearEnv(tpenv, namesR, takenNamesR)
172-
[id.idText], patEnvR
212+
// 2) bind the current curried group
213+
let namesOut, patEnvOut = bindCurriedGroup synSimplePats
173214

174-
| SynSimplePats.SimplePats (pats = [synSimplePat]) ->
175-
let v, patEnv = TcSimplePat optionalArgsOK checkConstraints cenv ty env patEnv synSimplePat []
176-
[v], patEnv
215+
// 3) post-augment takenNames for later groups (using the original first-group pattern)
216+
let patEnvOut = augmentTakenNamesFromFirstGroup parsedPatterns patEnvOut
177217

178-
| SynSimplePats.SimplePats (ps, _, m) ->
179-
let ptys = UnifyRefTupleType env.eContextInfo cenv env.DisplayEnv m ty ps
180-
let ps', patEnvR = (patEnv, List.zip ptys ps) ||> List.mapFold (fun patEnv (ty, pat) -> TcSimplePat optionalArgsOK checkConstraints cenv ty env patEnv pat [])
181-
ps', patEnvR
218+
namesOut, patEnvOut
182219

183220
and TcSimplePatsOfUnknownType (cenv: cenv) optionalArgsOK checkConstraints env tpenv (pat: SynPat) =
184221
let g = cenv.g
185222
let argTy = NewInferenceType g
186223
let patEnv = TcPatLinearEnv (tpenv, NameMap.empty, Set.empty)
187224
let spats, _ = SimplePatsOfPat cenv.synArgNameGenerator pat
188-
let names, patEnv = TcSimplePats cenv optionalArgsOK checkConstraints argTy env patEnv spats
225+
let names, patEnv = TcSimplePats cenv optionalArgsOK checkConstraints argTy env patEnv spats ([], false)
189226
names, patEnv, spats
190227

191228
and TcPatBindingName cenv env id ty isMemberThis vis1 valReprInfo (vFlags: TcPatValFlags) (names, takenNames: Set<string>) =

src/Compiler/Checking/CheckPatterns.fsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ val TcSimplePats:
3939
env: TcEnv ->
4040
patEnv: TcPatLinearEnv ->
4141
synSimplePats: SynSimplePats ->
42+
parsedPatterns: SynPat list * bool ->
4243
string list * TcPatLinearEnv

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6484,10 +6484,15 @@ and TcExprILAssembly (cenv: cenv) overallTy env tpenv (ilInstrs, synTyArgs, synA
64846484
and TcIteratedLambdas (cenv: cenv) isFirst (env: TcEnv) overallTy takenNames tpenv e =
64856485
let g = cenv.g
64866486
match e with
6487-
| SynExpr.Lambda (isMember, isSubsequent, synSimplePats, bodyExpr, _parsedData, m, _trivia) when isMember || isFirst || isSubsequent ->
6487+
| SynExpr.Lambda (isMember, isSubsequent, synSimplePats, bodyExpr, parsedData, m, _trivia) when isMember || isFirst || isSubsequent ->
64886488
let domainTy, resultTy = UnifyFunctionType None cenv env.DisplayEnv m overallTy.Commit
6489+
let parsedPatterns =
6490+
parsedData
6491+
|> Option.map fst
6492+
|> Option.defaultValue []
6493+
64896494
let vs, (TcPatLinearEnv (tpenv, names, takenNames)) =
6490-
cenv.TcSimplePats cenv isMember CheckCxs domainTy env (TcPatLinearEnv (tpenv, Map.empty, takenNames)) synSimplePats
6495+
cenv.TcSimplePats cenv isMember CheckCxs domainTy env (TcPatLinearEnv (tpenv, Map.empty, takenNames)) synSimplePats (parsedPatterns, isFirst)
64916496

64926497
let envinner, _, vspecMap = MakeAndPublishSimpleValsForMergedScope cenv env m names
64936498
let byrefs = vspecMap |> Map.map (fun _ v -> isByrefTy g v.Type, v)
@@ -11296,6 +11301,8 @@ and TcNonRecursiveBinding declKind cenv env tpenv ty binding =
1129611301
| _ -> ()
1129711302
| _ -> ()
1129811303

11304+
11305+
1129911306
let binding = BindingNormalization.NormalizeBinding ValOrMemberBinding cenv env binding
1130011307
let explicitTyparInfo, tpenv = TcNonrecBindingTyparDecls cenv env tpenv binding
1130111308
TcNormalizedBinding declKind cenv env tpenv ty None NoSafeInitInfo ([], explicitTyparInfo) binding
@@ -11743,7 +11750,7 @@ and ApplyTypesFromArgumentPatterns (cenv: cenv, env, optionalArgsOK, ty, m, tpen
1174311750
let domainTy, resultTy = UnifyFunctionType None cenv env.DisplayEnv m ty
1174411751
// We apply the type information from the patterns by type checking the
1174511752
// "simple" patterns against 'domainTyR'. They get re-typechecked later.
11746-
ignore (cenv.TcSimplePats cenv optionalArgsOK CheckCxs domainTy env (TcPatLinearEnv (tpenv, Map.empty, Set.empty)) pushedPat)
11753+
ignore (cenv.TcSimplePats cenv optionalArgsOK CheckCxs domainTy env (TcPatLinearEnv (tpenv, Map.empty, Set.empty)) pushedPat ([], false))
1174711754
ApplyTypesFromArgumentPatterns (cenv, env, optionalArgsOK, resultTy, m, tpenv, NormalizedBindingRhs (morePushedPats, retInfoOpt, e), memberFlagsOpt)
1174811755

1174911756
/// Check if the type annotations and inferred type information in a value give a

0 commit comments

Comments
 (0)