Skip to content

Commit d52230b

Browse files
dsymelatkin
authored andcommitted
skip unloadable interfaces
When the assembly reference set for the compilation is incomplete, some nterfaces to types relevant to compilation may lie in assemblies outside the assembly reference set. This applies particularly to private ("internals visible to") interfaces found in .NET assemblies. This causes very substantial usability bugs in practice as various parts of type inference and other checking "give up" when you get this condition. The C# compiler doesn't give up in the same way. In most cases it is reasonable to simply skip interfaces-that-lie-outside-the- set-of-referenced-assemblies during F# compilation. Skipping unresolvable interfaces is pretty much harmless: any substantive analysis on the interface type (such as implementing it) will require the assembly holding the interface type. There are some exceptions: if an interface I1 lies outside the referenceable set and you try to implement I2 inheriting from I1 then we'd better not skip I1. Indeed if you even try to find the methods on I2 then we'd better not skip I1. These are covered by "FoldPrimaryHierarchyOfType" in the code. fixes #337 closes #356 commit dd5205c769828e2e16e736c126cb62d68e7beb87 Author: Don Syme <donsyme@fastmail.fm> Date: Thu Apr 23 23:53:59 2015 +0100 add test case commit db28771c75d022247dee6dea60a89b8c29fab4b5 Author: Don Syme <donsyme@fastmail.fm> Date: Fri Apr 10 12:18:50 2015 +0200 skip unloadable interfaces (2) commit 18a47124480efdf58a75ce2fa7273c6f7550c1c0 Author: Don Syme <donsyme@fastmail.fm> Date: Fri Apr 10 11:53:39 2015 +0200 skip unloadable interfaces
1 parent 90777db commit d52230b

File tree

8 files changed

+86
-21
lines changed

8 files changed

+86
-21
lines changed

src/fsharp/NicePrint.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ module private TastDefinitionPrinting =
14191419
if suppressInheritanceAndInterfacesForTyInSimplifiedDisplays g amap m ty then
14201420
[]
14211421
else
1422-
GetImmediateInterfacesOfType g amap m ty |> List.map (fun ity -> wordL (if isInterfaceTy g ty then "inherit" else "interface") --- layoutType denv ity)
1422+
GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m ty |> List.map (fun ity -> wordL (if isInterfaceTy g ty then "inherit" else "interface") --- layoutType denv ity)
14231423

14241424
let props =
14251425
GetIntrinsicPropInfosOfType infoReader (None,ad,AllowMultiIntfInstantiations.Yes) PreferOverrides m ty

src/fsharp/check.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1369,7 +1369,7 @@ let CheckEntityDefn cenv env (tycon:Entity) =
13691369
if cenv.reportErrors then
13701370
if not tycon.IsTypeAbbrev then
13711371
let typ = generalizedTyconRef (mkLocalTyconRef tycon)
1372-
let immediateInterfaces = GetImmediateInterfacesOfType cenv.g cenv.amap m typ
1372+
let immediateInterfaces = GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes cenv.g cenv.amap m typ
13731373
let interfaces =
13741374
[ for ty in immediateInterfaces do
13751375
yield! AllSuperTypesOfType cenv.g cenv.amap m AllowMultiIntfInstantiations.Yes ty ]

src/fsharp/import.fs

+27
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ type ImportMap(g:TcGlobals,assemblyLoader:AssemblyLoader) =
6060
member this.assemblyLoader = assemblyLoader
6161
member this.ILTypeRefToTyconRefCache = typeRefToTyconRefCache
6262

63+
let CanImportILScopeRef (env:ImportMap) m scoref =
64+
match scoref with
65+
| ILScopeRef.Local -> true
66+
| ILScopeRef.Module _ -> true
67+
| ILScopeRef.Assembly assref ->
68+
match env.assemblyLoader.LoadAssembly (m,assref) with
69+
| UnresolvedCcu _ -> false
70+
| ResolvedCcu _ -> true
71+
72+
6373
/// Import a reference to a type definition, given the AbstractIL data for the type reference
6474
let ImportTypeRefData (env:ImportMap) m (scoref,path,typeName) =
6575
let ccu =
@@ -123,6 +133,10 @@ let ImportILTypeRef (env:ImportMap) m (tref:ILTypeRef) =
123133
env.ILTypeRefToTyconRefCache.[tref] <- tcref
124134
tcref
125135

