diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index ab0d663e26d7..a951a4e2f8ca 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1389,29 +1389,35 @@ function initSearch(rawSearchIndex) {
* @return {boolean} - Returns true if a match, false otherwise.
*/
function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
- const simplifiedGenerics = unifyFunctionTypeCheckBindings(
+ const solutions = unifyFunctionTypeCheckBindings(
fnType,
queryElem,
whereClause,
mgensInout
);
- if (!simplifiedGenerics) {
+ if (!solutions) {
return false;
}
- return unifyFunctionTypes(
- simplifiedGenerics,
- queryElem.generics,
- whereClause,
- mgensInout,
- mgens => {
- if (mgensInout) {
- for (const [fid, qid] of mgens.entries()) {
- mgensInout.set(fid, qid);
+ const simplifiedGenerics = solutions.simplifiedGenerics;
+ for (const mgens of solutions.mgens) {
+ if (unifyFunctionTypes(
+ simplifiedGenerics,
+ queryElem.generics,
+ whereClause,
+ mgens,
+ mgens => {
+ if (mgensInout) {
+ for (const [fid, qid] of mgens.entries()) {
+ mgensInout.set(fid, qid);
+ }
}
+ return true;
}
+ )) {
return true;
}
- );
+ }
+ return false;
}
/**
* This function checks if a list of search query `queryElems` can all be found in the
@@ -1545,33 +1551,36 @@ function initSearch(rawSearchIndex) {
for (j = i; j !== fl; ++j) {
const fnType = fnTypes[j];
if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
- const mgensScratch = new Map(mgens);
- const simplifiedGenerics = unifyFunctionTypeCheckBindings(
+ const solution = unifyFunctionTypeCheckBindings(
fnType,
queryElem,
whereClause,
- mgensScratch
+ mgens
);
- if (simplifiedGenerics) {
+ if (solution) {
if (!fnTypesScratch) {
fnTypesScratch = fnTypes.slice();
}
- unifyFunctionTypes(
- simplifiedGenerics,
- queryElem.generics,
- whereClause,
- mgensScratch,
- mgensScratch => {
- matchCandidates.push({
- fnTypesScratch,
- mgensScratch,
- queryElemsOffset: i,
- fnTypesOffset: j,
- unbox: false,
- });
- return false; // "reject" all candidates to gather all of them
- }
- );
+ const simplifiedGenerics = solution.simplifiedGenerics;
+ for (const solutionMgens of solution.mgens) {
+ unifyFunctionTypes(
+ simplifiedGenerics,
+ queryElem.generics,
+ whereClause,
+ solutionMgens,
+ mgensScratch => {
+ matchCandidates.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: false,
+ });
+ // "reject" all candidates to gather all of them
+ return false;
+ }
+ );
+ }
}
}
if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
@@ -1726,41 +1735,44 @@ function initSearch(rawSearchIndex) {
* @param {FunctionType} fnType
* @param {QueryElement} queryElem
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
- * @param {Map|null} mgensInout - Map functions generics to query generics.
- * Written on success.
- * @returns {boolean|FunctionType[]}
+ * @param {Map} mgensIn - Map functions generics to query generics.
+ * Never modified.
+ * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}}
*/
- function unifyFunctionTypeCheckBindings(fnType, queryElem, whereClause, mgensInout) {
- // Simplify generics now
- let simplifiedGenerics = fnType.generics;
- if (!simplifiedGenerics) {
- simplifiedGenerics = [];
- }
+ function unifyFunctionTypeCheckBindings(fnType, queryElem, whereClause, mgensIn) {
if (fnType.bindings.size < queryElem.bindings.size) {
return false;
}
+ let simplifiedGenerics = fnType.generics || [];
if (fnType.bindings.size > 0) {
- const mgensResults = new Map(mgensInout);
+ let mgensSolutionSet = [mgensIn];
for (const [name, constraints] of queryElem.bindings.entries()) {
- if (!fnType.bindings.has(name)) {
+ if (mgensSolutionSet.length === 0) {
return false;
}
- // Since both items must have exactly one entry per name,
- // we don't need to backtrack here, but do need to write mgens.
- if (!unifyFunctionTypes(
- fnType.bindings.get(name),
- constraints,
- whereClause,
- mgensResults,
- mgens => {
- for (const [fid, qid] of mgens.entries()) {
- mgensResults.set(fid, qid);
- }
- return true;
- }
- )) {
+ if (!fnType.bindings.has(name)) {
return false;
}
+ const fnTypeBindings = fnType.bindings.get(name);
+ mgensSolutionSet = mgensSolutionSet.flatMap(mgens => {
+ const newSolutions = [];
+ unifyFunctionTypes(
+ fnTypeBindings,
+ constraints,
+ whereClause,
+ mgens,
+ newMgens => {
+ newSolutions.push(newMgens);
+ // return `false` makes unifyFunctionTypes return the full set of
+ // possible solutions
+ return false;
+ }
+ );
+ return newSolutions;
+ });
+ }
+ if (mgensSolutionSet.length === 0) {
+ return false;
}
const binds = Array.from(fnType.bindings.entries()).flatMap(entry => {
const [name, constraints] = entry;
@@ -1775,13 +1787,9 @@ function initSearch(rawSearchIndex) {
} else {
simplifiedGenerics = binds;
}
- if (mgensInout) {
- for (const [fid, qid] of mgensResults.entries()) {
- mgensInout.set(fid, qid);
- }
- }
+ return { simplifiedGenerics, mgens: mgensSolutionSet };
}
- return simplifiedGenerics;
+ return { simplifiedGenerics, mgens: [mgensIn] };
}
/**
* @param {FunctionType} fnType
@@ -1805,7 +1813,7 @@ function initSearch(rawSearchIndex) {
// `fn read_all(R) -> Result`
// generic `R` is considered "unboxed"
return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
- } else if (fnType.generics.length > 0 || fnType.bindings.length > 0) {
+ } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) {
const simplifiedGenerics = [
...fnType.generics,
...Array.from(fnType.bindings.values()).flat(),
diff --git a/tests/rustdoc-js/assoc-type-backtrack.js b/tests/rustdoc-js/assoc-type-backtrack.js
index 6560cac7ca45..493e1a9910d1 100644
--- a/tests/rustdoc-js/assoc-type-backtrack.js
+++ b/tests/rustdoc-js/assoc-type-backtrack.js
@@ -63,4 +63,101 @@ const EXPECTED = [
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' },
],
},
+ // The first two define the base case.
+ {
+ 'query': 'myintofuture> -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ {
+ 'query': 'myintofuture>, myintofuture> -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ // Unboxings of the one-argument case.
+ {
+ 'query': 'myfuture -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ {
+ 'query': 'myintofuture> -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ // Invalid unboxing of the one-argument case.
+ // If you unbox one of the myfutures, you need to unbox both of them.
+ {
+ 'query': 'myintofuture -> myfuture',
+ 'correction': null,
+ 'others': [],
+ },
+ // Unboxings of the two-argument case.
+ {
+ 'query': 'myintofuture, myintofuture -> t',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ {
+ 'query': 'myintofuture, myintofuture -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ {
+ 'query': 'myintofuture, myintofuture -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ {
+ 'query': 'myfuture, myfuture -> myfuture',
+ 'correction': null,
+ 'others': [
+ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+ ],
+ },
+ // Invalid unboxings of the two-argument case.
+ // If you unbox one of the myfutures, you need to unbox all of them.
+ {
+ 'query': 'myintofuture, myintofuture> -> myfuture',
+ 'correction': null,
+ 'others': [],
+ },
+ {
+ 'query': 'myintofuture>, myintofuture -> myfuture',
+ 'correction': null,
+ 'others': [],
+ },
+ {
+ 'query': 'myintofuture>, myintofuture> -> t',
+ 'correction': null,
+ 'others': [],
+ },
+ // different generics don't match up either
+ {
+ 'query': 'myintofuture>, myintofuture> -> myfuture',
+ 'correction': null,
+ 'others': [],
+ },
+ {
+ 'query': 'myintofuture