Skip to content

Allow user to specify aliasing model for Miri #1074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/base/cargo-miri-playground
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 17 additions & 1 deletion compiler/base/orchestrator/src/coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -654,6 +660,7 @@ pub struct MiriRequest {
pub channel: Channel,
pub crate_type: CrateType,
pub edition: Edition,
pub aliasing_model: AliasingModel,
pub code: String,
}

Expand All @@ -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,
}
}
Expand Down Expand Up @@ -3939,6 +3954,7 @@ mod tests {
channel: Channel::Nightly,
crate_type: CrateType::Binary,
edition: Edition::Rust2021,
aliasing_model: AliasingModel::Stacked,
code: String::new(),
};

Expand Down
2 changes: 1 addition & 1 deletion compiler/build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash

set -euv -o pipefail

Expand Down
2 changes: 1 addition & 1 deletion compiler/fetch.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash

set -euv -o pipefail

Expand Down
2 changes: 1 addition & 1 deletion tests/spec/features/backtrace_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

context "backtraces are enabled" do
before do
in_advanced_options_menu { choose 'enabled' }
in_advanced_options_menu { within(:config_option, 'Backtrace') { choose 'On' } }
within(:header) { click_on("Run") }
end

Expand Down
24 changes: 24 additions & 0 deletions tests/spec/features/tools_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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") }
Expand Down
6 changes: 6 additions & 0 deletions tests/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@
css { '[data-test-id = "notification"]' }
end

Capybara.add_selector(:config_option) do
xpath do |label|
".//div[span[contains(., '#{label}')]]"
end
end

RSpec.configure do |config|
config.after(:example, :js) do
page.execute_script <<~JS
Expand Down
2 changes: 2 additions & 0 deletions ui/frontend/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
91 changes: 64 additions & 27 deletions ui/frontend/AdvancedOptionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,83 @@
import React, { useCallback } from 'react';

import * as config from './reducers/configuration';
import { Either as EitherConfig, Select as SelectConfig } from './ConfigElement';
import MenuAside from './MenuAside';
import MenuGroup from './MenuGroup';
import * as selectors from './selectors';
import { Backtrace, Edition } from './types';
import { useAppDispatch, useAppSelector } from './hooks';
import * as config from './reducers/configuration';
import * as selectors from './selectors';
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 = () => (
<MenuAside>
Code that is accepted by <a href={MIRI_TREE_BORROWS_URL}>Tree Borrows</a> may be declared
undefined behavior in the future.
</MenuAside>
);

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 isAliasingModelDefault = useAppSelector(selectors.isAliasingModelDefault);
const aliasingModel = useAppSelector((state) => state.configuration.aliasingModel);

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],
);
const changeAliasingModel = useCallback(
(b: AliasingModel) => dispatch(config.changeAliasingModel(b)),
[dispatch],
);

return (
<MenuGroup title="Advanced options">
<SelectConfig
name="Edition"
value={edition}
isNotDefault={!isEditionDefault}
onChange={changeEdition}
>
<option value={Edition.Rust2015}>2015</option>
<option value={Edition.Rust2018}>2018</option>
<option value={Edition.Rust2021}>2021</option>
<option value={Edition.Rust2024}>2024</option>
</SelectConfig>

<EitherConfig
id="backtrace"
name="Backtrace"
a={Backtrace.Disabled}
b={Backtrace.Enabled}
value={backtrace}
isNotDefault={isBacktraceSet}
onChange={changeBacktrace} />
</MenuGroup>
<>
<MenuGroup title="Advanced options">
<SelectConfig
name="Edition"
value={edition}
isDefault={isEditionDefault}
onChange={changeEdition}
>
<option value={Edition.Rust2015}>2015</option>
<option value={Edition.Rust2018}>2018</option>
<option value={Edition.Rust2021}>2021</option>
<option value={Edition.Rust2024}>2024</option>
</SelectConfig>

<EitherConfig
id="backtrace"
name="Backtrace"
a={Backtrace.Enabled}
b={Backtrace.Disabled}
aLabel="On"
bLabel="Off"
value={backtrace}
isDefault={isBacktraceDefault}
onChange={changeBacktrace}
/>
</MenuGroup>

<MenuGroup title="Miri">
<EitherConfig
id="aliasingModel"
name="Aliasing model"
a={AliasingModel.Stacked}
b={AliasingModel.Tree}
value={aliasingModel}
isDefault={isAliasingModelDefault}
onChange={changeAliasingModel}
aside={<TreeBorrowAside />}
/>
</MenuGroup>
</>
);
};

