profile
viewpoint

tmandry/AXSwift 119

Swift wrapper for accessibility clients

tmandry/lavender 47

Generate Visual Studio projects for Bazel (experimental)

tmandry/great-start 2

Great starting point for Rails projects. Includes Mongoid, Devise, RSpec, Spork, Guard, Slim, and Bootstrap.

tmandry/django-fresh 1

Auto-refreshes your browser after updating files in your project in a development environment.

kickasstimus/IOBoard 0

IOBoard is a simple in/out board that shows who is in or out of the office and whether they are available.

tmandry/adage 0

2D game and AI engine built using Qt/C++ (high school side project)

tmandry/agent 0

The configuration application of the Ultimate Hacking Keyboard

tmandry/alloy 0

Toy OS project in Rust

tmandry/askbot-devel 0

ASKBOT is a StackOverflow-like Q&A forum, based on CNPROG.

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -         // an `if condition { block }` has a span that includes the executed block, if true,         // but for coverage, the code region executed, up to *and* through the SwitchInt,         // actually stops before the if's block.)-        TerminatorKind::Unreachable // Unreachable blocks are not connected to the CFG+        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG         | TerminatorKind::Assert { .. }         | TerminatorKind::Drop { .. }         | TerminatorKind::DropAndReplace { .. }         | TerminatorKind::SwitchInt { .. }-        | TerminatorKind::Goto { .. }         // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.+        // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons+        // described below. Add tests to confirm whether or not similar cases also apply to+        // `FalseEdge`.         | TerminatorKind::FalseEdge { .. } => None, +        // FIXME(richkadel): Note that `Goto` was initially filtered out (by returning `None`, as

Might be worth opening an issue to track all the kinds of "span weirdness" encountered and collect FIXMEs like this. I'd like to see some discussion about what we could change in MIR building / other upstream code.

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -         // an `if condition { block }` has a span that includes the executed block, if true,         // but for coverage, the code region executed, up to *and* through the SwitchInt,         // actually stops before the if's block.)-        TerminatorKind::Unreachable // Unreachable blocks are not connected to the CFG+        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG         | TerminatorKind::Assert { .. }         | TerminatorKind::Drop { .. }         | TerminatorKind::DropAndReplace { .. }         | TerminatorKind::SwitchInt { .. }-        | TerminatorKind::Goto { .. }         // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.+        // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons+        // described below. Add tests to confirm whether or not similar cases also apply to+        // `FalseEdge`.         | TerminatorKind::FalseEdge { .. } => None, +        // FIXME(richkadel): Note that `Goto` was initially filtered out (by returning `None`, as+        // with the `TerminatorKind`s above) because its `Span` was way to broad to be beneficial,+        // and, at the time, `Goto` didn't seem to provide any additional contributions to the+        // coverage analysis. Upon further review, `Goto` terminated blocks do appear to benefit+        // the coverage analysis, and the BCB CFG. To overcome the issues with the `Spans`, the+        // coverage algorithms--and the final coverage map generation--include some exceptional+        // behaviors.+        //+        // `Goto`s are often the targets of `SwitchInt` branches, and certain important+        // optimizations to replace some `Counter`s with `Expression`s require a separate+        // `BasicCoverageBlock` for each branch, to support the `Counter`, when needed.+        //+        // Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s,+        // provided useful context for coverage, such as to count and show when `if` blocks+        // _without_ `else` blocks execute the `false` case (counting when the body of the `if`+        // was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan`+        // of 1 character, at the end of it's original `Span`.+        //+        // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto`+        // block must still be counted (for example, to contribute its count to an `Expression`+        // that reports the execution count for some other block). In these cases, the code region+        // is set to `None`.

