Skip to content

Commit 30ed930

Browse files
committed
fix(baseline): exclusion must check all APIs in merged builds
The --baseline pass used .any() to decide whether an extension was fully promoted into a baseline core version, meaning a single API having the extension in core was enough to suppress it. In a merged build (e.g. --api gl:core,gles2 --baseline gl:core=3.3,gles2=3.0), this silently dropped extensions that were promoted for one API but still needed as actual extensions for another. Switch to .all() over the build API set: an extension is only baseline-excluded when every build API that supports it has it covered by its respective baseline core. If any build API lacks a baseline entry or hasn't promoted the extension, it survives. Non-merged builds have exactly one API, so any/all are equivalent and behaviour is unchanged. Signed-off-by: Steven Noonan <steven@uplinklabs.net>
1 parent 18bb14a commit 30ed930

1 file changed

Lines changed: 44 additions & 29 deletions

File tree

src/resolve.rs

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,40 +1092,55 @@ fn select_extensions<'a>(
10921092
m
10931093
};
10941094

1095-
// An extension is "fully promoted into baseline" if, for at least one
1096-
// API in the baseline set that the extension supports, ALL of the
1095+
// An extension is "fully promoted into baseline" only if, for EVERY
1096+
// API in the current build that the extension supports, ALL of the
10971097
// extension's commands for that API appear in the baseline core set
1098-
// (directly or via alias). Partial promotion doesn't count — if even
1099-
// one command isn't in core, the extension is still needed.
1100-
let baseline_api_set: HashSet<&str> = baseline.iter().map(|r| r.name.as_str()).collect();
1098+
// (directly or via alias). Using `all` rather than `any` is critical
1099+
// for --merge correctness: if GL_ARB_sync is promoted into GL 3.3 core
1100+
// but GLES2 still needs it as an extension, the merged output must keep
1101+
// it. If a build API has no corresponding baseline entry, the extension
1102+
// cannot be considered dominated for that API — the `baseline_core_cmds`
1103+
// lookup returns None and the `all` short-circuits to false.
1104+
//
1105+
// For non-merged builds there is only one API, so `all` and `any` are
1106+
// equivalent and the behaviour is unchanged.
1107+
let build_api_set: HashSet<&str> = requests.iter().map(|r| r.name.as_str()).collect();
11011108

11021109
for ext in &raw.extensions {
1103-
let dominated = ext
1110+
// Which APIs in the current build does this extension support?
1111+
let ext_build_apis: Vec<&str> = ext
11041112
.supported
11051113
.iter()
1106-
.filter(|s| baseline_api_set.contains(canonical_api_name(s.as_str())))
1107-
.any(|api| {
1108-
let Some(core_cmds) = baseline_core_cmds.get(canonical_api_name(api.as_str()))
1109-
else {
1110-
return false;
1111-
};
1112-
// Collect all commands this extension contributes for this API.
1113-
let ext_cmds: Vec<&str> = ext
1114-
.requires
1115-
.iter()
1116-
.filter(|req| api_profile_matches(req.api.as_deref(), None, api, None))
1117-
.flat_map(|req| req.commands.iter().map(String::as_str))
1118-
.collect();
1119-
// Extension must have at least one command, and ALL must be
1120-
// in the baseline core (directly or via alias).
1121-
!ext_cmds.is_empty()
1122-
&& ext_cmds.iter().all(|c| {
1123-
core_cmds.contains(*c)
1124-
|| baseline_cmd_aliases
1125-
.get(c)
1126-
.is_some_and(|a| core_cmds.contains(*a))
1127-
})
1128-
});
1114+
.map(|s| canonical_api_name(s.as_str()))
1115+
.filter(|s| build_api_set.contains(s))
1116+
.collect();
1117+
1118+
if ext_build_apis.is_empty() {
1119+
continue;
1120+
}
1121+
1122+
let dominated = ext_build_apis.iter().all(|api| {
1123+
let Some(core_cmds) = baseline_core_cmds.get(*api) else {
1124+
// This API has no baseline → extension is still needed.
1125+
return false;
1126+
};
1127+
// Collect all commands this extension contributes for this API.
1128+
let ext_cmds: Vec<&str> = ext
1129+
.requires
1130+
.iter()
1131+
.filter(|req| api_profile_matches(req.api.as_deref(), None, api, None))
1132+
.flat_map(|req| req.commands.iter().map(String::as_str))
1133+
.collect();
1134+
// Extension must have at least one command, and ALL must be
1135+
// in the baseline core (directly or via alias).
1136+
!ext_cmds.is_empty()
1137+
&& ext_cmds.iter().all(|c| {
1138+
core_cmds.contains(*c)
1139+
|| baseline_cmd_aliases
1140+
.get(c)
1141+
.is_some_and(|a| core_cmds.contains(*a))
1142+
})
1143+
});
11291144

11301145
if dominated {
11311146
baseline_excludes.insert(ext.name.clone());

0 commit comments

Comments
 (0)