Skip to content

Commit

Permalink
Fix a few AND operator parser bugs and regressions (#17113)
Browse files Browse the repository at this point in the history
  • Loading branch information
psfinaki committed May 15, 2024
1 parent bd24c88 commit 3f7772a
Show file tree
Hide file tree
Showing 48 changed files with 1,055 additions and 14 deletions.
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* Fix calling an overridden virtual static method via the interface ([PR #17013](https://github.com/dotnet/fsharp/pull/17013))
* Fix state machines compilation, when big decision trees are involved, by removing code split when resumable code is detected ([PR #17076](https://github.com/dotnet/fsharp/pull/17076))
* Fix for exponential runtime in CE builders when using nested implicit yields [PR #17096](https://github.com/dotnet/fsharp/pull/17096)
* Fix several AND operator parser bugs and regressions ([Issue #16447](https://github.com/dotnet/fsharp/issues/16447), [Issue #17134](https://github.com/dotnet/fsharp/issues/17134), [Issue #16309](https://github.com/dotnet/fsharp/issues/16309), [PR #17113](https://github.com/dotnet/fsharp/pull/17113))

### Added

Expand Down
30 changes: 17 additions & 13 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,10 @@ opt_explicitValTyparDecls:
|
{ SynValTyparDecls(None, true) }

hashConstraint:
| HASH atomType
{ SynType.HashConstraint($2, lhs parseState) }

/* Any tokens in this grammar must be added to the lex filter rule 'peekAdjacentTypars' */
/* See the F# specification "Lexical analysis of type applications and type parameter definitions" */
opt_typeConstraints:
Expand All @@ -2579,20 +2583,20 @@ typeConstraints:
/* Any tokens in this grammar must be added to the lex filter rule 'peekAdjacentTypars' */
/* See the F# specification "Lexical analysis of type applications and type parameter definitions" */
intersectionConstraints:
| intersectionConstraints AMP atomType %prec prec_no_more_attr_bindings // todo precedence
| intersectionConstraints AMP hashConstraint %prec prec_no_more_attr_bindings
{ let constraints, mAmpersands = $1
($3 :: constraints), (rhs parseState 2 :: mAmpersands) }

match $3 with
| SynType.HashConstraint _ -> ()
| ty -> errorR(Error(FSComp.SR.parsConstraintIntersectionSyntaxUsedWithNonFlexibleType(), ty.Range))

| intersectionConstraints AMP atomType %prec prec_no_more_attr_bindings
{ let constraints, mAmpersands = $1
errorR(Error(FSComp.SR.parsConstraintIntersectionSyntaxUsedWithNonFlexibleType(), $3.Range))
($3 :: constraints), (rhs parseState 2 :: mAmpersands) }

| atomType
{ match $1 with
| SynType.HashConstraint _ -> ()
| ty -> errorR(Error(FSComp.SR.parsConstraintIntersectionSyntaxUsedWithNonFlexibleType(), ty.Range))
| hashConstraint
{ [ $1 ], [] }

| atomType
{ errorR(Error(FSComp.SR.parsConstraintIntersectionSyntaxUsedWithNonFlexibleType(), $1.Range))
[ $1 ], [] }

/* Any tokens in this grammar must be added to the lex filter rule 'peekAdjacentTypars' */
Expand Down Expand Up @@ -5993,11 +5997,11 @@ tupleOrQuotTypeElements:
{ [ SynTupleTypeSegment.Type $1 ] }

intersectionType:
| typar AMP intersectionConstraints %prec prec_no_more_attr_bindings // todo precedence
| typar AMP intersectionConstraints %prec prec_no_more_attr_bindings
{ let constraints, mAmpersands = $3
SynType.Intersection(Some $1, List.rev constraints, lhs parseState, { AmpersandRanges = rhs parseState 2 :: List.rev mAmpersands }) }

| atomType AMP intersectionConstraints %prec prec_no_more_attr_bindings // todo precedence
| hashConstraint AMP intersectionConstraints %prec prec_no_more_attr_bindings
{ let constraints, mAmpersands = $3
SynType.Intersection(None, $1 :: List.rev constraints, lhs parseState, { AmpersandRanges = rhs parseState 2 :: List.rev mAmpersands }) }

Expand Down Expand Up @@ -6213,8 +6217,8 @@ atomTypeOrAnonRecdType:
/* Any tokens in this grammar must be added to the lex filter rule 'peekAdjacentTypars' */
/* See the F# specification "Lexical analysis of type applications and type parameter definitions" */
atomType:
| HASH atomType
{ SynType.HashConstraint($2, lhs parseState) }
| hashConstraint
{ $1 }

| appTypeConPower %prec prec_atomtyp_path
{ $1 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ module And =
|> typecheck
|> shouldSucceed

[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"andPattern04.fs"|])>]
let ``And - andPattern04_fs`` compilation =
compilation
|> asFs
|> typecheck
|> shouldSucceed

// This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/And)
[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"E_IdentBoundTwice.fs"|])>]
let ``And - E_IdentBoundTwice_fs - --test:ErrorRanges`` compilation =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// #Conformance #PatternMatching #PatternMatchingGuards

open System

let x: Result<unit, exn> = Error (NullReferenceException())

match x with
| Error (_: exn & :? NullReferenceException) -> printfn "NullRef"
| _ -> ()

match x with
| Error (_: exn & (:? NullReferenceException)) -> printfn "NullRef"
| _ -> ()

match x with
| Error ((_: exn) & :? NullReferenceException) -> printfn "NullRef"
| _ -> ()

match x with
| Error ((_: exn) & (:? NullReferenceException)) -> printfn "NullRef"
| _ -> ()
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,31 @@ module Named =
|> withOptions ["--test:ErrorRanges"]
|> typecheck
|> shouldSucceed

[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"activePatterns09.fs"|])>]
let ``Named - activePatterns09_fs`` compilation =
compilation
|> asFs
|> typecheck
|> shouldSucceed

[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"activePatterns10.fs"|])>]
let ``Named - activePatterns10_fs`` compilation =
compilation
|> asFs
|> typecheck
|> shouldFail
|> withDiagnostics [
(Warning 25, Line 6, Col 5, Line 6, Col 26, "Incomplete pattern matches on this expression.")
(Warning 25, Line 7, Col 5, Line 7, Col 28, "Incomplete pattern matches on this expression.")
(Warning 25, Line 8, Col 5, Line 8, Col 28, "Incomplete pattern matches on this expression.")
(Warning 25, Line 9, Col 5, Line 9, Col 30, "Incomplete pattern matches on this expression.")
(Warning 25, Line 13, Col 5, Line 13, Col 22, "Incomplete pattern matches on this expression. For example, the value '``some-other-subtype``' may indicate a case not covered by the pattern(s).")
(Warning 25, Line 14, Col 5, Line 14, Col 24, "Incomplete pattern matches on this expression. For example, the value '``some-other-subtype``' may indicate a case not covered by the pattern(s).")
(Warning 25, Line 15, Col 5, Line 15, Col 24, "Incomplete pattern matches on this expression. For example, the value '``some-other-subtype``' may indicate a case not covered by the pattern(s).")
(Warning 25, Line 16, Col 5, Line 16, Col 26, "Incomplete pattern matches on this expression. For example, the value '``some-other-subtype``' may indicate a case not covered by the pattern(s).")
]

// This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Named)
[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"ActivePatternUnconstrained01.fs"|])>]
let ``Named - ActivePatternUnconstrained01_fs - --test:ErrorRanges`` compilation =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// #Conformance #PatternMatching #ActivePatterns
#light

