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
46 changes: 30 additions & 16 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4538,8 +4538,8 @@ and TcTypeOrMeasure kindOpt cenv newOk checkConstraints occ env (tpenv: Unscoped
| SynType.LongIdentApp (synLeftTy, synLongId, _, args, _commas, _, m) ->
TcNestedAppType cenv newOk checkConstraints occ env tpenv synLeftTy synLongId args m

| SynType.Tuple(isStruct, args, m) ->
TcTupleType kindOpt cenv newOk checkConstraints occ env tpenv isStruct args m
| SynType.Tuple(isStruct, segments, m) ->
TcTupleType kindOpt cenv newOk checkConstraints occ env tpenv isStruct segments m

| SynType.AnonRecd(_, [],m) ->
error(Error((FSComp.SR.tcAnonymousTypeInvalidInDeclaration()), m))
Expand Down Expand Up @@ -4649,8 +4649,7 @@ and TcNestedAppType cenv newOk checkConstraints occ env tpenv synLeftTy synLongI
| _ ->
error(Error(FSComp.SR.tcTypeHasNoNestedTypes(), m))

and TcTupleType kindOpt cenv newOk checkConstraints occ env tpenv isStruct args m =

and TcTupleType kindOpt cenv newOk checkConstraints occ env tpenv isStruct (args: SynTupleTypeSegment list) m =
let tupInfo = mkTupInfo isStruct
if isStruct then
let argsR,tpenv = TcTypesAsTuple cenv newOk checkConstraints occ env tpenv args m
Expand All @@ -4659,8 +4658,9 @@ and TcTupleType kindOpt cenv newOk checkConstraints occ env tpenv isStruct args
let isMeasure =
match kindOpt with
| Some TyparKind.Measure -> true
| None -> List.exists (fun (isquot,_) -> isquot) args | _ -> false

| None -> args |> List.exists(function | SynTupleTypeSegment.Slash _ -> true | _ -> false)
| Some _ -> false

if isMeasure then
let ms,tpenv = TcMeasuresAsTuple cenv newOk checkConstraints occ env tpenv args m
TType_measure ms,tpenv
Expand All @@ -4670,7 +4670,7 @@ and TcTupleType kindOpt cenv newOk checkConstraints occ env tpenv isStruct args

and TcAnonRecdType cenv newOk checkConstraints occ env tpenv isStruct args m =
let tupInfo = mkTupInfo isStruct
let tup = args |> List.map snd |> List.map (fun x -> (false, x))
let tup = args |> List.map (fun (_, t) -> SynTupleTypeSegment.Type t)
let argsR,tpenv = TcTypesAsTuple cenv newOk checkConstraints occ env tpenv tup m
let unsortedFieldIds = args |> List.map fst |> List.toArray
let anonInfo = AnonRecdTypeInfo.Create(cenv.thisCcu, tupInfo, unsortedFieldIds)
Expand Down Expand Up @@ -4808,25 +4808,39 @@ and TcAnonTypeOrMeasure kindOpt _cenv rigid dyn newOk m =
and TcTypes cenv newOk checkConstraints occ env tpenv args =
List.mapFold (TcTypeAndRecover cenv newOk checkConstraints occ env) tpenv args

and TcTypesAsTuple cenv newOk checkConstraints occ env tpenv args m =
and TcTypesAsTuple cenv newOk checkConstraints occ env tpenv (args: SynTupleTypeSegment list) m =
let hasASlash =
args
|> List.exists(function | SynTupleTypeSegment.Slash _ -> true | _ -> false)

if hasASlash then errorR(Error(FSComp.SR.tcUnexpectedSlashInType(), m))

let args : SynType list = getTypeFromTuplePath args
match args with
| [] -> error(InternalError("empty tuple type", m))
| [(_, ty)] -> let ty, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ env tpenv ty in [ty], tpenv
| (isquot, ty) :: args ->
| [ty] -> let ty, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ env tpenv ty in [ty], tpenv
| ty :: args ->
let ty, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ env tpenv ty
let args = List.map SynTupleTypeSegment.Type args
let tys, tpenv = TcTypesAsTuple cenv newOk checkConstraints occ env tpenv args m
if isquot then errorR(Error(FSComp.SR.tcUnexpectedSlashInType(), m))
ty :: tys, tpenv

// Type-check a list of measures separated by juxtaposition, * or /
and TcMeasuresAsTuple cenv newOk checkConstraints occ env (tpenv: UnscopedTyparEnv) args m =
let rec gather args tpenv isquot acc =
and TcMeasuresAsTuple cenv newOk checkConstraints occ env (tpenv: UnscopedTyparEnv) (args: SynTupleTypeSegment list) m =
let rec gather (args: SynTupleTypeSegment list) tpenv acc =
match args with
| [] -> acc, tpenv
| (nextisquot, ty) :: args ->
| SynTupleTypeSegment.Type ty :: args ->
let ms1, tpenv = TcMeasure cenv newOk checkConstraints occ env tpenv ty m
gather args tpenv ms1
| SynTupleTypeSegment.Star _ :: SynTupleTypeSegment.Type ty :: args ->
let ms1, tpenv = TcMeasure cenv newOk checkConstraints occ env tpenv ty m
gather args tpenv (Measure.Prod(acc, ms1))
| SynTupleTypeSegment.Slash _ :: SynTupleTypeSegment.Type ty :: args ->
let ms1, tpenv = TcMeasure cenv newOk checkConstraints occ env tpenv ty m
gather args tpenv nextisquot (if isquot then Measure.Prod(acc, Measure.Inv ms1) else Measure.Prod(acc, ms1))
gather args tpenv false Measure.One
gather args tpenv (Measure.Prod(acc, Measure.Inv ms1))
| _ -> failwith "inpossible"
gather args tpenv Measure.One

and TcTypesOrMeasures optKinds cenv newOk checkConstraints occ env tpenv args m =
match optKinds with
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Service/ServiceParseTreeWalk.fs
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ module SyntaxTraversal =
| SynType.Array (_, ty, _) -> traverseSynType path ty
| SynType.StaticConstantNamed (ty1, ty2, _)
| SynType.MeasureDivide (ty1, ty2, _) -> [ ty1; ty2 ] |> List.tryPick (traverseSynType path)
| SynType.Tuple (_, tys, _) -> tys |> List.map snd |> List.tryPick (traverseSynType path)
| SynType.Tuple (path = segments) -> getTypeFromTuplePath segments |> List.tryPick (traverseSynType path)
| SynType.StaticConstantExpr (expr, _) -> traverseSynExpr [] expr
| SynType.Anon _ -> None
| _ -> None
Expand Down
4 changes: 2 additions & 2 deletions src/Compiler/Service/ServiceParsedInputOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ module ParsedInput =
None
| SynType.App (ty, _, types, _, _, _, _) -> walkType ty |> Option.orElseWith (fun () -> List.tryPick walkType types)
| SynType.LongIdentApp (_, _, _, types, _, _, _) -> List.tryPick walkType types
| SynType.Tuple (_, ts, _) -> ts |> List.tryPick (fun (_, t) -> walkType t)
| SynType.Tuple (path = segments) -> getTypeFromTuplePath segments |> List.tryPick walkType
| SynType.Array (_, t, _) -> walkType t
| SynType.Fun (argType = t1; returnType = t2) -> walkType t1 |> Option.orElseWith (fun () -> walkType t2)
| SynType.WithGlobalConstraints (t, _, _) -> walkType t
Expand Down Expand Up @@ -1669,7 +1669,7 @@ module ParsedInput =
walkType ty
List.iter walkType types
| SynType.LongIdentApp (_, _, _, types, _, _, _) -> List.iter walkType types
| SynType.Tuple (_, ts, _) -> ts |> List.iter (fun (_, t) -> walkType t)
| SynType.Tuple (path = segment) -> getTypeFromTuplePath segment |> List.iter walkType
| SynType.WithGlobalConstraints (t, typeConstraints, _) ->
walkType t
List.iter walkTypeConstraint typeConstraints
Expand Down
12 changes: 12 additions & 0 deletions src/Compiler/SyntaxTree/ParseHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -811,3 +811,15 @@ let mkSynMemberDefnGetSet
[]
| _ -> []
| _ -> []

// The last element of elementTypes does not have a star or slash
let mkSynTypeTuple (isStruct: bool) (elementTypes: SynTupleTypeSegment list) : SynType =
let range =
match elementTypes with
| [] -> Range.Zero
| head :: tail ->

(head.Range, tail)
||> List.fold (fun acc segment -> unionRanges acc segment.Range)

SynType.Tuple(isStruct, elementTypes, range)
2 changes: 2 additions & 0 deletions src/Compiler/SyntaxTree/ParseHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,5 @@ val mkSynMemberDefnGetSet:
attrs: SynAttributeList list ->
rangeStart: range ->
SynMemberDefn list

val mkSynTypeTuple: isStruct: bool -> elementTypes: SynTupleTypeSegment list -> SynType
14 changes: 13 additions & 1 deletion src/Compiler/SyntaxTree/SyntaxTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@ type SynTyparDecls =
| PrefixList (range = range) -> range
| SinglePrefix (range = range) -> range

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type SynTupleTypeSegment =
| Type of typeName: SynType
| Star of range: range
| Slash of range: range

member this.Range =
match this with
| SynTupleTypeSegment.Type t -> t.Range
| SynTupleTypeSegment.Star (range = range)
| SynTupleTypeSegment.Slash (range = range) -> range

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type SynType =

Expand All @@ -392,7 +404,7 @@ type SynType =
greaterRange: range option *
range: range

| Tuple of isStruct: bool * elementTypes: (bool * SynType) list * range: range
| Tuple of isStruct: bool * path: SynTupleTypeSegment list * range: range

| AnonRecd of isStruct: bool * fields: (Ident * SynType) list * range: range

Expand Down
14 changes: 9 additions & 5 deletions src/Compiler/SyntaxTree/SyntaxTree.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,14 @@ type SynTyparDecls =
member Constraints: SynTypeConstraint list
member Range: range

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type SynTupleTypeSegment =
| Type of typeName: SynType
| Star of range: range
| Slash of range: range

member Range: range

/// Represents a syntax tree for F# types
[<NoEquality; NoComparison; RequireQualifiedAccess>]
type SynType =
Expand Down Expand Up @@ -457,11 +465,7 @@ type SynType =

/// F# syntax: type * ... * type
/// F# syntax: struct (type * ... * type)
| Tuple of
// the bool is true if / rather than * follows the type
isStruct: bool *
elementTypes: (bool * SynType) list *
range: range
| Tuple of isStruct: bool * path: SynTupleTypeSegment list * range: range

/// F# syntax: {| id: type; ...; id: type |}
/// F# syntax: struct {| id: type; ...; id: type |}
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,3 +1017,9 @@ let rec desugarGetSetMembers (memberDefns: SynMemberDefns) =
let members = Option.map desugarGetSetMembers members
[ SynMemberDefn.Interface(interfaceType, withKeyword, members, m) ]
| md -> [ md ])

