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
6 changes: 6 additions & 0 deletions fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
<Compile Include="$(FSharpSourcesRoot)/fsharp/sr.fs">
<Link>ErrorText/sr.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/fsharp/LanguageFeatures.fsi">
<Link>Driver\LanguageFeatures.fsi</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/fsharp/LanguageFeatures.fs">
<Link>Driver\LanguageFeatures.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/utils/prim-lexing.fsi">
<Link>LexYaccRuntime/prim-lexing.fsi</Link>
</Compile>
Expand Down
91 changes: 9 additions & 82 deletions src/fsharp/CompileOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ open FSharp.Compiler.AttributeChecking
open FSharp.Compiler.ConstraintSolver
open FSharp.Compiler.DiagnosticMessage
open FSharp.Compiler.ErrorLogger
open FSharp.Compiler.Features
open FSharp.Compiler.Import
open FSharp.Compiler.Infos
open FSharp.Compiler.Lexhelp
Expand Down Expand Up @@ -1735,7 +1736,7 @@ let CollectDiagnostic (implicitIncludeDir, showFullPaths, flattenErrors, errorSt
let os = System.Text.StringBuilder()
OutputPhasedDiagnostic os err flattenErrors suggestNames
errors.Add( Diagnostic.Short(isError, os.ToString()) )

relatedErrors |> List.iter OutputRelatedError

match err with
Expand Down Expand Up @@ -1834,82 +1835,6 @@ let ComputeMakePathAbsolute implicitIncludeDir (path: string) =
with
:? System.ArgumentException -> path


//------------------------------------------------------------------------------------------------------------------
// Language version command line switch
//------------------------------------------------------------------------------------------------------------------
// Add your features to this List - in code use languageVersion.SupportsFeature(LanguageFeatures.yourFeature)
// a return value of false means your feature is not supported by the user's language selection
// All new language features added from now on must be protected by this.
// Note:
// * The fslang design process will require a decision about feature name and whether it is required.
// * When a feature is assigned a release language, we will scrub the code of feature references and apply
// the Release Language version.

/// LanguageFeature enumeration
[<RequireQualifiedAccess>]
type LanguageFeature =
| LanguageVersion46 = 0
| LanguageVersion47 = 1
| Nullness = 1000
| ScriptingPackageManagement = 1001

/// LanguageVersion management
type LanguageVersion (specifiedVersion) =

// When we increment language versions here preview is higher than current RTM version
static let languageVersion46 = 4.6m
static let languageVersion47 = 4.7m

static let previewVersion = languageVersion47 // Language version when preview specified
static let defaultVersion = languageVersion46 // Language version when default specified
static let latestVersion = defaultVersion // Language version when latest specified
static let latestMajorVersion = languageVersion46 // Language version when latestmajor specified

static let validOptions = [| "preview"; "default"; "latest"; "latestmajor" |]
static let languageVersions = set [| latestVersion |]

static let features = dict [|
// Add new LanguageVersions here ...
LanguageFeature.LanguageVersion47, 4.7m
LanguageFeature.LanguageVersion46, 4.6m
LanguageFeature.Nullness, previewVersion
LanguageFeature.ScriptingPackageManagement, previewVersion
// Add new LanguageFeatures here ...
|]

static let dumpAllowedValues () =
printfn "%s" (FSComp.SR.optsSupportedLangVersions())
for v in validOptions do printfn "%s" v
for v in languageVersions |> Seq.sort do
let label = if v = defaultVersion || v = latestVersion then "(Default)" else ""
printf "%M %s" v label
exit 0
0m

let specified =
match specifiedVersion with
| "?" -> dumpAllowedValues()
| "preview" -> previewVersion
| "default" -> latestVersion
| "latest" -> latestVersion
| "latestmajor" -> latestMajorVersion
| _ ->
let raiseError () = error(Error(FSComp.SR.optsUnrecognizedLanguageVersion specifiedVersion, rangeCmdArgs))
match Decimal.TryParse(specifiedVersion) with
| true, v ->
if languageVersions.Contains(v) then v
else raiseError (); 0m
| _ ->
raiseError ()
0m

/// Check if this feature is supported by the selected langversion
member __.SupportsFeature featureId =
match features.TryGetValue featureId with
| true, v -> v <= specified
| false, _ -> false

//----------------------------------------------------------------------------
// Configuration
//----------------------------------------------------------------------------
Expand Down Expand Up @@ -3556,8 +3481,8 @@ let ParseOneInputFile (tcConfig: TcConfig, lexResourceManager, conditionalCompil
if List.exists (Filename.checkSuffix lower) (FSharpSigFileSuffixes@FSharpImplFileSuffixes) then
if not(FileSystem.SafeExists filename) then
error(Error(FSComp.SR.buildCouldNotFindSourceFile filename, rangeStartup))
// bug 3155: if the file name is indirect, use a full path
let lexbuf = UnicodeLexing.UnicodeFileAsLexbuf(filename, tcConfig.inputCodePage, retryLocked)
let isFeatureSupported featureId = tcConfig.langVersion.SupportsFeature featureId
let lexbuf = UnicodeLexing.UnicodeFileAsLexbuf(isFeatureSupported, filename, tcConfig.inputCodePage, retryLocked)
ParseOneInputLexbuf(tcConfig, lexResourceManager, conditionalCompilationDefines, lexbuf, filename, isLastCompiland, errorLogger)
else error(Error(FSComp.SR.buildInvalidSourceFileExtension(SanitizeFileName filename tcConfig.implicitIncludeDir), rangeStartup))
with e -> (* errorR(Failure("parse failed")); *) errorRecovery e rangeStartup; None
Expand Down Expand Up @@ -5080,11 +5005,13 @@ module private ScriptPreprocessClosure =
| CodeContext.CompilationAndEvaluation -> ["INTERACTIVE"]
| CodeContext.Compilation -> ["COMPILED"]
| CodeContext.Editing -> "EDITING" :: (if IsScript filename then ["INTERACTIVE"] else ["COMPILED"])
let lexbuf = UnicodeLexing.SourceTextAsLexbuf(sourceText)


let isFeatureSupported featureId = tcConfig.langVersion.SupportsFeature featureId
let lexbuf = UnicodeLexing.SourceTextAsLexbuf(isFeatureSupported, sourceText)

let isLastCompiland = (IsScript filename), tcConfig.target.IsExe // The root compiland is last in the list of compilands.
ParseOneInputLexbuf (tcConfig, lexResourceManager, defines, lexbuf, filename, isLastCompiland, errorLogger)

/// Create a TcConfig for load closure starting from a single .fsx file
let CreateScriptTextTcConfig
(legacyReferenceResolver, defaultFSharpBinariesDir,
Expand Down
14 changes: 1 addition & 13 deletions src/fsharp/CompileOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ open FSharp.Compiler.TypeChecker
open FSharp.Compiler.Range
open FSharp.Compiler.Ast
open FSharp.Compiler.ErrorLogger
open FSharp.Compiler.Features
open FSharp.Compiler.Tast
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Text
Expand Down Expand Up @@ -217,19 +218,6 @@ type ICompilationThread =
/// Enqueue work to be done on a compilation thread.
abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit

/// LanguageFeature enumeration
[<RequireQualifiedAccess>]
type LanguageFeature =
| LanguageVersion46 = 0
| LanguageVersion47 = 1
| Nullness = 1000
| ScriptingPackageManagement = 1001

/// LanguageVersion management
type LanguageVersion =
new: string -> LanguageVersion
member SupportsFeature: LanguageFeature-> bool

[<RequireQualifiedAccess>]
type CompilerTarget =
| WinExe
Expand Down
19 changes: 17 additions & 2 deletions src/fsharp/CompileOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Tast
open FSharp.Compiler.Tastops
open FSharp.Compiler.ErrorLogger
open FSharp.Compiler.Features
open FSharp.Compiler.Lib
open FSharp.Compiler.Range
open FSharp.Compiler.IlxGen
Expand Down Expand Up @@ -813,13 +814,27 @@ let codeGenerationFlags isFsi (tcConfigB: TcConfigBuilder) =
//----------------------

let defineSymbol tcConfigB s = tcConfigB.conditionalCompilationDefines <- s :: tcConfigB.conditionalCompilationDefines

let mlCompatibilityFlag (tcConfigB: TcConfigBuilder) =
CompilerOption
("mlcompatibility", tagNone,
OptionUnit (fun () -> tcConfigB.mlCompatibility<-true; tcConfigB.TurnWarningOff(rangeCmdArgs, "62")), None,
Some (FSComp.SR.optsMlcompatibility()))

/// LanguageVersion management
let setLanguageVersion (specifiedVersion) =

let languageVersion = new LanguageVersion(specifiedVersion)
let dumpAllowedValues () =
printfn "%s" (FSComp.SR.optsSupportedLangVersions())
for v in languageVersion.ValidOptions do printfn "%s" v
for v in languageVersion.ValidVersions do printfn "%s" v
exit 0

if specifiedVersion = "?" then dumpAllowedValues ()
if not (languageVersion.ContainsVersion specifiedVersion) then error(Error(FSComp.SR.optsUnrecognizedLanguageVersion specifiedVersion, rangeCmdArgs))
languageVersion

let languageFlags tcConfigB =
[
// -langversion:? Display the allowed values for language version
Expand All @@ -828,7 +843,7 @@ let languageFlags tcConfigB =
// 'latest' (latest version, including minor versions),
// 'preview' (features for preview)
// or specific versions like '4.7'
CompilerOption("langversion", tagLangVersionValues, OptionString (fun switch -> tcConfigB.langVersion <- LanguageVersion(switch)), None, Some (FSComp.SR.optsLangVersion()))
CompilerOption("langversion", tagLangVersionValues, OptionString (fun switch -> tcConfigB.langVersion <- setLanguageVersion(switch)), None, Some (FSComp.SR.optsLangVersion()))

CompilerOption("checked", tagNone, OptionSwitch (fun switch -> tcConfigB.checkOverflow <- (switch = OptionSwitch.On)), None, Some (FSComp.SR.optsChecked()))
CompilerOption("define", tagString, OptionString (defineSymbol tcConfigB), None, Some (FSComp.SR.optsDefine()))
Expand Down
1 change: 1 addition & 0 deletions src/fsharp/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ parsAttributesMustComeBeforeVal,"Attributes should be placed before 'val'"
568,parsAllEnumFieldsRequireValues,"All enum fields must be given values"
569,parsInlineAssemblyCannotHaveVisibilityDeclarations,"Accessibility modifiers are not permitted on inline assembly code types"
571,parsUnexpectedIdentifier,"Unexpected identifier: '%s'"
10,parsUnexpectedSymbolDot,"Unexpected symbol '.' in member definition. Expected 'with', '=' or other token."

@TIHan TIHan Jun 4, 2019 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is interesting that we have to put this kind of error message in FSComp. Before, we relied on what the parser would give us for the error. But since we are adding logic for language version, we have no choice but to copy the same error message that the parser would create, and then create a new resource in FSComp in order to raise the same error message in conjunction with language version. While this solves the problem for the language version, there is now potential for error text to be out of sync if the parser decides to change the text for the same kind of error in a different rule. Though, in this case, it is unlikely to change anytime soon.

While we do have other parser errors in FSComp, I don't think we have errors with exact text of what the parser would normally produce on its own; we only have specific parse errors that try to be more descriptive for the user.

This is sort of a code smell in my opinion, but not a big deal. It isn't necessarily due to the language version flag itself; this problem will exist if you try to preserve old parser logic and errors behind any kind of flag. However, I only think a flag would be absolutely necessary if we change the syntax in such a way that broke existing code; which that isn't even the purpose of the language version flag anyway or something that we ever wish to do.

572,parsUnionCasesCannotHaveVisibilityDeclarations,"Accessibility modifiers are not permitted on union cases. Use 'type U = internal ...' or 'type U = private ...' to give an accessibility to the whole representation."
573,parsEnumFieldsCannotHaveVisibilityDeclarations,"Accessibility modifiers are not permitted on enumeration fields"
parsConsiderUsingSeparateRecordType,"Consider using a separate record type instead"
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/FSharp.Build/Fsc.fs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ type public Fsc () as this =
for item in embeddedFiles do
builder.AppendSwitchIfNotNull("--embed:", item.ItemSpec)
builder.AppendSwitchIfNotNull("--sourcelink:", sourceLink)
builder.AppendSwitchIfNotNull("--langVersion:", langVersion)
builder.AppendSwitchIfNotNull("--langversion:", langVersion)
// NoFramework
if noFramework then
builder.AppendSwitch("--noframework")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@
<Compile Include="..\sr.fs">
<Link>ErrorText\sr.fs</Link>
</Compile>
<Compile Include="..\LanguageFeatures.fsi">
<Link>Driver\LanguageFeatures.fsi</Link>
</Compile>
<Compile Include="..\LanguageFeatures.fs">
<Link>Driver\LanguageFeatures.fs</Link>
</Compile>
<Compile Include="..\..\utils\prim-lexing.fsi">
<Link>LexYaccRuntime\prim-lexing.fsi</Link>
</Compile>
Expand Down
90 changes: 90 additions & 0 deletions src/fsharp/LanguageFeatures.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

/// Coordinating compiler operations - configuration, loading initial context, reporting errors etc.
module internal FSharp.Compiler.Features

open System

//------------------------------------------------------------------------------------------------------------------
// Language version command line switch
//------------------------------------------------------------------------------------------------------------------
// Add your features to this List - in code use languageVersion.SupportsFeature(LanguageFeatures.yourFeature)
// a return value of false means your feature is not supported by the user's language selection
// All new language features added from now on must be protected by this.
// Note:
// * The fslang design process will require a decision about feature name and whether it is required.
// * When a feature is assigned a release language, we will scrub the code of feature references and apply
// the Release Language version.

/// LanguageFeature enumeration
[<RequireQualifiedAccess>]
type LanguageFeature =
| LanguageVersion46 = 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really a language feature? Wouldn't it be better as a separate enum for language versions? It could be quite straightforward to check if a feature supported by a language version.

val langFeatureSupported: langVersion -> langFeature -> bool

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@auduchinok,

There are two audiences … compiler users are focused on language version. Thus the langversion switch. Feature developers are focused on their feature. Until a feature is confirmed for a specific language version, it is assigned to the version beyond latest. The developer merely asks if their feature is supported by the specified LanguageVersion, and invokes the feature code or the old version failure path.

When a feature is approved for an RTM language version, the code will be modified to check for that specific language version and the feature flag will disappear. We will do this to keep the compatibility matrix from blowing our minds. The roslyn guys tell me this is what they do too, because no one was happy with enabling individual features.

| LanguageVersion47 = 1
| SingleUnderscorePattern = 2
| Nullness = 1000
| ScriptingPackageManagement = 1001

/// LanguageVersion management
type LanguageVersion (specifiedVersion) =

// When we increment language versions here preview is higher than current RTM version
static let languageVersion46 = 4.6m

@auduchinok auduchinok May 31, 2019 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that using numbers is better than enum here.
Numbers could be good as an underlying type inside some LanguageVersion type and wouldn't be exposed this way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we do a numeric comparison on the number, to figure out if the version is supported or not. Sure we could compare enum ordinals, but it's not really much of a thing.

static let languageVersion47 = 4.7m

static let previewVersion = languageVersion47 // Language version when preview specified
static let defaultVersion = languageVersion46 // Language version when default specified
static let latestVersion = defaultVersion // Language version when latest specified
static let latestMajorVersion = languageVersion46 // Language version when latestmajor specified

static let validOptions = [| "preview"; "default"; "latest"; "latestmajor" |]
static let languageVersions = set [| latestVersion |]

static let features = dict [|
// Add new LanguageVersions here ...
LanguageFeature.LanguageVersion47, 4.7m
LanguageFeature.LanguageVersion46, 4.6m
LanguageFeature.Nullness, previewVersion
LanguageFeature.ScriptingPackageManagement, previewVersion
LanguageFeature.SingleUnderscorePattern, previewVersion

// Add new LanguageFeatures here ...
|]

let specified =
match specifiedVersion with
| "?" -> 0m
| "preview" -> previewVersion
| "default" -> latestVersion
| "latest" -> latestVersion
| "latestmajor" -> latestMajorVersion
| _ ->
match Decimal.TryParse(specifiedVersion) with
| true, v -> v
| _ -> 0m

/// Check if this feature is supported by the selected langversion
member __.SupportsFeature featureId =
match features.TryGetValue featureId with
| true, v -> v <= specified
| false, _ -> false

/// Does the languageVersion support this version string
member __.ContainsVersion version =
match version with
| "?" | "preview" | "default" | "latest" | "latestmajor" -> true
| _ ->
match Decimal.TryParse(specifiedVersion) with
| true, v -> languageVersions.Contains v
| _ -> false

/// Get a list of valid strings for help text
member __.ValidOptions = validOptions

/// Get a list of valid versions for help text
member __.ValidVersions = [|
for v in languageVersions |> Seq.sort do
let label = if v = defaultVersion || v = latestVersion then "(Default)" else ""
yield sprintf "%M %s" v label
|]

31 changes: 31 additions & 0 deletions src/fsharp/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

/// Coordinating compiler operations - configuration, loading initial context, reporting errors etc.
module internal FSharp.Compiler.Features

/// LanguageFeature enumeration
[<RequireQualifiedAccess>]
type LanguageFeature =
| LanguageVersion46 = 0
| LanguageVersion47 = 1
| SingleUnderscorePattern = 2
| Nullness = 1000
| ScriptingPackageManagement = 1001

/// LanguageVersion management
type LanguageVersion =

/// Create a LanguageVersion management object
new: string -> LanguageVersion

/// Get the list of valid versions
member ContainsVersion: string -> bool

/// Does the specified LanguageVersion support the specified feature
member SupportsFeature: LanguageFeature -> bool

/// Get the list of valid versions
member ValidVersions: string array

/// Get the list of valid options
member ValidOptions: string array
Loading