From 9ce6f526337ad92f5001f36a0c023294d3e4b312 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Fri, 28 Feb 2025 10:34:59 -0800 Subject: [PATCH 1/6] Apply prettier to AdvancedOptionsMenu, ConfigElement --- ui/frontend/.prettierignore | 2 + ui/frontend/AdvancedOptionsMenu.tsx | 12 +++-- ui/frontend/ConfigElement.tsx | 69 ++++++++++++++++++----------- 3 files changed, 52 insertions(+), 31 deletions(-) diff --git a/ui/frontend/.prettierignore b/ui/frontend/.prettierignore index 2b484bba..3be14a17 100644 --- a/ui/frontend/.prettierignore +++ b/ui/frontend/.prettierignore @@ -12,8 +12,10 @@ node_modules *.scss # Slowly migrate files that we've touched +!AdvancedOptionsMenu.tsx !BuildMenu.tsx !ButtonSet.tsx +!ConfigElement.tsx !Header.tsx !HelpExample.tsx !Notifications.tsx diff --git a/ui/frontend/AdvancedOptionsMenu.tsx b/ui/frontend/AdvancedOptionsMenu.tsx index 4e73ba6b..afdfe2c1 100644 --- a/ui/frontend/AdvancedOptionsMenu.tsx +++ b/ui/frontend/AdvancedOptionsMenu.tsx @@ -1,11 +1,11 @@ import React, { useCallback } from 'react'; -import * as config from './reducers/configuration'; import { Either as EitherConfig, Select as SelectConfig } from './ConfigElement'; import MenuGroup from './MenuGroup'; +import { useAppDispatch, useAppSelector } from './hooks'; +import * as config from './reducers/configuration'; import * as selectors from './selectors'; import { Backtrace, Edition } from './types'; -import { useAppDispatch, useAppSelector } from './hooks'; const AdvancedOptionsMenu: React.FC = () => { const isEditionDefault = useAppSelector(selectors.isEditionDefault); @@ -16,7 +16,10 @@ const AdvancedOptionsMenu: React.FC = () => { const dispatch = useAppDispatch(); const changeEdition = useCallback((e: Edition) => dispatch(config.changeEdition(e)), [dispatch]); - const changeBacktrace = useCallback((b: Backtrace) => dispatch(config.changeBacktrace(b)), [dispatch]); + const changeBacktrace = useCallback( + (b: Backtrace) => dispatch(config.changeBacktrace(b)), + [dispatch], + ); return ( @@ -39,7 +42,8 @@ const AdvancedOptionsMenu: React.FC = () => { b={Backtrace.Enabled} value={backtrace} isNotDefault={isBacktraceSet} - onChange={changeBacktrace} /> + onChange={changeBacktrace} + /> ); }; diff --git a/ui/frontend/ConfigElement.tsx b/ui/frontend/ConfigElement.tsx index d3e1b2ad..1943b0dc 100644 --- a/ui/frontend/ConfigElement.tsx +++ b/ui/frontend/ConfigElement.tsx @@ -14,27 +14,39 @@ interface EitherProps extends ConfigElementProps { onChange: (_: T) => void; } -export const Either = - ({ id, a, b, aLabel = a, bLabel = b, value, onChange, ...rest }: EitherProps) => ( - -
- onChange(a as T)} /> - - onChange(b as T)} /> - -
-
- ); +export const Either = ({ + id, + a, + b, + aLabel = a, + bLabel = b, + value, + onChange, + ...rest +}: EitherProps) => ( + +
+ onChange(a as T)} + /> + + onChange(b as T)} + /> + +
+
+); interface SelectProps extends ConfigElementProps { children: React.ReactNode; @@ -42,9 +54,14 @@ interface SelectProps extends ConfigElementProps { onChange: (_: T) => void; } -export const Select = ({ value, onChange, children, ...rest }: SelectProps) => ( +export const Select = ({ + value, + onChange, + children, + ...rest +}: SelectProps) => ( - onChange(e.target.value as T)}> {children} @@ -54,16 +71,14 @@ interface ConfigElementProps { children?: React.ReactNode; name: string; isNotDefault?: boolean; - aside?: JSX.Element, + aside?: JSX.Element; } const ConfigElement: React.FC = ({ name, isNotDefault, aside, children }) => (
{name} -
- {children} -
+
{children}
{aside}
From 7c6bb8ef0a01b6af3a08f88f99d0d7d95b1d11d5 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Fri, 28 Feb 2025 10:55:28 -0800 Subject: [PATCH 2/6] Consistently check if advanced options are set --- ui/frontend/AdvancedOptionsMenu.tsx | 4 ++-- ui/frontend/selectors/index.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/frontend/AdvancedOptionsMenu.tsx b/ui/frontend/AdvancedOptionsMenu.tsx index afdfe2c1..bdf5c5f6 100644 --- a/ui/frontend/AdvancedOptionsMenu.tsx +++ b/ui/frontend/AdvancedOptionsMenu.tsx @@ -10,7 +10,7 @@ import { Backtrace, Edition } from './types'; const AdvancedOptionsMenu: React.FC = () => { const isEditionDefault = useAppSelector(selectors.isEditionDefault); const edition = useAppSelector((state) => state.configuration.edition); - const isBacktraceSet = useAppSelector(selectors.getBacktraceSet); + const isBacktraceDefault = useAppSelector(selectors.isBacktraceDefault); const backtrace = useAppSelector((state) => state.configuration.backtrace); const dispatch = useAppDispatch(); @@ -41,7 +41,7 @@ const AdvancedOptionsMenu: React.FC = () => { a={Backtrace.Disabled} b={Backtrace.Enabled} value={backtrace} - isNotDefault={isBacktraceSet} + isNotDefault={!isBacktraceDefault} onChange={changeBacktrace} /> diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index 09805f1a..765ff0e4 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -172,18 +172,18 @@ export const getChannelLabel = createSelector(channelSelector, (channel) => `${c export const isEditionDefault = createSelector( editionSelector, - edition => edition == Edition.Rust2024, + edition => edition === Edition.Rust2024, ); -export const getBacktraceSet = (state: State) => ( - state.configuration.backtrace !== Backtrace.Disabled +export const isBacktraceDefault = (state: State) => ( + state.configuration.backtrace === Backtrace.Disabled ); +export const getBacktraceSet = createSelector(isBacktraceDefault, (b) => !b); + export const getAdvancedOptionsSet = createSelector( - isEditionDefault, getBacktraceSet, - (editionDefault, backtraceSet) => ( - !editionDefault || backtraceSet - ), + isEditionDefault, isBacktraceDefault, + (...areDefault) => !areDefault.every(n => n), ); export const hasProperties = (obj: object) => Object.values(obj).some(val => !!val); From 01bb4abc4d8bdac66cbc6ef062aff0b152c6fc03 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Fri, 28 Feb 2025 11:08:14 -0800 Subject: [PATCH 3/6] Remove double negation in ConfigElement --- ui/frontend/AdvancedOptionsMenu.tsx | 4 ++-- ui/frontend/ConfigElement.tsx | 25 +++++++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ui/frontend/AdvancedOptionsMenu.tsx b/ui/frontend/AdvancedOptionsMenu.tsx index bdf5c5f6..aed0c00f 100644 --- a/ui/frontend/AdvancedOptionsMenu.tsx +++ b/ui/frontend/AdvancedOptionsMenu.tsx @@ -26,7 +26,7 @@ const AdvancedOptionsMenu: React.FC = () => { @@ -41,7 +41,7 @@ const AdvancedOptionsMenu: React.FC = () => { a={Backtrace.Disabled} b={Backtrace.Enabled} value={backtrace} - isNotDefault={!isBacktraceDefault} + isDefault={isBacktraceDefault} onChange={changeBacktrace} /> diff --git a/ui/frontend/ConfigElement.tsx b/ui/frontend/ConfigElement.tsx index 1943b0dc..02f5ce67 100644 --- a/ui/frontend/ConfigElement.tsx +++ b/ui/frontend/ConfigElement.tsx @@ -70,16 +70,21 @@ export const Select = ({ interface ConfigElementProps { children?: React.ReactNode; name: string; - isNotDefault?: boolean; + isDefault?: boolean; aside?: JSX.Element; } -const ConfigElement: React.FC = ({ name, isNotDefault, aside, children }) => ( - -
- {name} -
{children}
-
- {aside} -
-); +const ConfigElement: React.FC = ({ name, isDefault, aside, children }) => { + const actuallyDefault = isDefault ?? true; + const defaultStyle = actuallyDefault ? styles.name : styles.notDefault; + + return ( + +
+ {name} +
{children}
+
+ {aside} +
+ ); +}; From add6f6b87d76cc8a662fb223ae481e47d1dbd810 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 3 Mar 2025 13:38:19 -0500 Subject: [PATCH 4/6] Make the backtrace configuration UI match other booleans This has the side benefit of not exceeding the width of the config widget. --- ui/frontend/AdvancedOptionsMenu.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/frontend/AdvancedOptionsMenu.tsx b/ui/frontend/AdvancedOptionsMenu.tsx index aed0c00f..61161084 100644 --- a/ui/frontend/AdvancedOptionsMenu.tsx +++ b/ui/frontend/AdvancedOptionsMenu.tsx @@ -38,8 +38,10 @@ const AdvancedOptionsMenu: React.FC = () => { Date: Mon, 22 Jul 2024 19:32:59 +0300 Subject: [PATCH 5/6] Use portable shebang in `compiler/*.sh` `#!/bin/bash` is non-standard and doesn't work on e.g. NixOS. --- compiler/build.sh | 2 +- compiler/fetch.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/build.sh b/compiler/build.sh index c7a4cb60..9f17daa3 100755 --- a/compiler/build.sh +++ b/compiler/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euv -o pipefail diff --git a/compiler/fetch.sh b/compiler/fetch.sh index ce455ff6..3b74b274 100755 --- a/compiler/fetch.sh +++ b/compiler/fetch.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euv -o pipefail From 0e65195d5aa78eef18eb4ebc7685eec59d0cc93a Mon Sep 17 00:00:00 2001 From: Goldstein Date: Mon, 22 Jul 2024 20:14:29 +0300 Subject: [PATCH 6/6] Allow user to specify aliasing model for Miri --- compiler/base/cargo-miri-playground | 2 +- compiler/base/orchestrator/src/coordinator.rs | 18 +++- tests/spec/features/tools_spec.rb | 24 ++++++ ui/frontend/AdvancedOptionsMenu.tsx | 83 +++++++++++++------ ui/frontend/reducers/configuration.ts | 8 ++ ui/frontend/reducers/output/miri.ts | 2 + ui/frontend/selectors/index.ts | 12 ++- ui/frontend/types.ts | 5 ++ ui/src/metrics.rs | 1 + ui/src/public_http_api.rs | 2 + ui/src/server_axum.rs | 28 ++++++- 11 files changed, 154 insertions(+), 31 deletions(-) diff --git a/compiler/base/cargo-miri-playground b/compiler/base/cargo-miri-playground index ae6e3740..74f393ec 100755 --- a/compiler/base/cargo-miri-playground +++ b/compiler/base/cargo-miri-playground @@ -3,5 +3,5 @@ set -eu export MIRI_SYSROOT=~/.cache/miri -export MIRIFLAGS="-Zmiri-disable-isolation" +export MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-disable-isolation" exec cargo miri run diff --git a/compiler/base/orchestrator/src/coordinator.rs b/compiler/base/orchestrator/src/coordinator.rs index 75352fcb..62f24e54 100644 --- a/compiler/base/orchestrator/src/coordinator.rs +++ b/compiler/base/orchestrator/src/coordinator.rs @@ -239,6 +239,12 @@ pub enum Channel { Nightly, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum AliasingModel { + Stacked, + Tree, +} + impl Channel { #[cfg(test)] pub(crate) const ALL: [Self; 3] = [Self::Stable, Self::Beta, Self::Nightly]; @@ -654,6 +660,7 @@ pub struct MiriRequest { pub channel: Channel, pub crate_type: CrateType, pub edition: Edition, + pub aliasing_model: AliasingModel, pub code: String, } @@ -667,10 +674,18 @@ impl LowerRequest for MiriRequest { } fn execute_cargo_request(&self) -> ExecuteCommandRequest { + let mut miriflags = Vec::new(); + + if matches!(self.aliasing_model, AliasingModel::Tree) { + miriflags.push("-Zmiri-tree-borrows"); + } + + let miriflags = miriflags.join(" "); + ExecuteCommandRequest { cmd: "cargo".to_owned(), args: vec!["miri-playground".to_owned()], - envs: Default::default(), + envs: HashMap::from_iter([("MIRIFLAGS".to_owned(), miriflags)]), cwd: None, } } @@ -3939,6 +3954,7 @@ mod tests { channel: Channel::Nightly, crate_type: CrateType::Binary, edition: Edition::Rust2021, + aliasing_model: AliasingModel::Stacked, code: String::new(), }; diff --git a/tests/spec/features/tools_spec.rb b/tests/spec/features/tools_spec.rb index e20252dd..0c1400f1 100644 --- a/tests/spec/features/tools_spec.rb +++ b/tests/spec/features/tools_spec.rb @@ -54,6 +54,30 @@ def code_with_undefined_behavior EOF end + scenario "configure Miri for tree borrows" do + editor.set code_valid_under_tree_borrows_but_not_stacked_borrows + in_advanced_options_menu { choose("tree") } + in_tools_menu { click_on("Miri") } + + within(:output, :stdout) do + expect(page).to have_content %r{[1, 2]}, wait: 10 + end + + within(:output, :stderr) do + expect(page).to_not have_content %r{Undefined Behavior} + end + end + + def code_valid_under_tree_borrows_but_not_stacked_borrows + <<~EOF + fn main() { + let val = [1u8, 2]; + let ptr = &val[0] as *const u8; + let _val = unsafe { *ptr.add(1) }; + } + EOF + end + scenario "expand macros with the nightly compiler" do editor.set code_that_uses_macros in_tools_menu { click_on("Expand macros") } diff --git a/ui/frontend/AdvancedOptionsMenu.tsx b/ui/frontend/AdvancedOptionsMenu.tsx index 61161084..167aee57 100644 --- a/ui/frontend/AdvancedOptionsMenu.tsx +++ b/ui/frontend/AdvancedOptionsMenu.tsx @@ -1,17 +1,29 @@ import React, { useCallback } from 'react'; import { Either as EitherConfig, Select as SelectConfig } from './ConfigElement'; +import MenuAside from './MenuAside'; import MenuGroup from './MenuGroup'; import { useAppDispatch, useAppSelector } from './hooks'; import * as config from './reducers/configuration'; import * as selectors from './selectors'; -import { Backtrace, Edition } from './types'; +import { AliasingModel, Backtrace, Edition } from './types'; + +const MIRI_TREE_BORROWS_URL = 'https://github.com/rust-lang/miri#user-content--zmiri-tree-borrows'; + +const TreeBorrowAside: React.FC = () => ( + + Code that is accepted by Tree Borrows may be declared + undefined behavior in the future. + +); const AdvancedOptionsMenu: React.FC = () => { const isEditionDefault = useAppSelector(selectors.isEditionDefault); const edition = useAppSelector((state) => state.configuration.edition); const isBacktraceDefault = useAppSelector(selectors.isBacktraceDefault); const backtrace = useAppSelector((state) => state.configuration.backtrace); + const isAliasingModelDefault = useAppSelector(selectors.isAliasingModelDefault); + const aliasingModel = useAppSelector((state) => state.configuration.aliasingModel); const dispatch = useAppDispatch(); @@ -20,33 +32,52 @@ const AdvancedOptionsMenu: React.FC = () => { (b: Backtrace) => dispatch(config.changeBacktrace(b)), [dispatch], ); + const changeAliasingModel = useCallback( + (b: AliasingModel) => dispatch(config.changeAliasingModel(b)), + [dispatch], + ); return ( - - - - - - - - - - + <> + + + + + + + + + + + + + } + /> + + ); }; diff --git a/ui/frontend/reducers/configuration.ts b/ui/frontend/reducers/configuration.ts index 37752074..23fb9b4c 100644 --- a/ui/frontend/reducers/configuration.ts +++ b/ui/frontend/reducers/configuration.ts @@ -1,6 +1,7 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; import { + AliasingModel, AssemblyFlavor, Backtrace, Channel, @@ -36,6 +37,7 @@ interface State { mode: Mode; edition: Edition; backtrace: Backtrace; + aliasingModel: AliasingModel; } const initialState: State = { @@ -58,6 +60,7 @@ const initialState: State = { mode: Mode.Debug, edition: Edition.Rust2024, backtrace: Backtrace.Disabled, + aliasingModel: AliasingModel.Stacked, }; const slice = createSlice({ @@ -76,6 +79,10 @@ const slice = createSlice({ state.backtrace = action.payload; }, + changeAliasingModel: (state, action: PayloadAction) => { + state.aliasingModel = action.payload; + }, + changeChannel: (state, action: PayloadAction) => { state.channel = action.payload; }, @@ -146,6 +153,7 @@ export const { changeAceTheme, changeAssemblyFlavor, changeBacktrace, + changeAliasingModel, changeChannel, changeDemangleAssembly, changeEdition, diff --git a/ui/frontend/reducers/output/miri.ts b/ui/frontend/reducers/output/miri.ts index 0c52f12d..0ba94e84 100644 --- a/ui/frontend/reducers/output/miri.ts +++ b/ui/frontend/reducers/output/miri.ts @@ -4,6 +4,7 @@ import * as z from 'zod'; import { jsonPost, routes } from '../../api'; import { State as RootState } from '../../reducers'; import { miriRequestSelector } from '../../selectors'; +import { AliasingModel } from '../../types'; const sliceName = 'output/miri'; @@ -21,6 +22,7 @@ interface State { interface MiriRequestBody { code: string; edition: string; + aliasingModel: AliasingModel; } const MiriResponseBody = z.object({ diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index 765ff0e4..69b93912 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -6,6 +6,7 @@ import { Backtrace, Channel, Edition, + AliasingModel, Focus, Orientation, PrimaryActionAuto, @@ -147,6 +148,7 @@ export const rustfmtVersionDetailsText = createSelector(getRustfmt, versionDetai export const miriVersionDetailsText = createSelector(getMiri, versionDetails); const editionSelector = (state: State) => state.configuration.edition; +export const aliasingModelSelector = (state: State) => state.configuration.aliasingModel; export const isNightlyChannel = createSelector( channelSelector, @@ -181,8 +183,13 @@ export const isBacktraceDefault = (state: State) => ( export const getBacktraceSet = createSelector(isBacktraceDefault, (b) => !b); +export const isAliasingModelDefault = createSelector( + aliasingModelSelector, + aliasingModel => aliasingModel == AliasingModel.Stacked, +); + export const getAdvancedOptionsSet = createSelector( - isEditionDefault, isBacktraceDefault, + isEditionDefault, isBacktraceDefault, isAliasingModelDefault, (...areDefault) => !areDefault.every(n => n), ); @@ -391,7 +398,8 @@ export const formatRequestSelector = createSelector( export const miriRequestSelector = createSelector( editionSelector, codeSelector, - (edition, code) => ({ edition, code }), + aliasingModelSelector, + (edition, code, aliasingModel) => ({ edition, code, aliasingModel }), ); export const macroExpansionRequestSelector = createSelector( diff --git a/ui/frontend/types.ts b/ui/frontend/types.ts index 4b49511c..eb06e7ab 100644 --- a/ui/frontend/types.ts +++ b/ui/frontend/types.ts @@ -150,6 +150,11 @@ export enum Backtrace { Enabled = 'enabled', } +export enum AliasingModel { + Stacked = 'stacked', + Tree = 'tree', +} + export enum Focus { Clippy = 'clippy', Miri = 'miri', diff --git a/ui/src/metrics.rs b/ui/src/metrics.rs index b1d7ab9a..973e5d4e 100644 --- a/ui/src/metrics.rs +++ b/ui/src/metrics.rs @@ -361,6 +361,7 @@ impl HasLabelsCore for coordinator::MiriRequest { channel, crate_type, edition, + aliasing_model: _, code: _, } = *self; diff --git a/ui/src/public_http_api.rs b/ui/src/public_http_api.rs index 6f6a32b4..6ca71b9f 100644 --- a/ui/src/public_http_api.rs +++ b/ui/src/public_http_api.rs @@ -103,6 +103,8 @@ pub(crate) struct MiriRequest { pub(crate) code: String, #[serde(default)] pub(crate) edition: String, + #[serde(default, rename = "aliasingModel")] + pub(crate) aliasing_model: Option, } #[derive(Debug, Clone, Serialize)] diff --git a/ui/src/server_axum.rs b/ui/src/server_axum.rs index ec65ba46..f4d38393 100644 --- a/ui/src/server_axum.rs +++ b/ui/src/server_axum.rs @@ -1285,12 +1285,22 @@ pub(crate) mod api_orchestrator_integration_impls { type Error = ParseMiriRequestError; fn try_from(other: api::MiriRequest) -> std::result::Result { - let api::MiriRequest { code, edition } = other; + let api::MiriRequest { + code, + edition, + aliasing_model, + } = other; + + let aliasing_model = match aliasing_model { + Some(am) => parse_aliasing_model(&am)?, + None => AliasingModel::Stacked, + }; Ok(MiriRequest { channel: Channel::Nightly, // TODO: use what user has submitted crate_type: CrateType::Binary, // TODO: use what user has submitted edition: parse_edition(&edition)?, + aliasing_model, code, }) } @@ -1300,6 +1310,8 @@ pub(crate) mod api_orchestrator_integration_impls { pub(crate) enum ParseMiriRequestError { #[snafu(transparent)] Edition { source: ParseEditionError }, + #[snafu(transparent)] + AliasingMode { source: ParseAliasingModelError }, } impl From> for api::MiriResponse { @@ -1521,6 +1533,20 @@ pub(crate) mod api_orchestrator_integration_impls { value: String, } + pub(crate) fn parse_aliasing_model(s: &str) -> Result { + Ok(match s { + "stacked" => AliasingModel::Stacked, + "tree" => AliasingModel::Tree, + value => return ParseAliasingModelSnafu { value }.fail(), + }) + } + + #[derive(Debug, Snafu)] + #[snafu(display("'{value}' is not a valid aliasing model"))] + pub(crate) struct ParseAliasingModelError { + value: String, + } + impl From for api::MetaGistResponse { fn from(me: gist::Gist) -> Self { api::MetaGistResponse {