Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support constraint intersection syntax #15413

Merged
merged 16 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 2 additions & 2 deletions src/Compiler/Checking/CheckDeclarations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2536,7 +2536,7 @@ module EstablishTypeDefinitionCores =
suppressErrorReporting (fun () ->
synTypars|> List.forall (fun synTypar ->
try
let (SynTyparDecl(Attributes synAttrs, _)) = synTypar
let (SynTyparDecl (attributes = Attributes synAttrs)) = synTypar
let attrs = TcAttributes cenv env AttributeTargets.GenericParameter synAttrs
HasFSharpAttribute cenv.g cenv.g.attrib_MeasureAttribute attrs
with _ -> false))
Expand Down Expand Up @@ -3954,7 +3954,7 @@ module TcDeclarations =
if tcref.TyparsNoRange.Length = synTypars.Length then
(tcref.TyparsNoRange, synTypars)
||> List.zip
|> List.iter (fun (typar, SynTyparDecl.SynTyparDecl(_, SynTypar(ident = untypedIdent))) ->
|> List.iter (fun (typar, SynTyparDecl.SynTyparDecl (typar = SynTypar (ident = untypedIdent))) ->
typar.SetIdent(untypedIdent)
)

Expand Down
30 changes: 28 additions & 2 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4300,8 +4300,8 @@ and TcTypar (cenv: cenv) env newOk tpenv tp : Typar * UnscopedTyparEnv =

and TcTyparDecl (cenv: cenv) env synTyparDecl =
let g = cenv.g
let (SynTyparDecl(Attributes synAttrs, synTypar)) = synTyparDecl
let (SynTypar(id, _, _)) = synTypar
let (SynTyparDecl (attributes = Attributes synAttrs; typar = synTypar)) = synTyparDecl
let (SynTypar (ident = id)) = synTypar

let attrs = TcAttributes cenv env AttributeTargets.GenericParameter synAttrs
let hasMeasureAttr = HasFSharpAttribute g g.attrib_MeasureAttribute attrs
Expand Down Expand Up @@ -4377,6 +4377,9 @@ and TcTypeOrMeasure kindOpt (cenv: cenv) newOk checkConstraints occ (iwsam: Warn
| SynType.HashConstraint(synInnerTy, m) ->
TcTypeHashConstraint cenv env newOk checkConstraints occ tpenv synInnerTy m

| SynType.Intersection (tp, tys, m, _) ->
TcIntersectionConstraint cenv env newOk checkConstraints occ tpenv tp tys m

| SynType.StaticConstant (synConst, m) ->
TcTypeStaticConstant kindOpt tpenv synConst m

Expand Down Expand Up @@ -4562,6 +4565,29 @@ and TcTypeHashConstraint (cenv: cenv) env newOk checkConstraints occ tpenv synTy
AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace ty (mkTyparTy tp)
tp.AsType, tpenv

// (x: 't & #I1 & #I2)
// (x: #I1 & #I2)
and TcIntersectionConstraint (cenv: cenv) env newOk checkConstraints occ tpenv synTypar synTys m =
let tp, tpenv =
match synTypar with
| Some synTypar -> TcTypeOrMeasureParameter (Some TyparKind.Type) cenv env newOk tpenv synTypar
| _ -> TcAnonTypeOrMeasure (Some TyparKind.Type) cenv TyparRigidity.WarnIfNotRigid TyparDynamicReq.Yes newOk m, tpenv

let typarTy = mkTyparTy tp

let tpenv =
synTys
|> List.fold (fun tpenv ty ->
match ty with
| SynType.HashConstraint (ty, m) ->
let ty, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ WarnOnIWSAM.No env tpenv ty
AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace ty typarTy
tpenv
| _ -> tpenv
) tpenv

tp.AsType, tpenv

and TcTypeStaticConstant kindOpt tpenv c m =
match c, kindOpt with
| _, Some TyparKind.Type ->
Expand Down
6 changes: 5 additions & 1 deletion src/Compiler/Driver/GraphChecking/FileContentMapping.fs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ let visitSynType (t: SynType) : FileContentEntry list =
let continuations = List.map visit [ lhsType; rhsType ]
Continuation.concatenate continuations continuation
| SynType.FromParseError _ -> continuation []
| SynType.Intersection (types = types) ->
let continuations = List.map visit types
Continuation.concatenate continuations continuation

visit t id

Expand All @@ -277,7 +280,8 @@ let visitSynTyparDecls (td: SynTyparDecls) : FileContentEntry list =
| SynTyparDecls.PrefixList (decls = decls) -> List.collect visitSynTyparDecl decls
| SynTyparDecls.SinglePrefix (decl = decl) -> visitSynTyparDecl decl

let visitSynTyparDecl (SynTyparDecl (attributes = attributes)) = visitSynAttributes attributes
let visitSynTyparDecl (SynTyparDecl (attributes = attributes; intersectionConstraints = constraints)) =
visitSynAttributes attributes @ List.collect visitSynType constraints

let visitSynTypeConstraint (tc: SynTypeConstraint) : FileContentEntry list =
[
Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,7 @@ featureExtendedStringInterpolation,"Extended string interpolation similar to C#
featureWarningWhenMultipleRecdTypeChoice,"Raises warnings when multiple record type matches were found during name resolution because of overlapping field names."
featureImprovedImpliedArgumentNames,"Improved implied argument names"
featureStrictIndentation,"Raises errors on incorrect indentation, allows better recovery and analysis during editing"
featureConstraintIntersectionOnFlexibleTypes,"Constraint intersection on flexible types"
3353,fsiInvalidDirective,"Invalid directive '#%s %s'"
3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
Expand Down Expand Up @@ -1697,4 +1698,5 @@ featureEscapeBracesInFormattableString,"Escapes curly braces before calling Form
3565,parsExpectingType,"Expecting type"
featureInformationalObjInferenceDiagnostic,"Diagnostic 3559 (warn when obj inferred) at informational level, off by default"
3566,tcMultipleRecdTypeChoice,"Multiple type matches were found:\n%s\nThe type '%s' was used. Due to the overlapping field names\n%s\nconsider using type annotations or change the order of open statements."
3567,parsMissingMemberBody,"Expecting member body"
3567,parsMissingMemberBody,"Expecting member body"
3568,parsConstraintIntersectionSyntaxUsedWithNonFlexibleType,"Constraint intersection syntax may only be used with flexible types, e.g. '#IDisposable & #ISomeInterface'."
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type LanguageFeature =
| WarningWhenMultipleRecdTypeChoice
| ImprovedImpliedArgumentNames
| DiagnosticForObjInference
| ConstraintIntersectionOnFlexibleTypes

/// LanguageVersion management
type LanguageVersion(versionText) =
Expand Down Expand Up @@ -165,6 +166,7 @@ type LanguageVersion(versionText) =
LanguageFeature.ImprovedImpliedArgumentNames, previewVersion
LanguageFeature.DiagnosticForObjInference, previewVersion
LanguageFeature.StrictIndentation, previewVersion
LanguageFeature.ConstraintIntersectionOnFlexibleTypes, previewVersion

]

Expand Down Expand Up @@ -291,6 +293,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.ImprovedImpliedArgumentNames -> FSComp.SR.featureImprovedImpliedArgumentNames ()
| LanguageFeature.DiagnosticForObjInference -> FSComp.SR.featureInformationalObjInferenceDiagnostic ()
| LanguageFeature.StrictIndentation -> FSComp.SR.featureStrictIndentation ()
| LanguageFeature.ConstraintIntersectionOnFlexibleTypes -> FSComp.SR.featureConstraintIntersectionOnFlexibleTypes ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type LanguageFeature =
| WarningWhenMultipleRecdTypeChoice
| ImprovedImpliedArgumentNames
| DiagnosticForObjInference
| ConstraintIntersectionOnFlexibleTypes

/// LanguageVersion management
type LanguageVersion =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Service/ServiceParseTreeWalk.fs
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,7 @@ module SyntaxTraversal =
| SynType.StaticConstantExpr (expr, _) -> traverseSynExpr [] expr
| SynType.Paren (innerType = t)
| SynType.SignatureParameter (usedType = t) -> traverseSynType path t
| SynType.Intersection (types = types) -> List.tryPick (traverseSynType path) types
| SynType.Anon _
| SynType.AnonRecd _
| SynType.LongIdent _
Expand Down
8 changes: 6 additions & 2 deletions src/Compiler/Service/ServiceParsedInputOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -599,10 +599,11 @@ module ParsedInput =
ifPosInRange ident.idRange (fun _ -> Some EntityKind.Type)

and walkTyparDecl typarDecl =
let (SynTyparDecl (Attributes attrs, typar)) = typarDecl
let (SynTyparDecl (Attributes attrs, typar, intersectionContraints, _)) = typarDecl

List.tryPick walkAttribute attrs
|> Option.orElseWith (fun () -> walkTypar typar)
|> Option.orElseWith (fun () -> intersectionContraints |> List.tryPick walkType)

and walkTypeConstraint cx =
match cx with
Expand Down Expand Up @@ -681,6 +682,7 @@ module ParsedInput =
| SynType.SignatureParameter (usedType = t) -> walkType t
| SynType.StaticConstantExpr (e, _) -> walkExpr e
| SynType.StaticConstantNamed (ident, value, _) -> List.tryPick walkType [ ident; value ]
| SynType.Intersection (types = types) -> List.tryPick walkType types
| SynType.Anon _
| SynType.AnonRecd _
| SynType.LongIdent _
Expand Down Expand Up @@ -1609,9 +1611,10 @@ module ParsedInput =
addLongIdentWithDots attr.TypeName
walkExpr attr.ArgExpr

and walkTyparDecl (SynTyparDecl.SynTyparDecl (Attributes attrs, typar)) =
and walkTyparDecl (SynTyparDecl.SynTyparDecl (Attributes attrs, typar, intersectionConstraints, _)) =
List.iter walkAttribute attrs
walkTypar typar
List.iter walkType intersectionConstraints

and walkTypeConstraint cx =
match cx with
Expand Down Expand Up @@ -1699,6 +1702,7 @@ module ParsedInput =
| SynType.StaticConstantNamed (ident, value, _) ->
walkType ident
walkType value
| SynType.Intersection (types = types) -> List.iter walkType types
| SynType.Anon _
| SynType.AnonRecd _
| SynType.Var _
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Service/ServiceStructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module Structure =
| [] -> other
| ls ->
ls
|> List.map (fun (SynTyparDecl (_, typarg)) -> typarg.Range)
|> List.map (fun (SynTyparDecl (typar = typarg)) -> typarg.Range)
|> List.reduce unionRanges

/// Collapse indicates the way a range/snapshot should be collapsed. `Same` is for a scope inside
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/SyntaxTree/LexFilter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ type LexFilterImpl (
// f<{| C : int |}>x
// f<x # x>x
// f<x ' x>x
| DEFAULT | COLON | COLON_GREATER | STRUCT | NULL | DELEGATE | AND | WHEN
| DEFAULT | COLON | COLON_GREATER | STRUCT | NULL | DELEGATE | AND | WHEN | AMP
| DOT_DOT
| NEW
| LBRACE_BAR
Expand Down
25 changes: 22 additions & 3 deletions src/Compiler/SyntaxTree/SyntaxTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ type SynBindingKind =
| Do

[<NoEquality; NoComparison>]
type SynTyparDecl = SynTyparDecl of attributes: SynAttributes * SynTypar
type SynTyparDecl =
| SynTyparDecl of attributes: SynAttributes * typar: SynTypar * intersectionConstraints: SynType list * trivia: SynTyparDeclTrivia

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type SynTypeConstraint =
Expand Down Expand Up @@ -367,13 +368,28 @@ type SynTyparDecls =

member x.Constraints =
match x with
| PostfixList (constraints = constraints) -> constraints
| PostfixList (decls = decls; constraints = constraints) ->
// Synthesize SynTypeConstraints implied with any intersection constraints in SynTyparDecl
// The parser makes sure we're only dealing with hash constraints here
let intersectionConstraints =
decls
|> List.collect (fun (SynTyparDecl (typar = tp; intersectionConstraints = tys)) ->
tys
|> List.map (fun ty ->
let ty =
match ty with
| SynType.HashConstraint (ty, _) -> ty
| _ -> ty

SynTypeConstraint.WhereTyparSubtypeOfType(tp, ty, ty.Range)))

List.append intersectionConstraints constraints
| _ -> []

member x.Range =
match x with
| PostfixList (range = range)
| PrefixList (range = range) -> range
| PrefixList (range = range)
| SinglePrefix (range = range) -> range

[<NoEquality; NoComparison; RequireQualifiedAccess>]
Expand Down Expand Up @@ -443,6 +459,8 @@ type SynType =

| FromParseError of range: range

| Intersection of typar: SynTypar option * types: SynType list * range: range * trivia: SynTypeIntersectionTrivia

member x.Range =
match x with
| SynType.App (range = m)
Expand All @@ -462,6 +480,7 @@ type SynType =
| SynType.Paren (range = m)
| SynType.SignatureParameter (range = m)
| SynType.Or (range = m)
| SynType.Intersection (range = m)
| SynType.FromParseError (range = m) -> m
| SynType.LongIdent lidwd -> lidwd.Range

Expand Down
12 changes: 11 additions & 1 deletion src/Compiler/SyntaxTree/SyntaxTree.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,12 @@ type SynBindingKind =

/// Represents the explicit declaration of a type parameter
[<NoEquality; NoComparison>]
type SynTyparDecl = SynTyparDecl of attributes: SynAttributes * SynTypar
type SynTyparDecl =
| SynTyparDecl of
attributes: SynAttributes *
typar: SynTypar *
intersectionConstraints: SynType list *
trivia: SynTyparDeclTrivia
0101 marked this conversation as resolved.
Show resolved Hide resolved

/// The unchecked abstract syntax tree of F# type constraints
[<NoEquality; NoComparison; RequireQualifiedAccess>]
Expand Down Expand Up @@ -521,6 +526,11 @@ type SynType =
/// A type arising from a parse error
| FromParseError of range: range

/// F# syntax: x: #I1 & #I2
/// F# syntax: x: 't & #I1 & #I2
/// Shorthand for x: 't when 't :> I1 and 't :> I2
| Intersection of typar: SynTypar option * types: SynType list * range: range * trivia: SynTypeIntersectionTrivia

/// Gets the syntax range of this construct
member Range: range

Expand Down
11 changes: 11 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTrivia.fs
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,14 @@ type SynMemberSigMemberTrivia =
}

static member Zero: SynMemberSigMemberTrivia = { GetSetKeywords = None }

[<NoEquality; NoComparison>]
type SynTypeIntersectionTrivia = { AmpersandRanges: range list }

[<NoEquality; NoComparison>]
type SynTyparDeclTrivia =
{
AmpersandRanges: range list
}

static member Zero: SynTyparDeclTrivia = { AmpersandRanges = [] }
18 changes: 18 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTrivia.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,21 @@ type SynMemberSigMemberTrivia =
}

static member Zero: SynMemberSigMemberTrivia

/// Represents additional information for SynType.Intersection
[<NoEquality; NoComparison>]
type SynTypeIntersectionTrivia =
{
/// The syntax ranges of the `&` tokens
AmpersandRanges: range list
}

/// Represents additional information for SynTyparDecl
[<NoEquality; NoComparison>]
type SynTyparDeclTrivia =
{
/// The syntax ranges of the `&` tokens
AmpersandRanges: range list
}

static member Zero: SynTyparDeclTrivia
Loading
Loading