// Verify AND on active patterns with exceptions

let (_ : exn & Failure _ | _) = exn ()
let ((_ : exn) & (Failure _) | (_)) = exn ()
let (_ : exn & (Failure _) | _) = exn ()
let ((_ : exn) & Failure _ | (_)) = exn ()

exception MyExn

let (_ : exn & MyExn | _) = exn ()
let ((_ : exn) & (MyExn) | (_)) = exn ()
let (_ : exn & (MyExn) | _) = exn ()
let ((_ : exn) & MyExn | (_)) = exn ()

exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// #Conformance #PatternMatching #ActivePatterns
#light

// Verify AND on incomplete active patterns with exceptions

let (_ : exn & Failure _) = exn ()
let ((_ : exn) & Failure _) = exn ()
let (_ : exn & (Failure _)) = exn ()
let ((_ : exn) & (Failure _)) = exn ()

exception MyExn

let (_ : exn & MyExn) = exn ()
let ((_ : exn) & MyExn) = exn ()
let (_ : exn & (MyExn)) = exn ()
let ((_ : exn) & (MyExn)) = exn ()

exit 0
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,18 @@ let y (f: #seq<int> & System.IDisposable) = ()
|> withDiagnostics [
Error 3572, Line 2, Col 25, Line 2, Col 43, "Constraint intersection syntax may only be used with flexible types, e.g. '#IDisposable & #ISomeInterface'."
Error 3572, Line 4, Col 23, Line 4, Col 41, "Constraint intersection syntax may only be used with flexible types, e.g. '#IDisposable & #ISomeInterface'."
]
]