let getTypeFromTuplePath (path: SynTupleTypeSegment list) : SynType list =
path
|> List.choose (function
| SynTupleTypeSegment.Type t -> Some t
| _ -> None)
2 changes: 2 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTreeOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,5 @@ val mkDynamicArgExpr: expr: SynExpr -> SynExpr
val normalizeTupleExpr: exprs: SynExpr list -> commas: range list -> SynExpr list * range List

val desugarGetSetMembers: memberDefns: SynMemberDefns -> SynMemberDefns

val getTypeFromTuplePath: path: SynTupleTypeSegment list -> SynType list
40 changes: 29 additions & 11 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -5029,17 +5029,23 @@ topType:

topTupleType:
| topAppType STAR topTupleTypeElements
{ let ty, mdata = $1 in let tys, mdatas = List.unzip $3 in (SynType.Tuple(false, List.map (fun ty -> (false, ty)) (ty :: tys), lhs parseState)), (mdata :: mdatas) }
{ let t, argInfo = $1
let path = SynTupleTypeSegment.Type t :: (List.map fst $3)
let mdata = argInfo :: (List.choose snd $3)
mkSynTypeTuple false path, mdata }

| topAppType
{ let ty, mdata = $1 in ty, [mdata] }

topTupleTypeElements:
| topAppType STAR topTupleTypeElements
{ $1 :: $3 }
{ let t, argInfo = $1
let mStar = rhs parseState 2
(SynTupleTypeSegment.Type t, Some argInfo) :: (SynTupleTypeSegment.Star mStar, None) :: $3 }