where?

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 pub(crate) fn provide(providers: &mut Providers) { /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the /// calls may not work; but computing the number of counters or expressions by adding `1` to the /// highest ID (for a given instrumented function) is valid.+///+/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum+/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a+/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression+/// IDs referenced by expression operands, if not already seen.

What keeps us from doing this in one pass? If we're falling back to the MAX_COUNTER_GUARD anyway it seems like we could always use that?

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 impl<'a> CoverageSpanRefinery<'a> {             } else if self.curr().is_closure {                 self.carve_out_span_for_closure();             } else if self.prev_original_span == self.curr().span {+                // Note that this compares the new span to `prev_original_span`, which may not+                // be the full `prev.span` (if merged during the previous iteration).                 self.hold_pending_dups_unless_dominated();             } else {                 self.cutoff_prev_at_overlapping_curr();             }         }+         debug!("    AT END, adding last prev={:?}", self.prev());-        let pending_dups = self.pending_dups.split_off(0);-        for dup in pending_dups.into_iter() {+        let prev = self.take_prev();+        let CoverageSpans {+            mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, ..+        } = self;+        for dup in pending_dups {             debug!("    ...adding at least one pending dup={:?}", dup);-            self.add_refined_span(dup);+            refined_spans.push(dup);         }-        let prev = self.take_prev();-        self.add_refined_span(prev);--        // FIXME(richkadel): Replace some counters with expressions if they can be calculated based-        // on branching. (For example, one branch of a SwitchInt can be computed from the counter-        // for the CoverageSpan just prior to the SwitchInt minus the sum of the counters of all-        // other branches).--        self.to_refined_spans_without_closures()-    }+        refined_spans.push(prev);++        // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at+        // least one other non-empty `CoverageSpan`.+        let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes());+        for covspan in &refined_spans {+            if !covspan.span.is_empty() {+                has_coverage.insert(covspan.bcb_leader_bb);+            }+        }+        refined_spans.retain(|covspan| {+            !(covspan.span.is_empty()+                && is_goto(&mir_body[covspan.bcb_leader_bb].terminator().kind)+                && has_coverage.contains(covspan.bcb_leader_bb))+        }); -    fn add_refined_span(&mut self, coverage_span: CoverageSpan) {-        self.refined_spans.push(coverage_span);+        // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage+        // regions for the current function leave room for the closure's own coverage regions+        // (injected separately, from the closure's own MIR).+        refined_spans.retain(|covspan| !covspan.is_closure);+        refined_spans     } -    /// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage-    /// regions for the current function leave room for the closure's own coverage regions-    /// (injected separately, from the closure's own MIR).-    fn to_refined_spans_without_closures(mut self) -> Vec<CoverageSpan> {-        self.refined_spans.retain(|covspan| !covspan.is_closure);-        self.refined_spans+    // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of+    // the `BasicBlock`(s) in the given `BasicCoverageBlock`. One `CoverageSpan` is generated+    // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will+    // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple+    // `Statement`s and/or `Terminator`s.)+    fn bcb_to_initial_coverage_spans(&self, bcb: &BasicCoverageBlock) -> Vec<CoverageSpan> {+        bcb.blocks+            .iter()+            .map(|bbref| {+                let bb = *bbref;

nit: I think this would work

            .map(|ref bb| {
richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 impl<'a> CoverageSpanRefinery<'a> {             } else if self.curr().is_closure {                 self.carve_out_span_for_closure();             } else if self.prev_original_span == self.curr().span {+                // Note that this compares the new span to `prev_original_span`, which may not+                // be the full `prev.span` (if merged during the previous iteration).                 self.hold_pending_dups_unless_dominated();             } else {                 self.cutoff_prev_at_overlapping_curr();             }         }+         debug!("    AT END, adding last prev={:?}", self.prev());-        let pending_dups = self.pending_dups.split_off(0);-        for dup in pending_dups.into_iter() {+        let prev = self.take_prev();+        let CoverageSpans {+            mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, ..+        } = self;+        for dup in pending_dups {             debug!("    ...adding at least one pending dup={:?}", dup);-            self.add_refined_span(dup);+            refined_spans.push(dup);         }-        let prev = self.take_prev();-        self.add_refined_span(prev);--        // FIXME(richkadel): Replace some counters with expressions if they can be calculated based-        // on branching. (For example, one branch of a SwitchInt can be computed from the counter-        // for the CoverageSpan just prior to the SwitchInt minus the sum of the counters of all-        // other branches).--        self.to_refined_spans_without_closures()-    }+        refined_spans.push(prev);++        // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at+        // least one other non-empty `CoverageSpan`.+        let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes());+        for covspan in &refined_spans {+            if !covspan.span.is_empty() {+                has_coverage.insert(covspan.bcb_leader_bb);+            }+        }+        refined_spans.retain(|covspan| {+            !(covspan.span.is_empty()+                && is_goto(&mir_body[covspan.bcb_leader_bb].terminator().kind)+                && has_coverage.contains(covspan.bcb_leader_bb))+        }); -    fn add_refined_span(&mut self, coverage_span: CoverageSpan) {-        self.refined_spans.push(coverage_span);+        // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage+        // regions for the current function leave room for the closure's own coverage regions+        // (injected separately, from the closure's own MIR).+        refined_spans.retain(|covspan| !covspan.is_closure);+        refined_spans     } -    /// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage-    /// regions for the current function leave room for the closure's own coverage regions-    /// (injected separately, from the closure's own MIR).-    fn to_refined_spans_without_closures(mut self) -> Vec<CoverageSpan> {-        self.refined_spans.retain(|covspan| !covspan.is_closure);-        self.refined_spans+    // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of+    // the `BasicBlock`(s) in the given `BasicCoverageBlock`. One `CoverageSpan` is generated+    // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will+    // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple+    // `Statement`s and/or `Terminator`s.)+    fn bcb_to_initial_coverage_spans(&self, bcb: &BasicCoverageBlock) -> Vec<CoverageSpan> {+        bcb.blocks+            .iter()+            .map(|bbref| {+                let bb = *bbref;+                let data = &self.mir_body[bb];+                data.statements+                    .iter()+                    .enumerate()+                    .filter_map(move |(index, statement)| {+                        filtered_statement_span(statement, self.body_span).map(|span| {+                            CoverageSpan::for_statement(statement, span, bcb, bb, index)+                        })+                    })+                    .chain(+                        filtered_terminator_span(data.terminator(), self.body_span)+                            .map(|span| CoverageSpan::for_terminator(span, bcb, bb)),+                    )+            })+            .flatten()

nit: I think you can use flat_map instead of map then flatten?

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {                 );                 let counter_operand = counter.as_operand_id();                 bb_counters[bb] = Some(counter_operand);-                self.inject_statement(file_name, &source_file, counter, span, bb);+                let code_region = make_code_region(file_name, &source_file, span, body_span);+                inject_statement(self.mir_body, counter, bb, Some(code_region));             }         }+    }+} -        if let Some(span_viewables) = span_viewables {-            let mut file = pretty::create_dump_file(-                tcx,-                "html",-                None,-                self.pass_name,-                &0,-                self.mir_body.source,-            )-            .expect("Unexpected error creating MIR spanview HTML file");-            let crate_name = tcx.crate_name(def_id.krate);-            let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();-            let title = format!("{}.{} - Coverage Spans", crate_name, item_name);-            spanview::write_document(tcx, def_id, span_viewables, &title, &mut file)-                .expect("Unexpected IO error dumping coverage spans as HTML");+/// Generates the MIR pass `CoverageSpan`-specific spanview dump file.+fn dump_coverage_spanview(+    tcx: TyCtxt<'tcx>,+    mir_body: &mir::Body<'tcx>,+    basic_coverage_blocks: &BasicCoverageBlocks,+    pass_name: &str,+    coverage_spans: &Vec<CoverageSpan>,+) {+    let mir_source = mir_body.source;+    let def_id = mir_source.def_id();++    let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans);+    let mut file = pretty::create_dump_file(tcx, "html", None, pass_name, &0, mir_source)+        .expect("Unexpected error creating MIR spanview HTML file");+    let crate_name = tcx.crate_name(def_id.krate);+    let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();+    let title = format!("{}.{} - Coverage Spans", crate_name, item_name);+    spanview::write_document(tcx, def_id, span_viewables, &title, &mut file)+        .expect("Unexpected IO error dumping coverage spans as HTML");+}++/// Converts the computed `BasicCoverageBlock`s into `SpanViewable`s.+fn span_viewables(+    tcx: TyCtxt<'tcx>,+    mir_body: &mir::Body<'tcx>,+    basic_coverage_blocks: &BasicCoverageBlocks,+    coverage_spans: &Vec<CoverageSpan>,+) -> Vec<SpanViewable> {+    let mut span_viewables = Vec::new();+    for coverage_span in coverage_spans {+        let tooltip = coverage_span.format_coverage_statements(tcx, mir_body);+        let CoverageSpan { span, bcb_leader_bb: bb, .. } = coverage_span;+        let bcb = &basic_coverage_blocks[*bb];+        let id = bcb.id();+        let leader_bb = bcb.leader_bb();+        span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip });+    }+    span_viewables+}++/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR+/// `Coverage` statements.+struct CoverageCounters {+    function_source_hash: u64,+    next_counter_id: u32,+    num_expressions: u32,+}++impl CoverageCounters {+    pub fn new(function_source_hash: u64) -> Self {+        Self {+            function_source_hash,+            next_counter_id: CounterValueReference::START.as_u32(),+            num_expressions: 0,         }     } -    fn make_counter(&mut self) -> CoverageKind {+    pub fn make_counter(&mut self) -> CoverageKind {         CoverageKind::Counter {-            function_source_hash: self.function_source_hash(),+            function_source_hash: self.function_source_hash,             id: self.next_counter(),         }     } -    fn make_expression(+    pub fn make_expression(         &mut self,         lhs: ExpressionOperandId,         op: Op,         rhs: ExpressionOperandId,     ) -> CoverageKind {-        CoverageKind::Expression { id: self.next_expression(), lhs, op, rhs }+        let id = self.next_expression();+        CoverageKind::Expression { id, lhs, op, rhs }     } -    fn inject_statement(-        &mut self,-        file_name: Symbol,-        source_file: &Lrc<SourceFile>,-        coverage_kind: CoverageKind,-        span: Span,-        block: BasicBlock,-    ) {-        let code_region = make_code_region(file_name, source_file, span);-        debug!("  injecting statement {:?} covering {:?}", coverage_kind, code_region);--        let data = &mut self.mir_body[block];-        let source_info = data.terminator().source_info;-        let statement = Statement {-            source_info,-            kind: StatementKind::Coverage(box Coverage { kind: coverage_kind, code_region }),-        };-        data.statements.push(statement);+    /// Counter IDs start from one and go up.+    fn next_counter(&mut self) -> CounterValueReference {+        assert!(self.next_counter_id < u32::MAX - self.num_expressions);+        let next = self.next_counter_id;+        self.next_counter_id += 1;+        CounterValueReference::from(next)     } -    /// Converts the computed `BasicCoverageBlock`s into `SpanViewable`s.-    fn span_viewables(&self, coverage_spans: &Vec<CoverageSpan>) -> Vec<SpanViewable> {-        let tcx = self.tcx;-        let mir_body = &self.mir_body;-        let mut span_viewables = Vec::new();-        for coverage_span in coverage_spans {-            let bcb = self.bcb_from_coverage_span(coverage_span);-            let CoverageSpan { span, bcb_leader_bb: bb, coverage_statements, .. } = coverage_span;-            let id = bcb.id();-            let mut sorted_coverage_statements = coverage_statements.clone();-            sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt {-                CoverageStatement::Statement(bb, _, index) => (bb, index),-                CoverageStatement::Terminator(bb, _) => (bb, usize::MAX),-            });-            let tooltip = sorted_coverage_statements-                .iter()-                .map(|covstmt| covstmt.format(tcx, mir_body))-                .collect::<Vec<_>>()-                .join("\n");-            span_viewables.push(SpanViewable { bb: *bb, span: *span, id, tooltip });-        }-        span_viewables+    /// Expression IDs start from u32::MAX and go down because a Expression can reference+    /// (add or subtract counts) of both Counter regions and Expression regions. The counter+    /// expression operand IDs must be unique across both types.+    fn next_expression(&mut self) -> InjectedExpressionId {+        assert!(self.next_counter_id < u32::MAX - self.num_expressions);+        let next = u32::MAX - self.num_expressions;+        self.num_expressions += 1;+        InjectedExpressionId::from(next)     }+}
}

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 impl FunctionCoverage {     ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {         let mut counter_expressions = Vec::with_capacity(self.expressions.len());         let mut expression_regions = Vec::with_capacity(self.expressions.len());-        let mut new_indexes =-            IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len());-        // Note, the initial value shouldn't matter since every index in use in `self.expressions`-        // will be set, and after that, `new_indexes` will only be accessed using those same-        // indexes.--        // Note that an `ExpressionRegion`s at any given index can include other expressions as+        let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len());+        // Note that an `Expression`s at any given index can include other expressions as         // operands, but expression operands can only come from the subset of expressions having-        // `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is+        // `expression_index`s lower than the referencing `Expression`. Therefore, it is         // reasonable to look up the new index of an expression operand while the `new_indexes`         // vector is only complete up to the current `ExpressionIndex`.         let id_to_counter =

I'm not sure I follow the big picture of what's going on in here. It seems like this closure creates a new counter whose value matches an existing one? Or on closer inspection it seems like we're translating from one representation to another, which requires mapping id's somehow. Comments would help I think.

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 pub(crate) fn provide(providers: &mut Providers) { /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the /// calls may not work; but computing the number of counters or expressions by adding `1` to the /// highest ID (for a given instrumented function) is valid.+///+/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum+/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a+/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression+/// IDs referenced by expression operands, if not already seen.+///+/// Ideally, every expression operand in the MIR will have a corresponding Counter or Expression,+/// but since current or future MIR optimizations can theoretically optimize out segments of a+/// MIR, it may not be possible to guarantee this, so the second pass ensures the `CoverageInfo`+/// counts include all referenced IDs. struct CoverageVisitor {     info: CoverageInfo,+    add_missing_operands: bool,+}++impl CoverageVisitor {+    // If an expression operand is encountered with an ID outside the range of known counters and+    // expressions, the only way to determine if the ID is a counter ID or an expression ID is to+    // assume a maximum possible counter ID value.+    const MAX_COUNTER_GUARD: u32 = (u32::MAX / 2) + 1;++    #[inline(always)]+    fn update_num_counters(&mut self, counter_id: u32) {+        self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);+    }++    #[inline(always)]+    fn update_num_expressions(&mut self, expression_id: u32) {+        let expression_index = u32::MAX - expression_id;+        self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1);+    }++    fn update_from_expression_operand(&mut self, operand_id: u32) {+        if operand_id >= self.info.num_counters {+            let operand_as_expression_index = u32::MAX - operand_id;+            if operand_as_expression_index >= self.info.num_expressions {+                if operand_id <= Self::MAX_COUNTER_GUARD {+                    self.update_num_counters(operand_id)+                } else {+                    self.update_num_expressions(operand_id)+                }+            }+        }+    } }  impl Visitor<'_> for CoverageVisitor {     fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) {-        match coverage.kind {-            CoverageKind::Counter { id, .. } => {-                let counter_id = u32::from(id);-                self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);+        if self.add_missing_operands {+            match coverage.kind {+                CoverageKind::Expression { lhs, rhs, .. } => {+                    self.update_from_expression_operand(u32::from(lhs));+                    self.update_from_expression_operand(u32::from(rhs));+                }+                _ => {}             }-            CoverageKind::Expression { id, .. } => {-                let expression_index = u32::MAX - u32::from(id);-                self.info.num_expressions =-                    std::cmp::max(self.info.num_expressions, expression_index + 1);+        } else {+            match coverage.kind {+                CoverageKind::Counter { id, .. } => {+                    self.update_num_counters(u32::from(id));+                }+                CoverageKind::Expression { id, .. } => {+                    self.update_num_expressions(u32::from(id));+                }+                _ => {}             }-            _ => {}         }     } }  fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {     let mir_body = tcx.optimized_mir(def_id); -    let mut coverage_visitor =-        CoverageVisitor { info: CoverageInfo { num_counters: 0, num_expressions: 0 } };+    let mut coverage_visitor = CoverageVisitor {+        info: CoverageInfo { num_counters: 0, num_expressions: 0 },+        add_missing_operands: false,+    };      coverage_visitor.visit_body(mir_body);++    coverage_visitor.add_missing_operands = true;+    coverage_visitor.visit_body(mir_body);+     coverage_visitor.info }
}

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 pub(crate) fn provide(providers: &mut Providers) { /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the /// calls may not work; but computing the number of counters or expressions by adding `1` to the /// highest ID (for a given instrumented function) is valid.+///+/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum+/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a+/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression+/// IDs referenced by expression operands, if not already seen.+///+/// Ideally, every expression operand in the MIR will have a corresponding Counter or Expression,
/// Ideally, every counter expression id operand in the MIR will have a corresponding Counter or Expression,
richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {         let Coverage { kind, code_region } = coverage;         match kind {             CoverageKind::Counter { function_source_hash, id } => {-                if bx.add_counter_region(self.instance, function_source_hash, id, code_region) {+                let covmap_updated = if let Some(code_region) = code_region {+                    // Note: Some counters do not have code regions, but may still be referenced from+                    // expressions.+                    bx.add_coverage_counter(self.instance, function_source_hash, id, code_region)+                } else {+                    bx.set_function_source_hash(self.instance, function_source_hash)

suggestion: Maybe the code should just always call this now, and remove the argument to add_coverage_counter?

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 pub(crate) fn provide(providers: &mut Providers) { /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the /// calls may not work; but computing the number of counters or expressions by adding `1` to the /// highest ID (for a given instrumented function) is valid.+///+/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum+/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a+/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression+/// IDs referenced by expression operands, if not already seen.+///+/// Ideally, every expression operand in the MIR will have a corresponding Counter or Expression,+/// but since current or future MIR optimizations can theoretically optimize out segments of a+/// MIR, it may not be possible to guarantee this, so the second pass ensures the `CoverageInfo`+/// counts include all referenced IDs. struct CoverageVisitor {     info: CoverageInfo,+    add_missing_operands: bool,+}++impl CoverageVisitor {+    // If an expression operand is encountered with an ID outside the range of known counters and+    // expressions, the only way to determine if the ID is a counter ID or an expression ID is to+    // assume a maximum possible counter ID value.+    const MAX_COUNTER_GUARD: u32 = (u32::MAX / 2) + 1;

Do we check that we don't go beyond this anywhere?

richkadel

comment created time in 2 days

Pull request review commentrust-lang/rust

Working expression optimization, and some improvements to branch-level source coverage

 impl From<CounterValueReference> for ExpressionOperandId {     } } -impl From<InjectedExpressionIndex> for ExpressionOperandId {+impl From<&mut CounterValueReference> for ExpressionOperandId {

Surprised this needs &mut

richkadel

comment created time in 2 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentrust-lang/rust

Revert "Set .llvmbc and .llvmcmd sections as allocatable"

@bors r+ @bors rollup=iffy since this caused a big change in libstd.so, maybe there are even perf impacts?

tmandry

comment created time in 3 days

PR opened rust-lang/rust

Revert "Set .llvmbc and .llvmcmd sections as allocatable"

Reverts rust-lang/rust#77961

+2 -2

0 comment

1 changed file

pr created time in 3 days

pull request commentrust-lang/rust

Set .llvmbc and .llvmcmd sections as allocatable

Sorry, I didn't expand the diff context enough to see that this was in the embed_bitcode function.

I think we're building libstd wrong in that case. If LTO is enabled it should terminate at a dylib boundary unless -Cembed-bitcode is explicitly set, IMO.

glandium

comment created time in 3 days

create barnchrust-lang/rust

branch : revert-77961-embed-bitcode

created branch time in 3 days

pull request commentrust-lang/rust

Set .llvmbc and .llvmcmd sections as allocatable

I don't think this change was correct. According to the PR description it only matches clang's behavior when -fembed-bitcode is specified, which is not an option people typically use.

We observed a huge increase in the size of libstd.so from 1.3M to 17.5M that was bisected to this change (or one of the other changes in the rollup, but probably this one.)

glandium

comment created time in 3 days

Pull request review commentrust-lang/async-book

Add section on async ecosystem

 Now, we can handle each connection concurrently by passing `handle_connection` i The closure function takes ownership of each `TcpStream`, and is run as soon as a new `TcpStream` becomes available. As long as `handle_connection` does not block, a slow request will no longer prevent other requests from completing. ```rust,ignore-{{#include ../../examples/08_04_concurrent_tcp_server/src/main.rs:main_func}}-```\ No newline at end of file+{{#include ../../examples/09_04_concurrent_tcp_server/src/main.rs:main_func}}+```+# Serving Requests in Parallel+Our example so far has largely presented concurrency (using async code)+as an alternative to parallelism (using threads).+However, async code and threads are not mutually exclusive.+In our example, `for_each_concurrent` processes each connection concurrently on the same thread as the `main` function.

for_each_concurrent processes each connection as a single task (i.e. on the same thread), but which thread depends on the executor. "processes each connection concurrently, but on the same thread" would be more accurate.

lbernick

comment created time in 4 days

Pull request review commentrust-lang/async-book

Add section on async ecosystem

+# The Async Ecosystem+Rust currently provides only the bare essentials for writing async code.+Importantly, executors, tasks, reactors, combinators, and low-level I/O futures and traits+are not yet provided in the standard library. In the meantime,+community-provided async ecosystems fill in these gaps.++## Async Runtimes+Async runtimes are libraries used for executing async applications.+Runtimes usually bundle together a *reactor* with one or more *executors*.+Reactors provide subscription mechanisms for external events, like async I/O, interprocess communication, and timers.+In an async runtime, subscribers are typically futures representing low-level I/O operations.+Executors handle the scheduling and execution of tasks.+They keep track of running and suspended tasks, poll futures to completion, and wake tasks when they can make progress.+The word "executor" is frequently used interchangeably with "runtime".+Here, we use the word "ecosystem" to describe a runtime bundled with compatible traits and features.++## Community-Provided Async Crates++### The Futures Crate+The [`futures` crate](https://docs.rs/futures/) contains traits and functions useful for writing async code.+This includes the `Stream`, `Sink`, `AsyncRead`, and `AsyncWrite` traits, and utilities such as combinators.+These utilities and traits may eventually become part of the standard library.++`futures` has its own executor, but not its own reactor, so it does not support execution of async I/O or timer futures.+For this reason, it's not considered a full runtime.+A common choice is to use utilities from `futures` with an executor from another crate.++### Popular Async Runtimes+There is no asynchronous runtime in the standard library, and none are officially recommended.+The following crates provide popular runtimes.+- [Tokio](https://docs.rs/tokio/): A popular async ecosystem with HTTP, gRPC, and tracing frameworks.+- [async-std](https://docs.rs/async-std/): A crate that provides asynchronous counterparts to standard library components.+- [smol](https://docs.rs/smol/): A small, simplified async runtime.+Provides the `Async` trait that can be used to wrap structs like `UnixStream` or `TcpListener`.+- [fuchsia-async](https://fuchsia.googlesource.com/fuchsia/+/master/src/lib/fuchsia-async/):+An executor for use in the Fuchsia OS.++## Determining Ecosystem Compatibility+Not all async applications, frameworks, and libraries are compatible with each other, or with every OS or platform.+Most async code can be used with any ecosystem, but some frameworks and libraries require the use of a specific ecosystem.+Ecosystem constraints are not always documented, but there are several rules of thumb to determine+whether a library, trait, or function depends on a specific ecosystem.++Any async code that interacts with async I/O, timers, interprocess communication, or tasks+generally depends on a specific async executor or reactor.+All other async code, such as async expressions, combinators, synchronization types, and streams+are usually ecosystem independent, provided that any nested futures are also ecosystem independent.+Before beginning a project, it's recommended to research relevant async frameworks and libraries to ensure+compatibility with your chosen runtime and with each other.++Notably, `Tokio` uses the `mio` reactor and defines its own versions of async I/O traits,+including `AsyncRead` and `AsyncWrite`.+On its own, it's not compatible with `async-std` and `smol`,+which rely on the [`async-executor` crate](https://docs.rs/async-executor), and the `AsyncRead` and `AsyncWrite`+traits defined in `futures`.++Conflicting runtime requirements can sometimes be resolved by compatibility layers+that allow you to call code written for one runtime within another.+For example, the [`async_compat` crate](https://docs.rs/async_compat) provides a compatibility layer between+`Tokio` and other runtimes.++Libraries exposing async APIs should not depend on a specific executor or reactor,+unless they need to spawn tasks or define their own async I/O or timer futures.+Ideally, only binaries should be responsible for scheduling and running tasks.++## Single Threaded vs Multi-Threaded Executors+Async executors can be single-threaded or multi-threaded.+For example, the `async-executor` crate has both a single-threaded `LocalExecutor` and a multi-threaded `Executor`.++A multi-threaded executor makes progress on several tasks simultaneously.

nit:

A multi-threaded executor makes progress on several tasks in parallel.

since simultaneously could also be taken to mean "concurrently" which is also provided by single-threaded runtimes.

lbernick

comment created time in 4 days

PullRequestReviewEvent
PullRequestReviewEvent

create barnchtmandry/llvm-project

branch : fix-47619

created branch time in 5 days

push eventtmandry/llvm-project

Matt Arsenault

commit sha ed9e9de18c5bfbcac50fa43f973ac6614b376e20

AArch64/GlobalISel: Narrow stack passed argument access size This fixes a verifier error in the testcase from bug 47619. The stack passed s3 value was widened to 4-bytes, and producing a 4-byte memory access with a < 1 byte result type. We need to either widen the result type or narrow the access size. This copies the code directly from the AMDGPU handling, which narrows the load size. I don't like that every target has to handle this, but this is currently broken on the 11 release branch and this is the simplest fix. This reverts commit 42bfa7c63b85e76fe16521d1671afcafaf8f64ed. (cherry picked from commit 6cb0d23f2ea6fb25106b0380797ccbc2141d71e1)

view details

push time in 5 days

pull request commentrust-lang/rust

Avoid complex diagnostics in snippets which contain newlines

Sorry for the delay on my end.

I still think the new message should include the has type {} which is not {} message that we have in the other case.

Otherwise, I think this looks good.

JohnTitor

comment created time in 5 days

issue commentrust-lang/rust

#[must_use] on async fns works on returned `Future` instead of the awaited value

In the short term we could make a lint to warn people who use the attribute on async fns that it won't do anything.

hyd-dev

comment created time in 6 days

issue commentrust-lang/rust

Impossible case reached: src/librustc_ty/needs_drop.rs:111

The reproducer above doesn't ICE anymore. Leaving this open to track adding a regression test.

@rustbot modify labels: +E-easy +E-help-wanted

Thomasdezeeuw

comment created time in 6 days

pull request commentrust-lang/rust

Improve lifetime name annotations for closures & async functions

Just to check, does this fix #75785?

SNCPlay42

comment created time in 6 days

issue commentrust-lang/rust

Lifetime error points to wrong reference in async fn return

@Aaron1011 Are you still planning to work on this?

SNCPlay42

comment created time in 6 days

pull request commentrust-lang/rust

Fix rustdoc rendering of by-value mutable arguments in async fn

Ping from triage. @ebkalderon, are you still working on this?

ebkalderon

comment created time in 6 days

issue commentrust-lang/rust

More friendly error msg when await on NONE ASYNC fn/block or return a obj that implements deprecated Future

Clearing assignment since there hasn't been progress, but feel free to re-claim.

CGQAQ

comment created time in 6 days

issue commentrust-lang/rust

#[must_use] on async fns works on returned `Future` instead of the awaited value

I think this is indeed a bug, or at the very least unexpected behavior. It might be challenging to fix, since I don't know of a way to express the desired desugaring.

hyd-dev

comment created time in 6 days

pull request commentrust-lang/rust

Do not ICE on pattern that uses a binding multiple times in generator

Thanks! @bors r+ rollup

LeSeulArtichaut

comment created time in 7 days

startedwarner/magic-wormhole

started time in 8 days

Pull request review commentrust-lang/rust

JSON backend experimental impl

 pub struct ItemSummary {  #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Item {+    /// The unique identifier of this item. Can be used to find this item in various mappings.+    pub id: Id,     /// This can be used as a key to the `external_crates` map of [`Crate`] to see which crate     /// this item came from.     pub crate_id: u32,     /// Some items such as impls don't have names.     pub name: Option<String>,+    /// Whether this item is meant to be omitted from the generated documentation due to `#doc(hidden)`,+    /// because it is private, or because it was inlined.+    pub stripped: bool,

--document-private-items is really useful for generating internal docs, and I think this feature should support it, even though it technically provides a way to do what you say.

P1n3appl3

comment created time in 12 days

PullRequestReviewEvent

pull request commentrust-lang/rust

JSON backend experimental impl

The code in this PR has several tests and I think it's fine to merge as-is, once conflicts are resolved and failing tests are fixed.

@GuillaumeGomez What do you think?

@P1n3appl3 I think this is basically done. Are you able to push this over the finish line? I'd also be happy to rebase and fix it up.

P1n3appl3

comment created time in 13 days

pull request commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

@bors r+ @bors rollup=never in case there's a perf impact on async code

dingxiangfei2009

comment created time in 13 days

Pull request review commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

 impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {         NestedVisitorMap::None     } +    fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {+        if arm.guard.is_some() {+            self.guard_bindings.push(<_>::default());+        }+        self.visit_pat(&arm.pat);+        if let Some(ref g) = arm.guard {

If you use .. it sort of defeats the purpose of what I was suggesting. I'd rather list out all fields and assign them to _.

dingxiangfei2009

comment created time in 13 days

PullRequestReviewEvent

issue commentrust-lang/rust

Unhelpful "cannot infer type" error for ? in async block with unclear Result Err type

We should update the part of the code that emits the help text for closures (search for give this closure an explicit return type) to fall back to another error message for async blocks.

Maybe we can still print the type as in the closure case, just to point to the _ as the part we can't infer? It might take some experimenting to get the presentation right.

joshtriplett

comment created time in 13 days

pull request commentrust-lang/rust

Include `llvm-dis`, `llc` and `opt` in `llvm-tools-preview` component

I was just thinking about something like this last week.. thanks!

Aaron1011

comment created time in 14 days

Pull request review commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

 impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {         NestedVisitorMap::None     } +    fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {+        if arm.guard.is_some() {+            self.guard_bindings.push(<_>::default());+        }+        self.visit_pat(&arm.pat);+        if let Some(ref g) = arm.guard {

instead of accessing the arm's fields directly, could you destructure it so if new fields are added, this gets updated? e.g. let Arm { guard, pat } = arm;

This code is duplicating what normally goes in super_visit_arm() so it's helpful to have static checks like this.

dingxiangfei2009

comment created time in 14 days

Pull request review commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

 impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {                         yield_data.expr_and_pat_count, self.expr_count, source_span                     ); -                    if yield_data.expr_and_pat_count >= self.expr_count {+                    if guard_borrowing_from_pattern

nit: It may worth be restating what you said above in a comment here, i.e. that we need to record these regardless of where they appear in the post-order traversal.

dingxiangfei2009

comment created time in 18 days

Pull request review commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

 impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {         NestedVisitorMap::None     } +    fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {+        if arm.guard.is_some() {+            self.guard_bindings.push(<_>::default());+        }

This is duplicated below, I think maybe you meant to take it out?

dingxiangfei2009

comment created time in 14 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

Sorry, I was on call last week but am taking a look now.

dingxiangfei2009

comment created time in 14 days

pull request commentrust-lang/rfcs

RFC: Promote aarch64-unknown-linux-gnu to a Tier-1 Rust target

With stack probing being scheduled for implementation, my remaining concern is mismatched expectations. I think we could solve that with clear and explicit documentation.

raw-bin

comment created time in 18 days

issue openedrust-lang/triagebot

Make label a one-word command

I tend to find @rustbot modify labels to unwieldy, both to remember and type. At this point I've typed it enough times that I probably won't forget, but I remember having to look up docs several times to remember the incantation I'm supposed to use.

I think @rustbot label +foo is just as self-explanatory and easier to remember.

Could we add this as a synonym?

created time in 18 days

pull request commentrust-lang/rust

Revert "Make libunwind build hermetic"

This would end up breaking Fuchsia; https://github.com/rust-lang/rust/issues/76020#issuecomment-705275128 has more details and talks about how to fix that bug correctly.

Keruspe

comment created time in 19 days

pull request commentrust-lang/rust

rust-demangler tool strips crate disambiguators with < 16 digits

It seems pretty clear now that this is a heuristic to remove disambiguators, even if it's one that's very likely to succeed.

That does seem acceptable for the tests we're using it for, but maybe not for everything. I think we should document that it's using a heuristic in the doc comment and help text, just so people know about it before they take on a dependency.

@bors delegate+ r=tmandry when done

richkadel

comment created time in 19 days

Pull request review commentrust-lang/wg-async-foundations

Draft RFC to add a `must_not_await` lint

+# RFC: Must not await lint++# Summary++Introduce a `#[must_not_await]` lint in the compiler that will warn the user when they are incorrectly holding a struct across an await boundary.++# Motivation++Enable users to fearlessly write concurrent async code without the need to understand the internals of runtimes and how their code will be affected. The goal is to provide a best effort warning that will let the user know of a possible side effect that is not visible by reading the code right away.++One example of these side effects is holding a `MutexGuard` across an await bound. This opens up the possibility of causing a deadlock since the future holding onto the lock did not relinquish it back before it yielded control. This is a problem for futures that run on single-threaded runtimes (`!Send`) where holding a local after a yield will result in a deadlock. Even on multi-threaded runtimes, it would be nice to provide a custom error message that explains why the user doesn't want to do this instead of only a generic message about their future not being `Send`. Any other kind of RAII guard which depends on behavior similar to that of a `MutexGuard` will have the same issue.++The big reason for including a lint like this is because under the hood the compiler will automatically transform async fn into a state machine which can store locals. This process is invisible to users and will produce code that is different than what is in the actual rust file. Due to this it is important to inform users that their code may not do what they expect.++# Guide-level explanation++Provide a lint that can be attached to structs to let the compiler know that this struct can not be held across an await boundary.++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}+```++This struct if held across an await boundary would cause a deny-by-default warning:++```rust+async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++The compiler might output something along the lines of:++```+warning: `MyStruct` should not be held across an await point.+```++Example use cases for this lint:++- `MutexGuard` holding this across a yield boundary in a single threaded executor could cause deadlocks. In a multi-threaded runtime the resulting future would become `!Send` which will stop the user from spawning this future and causing issues. But in a single threaded runtime which accepts `!Send` futures deadlocks could happen.++- The same applies to other such synchronization primitives such as locks from `parking-lot`.++- `tracing::Span` has the ability to enter the span via the `tracing::span::Entered` guard. While entering a span is totally normal, during an async fn the span only needs to be entered once before the `.await` call, which might potentially yield the execution.++- Any RAII guard might possibly create unintended behavior if held across an await boundary.++This lint will enable the compiler to warn the user that the generated MIR could produce unforeseen side effects. Some examples of this are:++- [`std::sync::MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html)+- [`tracing::span::Entered`](https://docs.rs/tracing/0.1.15/tracing/span/struct.Entered.html)++This will be a best effort lint to signal the user about unintended side-effects of using certain types across an await boundary.++# Reference-level explanation++The `must_not_await` attribute is used to issue a diagnostic warning when a value is not "used". It can be applied to user-defined composite types (structs, enums and unions), functions and traits.++The `must_not_await` attribute may include a message by using the [`MetaNameValueStr`] syntax such as `#[must_not_await = "example message"]`.  The message will be given alongside the warning.++When used on a user-defined composite type, if the [expression] of an [expression statement] has this type and is used across an await point, then this lint is violated.

I don't think expressions or expression statements are necessarily right here – a let binding that's in scope during an await could cause the same behavior. A temporary could also be held across an await point in the return expression of a function, for instance.

The existing reference doesn't give a whole lot of scaffolding to work with. Maybe "if a value exists during an await point" is the best we can do for now. If we want to get more formal than "value".. "value expression, temporary, or variable"?

LucioFranco

comment created time in 19 days

Pull request review commentrust-lang/wg-async-foundations

Draft RFC to add a `must_not_await` lint

+# RFC: Must not await lint++# Summary++Introduce a `#[must_not_await]` lint in the compiler that will warn the user when they are incorrectly holding a struct across an await boundary.++# Motivation++Enable users to fearlessly write concurrent async code without the need to understand the internals of runtimes and how their code will be affected. The goal is to provide a best effort warning that will let the user know of a possible side effect that is not visible by reading the code right away.++One example of these side effects is holding a `MutexGuard` across an await bound. This opens up the possibility of causing a deadlock since the future holding onto the lock did not relinquish it back before it yielded control. This is a problem for futures that run on single-threaded runtimes (`!Send`) where holding a local after a yield will result in a deadlock. Even on multi-threaded runtimes, it would be nice to provide a custom error message that explains why the user doesn't want to do this instead of only a generic message about their future not being `Send`. Any other kind of RAII guard which depends on behavior similar to that of a `MutexGuard` will have the same issue.++The big reason for including a lint like this is because under the hood the compiler will automatically transform async fn into a state machine which can store locals. This process is invisible to users and will produce code that is different than what is in the actual rust file. Due to this it is important to inform users that their code may not do what they expect.++# Guide-level explanation++Provide a lint that can be attached to structs to let the compiler know that this struct can not be held across an await boundary.++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}+```++This struct if held across an await boundary would cause a deny-by-default warning:++```rust+async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++The compiler might output something along the lines of:++```+warning: `MyStruct` should not be held across an await point.+```++Example use cases for this lint:++- `MutexGuard` holding this across a yield boundary in a single threaded executor could cause deadlocks. In a multi-threaded runtime the resulting future would become `!Send` which will stop the user from spawning this future and causing issues. But in a single threaded runtime which accepts `!Send` futures deadlocks could happen.++- The same applies to other such synchronization primitives such as locks from `parking-lot`.++- `tracing::Span` has the ability to enter the span via the `tracing::span::Entered` guard. While entering a span is totally normal, during an async fn the span only needs to be entered once before the `.await` call, which might potentially yield the execution.++- Any RAII guard might possibly create unintended behavior if held across an await boundary.++This lint will enable the compiler to warn the user that the generated MIR could produce unforeseen side effects. Some examples of this are:++- [`std::sync::MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html)+- [`tracing::span::Entered`](https://docs.rs/tracing/0.1.15/tracing/span/struct.Entered.html)++This will be a best effort lint to signal the user about unintended side-effects of using certain types across an await boundary.++# Reference-level explanation++The `must_not_await` attribute is used to issue a diagnostic warning when a value is not "used". It can be applied to user-defined composite types (structs, enums and unions), functions and traits.++The `must_not_await` attribute may include a message by using the [`MetaNameValueStr`] syntax such as `#[must_not_await = "example message"]`.  The message will be given alongside the warning.++When used on a user-defined composite type, if the [expression] of an [expression statement] has this type and is used across an await point, then this lint is violated.+++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}++async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++When used on a function, if the [expression] of an [expression statement] is a [call expression] to that function, and the expression is held across an await point, this lint is violated.++```rust+#[must_not_await]+fn foo() -> i32 { 5i32 }++async fn foo() {+  let bar = foo();+  my_async_op.await;+  println!("{:?}", bar);+}+```++When used on a [trait declaration], a [call expression] of an [expression statement] to a function that returns an [impl trait] of that trait and if the value is held across an await point, the lint is violated.++```rust+trait Trait {+    #[must_not_await]+    fn foo(&self) -> i32;+}++impl Trait for i32 {+    fn foo(&self) -> i32 { 0i32 }+}++async fn foo() {+  let bar = 5i32.foo();+  my_async_op.await;+  println!("{:?}", bar);+}+```++When used on a function in a trait implementation, the attribute does nothing.++[`MetaNameValueStr`]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax+[expression]: https://doc.rust-lang.org/reference/expressions.html+[expression statement]: https://doc.rust-lang.org/reference/statements.html#expression-statements+[call expression]: https://doc.rust-lang.org/reference/expressions/call-expr.html+[trait declaration]: https://doc.rust-lang.org/reference/items/traits.html+[impl trait]: https://doc.rust-lang.org/reference/types/impl-trait.html+++# Drawbacks+- There is a possibility it can produce a false positive warning and it could get noisy. But using the `allow` attribute would work similar to other `deny-by-default` lints.++# Rationale and alternatives++Going through the prior are we see two systems currently which provide simailar/semantically similar behavior:++## Clippy `#[await_holding_lock]` lint
## Clippy `await_holding_lock` lint

That's the name of the lint but I don't think it's an attribute

LucioFranco

comment created time in 19 days

Pull request review commentrust-lang/wg-async-foundations

Draft RFC to add a `must_not_await` lint

+# RFC: Must not await lint++# Summary++Introduce a `#[must_not_await]` lint in the compiler that will warn the user when they are incorrectly holding a struct across an await boundary.++# Motivation++Enable users to fearlessly write concurrent async code without the need to understand the internals of runtimes and how their code will be affected. The goal is to provide a best effort warning that will let the user know of a possible side effect that is not visible by reading the code right away.++One example of these side effects is holding a `MutexGuard` across an await bound. This opens up the possibility of causing a deadlock since the future holding onto the lock did not relinquish it back before it yielded control. This is a problem for futures that run on single-threaded runtimes (`!Send`) where holding a local after a yield will result in a deadlock. Even on multi-threaded runtimes, it would be nice to provide a custom error message that explains why the user doesn't want to do this instead of only a generic message about their future not being `Send`. Any other kind of RAII guard which depends on behavior similar to that of a `MutexGuard` will have the same issue.++The big reason for including a lint like this is because under the hood the compiler will automatically transform async fn into a state machine which can store locals. This process is invisible to users and will produce code that is different than what is in the actual rust file. Due to this it is important to inform users that their code may not do what they expect.++# Guide-level explanation++Provide a lint that can be attached to structs to let the compiler know that this struct can not be held across an await boundary.++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}+```++This struct if held across an await boundary would cause a deny-by-default warning:++```rust+async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++The compiler might output something along the lines of:++```+warning: `MyStruct` should not be held across an await point.+```++Example use cases for this lint:++- `MutexGuard` holding this across a yield boundary in a single threaded executor could cause deadlocks. In a multi-threaded runtime the resulting future would become `!Send` which will stop the user from spawning this future and causing issues. But in a single threaded runtime which accepts `!Send` futures deadlocks could happen.++- The same applies to other such synchronization primitives such as locks from `parking-lot`.++- `tracing::Span` has the ability to enter the span via the `tracing::span::Entered` guard. While entering a span is totally normal, during an async fn the span only needs to be entered once before the `.await` call, which might potentially yield the execution.++- Any RAII guard might possibly create unintended behavior if held across an await boundary.++This lint will enable the compiler to warn the user that the generated MIR could produce unforeseen side effects. Some examples of this are:++- [`std::sync::MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html)+- [`tracing::span::Entered`](https://docs.rs/tracing/0.1.15/tracing/span/struct.Entered.html)++This will be a best effort lint to signal the user about unintended side-effects of using certain types across an await boundary.++# Reference-level explanation++The `must_not_await` attribute is used to issue a diagnostic warning when a value is not "used". It can be applied to user-defined composite types (structs, enums and unions), functions and traits.++The `must_not_await` attribute may include a message by using the [`MetaNameValueStr`] syntax such as `#[must_not_await = "example message"]`.  The message will be given alongside the warning.++When used on a user-defined composite type, if the [expression] of an [expression statement] has this type and is used across an await point, then this lint is violated.+++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}++async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++When used on a function, if the [expression] of an [expression statement] is a [call expression] to that function, and the expression is held across an await point, this lint is violated.++```rust+#[must_not_await]+fn foo() -> i32 { 5i32 }++async fn foo() {+  let bar = foo();+  my_async_op.await;+  println!("{:?}", bar);+}+```++When used on a [trait declaration], a [call expression] of an [expression statement] to a function that returns an [impl trait] of that trait and if the value is held across an await point, the lint is violated.

ditto

LucioFranco

comment created time in 19 days

Pull request review commentrust-lang/wg-async-foundations

Draft RFC to add a `must_not_await` lint

+# RFC: Must not await lint++# Summary++Introduce a `#[must_not_await]` lint in the compiler that will warn the user when they are incorrectly holding a struct across an await boundary.++# Motivation++Enable users to fearlessly write concurrent async code without the need to understand the internals of runtimes and how their code will be affected. The goal is to provide a best effort warning that will let the user know of a possible side effect that is not visible by reading the code right away.++One example of these side effects is holding a `MutexGuard` across an await bound. This opens up the possibility of causing a deadlock since the future holding onto the lock did not relinquish it back before it yielded control. This is a problem for futures that run on single-threaded runtimes (`!Send`) where holding a local after a yield will result in a deadlock. Even on multi-threaded runtimes, it would be nice to provide a custom error message that explains why the user doesn't want to do this instead of only a generic message about their future not being `Send`. Any other kind of RAII guard which depends on behavior similar to that of a `MutexGuard` will have the same issue.++The big reason for including a lint like this is because under the hood the compiler will automatically transform async fn into a state machine which can store locals. This process is invisible to users and will produce code that is different than what is in the actual rust file. Due to this it is important to inform users that their code may not do what they expect.++# Guide-level explanation++Provide a lint that can be attached to structs to let the compiler know that this struct can not be held across an await boundary.++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}+```++This struct if held across an await boundary would cause a deny-by-default warning:++```rust+async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++The compiler might output something along the lines of:++```+warning: `MyStruct` should not be held across an await point.+```++Example use cases for this lint:++- `MutexGuard` holding this across a yield boundary in a single threaded executor could cause deadlocks. In a multi-threaded runtime the resulting future would become `!Send` which will stop the user from spawning this future and causing issues. But in a single threaded runtime which accepts `!Send` futures deadlocks could happen.++- The same applies to other such synchronization primitives such as locks from `parking-lot`.++- `tracing::Span` has the ability to enter the span via the `tracing::span::Entered` guard. While entering a span is totally normal, during an async fn the span only needs to be entered once before the `.await` call, which might potentially yield the execution.++- Any RAII guard might possibly create unintended behavior if held across an await boundary.++This lint will enable the compiler to warn the user that the generated MIR could produce unforeseen side effects. Some examples of this are:++- [`std::sync::MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html)+- [`tracing::span::Entered`](https://docs.rs/tracing/0.1.15/tracing/span/struct.Entered.html)++This will be a best effort lint to signal the user about unintended side-effects of using certain types across an await boundary.++# Reference-level explanation++The `must_not_await` attribute is used to issue a diagnostic warning when a value is not "used". It can be applied to user-defined composite types (structs, enums and unions), functions and traits.++The `must_not_await` attribute may include a message by using the [`MetaNameValueStr`] syntax such as `#[must_not_await = "example message"]`.  The message will be given alongside the warning.++When used on a user-defined composite type, if the [expression] of an [expression statement] has this type and is used across an await point, then this lint is violated.+++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}++async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++When used on a function, if the [expression] of an [expression statement] is a [call expression] to that function, and the expression is held across an await point, this lint is violated.

I also find the expression wording here a bit confusing. I think we can do something similar to the above here, and reference "the value returned by the function."

Now that I think about it, it isn't obvious to me that we want to allow this attribute on functions. Are there any use cases where we'd want it on the function but not the type? I realize this is consistent with #[must_use], though.

LucioFranco

comment created time in 19 days

Pull request review commentrust-lang/wg-async-foundations

Draft RFC to add a `must_not_await` lint

+# RFC: Must not await lint++# Summary++Introduce a `#[must_not_await]` lint in the compiler that will warn the user when they are incorrectly holding a struct across an await boundary.++# Motivation++Enable users to fearlessly write concurrent async code without the need to understand the internals of runtimes and how their code will be affected. The goal is to provide a best effort warning that will let the user know of a possible side effect that is not visible by reading the code right away.++One example of these side effects is holding a `MutexGuard` across an await bound. This opens up the possibility of causing a deadlock since the future holding onto the lock did not relinquish it back before it yielded control. This is a problem for futures that run on single-threaded runtimes (`!Send`) where holding a local after a yield will result in a deadlock. Even on multi-threaded runtimes, it would be nice to provide a custom error message that explains why the user doesn't want to do this instead of only a generic message about their future not being `Send`. Any other kind of RAII guard which depends on behavior similar to that of a `MutexGuard` will have the same issue.++The big reason for including a lint like this is because under the hood the compiler will automatically transform async fn into a state machine which can store locals. This process is invisible to users and will produce code that is different than what is in the actual rust file. Due to this it is important to inform users that their code may not do what they expect.++# Guide-level explanation++Provide a lint that can be attached to structs to let the compiler know that this struct can not be held across an await boundary.++```rust+#[must_not_await = "Your error message here"]+struct MyStruct {}+```++This struct if held across an await boundary would cause a deny-by-default warning:++```rust+async fn foo() {+  let my_struct = MyStruct {};+  my_async_op.await;+  println!("{:?}", my_struct);+}+```++The compiler might output something along the lines of:++```+warning: `MyStruct` should not be held across an await point.+```++Example use cases for this lint:++- `MutexGuard` holding this across a yield boundary in a single threaded executor could cause deadlocks. In a multi-threaded runtime the resulting future would become `!Send` which will stop the user from spawning this future and causing issues. But in a single threaded runtime which accepts `!Send` futures deadlocks could happen.++- The same applies to other such synchronization primitives such as locks from `parking-lot`.++- `tracing::Span` has the ability to enter the span via the `tracing::span::Entered` guard. While entering a span is totally normal, during an async fn the span only needs to be entered once before the `.await` call, which might potentially yield the execution.++- Any RAII guard might possibly create unintended behavior if held across an await boundary.++This lint will enable the compiler to warn the user that the generated MIR could produce unforeseen side effects. Some examples of this are:

nit: The MIR is more of an implementation detail. It's just the defined semantics of async functions, namely the fact that they suspend execution.

LucioFranco

comment created time in 19 days

PullRequestReviewEvent
PullRequestReviewEvent

issue commentrust-lang/rust

Build hang on Linux with nightly and beta toolchains

For this post I consulted with my friendly neighborhood LLVM expert, @petrhosek.

The libstdc++ symbols get exported from libLLVM.so because we don't compile with -fvisibility=hidden. If they didn't get exported the dynamic linker wouldn't override them. However, turning on this flag would likely break the rustllvm bridge, which calls into LLVM's private C++ ABI.

So it looks like our options are one of:

  1. Document that proc macros must not dynamically link the C++ standard library (or more generally, any dynamic libraries other than the proc macro runtime)
  2. Run proc macros in a separate process
  3. Link LLVM statically
  4. Build rustllvm into libLLVM.so somehow (@petrhosek said we could build a libLLVM.a and then link that into a rustllvm shared library, but this would likely require adding support for building a static library to LLVM's build system)
danieldeankon

comment created time in 20 days

pull request commentrust-lang/rust

Fix rustdoc rendering of by-value mutable arguments in async fn

In the simple case we aren't actually inserting let <pat> = <pat>; as the comment says. We're inserting the statement let <pat> = <ident>; where ident is the original identifier named in the function signature. That means we were effectively doing the following in the ref mut case:

fn foo(ref mut x: i32) {
    let ref mut y = x;
}

i.e. trying to take a mutable reference to x, which itself is a &mut i32. But x is not a mutable binding, i.e. the integer x points to could be changed, but which integer x points to cannot be changed. Taking a mutable reference to x would violate that, so this is a compiler error.

I think we don't want this is_simple_parameter behavior anytime ref is involved. Which means that changing to hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable was partly right.

However, we do actually want to preserve the original identifier for rustdoc to use, anytime the pattern is a binding. For ref cases, I don't see any harm in preserving the identifier and setting is_simple_parameter to false.

P.S. Keeping HirId's dense is just an optimization, not as important as correctness.

ebkalderon

comment created time in 20 days

pull request commentrust-lang/rust

[generator] Special cases for match guard when analyzing interior types in generators

Friendly ping @dingxiangfei2009, are you still able to work on this?

dingxiangfei2009

comment created time in 20 days

pull request commentrust-lang/rust

Avoid complex diagnostics in snippets which contain newlines

The PR needs a rebase and there are some review comments to address.

Ping @JohnTitor, are you still interested in working on this?

JohnTitor

comment created time in 20 days

issue closedrust-lang/rust

LLVM error: "Instruction does not dominate all uses!" on Windows

Code

Project is here: https://github.com/kryptan/llvm-error

Compile with cargo test --release on Windows-MSVC (compilation on Linux works fine).

src/lib.rs:

use std::future::Future;
use std::sync::Arc;

pub async fn run() {
    foo(|| async { create() }).await;
    foo(|| async { create() }).await;
}

async fn foo<F>(create: impl Fn() -> F)
where
    F: Future<Output = Struct>,
{
    call_empty::<()>(create().await, "").await;
}

struct Struct {
    _vec: Vec<u8>,
    _arc: Arc<()>,
}

fn create() -> Struct {
    Struct {
        _vec: Vec::new(),
        _arc: Arc::new(()),
    }
}

async fn call_empty<R>(_: Struct, _: &str) {
    empty()
}

fn empty() {}

tests/test.rs:

use std::future::Future;

#[test]
fn test() {
    let _: Box<dyn Future<Output = ()>> = Box::new(llvm_eror::run());
}

Meta

I tried two versions of the compiler (LLVM 9 & LLVM 10):

rustc --version --verbose:

rustc 1.45.0-nightly (9310e3bd4 2020-05-21)
binary: rustc
commit-hash: 9310e3bd4f425f84fc27878ebf2bda1f30935a63
commit-date: 2020-05-21
host: x86_64-pc-windows-msvc
release: 1.45.0-nightly
LLVM version: 10.0

rustc --version --verbose:

rustc 1.45.0-nightly (a74d1862d 2020-05-14)
binary: rustc
commit-hash: a74d1862d4d87a56244958416fd05976c58ca1a8
commit-date: 2020-05-14
host: x86_64-pc-windows-msvc
release: 1.45.0-nightly
LLVM version: 9.0

Error output

Instruction does not dominate all uses!
  %117 = phi i8* [ %117, %111 ], [ %117, %105 ], [ %43, %60 ], [ %43, %58 ], [ %51, %56 ]
  %117 = phi i8* [ %117, %111 ], [ %117, %105 ], [ %43, %60 ], [ %43, %58 ], [ %51, %56 ]
Instruction does not dominate all uses!
  %117 = phi i8* [ %117, %111 ], [ %117, %105 ], [ %43, %60 ], [ %43, %58 ], [ %51, %56 ]
  %117 = phi i8* [ %117, %111 ], [ %117, %105 ], [ %43, %60 ], [ %43, %58 ], [ %51, %56 ]
Instruction does not dominate all uses!
  %228 = phi i8* [ %228, %222 ], [ %228, %216 ], [ %155, %171 ], [ %155, %169 ], [ %162, %167 ]
  %228 = phi i8* [ %228, %222 ], [ %228, %216 ], [ %155, %171 ], [ %155, %169 ], [ %162, %167 ]
Instruction does not dominate all uses!
  %228 = phi i8* [ %228, %222 ], [ %228, %216 ], [ %155, %171 ], [ %155, %169 ], [ %162, %167 ]
  %228 = phi i8* [ %228, %222 ], [ %228, %216 ], [ %155, %171 ], [ %155, %169 ], [ %162, %167 ]
in function _ZN97_$LT$core..future..from_generator..GenFuture$LT$T$GT$$u20$as$u20$core..future..future..Future$GT$4poll17hf4840ccb0415d3e1E
LLVM ERROR: Broken function found, compilation aborted!
error: could not compile `llvm_eror`.

closed time in 20 days

kryptan

issue commentrust-lang/rust

LLVM error: "Instruction does not dominate all uses!" on Windows

Fixed by #77466.

kryptan

comment created time in 20 days

issue commentrust-lang/rust

More friendly error msg when await on NONE ASYNC fn/block or return a obj that implements deprecated Future

@nellshamrell Are you still planning to work on this?

CGQAQ

comment created time in 20 days

pull request commentrust-lang/rust

Working branch-level code coverage

@bors retry

richkadel

comment created time in 21 days

delete branch tmandry/team

delete branch : new-members

delete time in 21 days

delete branch tmandry/rust

delete branch : issue-73050

delete time in 23 days

pull request commentrust-lang/rust

Issue 72408 nested closures exponential

The reason for the confusion is that these issues pre-existed the breakage. It is true that there was code with bad build performance that also required increasing the type length limit; this change fixed those preexisting issues as well.

Another fix for similar regressions is in #76928. It doesn't look like we've fixed all of them yet, see #75992.

VFLashM

comment created time in 24 days

pull request commentrust-lang/rust

Issue 72408 nested closures exponential

@spastorino @pnkfelix @wesleywiser It's not true that the performance was always bad. There is code that worked fine up until the last stable release that now takes an unacceptably long time to compile. It was caused by another beta backport that happened just prior to a stable release, #75443.

VFLashM

comment created time in 24 days

pull request commentrust-lang/rust

Working branch-level code coverage

@bors r+

richkadel

comment created time in 24 days

pull request commentrust-lang/rust

Include scope id in SocketAddrV6::Display

@dtolnay Are there any issues with changing the Display impl here?

tamird

comment created time in 24 days

pull request commentrust-lang/rust

Working branch-level code coverage

@bors r+

richkadel

comment created time in 24 days

pull request commentrust-lang/team

Add @lbernick, @betamos, @bIgBV to wg-async-foundations

Ready to merge.

tmandry

comment created time in 24 days

push eventtmandry/team

Tyler Mandry

commit sha dd2422a98dbbd422d00cbd627728762fb50ba2a2

Update people/bIgBV.toml with email Co-authored-by: Bhargav <bIgBV@users.noreply.github.com>

view details

push time in 24 days

pull request commentrust-lang/rust

Working branch-level code coverage

@bors r+

richkadel

comment created time in 25 days

delete branch tmandry/rust

delete branch : bootstrap-host

delete time in a month

pull request commentrust-lang/rust

bootstrap: Always build for host, even when target is given

Pushed a change with your suggestion.

tmandry

comment created time in a month

pull request commentrust-lang/rust

bootstrap: Always build for host, even when target is given

Nice catch! I seem to have forgotten that I can reproduce these locally with Docker.

tmandry

comment created time in a month

push eventtmandry/rust

Tyler Mandry

commit sha bf7aeaa617e309c31e6d28a469ea6092e28393df

Filter out empty items in bootstrap::flags::split

view details

push time in a month

pull request commentrust-lang/rust

bootstrap: Always build for host, even when target is given

thread 'main' panicked at '

couldn't find required command: "arm-linux-gnueabihf-g++"

', src/bootstrap/sanity.rs:59:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failed to run: /checkout/obj/build/bootstrap/debug/bootstrap --stage 2 test --host= --target arm-unknown-linux-gnueabihf

I want this to be a spurious error but it probably isn't. Maybe there's a subtle change in the behavior that we haven't accounted for.

tmandry

comment created time in a month

pull request commentrust-lang/rust

bootstrap: Always build for host, even when target is given

Strange, maybe some version of Docker wasn't passing through empty '' args? I changed everything to use --host='' to see if that fixes things.

@bors r=Mark-Simulacrum rollup=never

tmandry

comment created time in a month

push eventtmandry/rust

Tyler Mandry

commit sha 63eaa60294ad5b67baf6c44d48ef67654f538710

Use --host='' instead of --host '' Trying to fix a problem in CI. Maybe some version of Docker is not passing '' args correctly?

view details

push time in a month

issue commentrust-lang/rust

False positive: single_use_lifetimes

Very strange. I'm thinking we have some stateful HIR lowering code that is hanging onto state it shouldn't.

Nugine

comment created time in a month

pull request commentrust-lang/rust

Working branch-level code coverage

For the performance concerns, we could add either a representative benchmark or just a benchmarking mode with -Zinject-coverage to rustc-perf, so we can track improvements over time.

I'm not sure how keen people are on adding a new mode, cc @Mark-Simulacrum

richkadel

comment created time in a month

Pull request review commentrust-lang/rust

Working branch-level code coverage

 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {         data.statements.push(statement);     } -    /// Converts the computed `CoverageRegion`s into `SpanViewable`s.-    fn span_viewables(&self, coverage_regions: &Vec<CoverageRegion>) -> Vec<SpanViewable> {-        let mut span_viewables = Vec::new();-        for coverage_region in coverage_regions {-            span_viewables.push(SpanViewable {-                span: coverage_region.span,-                id: format!("{}", coverage_region.blocks[0].index()),-                tooltip: self.make_tooltip_text(coverage_region),-            });+    /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be+    /// counted.+    ///+    /// The basic steps are:+    ///+    /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each+    ///    `BasicCoverageBlock`.+    /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position+    ///    are sorted with longer spans before shorter spans; and equal spans are sorted+    ///    (deterministically) based on "dominator" relationship (if any).+    /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance,+    ///    if another span or spans are already counting the same code region), or should be merged+    ///    into a broader combined span (because it represents a contiguous, non-branching, and+    ///    uninterrupted region of source code).+    ///+    ///    Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since+    ///    closures have their own MIR, their `Span` in their enclosing function should be left+    ///    "uncovered".+    ///+    /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need+    /// to be).+    fn coverage_spans(&self) -> Vec<CoverageSpan> {+        let mut initial_spans_by_lo =+            Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks().len() * 2);+        for bcb in self.basic_coverage_blocks().iter() {+            for coverage_span in self.extract_spans(bcb) {+                initial_spans_by_lo.push(coverage_span);+            }         }-        span_viewables-    } -    /// A custom tooltip renderer used in a spanview HTML+CSS document used for coverage analysis.-    fn make_tooltip_text(&self, coverage_region: &CoverageRegion) -> String {-        const INCLUDE_COVERAGE_STATEMENTS: bool = false;-        let tcx = self.tcx;-        let source_map = tcx.sess.source_map();-        let mut text = Vec::new();-        for (i, &bb) in coverage_region.blocks.iter().enumerate() {-            if i > 0 {-                text.push("\n".to_owned());+        if initial_spans_by_lo.is_empty() {+            // This can happen if, for example, the function is unreachable (contains only a+            // `BasicBlock`(s) with an `Unreachable` terminator).+            return initial_spans_by_lo;+        }++        initial_spans_by_lo.sort_unstable_by(|a, b| {+            if a.span.lo() == b.span.lo() {+                if a.span.hi() == b.span.hi() {+                    if a.is_in_same_bcb(b) {+                        Some(Ordering::Equal)+                    } else {+                        // Sort equal spans by dominator relationship, in reverse order (so+                        // dominators always come after the dominated equal spans). When later+                        // comparing two spans in order, the first will either dominate the second,+                        // or they will have no dominator relationship.+                        self.dominators().rank_partial_cmp(b.bcb_leader_bb, a.bcb_leader_bb)+                    }+                } else {+                    // Sort hi() in reverse order so shorter spans are attempted after longer spans.+                    // This guarantees that, if a `prev` span overlaps, and is not equal to, a+                    // `curr` span, the prev span either extends further left of the curr span, or+                    // they start at the same position and the prev span extends further right of+                    // the end of the curr span.+                    b.span.hi().partial_cmp(&a.span.hi())+                }+            } else {+                a.span.lo().partial_cmp(&b.span.lo())             }-            text.push(format!("{:?}: {}:", bb, &source_map.span_to_string(coverage_region.span)));-            let data = &self.mir_body.basic_blocks()[bb];-            for statement in &data.statements {-                let statement_string = match statement.kind {-                    StatementKind::Coverage(box ref coverage) => match coverage.kind {-                        CoverageKind::Counter { id, .. } => {-                            if !INCLUDE_COVERAGE_STATEMENTS {-                                continue;-                            }-                            format!("increment counter #{}", id.index())-                        }-                        CoverageKind::Expression { id, lhs, op, rhs } => {-                            if !INCLUDE_COVERAGE_STATEMENTS {-                                continue;-                            }-                            format!(-                                "expression #{} = {} {} {}",-                                id.index(),-                                lhs.index(),-                                if op == Op::Add { "+" } else { "-" },-                                rhs.index()-                            )+            .unwrap()+        });++        let mut coverage_spans = Vec::with_capacity(initial_spans_by_lo.len());+        let mut pending_dups = Vec::<CoverageSpan>::new();++        // Traverse the spans. Note the `debug!()` statements explain some of the logic here, in+        // lieu of duplicative comments.+        let mut drain = initial_spans_by_lo.drain(..);+        let mut prev = drain.next().expect("at least one span");+        let mut prev_original_span = prev.span;+        for mut curr in drain {+            if let Some(dup) = pending_dups.last() {+                if dup.span != prev.span {+                    // If this is the case, then one of the following two things happened during the+                    // previous iteration:+                    //   * the `span` of prev was modified (by `curr.merge_from(prev)`); or+                    //   * the `span` of prev advanced past the end of the span of pending_dups+                    //     (`prev.span.hi() <= curr.span.lo()`)+                    // In either case, no more spans will match the span of `pending_dups`, so+                    // add them if possible, and clear them.+                    debug!(+                        "    SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \+                        previous iteration, or prev started a new disjoint span"+                    );+                    if dup.span.hi() <= curr.span.lo() {+                        for dup in pending_dups.drain(..) {+                            debug!("    ...adding at least one pending={:?}", dup);+                            coverage_spans.push(dup);                         }-                        CoverageKind::Unreachable => {-                            if !INCLUDE_COVERAGE_STATEMENTS {-                                continue;-                            }-                            String::from("unreachable")+                    } else {+                        pending_dups.clear();+                    }+                }+            }+            debug!("FOR curr={:?}", curr);+            let curr_span = curr.span;+            if prev.span.lo() > curr.span.lo() {+                // This can only happen if a prior iteration updates `prev` to skip forward, such as+                // skipping past a closure.+                debug!(+                    "  prev.span starts after curr.span, so curr will be dropped (skipping past \+                    closure?); prev={:?}",+                    prev+                );+                continue; // without changing prev+            } else if curr.is_mergeable(&prev) {+                debug!("  same bcb (and neither is a closure), merge with prev={:?}", prev);+                curr.merge_from(prev); // Note that curr.span may now differ from curr_span+            } else if prev.span.hi() <= curr.span.lo() {+                // different and disjoint spans, so keep both+                debug!(+                    "  different bcbs and disjoint spans, so keep curr for next iter, and add \+                    prev={:?}",+                    prev+                );+                coverage_spans.push(prev);+            } else if prev.is_closure {+                // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the+                // next iter+                debug!(+                    "  curr overlaps a closure (prev). Drop curr and keep prev for next iter. \+                    prev={:?}",+                    prev+                );+                continue;+            } else if curr.is_closure {+                // if `prev`s span extends left of the closure (`curr`), carve out the closure's+                // span from `prev`'s span. (The closure's coverage counters will be injected when+                // processing the closure's own MIR.) Add the portion of the span to the left of the+                // closure; and if the span extends to the right of the closure, update `prev` to+                // that portion of the span. For any `pending_dups`, repeat the same process.+                let left_cutoff = curr.span.lo();+                let right_cutoff = curr.span.hi();+                let has_pre_closure_span = prev.span.lo() < right_cutoff;+                let has_post_closure_span = prev.span.hi() > right_cutoff;+                if has_pre_closure_span {

I noticed that right around here my eyes start glossing over a bit. If you have any ideas for how to simplify this nested logic tree, I think it would help. Maybe extracting helper functions so it's all high level code.

richkadel

comment created time in a month

PullRequestReviewEvent

Pull request review commentrust-lang/rust

Working branch-level code coverage

 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {     ///    into a broader combined span (because it represents a contiguous, non-branching, and     ///    uninterrupted region of source code).     ///+    ///    Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since

I wonder if this will always be true? Is there a more generic way we can query for a span being part of another function?

Ironically, I think this would be easier in HIR

richkadel

comment created time in a month

Pull request review commentrust-lang/rust

Working branch-level code coverage

 fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> O         // These statements have spans that are often outside the scope of the executed source code

General comment on this code: I think the ideal we'd like to work toward is not having special handling for each StatementKind, i.e. this feels like a workaround for issues in the SourceInfo that could maybe be fixed elsewhere.

richkadel

comment created time in a month

Pull request review commentrust-lang/rust

Working branch-level code coverage

 fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> O         // These statements have spans that are often outside the scope of the executed source code         // for their parent `BasicBlock`.         StatementKind::StorageLive(_)-        | StatementKind::StorageDead(_) => None,+        | StatementKind::StorageDead(_)+        // Coverage should not be encountered, but don't inject coverage coverage

😂

richkadel

comment created time in a month

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentrust-lang/rust

bootstrap: Always build for host, even when target is given

@bors r=Mark-Simulacrum @bors rollup=iffy since this touches bootstrap and CI

tmandry

comment created time in a month

push eventtmandry/rust

Tyler Mandry

commit sha ed975054aabfcf3ff5001b182a76ab2f2b2da8c4

Update CI scripts to accommodate --host change

view details

Tyler Mandry

commit sha 6bd57e3531573ea4727b247d1db37046537b4e22

Bump bootstrap version and update changelog

view details

push time in a month

push eventtmandry/rust

Laurence Tratt

commit sha 73ada2d40429488aaaacf37b608bababc137b910

Explicitly document the size guarantees that Option makes. Triggered by a discussion on wg-unsafe-code-guidelines about which layouts of `Option<T>` one can guarantee are optimised to a single pointer.

view details

Laurence Tratt

commit sha f5118a525fcf9db4102d903650331039158eff11

Clarify and add guarantee about `transmute`.

view details

Laurence Tratt

commit sha 83f47aa11bd664ed8a15ef9833063833b7b3e71c

Be clear about the reverse `transmute` guarantees.

view details

Laurence Tratt

commit sha f3d7196caec3f54e572c7389b1cef9fd9e62c1ed

Be clearer about Some/None transmute.

view details

Laurence Tratt

commit sha 8cb8955d570c76631840bfc98825ca49c0dd8eea

Change notation. Co-authored-by: Ralf Jung <post@ralfj.de>

view details

Laurence Tratt

commit sha 55802e3bf3bf6d1db5c76aea581a7912bd752890

Add Rust function pointers. Co-authored-by: Ralf Jung <post@ralfj.de>

view details

Laurence Tratt

commit sha 68209c3fe4e0f5c3758f18e98efc175af31c2e51

Rename the types for clarity.

view details

Laurence Tratt

commit sha 9bac5774d7b452b2227c9fb77a4c6de3f432ee55

Grammar tweak.

view details

Vali Schneider

commit sha 459969f88ff95c94b7b34043a7f0e13de91de4f8

added restriction lint that prohibits the usage of unimplemented, unreachable or panic in a function of type result or option

view details

Vali Schneider

commit sha ceab1a9167655eba9f9556f8766f8702e49dfef3

removed unnecessary comment

view details

Vali Schneider

commit sha 8462cce96081b87eba7a5bc89130a1a09fe1f6d0

edited documentation

view details

Vali Schneider

commit sha b2d8ca9a766703469178ea37d4d46067bb6fa926

ran cargo dev update lints

view details

Vali Schneider

commit sha b006522393a3c3c2656e1ccdfbb0076ff1bd7e99

added lint for todo and removed option

view details

Vali Schneider

commit sha a424a2c1676a29c147252873037e8943d54941d3

changed check_impl_item to check_fn and added a few more test cases

view details

Vali Schneider

commit sha 73a3288282e733bfc5893e9920d29f1de5a21591

uncommented fn

view details

Erik Desjardins

commit sha d3b9ece4c0028ff7e36e34df2d2b26366f9c0648

add tests related to tuple scalar layout with ZST in last field

view details

Erik Desjardins

commit sha e5d85f917b8965a5e62513c17cbb887366b152bc

allow reordering of the last field of a MaybeUnsized struct if it's a ZST

view details

Erik Desjardins

commit sha e9bc3ddb073f2261ac46832d985efe8db863ed6a

test that we do not change the offset of ZST tuple fields when unsizing

view details

Erik Desjardins

commit sha 68217c9e0f1269d29b4c1c72d08fb1b95bf441cd

ignore zst offsets instead

view details

Erik Desjardins

commit sha 6fc1d8bc13124bb134d7ab54e56237821a55912e

add codegen test

view details

push time in a month

pull request commentrust-lang/rust

bootstrap: Always build for host, even when target is given

Rebased.

tmandry

comment created time in a month

Pull request review commentrust-lang/rust

Working branch-level code coverage

 impl<'tcx> MirPass<'tcx> for InstrumentCoverage {     ) {         // If the InstrumentCoverage pass is called on promoted MIRs, skip them.         // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601-        if mir_source.promoted.is_none() {-            Instrumentor::new(&self.name(), tcx, mir_source, mir_body).inject_counters();+        if mir_source.promoted.is_some() {+            trace!(+                "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)",+                mir_source.def_id()+            );+            return;         }++        let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local());+        let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();++        // Only instrument functions, methods, and closures (not constants since they are evaluated+        // at compile time by Miri).+        if !is_fn_like {+            trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id());+            return;+        }+        // FIXME(richkadel): By comparison, the MIR pass `ConstProp` includes associated constants,+        // with functions, methods, and closures. I assume Miri is used for associated constants as+        // well. If not, we may need to include them here too.++        trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());+        Instrumentor::new(&self.name(), tcx, mir_source, mir_body).inject_counters();+        trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());     } } -#[derive(Clone)]-struct CoverageRegion {-    pub span: Span,+/// A BasicCoverageBlock (BCB) represents the maximal-length sequence of CFG (MIR) BasicBlocks+/// without conditional branches.+#[derive(Debug, Clone)]+struct BasicCoverageBlock {     pub blocks: Vec<BasicBlock>, } +impl BasicCoverageBlock {+    pub fn leader_bb(&self) -> BasicBlock {+        self.blocks[0]+    }++    pub fn id(&self) -> String {+        format!(+            "@{}",+            self.blocks+                .iter()+                .map(|bb| bb.index().to_string())+                .collect::<Vec<_>>()+                .join(ID_SEPARATOR)+        )+    }+}++struct BasicCoverageBlocks {+    vec: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,+}++impl BasicCoverageBlocks {+    pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self {+        let mut basic_coverage_blocks =+            BasicCoverageBlocks { vec: IndexVec::from_elem_n(None, mir_body.basic_blocks().len()) };+        basic_coverage_blocks.extract_from_mir(mir_body);+        basic_coverage_blocks+    }++    pub fn iter(&self) -> impl Iterator<Item = &BasicCoverageBlock> {+        self.vec.iter().filter_map(|option| option.as_ref())+    }++    fn extract_from_mir(&mut self, mir_body: &mir::Body<'tcx>) {+        // Traverse the CFG but ignore anything following an `unwind`+        let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| {+            let mut successors = term_kind.successors();+            match &term_kind {+                // SwitchInt successors are never unwind, and all of them should be traversed+                TerminatorKind::SwitchInt { .. } => successors,+                // For all other kinds, return only the first successor, if any, and ignore unwinds+                _ => successors.next().into_iter().chain(&[]),+            }+        });++        let mut blocks = Vec::new();+        for (bb, data) in cfg_without_unwind+            .filter(|(_, data)| data.terminator().kind != TerminatorKind::Unreachable)+        {+            if let Some(last) = blocks.last() {+                let predecessors = &mir_body.predecessors()[bb];+                if predecessors.len() > 1 || !predecessors.contains(last) {+                    self.add_basic_coverage_block(blocks.split_off(0));+                    debug!(+                        "  because {}",+                        if predecessors.len() > 1 {+                            "predecessors.len() > 1".to_owned()+                        } else {+                            format!("bb {} is not in precessors: {:?}", bb.index(), predecessors)+                        }+                    );+                }+            }+            blocks.push(bb);++            let term = data.terminator();++            // inject counters at branch blocks only

Oh, is there a successors() helper like predecessors()? That'd make the symmetry a bit more obvious to me and keep less knowledge of individual terminators out of this code. As written I'm a bit worried about the wildcard here, someone could add a new TerminatorKind and forget to update this code.

richkadel

comment created time in a month

PullRequestReviewEvent
more