136+
/// Import a reference to a type definition, given an AbstractIL ILTypeRef, with caching
137+
let CanImportILTypeRef (env:ImportMap) m (tref:ILTypeRef) =
138+
env.ILTypeRefToTyconRefCache.ContainsKey(tref) || CanImportILScopeRef env m tref.Scope
139+
126140
/// Import a type, given an AbstractIL ILTypeRef and an F# type instantiation.
127141
///
128142
/// Prefer the F# abbreviation for some built-in types, e.g. 'string' rather than
@@ -159,6 +173,19 @@ let rec ImportILType (env:ImportMap) m tinst typ =
159173
with _ ->
160174
error(Error(FSComp.SR.impNotEnoughTypeParamsInScopeWhileImporting(),m))
161175

176+
let rec CanImportILType (env:ImportMap) m typ =
177+
match typ with
178+
| ILType.Void -> true
179+
| ILType.Array(_bounds,ty) -> CanImportILType env m ty
180+
| ILType.Boxed tspec | ILType.Value tspec ->
181+
CanImportILTypeRef env m tspec.TypeRef
182+
&& tspec.GenericArgs |> ILList.toList |> List.forall (CanImportILType env m)
183+
| ILType.Byref ty -> CanImportILType env m ty
184+
| ILType.Ptr ty -> CanImportILType env m ty
185+
| ILType.FunctionPointer _ -> true
186+
| ILType.Modified(_,_,ty) -> CanImportILType env m ty
187+
| ILType.TypeVar _u16 -> true
188+
162189
#if EXTENSIONTYPING
163190

164191
/// Import a provided type reference as an F# type TyconRef

src/fsharp/import.fsi

+6
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,15 @@ type ImportMap =
4848
/// Import a reference to a type definition, given an AbstractIL ILTypeRef, with caching
4949
val internal ImportILTypeRef : ImportMap -> range -> ILTypeRef -> TyconRef
5050

51+
/// Pre-check for ability to import a reference to a type definition, given an AbstractIL ILTypeRef, with caching
52+
val internal CanImportILTypeRef : ImportMap -> range -> ILTypeRef -> bool
53+
5154
/// Import an IL type as an F# type.
5255
val internal ImportILType : ImportMap -> range -> TType list -> ILType -> TType
5356

57+
/// Pre-check for ability to import an IL type as an F# type.
58+
val internal CanImportILType : ImportMap -> range -> ILType -> bool
59+
5460
#if EXTENSIONTYPING
5561

5662
/// Import a provided type as an F# type.

src/fsharp/infos.fs

+36-15
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ open Microsoft.FSharp.Core.CompilerServices
3636
let ImportType scoref amap m importInst ilty =
3737
ilty |> rescopeILType scoref |> Import.ImportILType amap m importInst
3838

39+
let CanImportType scoref amap m ilty =
40+
ilty |> rescopeILType scoref |> Import.CanImportILType amap m
41+
3942
//-------------------------------------------------------------------------
4043
// Fold the hierarchy.
4144
// REVIEW: this code generalizes the iteration used below for member lookup.
@@ -86,17 +89,21 @@ let GetSuperTypeOfType g amap m typ =
8689
/// Make a type for System.Collections.Generic.IList<ty>
8790
let mkSystemCollectionsGenericIListTy g ty = TType_app(g.tcref_System_Collections_Generic_IList,[ty])
8891

92+
[<RequireQualifiedAccess>]
93+
/// Indicates whether we can skip interface types that lie outside the reference set
94+
type SkipUnrefInterfaces = Yes | No
95+
8996

9097
/// Collect the set of immediate declared interface types for an F# type, but do not
9198
/// traverse the type hierarchy to collect further interfaces.
92-
let rec GetImmediateInterfacesOfType g amap m typ =
99+
let rec GetImmediateInterfacesOfType skipUnref g amap m typ =
93100
let itys =
94101
if isAppTy g typ then
95102
let tcref,tinst = destAppTy g typ
96103
if tcref.IsMeasureableReprTycon then
97104
[ match tcref.TypeReprInfo with
98105
| TMeasureableRepr reprTy ->
99-
for ity in GetImmediateInterfacesOfType g amap m reprTy do
106+
for ity in GetImmediateInterfacesOfType skipUnref g amap m reprTy do
100107
if isAppTy g ity then
101108
let itcref = tcrefOfAppTy g ity
102109
if not (tyconRefEq g itcref g.system_GenericIComparable_tcref) &&
@@ -113,7 +120,18 @@ let rec GetImmediateInterfacesOfType g amap m typ =
113120
yield Import.ImportProvidedType amap m ity ]
114121
#endif
115122
| ILTypeMetadata (scoref,tdef) ->
116-
tdef.Implements |> ILList.toList |> List.map (ImportType scoref amap m tinst)
123+
124+
// ImportType may fail for an interface if the assembly load set is incomplete and the interface
125+
// comes from another assembly. In this case we simply skip the interface:
126+
// if we don't skip it, then compilation will just fail here, and if type checking
127+
// succeeds with fewer non-dereferencable interfaces reported then it would have
128+
// succeeded with more reported. There are pathological corner cases where this
129+
// doesn't apply: e.g. for mscorlib interfaces like IComparable, but we can always
130+
// assume those are present.
131+
[ for ity in tdef.Implements |> ILList.toList do
132+
if skipUnref = SkipUnrefInterfaces.No || CanImportType scoref amap m ity then
133+
yield ImportType scoref amap m tinst ity ]
134+
117135
| FSharpOrArrayOrByrefOrTupleOrExnTypeMetadata ->
118136
tcref.ImmediateInterfaceTypesOfFSharpTycon |> List.map (instType (mkInstForAppTy g typ))
119137
else
@@ -133,7 +151,7 @@ type AllowMultiIntfInstantiations = Yes | No
133151

