Skip to content

Fix 3016: Decode syntactic types using FSharpFunc, Tuple, ValueTuple #3283

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

Merged
merged 4 commits into from
Jul 6, 2017
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
95 changes: 48 additions & 47 deletions src/fsharp/TcGlobals.fs
Original file line number Diff line number Diff line change
Expand Up @@ -673,54 +673,52 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
let addFieldNeverAttrs (fdef:ILFieldDef) = {fdef with CustomAttrs = addNeverAttrs fdef.CustomAttrs}
let mkDebuggerTypeProxyAttribute (ty : ILType) = mkILCustomAttribute ilg (findSysILTypeRef tname_DebuggerTypeProxyAttribute, [ilg.typ_Type], [ILAttribElem.TypeRef (Some ty.TypeRef)], [])

let entries1 =
[| "Int32" , v_int_tcr
"IntPtr" , v_nativeint_tcr
"UIntPtr" , v_unativeint_tcr
"Int16" , v_int16_tcr
"Int64" , v_int64_tcr
"UInt16" , v_uint16_tcr
"UInt32" , v_uint32_tcr
"UInt64" , v_uint64_tcr
"SByte" , v_sbyte_tcr
"Decimal" , v_decimal_tcr
"Byte" , v_byte_tcr
"Boolean" , v_bool_tcr
"String" , v_string_tcr
"Object" , v_obj_tcr
"Exception", v_exn_tcr
"Char" , v_char_tcr
"Double" , v_float_tcr
"Single" , v_float32_tcr |]
|> Array.map (fun (nm, tcr) ->
let ty = mkNonGenericTy tcr
nm, findSysTyconRef sys nm, (fun _ -> ty))

let entries2 =
[|
"FSharpFunc`2" , v_fastFunc_tcr , (fun tinst -> mkFunTy (List.item 0 tinst) (List.item 1 tinst))
"Tuple`2" , v_ref_tuple2_tcr , decodeTupleTy tupInfoRef
"Tuple`3" , v_ref_tuple3_tcr , decodeTupleTy tupInfoRef
"Tuple`4" , v_ref_tuple4_tcr , decodeTupleTy tupInfoRef
"Tuple`5" , v_ref_tuple5_tcr , decodeTupleTy tupInfoRef
"Tuple`6" , v_ref_tuple6_tcr , decodeTupleTy tupInfoRef
"Tuple`7" , v_ref_tuple7_tcr , decodeTupleTy tupInfoRef
"Tuple`8" , v_ref_tuple8_tcr , decodeTupleTy tupInfoRef
"ValueTuple`2" , v_struct_tuple2_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`3" , v_struct_tuple3_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`4" , v_struct_tuple4_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`5" , v_struct_tuple5_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`6" , v_struct_tuple6_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`7" , v_struct_tuple7_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`8" , v_struct_tuple8_tcr , decodeTupleTy tupInfoStruct |]

// Build a map that uses the "canonical" F# type names and TyconRef's for these
// in preference to the .NET type names. Doing this normalization is a fairly performance critical
// piece of code as it is frequently invoked in the process of converting .NET metadata to F# internal
// compiler data structures (see import.fs).
let betterTyconRefMap =
begin
let entries1 =
[| "Int32" , v_int_tcr
"IntPtr" , v_nativeint_tcr
"UIntPtr" , v_unativeint_tcr
"Int16" , v_int16_tcr
"Int64" , v_int64_tcr
"UInt16" , v_uint16_tcr
"UInt32" , v_uint32_tcr
"UInt64" , v_uint64_tcr
"SByte" , v_sbyte_tcr
"Decimal" , v_decimal_tcr
"Byte" , v_byte_tcr
"Boolean" , v_bool_tcr
"String" , v_string_tcr
"Object" , v_obj_tcr
"Exception", v_exn_tcr
"Char" , v_char_tcr
"Double" , v_float_tcr
"Single" , v_float32_tcr |]
|> Array.map (fun (nm, tcr) ->
let ty = mkNonGenericTy tcr
nm, findSysTyconRef sys nm, (fun _ -> ty))