Expand Down
90 changes: 55 additions & 35 deletions ui/frontend/ConfigElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,54 @@ interface EitherProps<T extends string> extends ConfigElementProps {
onChange: (_: T) => void;
}

export const Either =
<T extends string,>({ id, a, b, aLabel = a, bLabel = b, value, onChange, ...rest }: EitherProps<T>) => (
<ConfigElement {...rest}>
<div className={styles.toggle}>
<input id={`${id}-a`}
name={id}
value={a}
type="radio"
checked={value === a}
onChange={() => onChange(a as T)} />
<label htmlFor={`${id}-a`}>{aLabel}</label>
<input id={`${id}-b`}
name={id}
value={b}
type="radio"
checked={value === b}
onChange={() => onChange(b as T)} />
<label htmlFor={`${id}-b`}>{bLabel}</label>
</div>
</ConfigElement>
);
export const Either = <T extends string>({
id,
a,
b,
aLabel = a,
bLabel = b,
value,
onChange,
...rest
}: EitherProps<T>) => (
<ConfigElement {...rest}>
<div className={styles.toggle}>
<input
id={`${id}-a`}
name={id}
value={a}
type="radio"
checked={value === a}
onChange={() => onChange(a as T)}
/>
<label htmlFor={`${id}-a`}>{aLabel}</label>
<input
id={`${id}-b`}
name={id}
value={b}
type="radio"
checked={value === b}
onChange={() => onChange(b as T)}
/>
<label htmlFor={`${id}-b`}>{bLabel}</label>
</div>
</ConfigElement>
);

interface SelectProps<T extends string> extends ConfigElementProps {
children: React.ReactNode;
value: T;
onChange: (_: T) => void;
}

export const Select = <T extends string,>({ value, onChange, children, ...rest }: SelectProps<T>) => (
export const Select = <T extends string>({
value,
onChange,
children,
...rest
}: SelectProps<T>) => (
<ConfigElement {...rest}>
<select className={styles.select} value={value} onChange={e => onChange(e.target.value as T)}>
<select className={styles.select} value={value} onChange={(e) => onChange(e.target.value as T)}>
{children}
</select>
</ConfigElement>
Expand All @@ -53,18 +70,21 @@ export const Select = <T extends string,>({ value, onChange, children, ...rest }
interface ConfigElementProps {
children?: React.ReactNode;
name: string;
isNotDefault?: boolean;
aside?: JSX.Element,
isDefault?: boolean;
aside?: JSX.Element;
}

const ConfigElement: React.FC<ConfigElementProps> = ({ name, isNotDefault, aside, children }) => (
<MenuItem>
<div className={styles.container}>
<span className={isNotDefault ? styles.notDefault : styles.name}>{name}</span>
<div className={styles.value}>
{children}
const ConfigElement: React.FC<ConfigElementProps> = ({ name, isDefault, aside, children }) => {
const actuallyDefault = isDefault ?? true;
const defaultStyle = actuallyDefault ? styles.name : styles.notDefault;

return (
<MenuItem>
<div className={styles.container}>
<span className={defaultStyle}>{name}</span>
<div className={styles.value}>{children}</div>
</div>
</div>
{aside}
</MenuItem>
);
{aside}
</MenuItem>
);
};
8 changes: 8 additions & 0 deletions ui/frontend/reducers/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit';

import {
AliasingModel,
AssemblyFlavor,
Backtrace,
Channel,
Expand Down Expand Up @@ -36,6 +37,7 @@ interface State {
mode: Mode;
edition: Edition;
backtrace: Backtrace;
aliasingModel: AliasingModel;
}

const initialState: State = {
Expand All @@ -58,6 +60,7 @@ const initialState: State = {
mode: Mode.Debug,
edition: Edition.Rust2024,
backtrace: Backtrace.Disabled,
aliasingModel: AliasingModel.Stacked,
};

const slice = createSlice({
Expand All @@ -76,6 +79,10 @@ const slice = createSlice({
state.backtrace = action.payload;
},

changeAliasingModel: (state, action: PayloadAction<AliasingModel>) => {
state.aliasingModel = action.payload;
},

changeChannel: (state, action: PayloadAction<Channel>) => {
state.channel = action.payload;
},
Expand Down Expand Up @@ -146,6 +153,7 @@ export const {
changeAceTheme,
changeAssemblyFlavor,
changeBacktrace,
changeAliasingModel,
changeChannel,
changeDemangleAssembly,
changeEdition,
Expand Down
Loading
Loading