Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions src/fsharp/PatternMatchCompilation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module internal Microsoft.FSharp.Compiler.PatternMatchCompilation

open System.Collections.Generic
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.AbstractIL.IL
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
open Microsoft.FSharp.Compiler.Range
Expand Down Expand Up @@ -169,6 +170,25 @@ type RefutedSet =
let notNullText = "some-non-null-value"
let otherSubtypeText = "some-other-subtype"

/// Create a TAST const value from an IL-initialized field read from .NET metadata
// (Originally moved from TcFieldInit in TypeChecker.fs -- feel free to move this somewhere more appropriate)
let ilFieldToTastConst lit =
match lit with
| ILFieldInit.String s -> Const.String s
| ILFieldInit.Null -> Const.Zero
| ILFieldInit.Bool b -> Const.Bool b
| ILFieldInit.Char c -> Const.Char (char (int c))
| ILFieldInit.Int8 x -> Const.SByte x
| ILFieldInit.Int16 x -> Const.Int16 x
| ILFieldInit.Int32 x -> Const.Int32 x
| ILFieldInit.Int64 x -> Const.Int64 x
| ILFieldInit.UInt8 x -> Const.Byte x
| ILFieldInit.UInt16 x -> Const.UInt16 x
| ILFieldInit.UInt32 x -> Const.UInt32 x
| ILFieldInit.UInt64 x -> Const.UInt64 x
| ILFieldInit.Single f -> Const.Single f
| ILFieldInit.Double f -> Const.Double f

exception CannotRefute
let RefuteDiscrimSet g m path discrims =
let mkUnknown ty = snd(mkCompGenLocal m "_" ty)
Expand Down Expand Up @@ -238,16 +258,28 @@ let RefuteDiscrimSet g m path discrims =
| Some c ->
match tryDestAppTy g ty with
| Some tcref when tcref.IsEnumTycon ->
// search for an enum value that pattern match (consts) does not contain
let nonCoveredEnumValues =
tcref.AllFieldsArray |> Array.tryFind (fun f ->
match f.rfield_const with
| None -> false
| Some fieldValue -> (not (consts.Contains fieldValue)) && f.rfield_static)
// We must distinguish between F#-defined enums and other .NET enums, as they are represented differently in the TAST
let enumValues =
if tcref.IsILEnumTycon then
let (TILObjectReprData(_, _, tdef)) = tcref.ILTyconInfo
tdef.Fields.AsList
|> Seq.choose (fun ilField ->
if ilField.IsStatic then
ilField.LiteralValue |> Option.map (fun ilValue ->
ilField.Name, ilFieldToTastConst ilValue)
else None)
else
tcref.AllFieldsArray |> Seq.choose (fun fsField ->
match fsField.rfield_const, fsField.rfield_static with
| Some fsFieldValue, true -> Some (fsField.rfield_id.idText, fsFieldValue)
| _ -> None)

let nonCoveredEnumValues = Seq.tryFind (fun (_, fldValue) -> not (consts.Contains fldValue)) enumValues

match nonCoveredEnumValues with
| None -> Expr.Const(c,m,ty), true
| Some f ->
let v = RecdFieldRef.RFRef(tcref, f.rfield_id.idText)
| Some (fldName, _) ->
let v = RecdFieldRef.RFRef(tcref, fldName)
Expr.Op(TOp.ValFieldGet v, [ty], [], m), false
| _ -> Expr.Const(c,m,ty), false

Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/PatternMatchCompilation.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

module internal Microsoft.FSharp.Compiler.PatternMatchCompilation

open Microsoft.FSharp.Compiler.AbstractIL.IL
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.Tastops
Expand Down Expand Up @@ -42,6 +43,8 @@ and PatternValBinding =
and TypedMatchClause =
| TClause of Pattern * Expr option * DecisionTreeTarget * range

val ilFieldToTastConst : ILFieldInit -> Tast.Const