| topAppType %prec prec_toptuptyptail_prefix
{ [$1] }
{ let t, argInfo = $1
[ SynTupleTypeSegment.Type t, Some argInfo ] }

topAppType:
| attributes appType COLON appType
Expand Down Expand Up @@ -5080,29 +5086,37 @@ typEOF:

tupleType:
| appType STAR tupleOrQuotTypeElements
{ SynType.Tuple(false, (false, $1) :: $3, lhs parseState) }
{ let mStar = rhs parseState 2
let path = SynTupleTypeSegment.Type $1 :: SynTupleTypeSegment.Star mStar :: $3
mkSynTypeTuple false path }

| INFIX_STAR_DIV_MOD_OP tupleOrQuotTypeElements
{ if $1 <> "/" then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnexpectedInfixOperator());
SynType.Tuple(false, (true, SynType.StaticConstant (SynConst.Int32 1, lhs parseState)) :: $2, lhs parseState) }
let mSlash = rhs parseState 1
let path = SynTupleTypeSegment.Slash mSlash :: $2
mkSynTypeTuple false path }

| appType INFIX_STAR_DIV_MOD_OP tupleOrQuotTypeElements
{ if $2 <> "/" then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnexpectedInfixOperator());
SynType.Tuple(false, (true, $1) :: $3, lhs parseState) }
let mSlash = rhs parseState 2
let path = SynTupleTypeSegment.Type $1 :: SynTupleTypeSegment.Slash mSlash :: $3
mkSynTypeTuple false path }