134152
/// Traverse the type hierarchy, e.g. f D (f C (f System.Object acc)).
135153
/// Visit base types and interfaces first.
136-
let private FoldHierarchyOfTypeAux followInterfaces allowMultiIntfInst visitor g amap m typ acc =
154+
let private FoldHierarchyOfTypeAux followInterfaces allowMultiIntfInst skipUnref visitor g amap m typ acc =
137155
let rec loop ndeep typ ((visitedTycon,visited:TyconRefMultiMap<_>,acc) as state) =
138156

139157
let seenThisTycon = isAppTy g typ && Set.contains (tcrefOfAppTy g typ).Stamp visitedTycon
@@ -157,7 +175,7 @@ let private FoldHierarchyOfTypeAux followInterfaces allowMultiIntfInst visitor g
157175
if isInterfaceTy g typ then
158176
List.foldBack
159177
(loop (ndeep+1))
160-
(GetImmediateInterfacesOfType g amap m typ)
178+
(GetImmediateInterfacesOfType skipUnref g amap m typ)
161179
(loop ndeep g.obj_ty state)
162180
elif isTyparTy g typ then
163181
let tp = destTyparTy g typ
@@ -186,7 +204,7 @@ let private FoldHierarchyOfTypeAux followInterfaces allowMultiIntfInst visitor g
186204
if followInterfaces then
187205
List.foldBack
188206
(loop (ndeep+1))
189-
(GetImmediateInterfacesOfType g amap m typ)
207+
(GetImmediateInterfacesOfType skipUnref g amap m typ)
190208
state
191209
else
192210
state
@@ -200,22 +218,25 @@ let private FoldHierarchyOfTypeAux followInterfaces allowMultiIntfInst visitor g
200218
(visitedTycon,visited,acc)
201219
loop 0 typ (Set.empty,TyconRefMultiMap<_>.Empty,acc) |> p33
202220

203-
/// Fold, do not follow interfaces
204-
let FoldPrimaryHierarchyOfType f g amap m allowMultiIntfInst typ acc = FoldHierarchyOfTypeAux false allowMultiIntfInst f g amap m typ acc
221+
/// Fold, do not follow interfaces (unless the type is itself an interface)
222+
let FoldPrimaryHierarchyOfType f g amap m allowMultiIntfInst typ acc =
223+
FoldHierarchyOfTypeAux false allowMultiIntfInst SkipUnrefInterfaces.No f g amap m typ acc
205224

206-
/// Fold, following interfaces
207-
let FoldEntireHierarchyOfType f g amap m allowMultiIntfInst typ acc = FoldHierarchyOfTypeAux true allowMultiIntfInst f g amap m typ acc
225+
/// Fold, following interfaces. Skipping interfaces that lie outside the referenced assembly set is allowed.
226+
let FoldEntireHierarchyOfType f g amap m allowMultiIntfInst typ acc =
227+
FoldHierarchyOfTypeAux true allowMultiIntfInst SkipUnrefInterfaces.Yes f g amap m typ acc
208228

209-
/// Iterate, following interfaces
210-
let IterateEntireHierarchyOfType f g amap m allowMultiIntfInst typ = FoldHierarchyOfTypeAux true allowMultiIntfInst (fun ty () -> f ty) g amap m typ ()
229+
/// Iterate, following interfaces. Skipping interfaces that lie outside the referenced assembly set is allowed.
230+
let IterateEntireHierarchyOfType f g amap m allowMultiIntfInst typ =
231+
FoldHierarchyOfTypeAux true allowMultiIntfInst SkipUnrefInterfaces.Yes (fun ty () -> f ty) g amap m typ ()
211232

212233
/// Search for one element satisfying a predicate, following interfaces
213234
let ExistsInEntireHierarchyOfType f g amap m allowMultiIntfInst typ =
214-
FoldHierarchyOfTypeAux true allowMultiIntfInst (fun ty acc -> acc || f ty ) g amap m typ false
235+
FoldHierarchyOfTypeAux true allowMultiIntfInst SkipUnrefInterfaces.Yes (fun ty acc -> acc || f ty ) g amap m typ false
215236