/// Compile a pattern into a decision tree and a set of targets.
val internal CompilePattern :
TcGlobals ->
Expand Down
18 changes: 1 addition & 17 deletions src/fsharp/TypeChecker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -905,23 +905,7 @@ let TcConst cenv ty m env c =
| SynConst.Bytes _ -> error (InternalError(FSComp.SR.tcUnexpectedConstByteArray(), m))

/// Convert an Abstract IL ILFieldInit value read from .NET metadata to a TAST constant
let TcFieldInit (_m:range) lit =
match lit with
| ILFieldInit.String s -> Const.String s
| ILFieldInit.Null -> Const.Zero
| ILFieldInit.Bool b -> Const.Bool b
| ILFieldInit.Char c -> Const.Char (char (int c))
| ILFieldInit.Int8 x -> Const.SByte x
| ILFieldInit.Int16 x -> Const.Int16 x
| ILFieldInit.Int32 x -> Const.Int32 x
| ILFieldInit.Int64 x -> Const.Int64 x
| ILFieldInit.UInt8 x -> Const.Byte x
| ILFieldInit.UInt16 x -> Const.UInt16 x
| ILFieldInit.UInt32 x -> Const.UInt32 x
| ILFieldInit.UInt64 x -> Const.UInt64 x
| ILFieldInit.Single f -> Const.Single f
| ILFieldInit.Double f -> Const.Double f

let TcFieldInit (_m:range) lit = PatternMatchCompilation.ilFieldToTastConst lit

//-------------------------------------------------------------------------
// Arities. These serve two roles in the system:
Expand Down
8 changes: 6 additions & 2 deletions tests/fsharp/typecheck/sigs/neg102.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ neg102.fs(18,14,18,22): typecheck error FS0025: Incomplete pattern matches on th

neg102.fs(22,14,22,22): typecheck error FS0025: Incomplete pattern matches on this expression. For example, the value 'Some (EnumABC.C)' may indicate a case not covered by the pattern(s).

neg102.fs(29,14,29,22): typecheck error FS0104: Enums may take values outside known cases. For example, the value 'enum<EnumABC> (2)' may indicate a case not covered by the pattern(s).
neg102.fs(27,14,27,22): typecheck error FS0025: Incomplete pattern matches on this expression. For example, the value 'System.DateTimeKind.Utc' may indicate a case not covered by the pattern(s).

neg102.fs(34,14,34,22): typecheck error FS0104: Enums may take values outside known cases. For example, the value 'Some (enum<EnumABC> (2))' may indicate a case not covered by the pattern(s).
neg102.fs(32,14,32,22): typecheck error FS0104: Enums may take values outside known cases. For example, the value 'enum<EnumABC> (2)' may indicate a case not covered by the pattern(s).

neg102.fs(37,14,37,22): typecheck error FS0104: Enums may take values outside known cases. For example, the value 'Some (enum<EnumABC> (2))' may indicate a case not covered by the pattern(s).

neg102.fs(44,14,44,22): typecheck error FS0104: Enums may take values outside known cases. For example, the value 'enum<System.DateTimeKind> (3)' may indicate a case not covered by the pattern(s).
13 changes: 11 additions & 2 deletions tests/fsharp/typecheck/sigs/neg102.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type UnionAB = A | B

module FS0025 =
// All of these should emit warning FS0025 ("Incomplete pattern match....")


let f1 = function
| UnionAB.A -> "A"
Expand All @@ -23,6 +23,9 @@ module FS0025 =
| Some(EnumABC.A) | Some(EnumABC.B) -> "A|B"
| None -> "neither"

// try a non-F#-defined enum
let f6 = function System.DateTimeKind.Unspecified -> 0

module FS0104 =
// These should emit warning FS0104 ("Enums may take values outside of known cases....")

Expand All @@ -35,4 +38,10 @@ module FS0104 =
| Some(EnumABC.A) -> "A"
| Some(EnumABC.B) -> "B"
| Some(EnumABC.C) -> "C"
| None -> "none"
| None -> "none"

// try a non-F#-defined enum
let f3 = function
| System.DateTimeKind.Unspecified -> "Unspecified"
| System.DateTimeKind.Utc -> "Utc"
| System.DateTimeKind.Local -> "Local"