| appType %prec prec_tuptyp_prefix
{ $1 }

tupleOrQuotTypeElements:
| appType STAR tupleOrQuotTypeElements
{ (false, $1) :: $3 }
{ let mStar = rhs parseState 2
SynTupleTypeSegment.Type $1 :: SynTupleTypeSegment.Star mStar :: $3 }

| appType INFIX_STAR_DIV_MOD_OP tupleOrQuotTypeElements
{ if $2 <> "/" then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnexpectedInfixOperator());
(true, $1) :: $3 }
let mSlash = rhs parseState 2
SynTupleTypeSegment.Type $1 :: SynTupleTypeSegment.Slash mSlash :: $3 }

| appType %prec prec_tuptyptail_prefix
{ [(false, $1)] }
{ [ SynTupleTypeSegment.Type $1 ] }

appTypeCon:
| path %prec prec_atomtyp_path
Expand Down Expand Up @@ -5236,11 +5250,15 @@ atomType:
SynType.Paren ($2, lhs parseState) }

| STRUCT LPAREN appType STAR tupleOrQuotTypeElements rparen
{ SynType.Tuple(true, (false, $3) :: $5, lhs parseState) }
{ let mStar = rhs parseState 4
let path = SynTupleTypeSegment.Type $3 :: SynTupleTypeSegment.Star mStar :: $5
mkSynTypeTuple true path }

| STRUCT LPAREN appType STAR tupleOrQuotTypeElements recover
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnmatchedParen())
SynType.Tuple(true, (false, $3) :: $5, lhs parseState) }
let mStar = rhs parseState 4
let path = SynTupleTypeSegment.Type $3 :: SynTupleTypeSegment.Star mStar :: $5
mkSynTypeTuple true path }

| STRUCT LPAREN appType STAR recover
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnmatchedParen())
Expand Down
Loading