216237
/// Search for one element where a function returns a 'Some' result, following interfaces
217238
let SearchEntireHierarchyOfType f g amap m typ =
218-
FoldHierarchyOfTypeAux true AllowMultiIntfInstantiations.Yes
239+
FoldHierarchyOfTypeAux true AllowMultiIntfInstantiations.Yes SkipUnrefInterfaces.Yes
219240
(fun ty acc ->
220241
match acc with
221242
| None -> if f ty then Some(ty) else None
@@ -224,7 +245,7 @@ let SearchEntireHierarchyOfType f g amap m typ =
224245

225246
/// Get all super types of the type, including the type itself
226247
let AllSuperTypesOfType g amap m allowMultiIntfInst ty =
227-
FoldHierarchyOfTypeAux true allowMultiIntfInst (ListSet.insert (typeEquiv g)) g amap m ty []
248+
FoldHierarchyOfTypeAux true allowMultiIntfInst SkipUnrefInterfaces.No (ListSet.insert (typeEquiv g)) g amap m ty []
228249

229250
/// Get all interfaces of a type, including the type itself if it is an interface
230251
let AllInterfacesOfType g amap m allowMultiIntfInst ty =

src/fsharp/typrelns.fs

+3-3
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ let rec TypeDefinitelySubsumesTypeNoCoercion ndeep g amap m ty1 ty2 =
7373
| Some ty -> TypeDefinitelySubsumesTypeNoCoercion (ndeep+1) g amap m ty1 ty) ||
7474

7575
(isInterfaceTy g ty1 &&
76-
ty2 |> GetImmediateInterfacesOfType g amap m
76+
ty2 |> GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m
7777
|> List.exists (TypeDefinitelySubsumesTypeNoCoercion (ndeep+1) g amap m ty1))))
7878

7979

@@ -129,7 +129,7 @@ let rec TypeFeasiblySubsumesType ndeep g amap m ty1 canCoerce ty2 =
129129
| None -> false
130130
| Some ty -> TypeFeasiblySubsumesType (ndeep+1) g amap m ty1 NoCoerce ty
131131
end ||
132-
ty2 |> GetImmediateInterfacesOfType g amap m
132+
ty2 |> GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m
133133
|> List.exists (TypeFeasiblySubsumesType (ndeep+1) g amap m ty1 NoCoerce))
134134

135135

@@ -1993,7 +1993,7 @@ let FinalTypeDefinitionChecksAtEndOfInferenceScope (infoReader:InfoReader) isImp
19931993
/// Look for the unique supertype of ty2 for which ty2 :> ty1 might feasibly hold
19941994
let FindUniqueFeasibleSupertype g amap m ty1 ty2 =
19951995
if not (isAppTy g ty2) then None else
1996-
let supertypes = Option.toList (GetSuperTypeOfType g amap m ty2) @ (GetImmediateInterfacesOfType g amap m ty2)
1996+
let supertypes = Option.toList (GetSuperTypeOfType g amap m ty2) @ (GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m ty2)
19971997
supertypes |> List.tryFind (TypeFeasiblySubsumesType 0 g amap m ty1 NoCoerce)
19981998

19991999

tests/fsharp/typecheck/sigs/build.bat

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ REM Configure the sample, i.e. where to find the F# compiler and C# compiler.
55

66
call %~d0%~p0..\..\..\config.bat
77

8+
"%FSC%" --noframework -r:"%FSCOREDLLPATH%" -r:"%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\mscorlib.dll" -r:"%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Core.dll" -r:"%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll" -r:"%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.dll" -r:"%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Numerics.dll" -a -o:pos21.dll pos21.fs
9+
@if ERRORLEVEL 1 goto Error
10+
811
call ..\..\single-neg-test.bat neg91
912
@if ERRORLEVEL 1 goto Error
1013

@@ -517,7 +520,6 @@ call ..\..\single-neg-test.bat neg35
517520
"%FSC%" %fsc_flags% -a -o:pos05.dll pos05.fs
518521
@if ERRORLEVEL 1 goto Error
519522

520-
521523
:Ok
522524
echo Built fsharp %~f0 ok.
523525
endlocal

tests/fsharp/typecheck/sigs/pos21.fs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Library2
2+
3+
open System
4+
open System.Data
5+
6+
module M =
7+
let dataset = new DataSet("test add reference")
8+
Console.WriteLine(dataset.DataSetName)
9+
Console.ReadKey(true)

0 commit comments

Comments
 (0)