@@ -1208,13 +1208,62 @@ function initSearch(rawSearchIndex) {
1208
1208
if ( ! fnTypes || fnTypes . length === 0 ) {
1209
1209
return false ;
1210
1210
}
1211
+ /**
1212
+ * @type Map<integer, QueryElement[]>
1213
+ */
1214
+ const queryElemSet = new Map ( ) ;
1215
+ const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet ( queryElem ) {
1216
+ let currentQueryElemList ;
1217
+ if ( queryElemSet . has ( queryElem . id ) ) {
1218
+ currentQueryElemList = queryElemSet . get ( queryElem . id ) ;
1219
+ } else {
1220
+ currentQueryElemList = [ ] ;
1221
+ queryElemSet . set ( queryElem . id , currentQueryElemList ) ;
1222
+ }
1223
+ currentQueryElemList . push ( queryElem ) ;
1224
+ } ;
1225
+ for ( const queryElem of queryElems ) {
1226
+ addQueryElemToQueryElemSet ( queryElem ) ;
1227
+ }
1211
1228
/**
1212
1229
* @type Map<integer, FunctionType[]>
1213
1230
*/
1214
1231
const fnTypeSet = new Map ( ) ;
1215
1232
const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet ( fnType ) {
1216
- if ( fnType . id === - 1 ) {
1217
- // Pure generic, needs to check into it.
1233
+ // Pure generic, or an item that's not matched by any query elems.
1234
+ // Try [unboxing] it.
1235
+ //
1236
+ // [unboxing]:
1237
+ // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
1238
+ const queryContainsArrayOrSliceElem = queryElemSet . has ( typeNameIdOfArrayOrSlice ) ;
1239
+ if ( fnType . id === - 1 || ! (
1240
+ queryElemSet . has ( fnType . id ) ||
1241
+ ( fnType . id === typeNameIdOfSlice && queryContainsArrayOrSliceElem ) ||
1242
+ ( fnType . id === typeNameIdOfArray && queryContainsArrayOrSliceElem )
1243
+ ) ) {
1244
+ for ( const innerFnType of fnType . generics ) {
1245
+ addFnTypeToFnTypeSet ( innerFnType ) ;
1246
+ }
1247
+ return ;
1248
+ }
1249
+ let currentQueryElemList = queryElemSet . get ( fnType . id ) || [ ] ;
1250
+ let matchIdx = currentQueryElemList . findIndex ( queryElem => {
1251
+ return typePassesFilter ( queryElem . typeFilter , fnType . ty ) &&
1252
+ checkGenerics ( fnType , queryElem ) ;
1253
+ } ) ;
1254
+ if ( matchIdx === - 1 &&
1255
+ ( fnType . id === typeNameIdOfSlice || fnType . id === typeNameIdOfArray ) &&
1256
+ queryContainsArrayOrSliceElem
1257
+ ) {
1258
+ currentQueryElemList = queryElemSet . get ( typeNameIdOfArrayOrSlice ) || [ ] ;
1259
+ matchIdx = currentQueryElemList . findIndex ( queryElem => {
1260
+ return typePassesFilter ( queryElem . typeFilter , fnType . ty ) &&
1261
+ checkGenerics ( fnType , queryElem ) ;
1262
+ } ) ;
1263
+ }
1264
+ // None of the query elems match the function type.
1265
+ // Try [unboxing] it.
1266
+ if ( matchIdx === - 1 ) {
1218
1267
for ( const innerFnType of fnType . generics ) {
1219
1268
addFnTypeToFnTypeSet ( innerFnType ) ;
1220
1269
}
@@ -1232,85 +1281,66 @@ function initSearch(rawSearchIndex) {
1232
1281
for ( const fnType of fnTypes ) {
1233
1282
addFnTypeToFnTypeSet ( fnType ) ;
1234
1283
}
1235
- // We need to find the type that matches the most to remove it in order
1236
- // to move forward.
1237
- const handleQueryElem = queryElem => {
1238
- if ( ! fnTypeSet . has ( queryElem . id ) ) {
1239
- return false ;
1284
+ const doHandleQueryElemList = ( currentFnTypeList , queryElemList ) => {
1285
+ if ( queryElemList . length === 0 ) {
1286
+ return true ;
1240
1287
}
1241
- const currentFnTypeList = fnTypeSet . get ( queryElem . id ) ;
1242
- const matchIdx = currentFnTypeList . findIndex ( fnType => {
1288
+ // Multiple items in one list might match multiple items in another.
1289
+ // Since an item with fewer generics can match an item with more, we
1290
+ // need to check all combinations for a potential match.
1291
+ const queryElem = queryElemList . pop ( ) ;
1292
+ const l = currentFnTypeList . length ;
1293
+ for ( let i = 0 ; i < l ; i += 1 ) {
1294
+ const fnType = currentFnTypeList [ i ] ;
1243
1295
if ( ! typePassesFilter ( queryElem . typeFilter , fnType . ty ) ) {
1244
- return false ;
1296
+ continue ;
1297
+ }
1298
+ if ( queryElem . generics . length === 0 || checkGenerics ( fnType , queryElem ) ) {
1299
+ currentFnTypeList . splice ( i , 1 ) ;
1300
+ const result = doHandleQueryElemList ( currentFnTypeList , queryElemList ) ;
1301
+ if ( result ) {
1302
+ return true ;
1303
+ }
1304
+ currentFnTypeList . splice ( i , 0 , fnType ) ;
1245
1305
}
1246
- return queryElem . generics . length === 0 || checkGenerics ( fnType , queryElem ) ;
1247
- } ) ;
1248
- if ( matchIdx === - 1 ) {
1249
- return false ;
1250
- }
1251
- currentFnTypeList . splice ( matchIdx , 1 ) ;
1252
- if ( currentFnTypeList . length === 0 ) {
1253
- fnTypeSet . delete ( queryElem . id ) ;
1254
1306
}
1255
- return true ;
1307
+ return false ;
1256
1308
} ;
1257
- // To do the right thing with type filters, we first process generics
1258
- // that have them, removing matching ones from the "bag," then do the
1259
- // ones with no type filter, which can match any entry regardless of its
1260
- // own type.
1261
- const needsUnboxed = [ ] ;
1262
- for ( const queryElem of queryElems ) {
1263
- if ( queryElem . typeFilter === TY_PRIMITIVE &&
1264
- queryElem . id === typeNameIdOfArrayOrSlice ) {
1265
- const queryElemArray = {
1266
- id : typeNameIdOfArray ,
1267
- typeFilter : TY_PRIMITIVE ,
1268
- generics : queryElem . generics ,
1269
- } ;
1270
- const queryElemSlice = {
1271
- id : typeNameIdOfSlice ,
1272
- typeFilter : TY_PRIMITIVE ,
1273
- generics : queryElem . generics ,
1274
- } ;
1275
- if ( ! handleQueryElem ( queryElemArray ) && ! handleQueryElem ( queryElemSlice ) ) {
1276
- needsUnboxed . push ( queryElem ) ;
1309
+ const handleQueryElemList = ( id , queryElemList ) => {
1310
+ if ( ! fnTypeSet . has ( id ) ) {
1311
+ if ( id === typeNameIdOfArrayOrSlice ) {
1312
+ return handleQueryElemList ( typeNameIdOfSlice , queryElemList ) ||
1313
+ handleQueryElemList ( typeNameIdOfArray , queryElemList ) ;
1277
1314
}
1278
- } else if ( queryElem . typeFilter !== - 1 && ! handleQueryElem ( queryElem ) ) {
1279
- needsUnboxed . push ( queryElem ) ;
1315
+ return false ;
1280
1316
}
1281
- }
1282
- for ( const queryElem of queryElems ) {
1283
- if ( queryElem . typeFilter === - 1 && ! handleQueryElem ( queryElem ) ) {
1284
- needsUnboxed . push ( queryElem ) ;
1317
+ const currentFnTypeList = fnTypeSet . get ( id ) ;
1318
+ if ( currentFnTypeList . length < queryElemList . length ) {
1319
+ // It's not possible for all the query elems to find a match.
1320
+ return false ;
1285
1321
}
1286
- }
1287
- // If the current item does not match, try [unboxing] the generic.
1288
- // [unboxing]:
1289
- // https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
1290
- unboxing: while ( needsUnboxed . length !== 0 ) {
1291
- for ( const [ i , queryElem ] of needsUnboxed . entries ( ) ) {
1292
- if ( handleQueryElem ( queryElem ) ) {
1293
- needsUnboxed . splice ( i , 1 ) ;
1294
- continue unboxing;
1322
+ const result = doHandleQueryElemList ( currentFnTypeList , queryElemList ) ;
1323
+ if ( result ) {
1324
+ // Found a solution.
1325
+ // Any items that weren't used for it can be unboxed, and might form
1326
+ // part of the solution for another item.
1327
+ for ( const innerFnType of currentFnTypeList ) {
1328
+ addFnTypeToFnTypeSet ( innerFnType ) ;
1295
1329
}
1330
+ fnTypeSet . delete ( id ) ;
1296
1331
}
1297
- for ( const [ id , fnTypeList ] of fnTypeSet ) {
1298
- for ( const [ i , fnType ] of fnTypeList . entries ( ) ) {
1299
- if ( fnType . generics . length !== 0 ) {
1300
- fnTypeList . splice ( i , 1 ) ;
1301
- for ( const innerFnType of fnType . generics ) {
1302
- addFnTypeToFnTypeSet ( innerFnType ) ;
1303
- }
1304
- if ( fnTypeList . length === 0 ) {
1305
- fnTypeSet . delete ( id ) ;
1306
- }
1307
- continue unboxing;
1308
- }
1332
+ return result ;
1333
+ } ;
1334
+ let queryElemSetSize = - 1 ;
1335
+ while ( queryElemSetSize !== queryElemSet . size ) {
1336
+ queryElemSetSize = queryElemSet . size ;
1337
+ for ( const [ id , queryElemList ] of queryElemSet ) {
1338
+ if ( handleQueryElemList ( id , queryElemList ) ) {
1339
+ queryElemSet . delete ( id ) ;
1309
1340
}
1310
1341
}
1311
- return false ;
1312
1342
}
1313
- return true ;
1343
+ return queryElemSetSize === 0 ;
1314
1344
}
1315
1345
1316
1346
/**
0 commit comments