// bug 16309
[<Fact>]
let ``Constraint intersection handles invalid types``() =
FSharp """
let f (x: 't when 't :> ABRAKADABRAA & #seq<int>) = ()
"""
|> withLangVersion80
|> typecheck
|> shouldFail
|> withDiagnostics [
Error 0010, Line 2, Col 40, Line 2, Col 41, "Unexpected symbol # in pattern"
Error 0583, Line 2, Col 7, Line 2, Col 8, "Unmatched '('"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let (_ : exn & Failure _) = exn ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
ImplFile
(ParsedImplFileInput
("/root/OperatorName/ActivePatternAnd 01.fs", false,
QualifiedNameOfFile ActivePatternAnd 01, [], [],
[SynModuleOrNamespace
([ActivePatternAnd 01], false, AnonModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Paren
(Ands
([Typed
(Wild (1,5--1,6),
LongIdent (SynLongIdent ([exn], [], [None])),
(1,5--1,12));
LongIdent
(SynLongIdent ([Failure], [], [None]), None, None,
Pats [Wild (1,23--1,24)], None, (1,15--1,24))],
(1,5--1,24)), (1,4--1,25)), None,
App
(NonAtomic, false, Ident exn, Const (Unit, (1,32--1,34)),
(1,28--1,34)), (1,4--1,25), Yes (1,0--1,34),
{ LeadingKeyword = Let (1,0--1,3)
InlineKeyword = None
EqualsRange = Some (1,26--1,27) })], (1,0--1,34))],
PreXmlDocEmpty, [], None, (1,0--1,34), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))

(1,0)-(1,34) parse warning The declarations in this file will be placed in an implicit module 'ActivePatternAnd 01' based on the file name 'ActivePatternAnd 01.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let ((_ : exn) & Failure _) = exn ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
ImplFile
(ParsedImplFileInput
("/root/OperatorName/ActivePatternAnd 02.fs", false,
QualifiedNameOfFile ActivePatternAnd 02, [], [],
[SynModuleOrNamespace
([ActivePatternAnd 02], false, AnonModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Paren
(Ands
([Paren
(Typed
(Wild (1,6--1,7),
LongIdent (SynLongIdent ([exn], [], [None])),
(1,6--1,13)), (1,5--1,14));
LongIdent
(SynLongIdent ([Failure], [], [None]), None, None,
Pats [Wild (1,25--1,26)], None, (1,17--1,26))],
(1,5--1,26)), (1,4--1,27)), None,
App
(NonAtomic, false, Ident exn, Const (Unit, (1,34--1,36)),
(1,30--1,36)), (1,4--1,27), Yes (1,0--1,36),
{ LeadingKeyword = Let (1,0--1,3)
InlineKeyword = None
EqualsRange = Some (1,28--1,29) })], (1,0--1,36))],
PreXmlDocEmpty, [], None, (1,0--1,36), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))