let entries2 =
[|
"FSharpFunc`2" , v_fastFunc_tcr , (fun tinst -> mkFunTy (List.item 0 tinst) (List.item 1 tinst))
"Tuple`2" , v_ref_tuple2_tcr , decodeTupleTy tupInfoRef
"Tuple`3" , v_ref_tuple3_tcr , decodeTupleTy tupInfoRef
"Tuple`4" , v_ref_tuple4_tcr , decodeTupleTy tupInfoRef
"Tuple`5" , v_ref_tuple5_tcr , decodeTupleTy tupInfoRef
"Tuple`6" , v_ref_tuple6_tcr , decodeTupleTy tupInfoRef
"Tuple`7" , v_ref_tuple7_tcr , decodeTupleTy tupInfoRef
"Tuple`8" , v_ref_tuple8_tcr , decodeTupleTy tupInfoRef
"ValueTuple`2" , v_struct_tuple2_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`3" , v_struct_tuple3_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`4" , v_struct_tuple4_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`5" , v_struct_tuple5_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`6" , v_struct_tuple6_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`7" , v_struct_tuple7_tcr , decodeTupleTy tupInfoStruct
"ValueTuple`8" , v_struct_tuple8_tcr , decodeTupleTy tupInfoStruct |]

let entries = Array.append entries1 entries2
let buildTyconMapper (entries: (string * TyconRef * _)[]) =
if compilingFslib then
// This map is for use when building FSharp.Core.dll. The backing Tycon's may not yet exist for
// the TyconRef's we have in our hands, hence we can't dereference them to find their stamps.
Expand Down Expand Up @@ -759,8 +757,10 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
let key = tcref2.Stamp
if dict.ContainsKey key then Some(dict.[key] tinst)
else None)
end


let betterTyconRefMapper = buildTyconMapper (Array.append entries1 entries2)

let decodeTyconRefMapper = buildTyconMapper entries2

override x.ToString() = "<TcGlobals>"
member __.ilg=ilg
Expand Down Expand Up @@ -1052,7 +1052,8 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
member val attrib_SecuritySafeCriticalAttribute = findSysAttrib "System.Security.SecuritySafeCriticalAttribute"
member val attrib_ComponentModelEditorBrowsableAttribute = findSysAttrib "System.ComponentModel.EditorBrowsableAttribute"

member __.better_tcref_map = betterTyconRefMap
member __.betterTyconRefMap = betterTyconRefMapper
member __.decodeTyconRefMap = decodeTyconRefMapper
member __.new_decimal_info = v_new_decimal_info
member __.seq_info = v_seq_info
member val seq_vref = (ValRefForIntrinsic v_seq_info)
Expand Down
33 changes: 23 additions & 10 deletions src/fsharp/TypeChecker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4858,31 +4858,44 @@ and TcProvidedTypeApp cenv env tpenv tcref args m =
/// Note that the generic type may be a nested generic type List<T>.ListEnumerator<U>.
/// In this case, 'args' is only the instantiation of the suffix type arguments, and pathTypeArgs gives
/// the prefix of type arguments.
and TcTypeApp cenv newOk checkCxs occ env tpenv m tcref pathTypeArgs (args: SynType list) =
and TcTypeApp cenv newOk checkCxs occ env tpenv m tcref pathTypeArgs (synArgTys: SynType list) =
CheckTyconAccessible cenv.amap m env.eAccessRights tcref |> ignore
CheckEntityAttributes cenv.g tcref m |> CommitOperationResult

#if EXTENSIONTYPING
// Provided types are (currently) always non-generic. Their names may include mangled
// static parameters, which are passed by the provider.
if tcref.Deref.IsProvided then TcProvidedTypeApp cenv env tpenv tcref args m else
if tcref.Deref.IsProvided then TcProvidedTypeApp cenv env tpenv tcref synArgTys m else
#endif

