diff --git a/src/absil/illib.fs b/src/absil/illib.fs
index ef71ec8df8fb610725f1bf88adde182cf183a1a6..98a8636991fda0f2fa4c6f973ff4a1871706aa45 100644
--- a/src/absil/illib.fs
+++ b/src/absil/illib.fs
@@ -503,9 +503,20 @@ module String =
let uppercase (s: string) =
s.ToUpperInvariant()
- let isUpper (s: string) =
- s.Length >= 1 && Char.IsUpper s.[0] && not (Char.IsLower s.[0])
-
+ // Scripts that distinguish between upper and lower case (bicameral) DU Discriminators and Active Pattern identifiers are required to start with an upper case character.
+ // For valid identifiers where the case of the identifier can not be determined because there is no upper and lower case we will allow DU Discriminators and upper case characters
+ // to be used. This means that developers using unicameral scripts such as hindi, are not required to prefix these identifiers with an Upper case latin character.
+ //
+ let isLeadingIdentifierCharacterUpperCase (s:string) =
+ let isUpperCaseCharacter c =
+ // if IsUpper and IsLower return the same value, then we can't tell if it's upper or lower case, so ensure it is a letter
+ // otherwise it is bicameral, so must be upper case
+ let isUpper = Char.IsUpper c
+ if isUpper = Char.IsLower c then Char.IsLetter c
+ else isUpper
+
+ s.Length >= 1 && isUpperCaseCharacter s.[0]
+
let capitalize (s: string) =
if s.Length = 0 then s
else uppercase s.[0..0] + s.[ 1.. s.Length - 1 ]
diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs
index 2cb72a3047bf19ba078eb04835bd309bb5e50529..a61a719d7106ebb797ee3e529565f7e46dcc8ddd 100755
--- a/src/fsharp/TypeChecker.fs
+++ b/src/fsharp/TypeChecker.fs
@@ -5233,7 +5233,7 @@ and TcPatBindingName cenv env id ty isMemberThis vis1 topValData (inlineFlag, de
let name = id.idText
match values.TryGetValue name with
| true, value ->
- if not (String.IsNullOrEmpty name) && Char.IsLower(name.[0]) then
+ if not (String.IsNullOrEmpty name) && not (String.isLeadingIdentifierCharacterUpperCase name) then
match env.eNameResEnv.ePatItems.TryGetValue name with
| true, Item.Value vref when vref.LiteralValue.IsSome ->
warning(Error(FSComp.SR.checkLowercaseLiteralBindingInPattern name, id.idRange))
@@ -12775,7 +12775,7 @@ module TcRecdUnionAndEnumDeclarations = begin
errorR(Error(FSComp.SR.tcUnionCaseNameConflictsWithGeneratedType(name, "Tags"), id.idRange))
CheckNamespaceModuleOrTypeName cenv.g id
- if not (String.isUpper name) && name <> opNameCons && name <> opNameNil then
+ if not (String.isLeadingIdentifierCharacterUpperCase name) && name <> opNameCons && name <> opNameNil then
errorR(NotUpperCaseConstructor(id.idRange))
let ValidateFieldNames (synFields: SynField list, tastFields: RecdField list) =
@@ -15174,7 +15174,7 @@ module TcExceptionDeclarations =
let TcExnDefnCore_Phase1A cenv env parent (SynExceptionDefnRepr(Attributes synAttrs, UnionCase(_, id, _, _, _, _), _, doc, vis, m)) =
let attrs = TcAttributes cenv env AttributeTargets.ExnDecl synAttrs
- if not (String.isUpper id.idText) then errorR(NotUpperCaseConstructor m)
+ if not (String.isLeadingIdentifierCharacterUpperCase id.idText) then errorR(NotUpperCaseConstructor m)
let vis, cpath = ComputeAccessAndCompPath env None m vis None parent
let vis = TcRecdUnionAndEnumDeclarations.CombineReprAccess parent vis
CheckForDuplicateConcreteType env (id.idText + "Exception") id.idRange
diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy
index 2c4d456aab4969b6dfd2a08b5d05a6411ef915bc..034ec5ec5d7ef98cb161e3512422d2deff82703e 100644
--- a/src/fsharp/pars.fsy
+++ b/src/fsharp/pars.fsy
@@ -3050,7 +3050,7 @@ atomicPattern:
| atomicPatternLongIdent %prec prec_atompat_pathop
{ let vis, lidwd = $1
- if not (isNilOrSingleton lidwd.Lid) || (let c = (List.head lidwd.Lid).idText.[0] in Char.IsUpper(c) && not (Char.IsLower c))
+ if not (isNilOrSingleton lidwd.Lid) || String.isLeadingIdentifierCharacterUpperCase (List.head lidwd.Lid).idText
then mkSynPatMaybeVar lidwd vis (lhs parseState)
else mkSynPatVar vis (List.head lidwd.Lid) }
@@ -5293,8 +5293,8 @@ operatorName:
/* One part of an active pattern name */
activePatternCaseName:
- | IDENT
- { if not (String.isUpper $1) then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsActivePatternCaseMustBeginWithUpperCase());
+ | IDENT
+ { if not (String.isLeadingIdentifierCharacterUpperCase _1) then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsActivePatternCaseMustBeginWithUpperCase());
if ($1.IndexOf('|') <> -1) then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsActivePatternCaseContainsPipe());
$1 }
diff --git a/tests/fsharpqa/Source/Conformance/PatternMatching/Named/E_ActivePatterns01.fs b/tests/fsharpqa/Source/Conformance/PatternMatching/Named/E_ActivePatterns01.fs
index c2cb3823771795f0c2dfaabe094872e642a8aa8b..19bf8faee6ca6a17bc5afb142dde4d0488e77dbb 100644
--- a/tests/fsharpqa/Source/Conformance/PatternMatching/Named/E_ActivePatterns01.fs
+++ b/tests/fsharpqa/Source/Conformance/PatternMatching/Named/E_ActivePatterns01.fs
@@ -1,12 +1,13 @@
// #Regression #Conformance #PatternMatching #ActivePatterns
// Verify error if Active Patterns do not start with an upper case letter
-//Active pattern case identifiers must begin with an uppercase letter
-//Active pattern case identifiers must begin with an uppercase letter
//Active pattern case identifiers must begin with an uppercase letter
-//Active pattern case identifiers must begin with an uppercase letter
-//Active pattern case identifiers must begin with an uppercase letter
-//The '\|' character is not permitted in active pattern case identifiers
-//The '\|' character is not permitted in active pattern case identifiers
+//Active pattern case identifiers must begin with an uppercase letter
+//Active pattern case identifiers must begin with an uppercase letter
+//Active pattern case identifiers must begin with an uppercase letter
+//Active pattern case identifiers must begin with an uppercase letter
+//The '\|' character is not permitted in active pattern case identifiers
+//The '\|' character is not permitted in active pattern case identifiers
+//Active pattern case identifiers must begin with an uppercase letter
let (|positive|negative|) n = if n < 0 then positive else negative
let (|`` A``|) (x:int) = x
@@ -14,5 +15,6 @@ let (|B1|``+B2``|) (x:int) = if x = 0 then OneA else ``+B2``
let (|`` C``|_|) (x:int) = if x = 0 then Some(x) else None
let (|``D|E``|F|) (x:int) = if x = 0 then D elif x = 1 then E else F
let (|G|``H||I``|) (x:int) = if x = 0 then G elif x = 1 then H else ``|I``
+let (|_J|) (x:int) = _J
exit 1
diff --git a/tests/fsharpqa/Source/Conformance/PatternMatching/Named/discUnion01.fs b/tests/fsharpqa/Source/Conformance/PatternMatching/Named/discUnion01.fs
index 89832cc9100029f42e94de75d984efb0161d817d..c1a896978cba3206f83debbcbe9a311876df43f1 100644
--- a/tests/fsharpqa/Source/Conformance/PatternMatching/Named/discUnion01.fs
+++ b/tests/fsharpqa/Source/Conformance/PatternMatching/Named/discUnion01.fs
@@ -6,17 +6,17 @@
type Foo =
| A of int
| B of string * int
-
+
let test x =
match x with
| A(1) | B(_,1) -> 1
| A(2) | B(_,2) -> 2
| B(_, _) -> -1
| A(_) -> -2
-
+
if test (A(1)) <> 1 then exit 1
if test (B("",1)) <> 1 then exit 1
-
+
if test (A(2)) <> 2 then exit 1
if test (B("",2)) <> 2 then exit 1
diff --git a/tests/fsharpqa/Source/Globalization/Hindi.fs b/tests/fsharpqa/Source/Globalization/Hindi.fs
index 3c041c812fd2f0ba303f2bdb313375b3e9a9122f..63a33edbdd7ed7f6f736af290a396b839b2c3555 100644
--- a/tests/fsharpqa/Source/Globalization/Hindi.fs
+++ b/tests/fsharpqa/Source/Globalization/Hindi.fs
@@ -14,8 +14,8 @@ module पिछले =
// DU
type ख़तरxनाक =
- | Uअलगाववादी // There's no uppercase/lowercase in Hindi, so I'm adding a latin char
- | Aमिलती of ख़तरनाक
+ | अलगाववादी // There's no uppercase/lowercase in Hindi, ensure that a Hindi character will suffice to start the DU case name
+ | मिलती of ख़तरनाक
| X
// Record