(1,0)-(1,36) parse warning The declarations in this file will be placed in an implicit module 'ActivePatternAnd 02' based on the file name 'ActivePatternAnd 02.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let (_ : exn & (Failure _)) = exn ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
ImplFile
(ParsedImplFileInput
("/root/OperatorName/ActivePatternAnd 03.fs", false,
QualifiedNameOfFile ActivePatternAnd 03, [], [],
[SynModuleOrNamespace
([ActivePatternAnd 03], false, AnonModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Paren
(Ands
([Typed
(Wild (1,5--1,6),
LongIdent (SynLongIdent ([exn], [], [None])),
(1,5--1,12));
Paren
(LongIdent
(SynLongIdent ([Failure], [], [None]), None, None,
Pats [Wild (1,24--1,25)], None, (1,16--1,25)),
(1,15--1,26))], (1,5--1,26)), (1,4--1,27)), None,
App
(NonAtomic, false, Ident exn, Const (Unit, (1,34--1,36)),
(1,30--1,36)), (1,4--1,27), Yes (1,0--1,36),
{ LeadingKeyword = Let (1,0--1,3)
InlineKeyword = None
EqualsRange = Some (1,28--1,29) })], (1,0--1,36))],
PreXmlDocEmpty, [], None, (1,0--1,36), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))

(1,0)-(1,36) parse warning The declarations in this file will be placed in an implicit module 'ActivePatternAnd 03' based on the file name 'ActivePatternAnd 03.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let ((_ : exn) & (Failure _)) = exn ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ImplFile
(ParsedImplFileInput
("/root/OperatorName/ActivePatternAnd 04.fs", false,
QualifiedNameOfFile ActivePatternAnd 04, [], [],
[SynModuleOrNamespace
([ActivePatternAnd 04], false, AnonModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Paren
(Ands
([Paren
(Typed
(Wild (1,6--1,7),
LongIdent (SynLongIdent ([exn], [], [None])),
(1,6--1,13)), (1,5--1,14));
Paren
(LongIdent
(SynLongIdent ([Failure], [], [None]), None, None,
Pats [Wild (1,26--1,27)], None, (1,18--1,27)),
(1,17--1,28))], (1,5--1,28)), (1,4--1,29)), None,
App
(NonAtomic, false, Ident exn, Const (Unit, (1,36--1,38)),
(1,32--1,38)), (1,4--1,29), Yes (1,0--1,38),
{ LeadingKeyword = Let (1,0--1,3)
InlineKeyword = None
EqualsRange = Some (1,30--1,31) })], (1,0--1,38))],
PreXmlDocEmpty, [], None, (1,0--1,38), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))

(1,0)-(1,38) parse warning The declarations in this file will be placed in an implicit module 'ActivePatternAnd 04' based on the file name 'ActivePatternAnd 04.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let (_ : exn & Failure _ | _) = exn ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ImplFile
(ParsedImplFileInput
("/root/OperatorName/ActivePatternAnd 05.fs", false,
QualifiedNameOfFile ActivePatternAnd 05, [], [],
[SynModuleOrNamespace
([ActivePatternAnd 05], false, AnonModule,
[Let
(false,
[SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(None, SynValInfo ([], SynArgInfo ([], false, None)), None),
Paren
(Or
(Ands
([Typed
(Wild (1,5--1,6),
LongIdent (SynLongIdent ([exn], [], [None])),
(1,5--1,12));
LongIdent
(SynLongIdent ([Failure], [], [None]), None, None,
Pats [Wild (1,23--1,24)], None, (1,15--1,24))],
(1,5--1,24)), Wild (1,27--1,28), (1,5--1,28),
{ BarRange = (1,25--1,26) }), (1,4--1,29)), None,
App
(NonAtomic, false, Ident exn, Const (Unit, (1,36--1,38)),
(1,32--1,38)), (1,4--1,29), Yes (1,0--1,38),
{ LeadingKeyword = Let (1,0--1,3)
InlineKeyword = None
EqualsRange = Some (1,30--1,31) })], (1,0--1,38))],
PreXmlDocEmpty, [], None, (1,0--1,38), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))

(1,0)-(1,38) parse warning The declarations in this file will be placed in an implicit module 'ActivePatternAnd 05' based on the file name 'ActivePatternAnd 05.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let ((_ : exn) & (Failure _) | (_)) = exn ()

0 comments on commit 3f7772a

Please sign in to comment.