let tps,_,tinst,_ = infoOfTyconRef m tcref

// If we're not checking constraints, i.e. when we first assert the super/interfaces of a type definition, then just
// clear the constraint lists of the freshly generated type variables. A little ugly but fairly localized.
if checkCxs = NoCheckCxs then tps |> List.iter (fun tp -> tp.typar_constraints <- [])
if tinst.Length <> pathTypeArgs.Length + args.Length then
error (TyconBadArgs(env.DisplayEnv,tcref,pathTypeArgs.Length + args.Length,m))
let args',tpenv =
if tinst.Length <> pathTypeArgs.Length + synArgTys.Length then
error (TyconBadArgs(env.DisplayEnv,tcref,pathTypeArgs.Length + synArgTys.Length,m))

let argTys,tpenv =
// Get the suffix of typars
let tpsForArgs = List.drop (tps.Length - args.Length) tps
let tpsForArgs = List.drop (tps.Length - synArgTys.Length) tps
let kindsForArgs = tpsForArgs |> List.map (fun tp -> tp.Kind)
TcTypesOrMeasures (Some kindsForArgs) cenv newOk checkCxs occ env tpenv args m
let args' = pathTypeArgs @ args'
TcTypesOrMeasures (Some kindsForArgs) cenv newOk checkCxs occ env tpenv synArgTys m

// Add the types of the enclosing class for a nested type
let actualArgTys = pathTypeArgs @ argTys

if checkCxs = CheckCxs then
List.iter2 (UnifyTypes cenv env m) tinst args'
mkAppTy tcref args', tpenv
List.iter2 (UnifyTypes cenv env m) tinst actualArgTys

// Try to decode System.Tuple --> F~ tuple types etc.
let ty =
let decode = if cenv.g.compilingFslib then None else cenv.g.decodeTyconRefMap tcref actualArgTys
match decode with
| Some res -> res
| None -> mkAppTy tcref actualArgTys

ty, tpenv

and TcTypeOrMeasureAndRecover optKind cenv newOk checkCxs occ env tpenv ty =
try TcTypeOrMeasure optKind cenv newOk checkCxs occ env tpenv ty
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/import.fs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ let CanImportILTypeRef (env:ImportMap) m (tref:ILTypeRef) =
/// Prefer the F# abbreviation for some built-in types, e.g. 'string' rather than
/// 'System.String', since we prefer the F# abbreviation to the .NET equivalents.
let ImportTyconRefApp (env:ImportMap) tcref tyargs =
match env.g.better_tcref_map tcref tyargs with
match env.g.betterTyconRefMap tcref tyargs with
| Some res -> res
| None -> TType_app (tcref,tyargs)

Expand Down
8 changes: 8 additions & 0 deletions tests/fsharp/tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,14 @@ module TypecheckTests =
#endif

#if !FSHARP_SUITE_DRIVES_CORECLR_TESTS
[<Test>]
let ``sigs pos27`` () =
let cfg = testConfig "typecheck/sigs"
fsc cfg "%s --target:exe -o:pos27.exe" cfg.fsc_flags ["pos27.fs"]
copy_y cfg (cfg.FSCBinPath ++ "System.ValueTuple.dll") ("." ++ "System.ValueTuple.dll")

peverify cfg "pos27.exe"

[<Test>]
let ``sigs pos26`` () =
let cfg = testConfig "typecheck/sigs"
Expand Down
12 changes: 6 additions & 6 deletions tests/fsharp/typecheck/sigs/neg23.bsl
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@

neg23.fs(9,21,9,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure1.SomeClass' once tuples, functions, units of measure and/or provided types are erased.
neg23.fs(9,21,9,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure1.SomeClass'.

neg23.fs(7,21,7,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure1.SomeClass' once tuples, functions, units of measure and/or provided types are erased.
neg23.fs(7,21,7,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure1.SomeClass'.

neg23.fs(19,21,19,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure2.SomeClass' once tuples, functions, units of measure and/or provided types are erased.
neg23.fs(19,21,19,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure2.SomeClass'.

neg23.fs(17,21,17,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure2.SomeClass' once tuples, functions, units of measure and/or provided types are erased.
neg23.fs(17,21,17,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure2.SomeClass'.

neg23.fs(28,21,28,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure3.SomeClass' once tuples, functions, units of measure and/or provided types are erased.
neg23.fs(28,21,28,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure3.SomeClass'.

neg23.fs(26,21,26,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure3.SomeClass' once tuples, functions, units of measure and/or provided types are erased.
neg23.fs(26,21,26,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure3.SomeClass'.

neg23.fs(55,21,55,24): typecheck error FS0438: Duplicate method. The method 'Foo' has the same name and signature as another method in type 'DuplicateOverloadUpToErasure6.SomeClass' once tuples, functions, units of measure and/or provided types are erased.

Expand Down
25 changes: 25 additions & 0 deletions tests/fsharp/typecheck/sigs/pos27.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Pos27

module TUple =
let x1: System.Tuple<int> = System.Tuple.Create(1)
let x2: System.Tuple<int,int> = System.Tuple.Create(1,2)
let x3: System.Tuple<int,int,int> = System.Tuple.Create(1,2,3)
let x4: System.Tuple<int,int,int,int> = System.Tuple.Create(1,2,3,4)
let x5: System.Tuple<int,int,int,int,int> = System.Tuple.Create(1,2,3,4,5)
let x6: System.Tuple<int,int,int,int,int,int> = System.Tuple.Create(1,2,3,4,5,6)
let x7: System.Tuple<int,int,int,int,int,int,int> = System.Tuple.Create(1,2,3,4,5,6,7)
let x9: System.Tuple<int,int,int,int,int,int,int,System.Tuple<int>> = System.Tuple.Create(1,2,3,4,5,6,7,8)

module ValueTuple =
let x1: System.ValueTuple<int> = System.ValueTuple.Create(1)
let x2: System.ValueTuple<int,int> = System.ValueTuple.Create(1,2)
let x3: System.ValueTuple<int,int,int> = System.ValueTuple.Create(1,2,3)
let x4: System.ValueTuple<int,int,int,int> = System.ValueTuple.Create(1,2,3,4)
let x5: System.ValueTuple<int,int,int,int,int> = System.ValueTuple.Create(1,2,3,4,5)
let x6: System.ValueTuple<int,int,int,int,int,int> = System.ValueTuple.Create(1,2,3,4,5,6)
let x7: System.ValueTuple<int,int,int,int,int,int,int> = System.ValueTuple.Create(1,2,3,4,5,6,7)
let x9: System.ValueTuple<int,int,int,int,int,int,int,System.ValueTuple<int>> = System.ValueTuple.Create(1,2,3,4,5,6,7,8)

module FSharpFunc =
let x1: FSharpFunc<int,int> = (fun x -> x + 1)
let x2: FSharpFunc<int,FSharpFunc<int,int>> = (fun x y -> x + 1 + y)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// #Regression #Conformance #DeclarationElements #MemberDefinitions #Overloading
// Regression test for FSharp1.0:3762 - Using FastFunc explicitly is not differentiate from function types, thus causing compiler to create bad method tables, maybe other problems
//<Expects span="(7,17-7,20)" id="FS0438" status="error">Duplicate method\. The method 'Foo' has the same name and signature as another method in type 'SomeClass' once tuples, functions, units of measure and/or provided types are erased\.</Expects>
//<Expects span="(7,17-7,20)" id="FS0438" status="error">Duplicate method\. The method 'Foo' has the same name and signature as another method in type 'SomeClass'\.</Expects>
// Note: as of Beta2, FastFunc became FSharpFunc
type SomeClass() =

Expand Down