Skip to content

Commit

Permalink
Stash that thing
Browse files Browse the repository at this point in the history
  • Loading branch information
xFrednet committed Apr 18, 2024
1 parent e2414a7 commit 6fac5c8
Show file tree
Hide file tree
Showing 10 changed files with 382 additions and 1,795 deletions.
1 change: 1 addition & 0 deletions clippy_lints/src/borrow_pats/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl<'tcx> AnalysisInfo<'tcx> {
// In this context it is safe, this struct will never outlive `cx`
let cx = unsafe { core::mem::transmute::<&LateContext<'tcx>, &'tcx LateContext<'tcx>>(cx) };

eprintln!("AnalysisInfo for: {def_id:#?}");
let borrowck::consumers::BodyWithBorrowckFacts {
body,
// borrow_set, location_table
Expand Down
5 changes: 4 additions & 1 deletion clippy_lints/src/borrow_pats/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,13 @@ impl<'tcx> LateLintPass<'tcx> for BorrowPats {
// eprintln!("{body:#?}");
print_debug_info(cx, body, def);
}

if lint_level != Level::Allow {
let mut info = AnalysisInfo::new(cx, def);
if lint_level == Level::Forbid {
println!("{info:#?}");
} else {
return;
}

info.return_pats = ret::ReturnAnalysis::run(&info);
Expand Down Expand Up @@ -171,6 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowPats {
}

fn print_debug_info<'tcx>(cx: &LateContext<'tcx>, body: &hir::Body<'tcx>, def: hir::def_id::LocalDefId) {
eprintln!("Body for: {def:#?}");
let borrowck = get_body_with_borrowck_facts(cx.tcx, def, ConsumerOptions::RegionInferenceContext);
println!("=====");
print_body(&borrowck.body);
Expand Down
3 changes: 1 addition & 2 deletions clippy_lints/src/borrow_pats/owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ impl<'a, 'tcx> Visitor<'tcx> for OwnedAnalysis<'a, 'tcx> {
self.use_count += 1;
}
}

}

impl<'a, 'tcx> OwnedAnalysis<'a, 'tcx> {
Expand Down Expand Up @@ -332,7 +331,7 @@ impl<'a, 'tcx> OwnedAnalysis<'a, 'tcx> {
}
}

if let mir::Rvalue::Ref(_reg, _kind, src) = &rval
if let mir::Rvalue::Ref(_reg, _kind, src) = &rval
&& let Some(muta) = self.states[bb].temp_bros.get(&src.local).copied()
{
match src.projection.as_slice() {
Expand Down
158 changes: 156 additions & 2 deletions clippy_lints/src/borrow_pats/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque};

use clippy_utils::ty::{for_each_ref_region, for_each_region};
use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, BasicBlock, Local, Operand, START_BLOCK};
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{FnSig, GenericArgsRef, GenericPredicates, Region, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::source_map::Spanned;

use super::AnalysisInfo;
Expand All @@ -19,12 +21,164 @@ impl<T> std::fmt::Debug for PrintPrevent<T> {
}
}

/// A helper struct to build relations between function arguments and the return
///
/// I really should stop using such stupid names. At this pooint I'm just making fun
/// of everything to make this work somehow tollerable.
#[derive(Debug)]
struct FuncReals<'tcx> {
tcx: PrintPrevent<TyCtxt<'tcx>>,
/// A list of several universes
///
/// Mapping from `'short` (key) is outlives by `'long` (value)
multiverse: BTreeMap<Region<'tcx>, BTreeSet<Region<'tcx>>>,
sig: FnSig<'tcx>,
}

impl<'tcx> FuncReals<'tcx> {
fn from_fn_def(tcx: TyCtxt<'tcx>, def_id: DefId, _args: GenericArgsRef<'tcx>) -> Self {
// FIXME: The proper and long therm solution would be to use HIR
// to find the call with generics that still have valid region markers.
// However, for now I need to get this zombie in the air and not pefect
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();

// On other functions this shouldn't matter. Even if they have late bounds
// in their signature. We don't know how it's used and more imporantly,
// The input and return types still need to follow Rust's type rules
let fn_sig = fn_sig.skip_binder();

let mut reals = Self {
tcx: PrintPrevent(tcx),
multiverse: Default::default(),
sig: fn_sig,
};

// FYI: Predicates don't include transitive bounds
let item_predicates = tcx.predicates_of(def_id);
// TODO Test: `inferred_outlives_of`
reals.build_multiverse(item_predicates);

reals
}

fn build_multiverse(&mut self, predicates: GenericPredicates<'tcx>) {
let preds = predicates
.predicates
.iter()
.filter_map(|(clause, _span)| clause.as_region_outlives_clause());

/// I know this can be done in linear time, but I wasn't able to get this to
/// work quickly. So I'll do the n^2 version for now
for binder in preds {
// By now I believe (aka. wish) this is unimportant and can be removed.
// But first I need to find something which actually triggers this todo.
if !binder.bound_vars().is_empty() {
todo!("Non empty depressing bounds 2: {binder:#?}");
}

let constaint = binder.skip_binder();
let long = constaint.0;
let short = constaint.1;

let longi_verse = self.multiverse.get(&long).cloned().unwrap_or_default();
let shorti_verse = self.multiverse.entry(short).or_default();
if !shorti_verse.insert(long) {
continue;
}
shorti_verse.extend(longi_verse);

for (_, universe) in &mut self.multiverse {
if universe.contains(&short) {
universe.insert(long);
}
}
}
}

fn relations(&self, dest: Local, args: &Vec<Spanned<Operand<'tcx>>>) -> FxHashMap<Local, Vec<Local>> {
let mut reals = FxHashMap::default();
let ret_rels = self.return_relations();
if !ret_rels.is_empty() {
let locals: Vec<_> = ret_rels
.into_iter()
.filter_map(|idx| args[idx].node.place())
.map(|place| place.local)
.collect();
reals.insert(dest, locals);
}

for (arg_index, arg_ty) in self.sig.inputs().iter().enumerate() {
let mut arg_rels = FxHashSet::default();
for_each_ref_region(*arg_ty, &mut |reg, child_ty, mutability| {
// `&mut &X` is not really interesting here
if matches!(mutability, Mutability::Mut) && !child_ty.is_ref() {
arg_rels.extend(self.find_relations(child_ty, arg_index));
}
});

if !arg_rels.is_empty() {
// It has to be a valid place, since we found a location
let place = args[arg_index].node.place().unwrap();
assert!(!place.has_projections());

let locals: Vec<_> = arg_rels
.into_iter()
.filter_map(|idx| args[idx].node.place())
.map(|place| place.local)
.collect();
reals.insert(place.local, locals);
}
}

reals
}

fn return_relations(&self) -> FxHashSet<usize> {
self.find_relations(self.sig.output(), usize::MAX)
}

fn find_relations(&self, child_ty: Ty<'tcx>, child_index: usize) -> FxHashSet<usize> {
let mut child_regions = FxHashSet::default();
for_each_region(child_ty, |region| {
child_regions.insert(region);
});

let mut parents = FxHashSet::default();
if child_regions.is_empty() {
return parents;
}

for (index, ty) in self.sig.inputs().iter().enumerate() {
if index == child_index {
continue;
}

// "Here to stab things, don't case"
for_each_ref_region(*ty, &mut |reg, _ty, mutability| {
if child_regions.contains(&reg) {
parents.insert(index);
}
});
}

parents
}
}

pub fn calc_call_local_relations<'tcx>(
tcx: TyCtxt<'tcx>,
func: &Operand<'tcx>,
dest: Local,
args: &Vec<Spanned<Operand<'tcx>>>,
) -> BTreeMap<Local, Vec<Local>> {
return Default::default();
if let Some((def_id, generic_args)) = func.const_fn_def() && false {
let builder = FuncReals::from_fn_def(tcx, def_id, generic_args);
return Default::default();
let relations = builder.relations(dest, args);
return Default::default();
}

let ret_rels = get_parents_of_return(tcx, func);
let mut rels = BTreeMap::new();
let locals: Vec<_> = ret_rels
Expand Down Expand Up @@ -98,7 +252,7 @@ pub fn get_parents_of_return<'tcx>(tcx: TyCtxt<'tcx>, op: &mir::Operand<'tcx>) -
for (index, input) in fn_sig.inputs().iter().enumerate() {
// "Here to stab things, don't case"
for_each_ref_region(*input, &mut |reg, _ty, mutability| {
assert_eq!(mutability, Mutability::Not);
// assert_eq!(mutability, Mutability::Not);
if ret_regions.contains(&reg) {
input_indices.push(index);
}
Expand Down
13 changes: 12 additions & 1 deletion tests/ui/thesis/fn_call_relations.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! A file to test dependencies between function parameters
#![allow(unused)]

fn no_rep(_: u32, _: String) -> u16 {
12
Expand All @@ -24,18 +25,28 @@ fn lifetime_dep_prim<'a>(_: &'a u32, a: &'a u32) -> &'a u32 {
a
}

fn lifetime_outlives_middle_1<'a, 'd: 'a, 'b: 'd, 'c: 'd>(b: &'b String, _c: &'c String) -> &'a String {
b
}

fn lifetime_outlives_middle_2<'a, 'b: 'd, 'c: 'd, 'd: 'a>(b: &'b String, _c: &'c String) -> &'a String {
b
}

fn lifetime_outlives<'a, 'b: 'a, 'c: 'a>(b: &'b String, _c: &'c String) -> &'a String {
b
}

#[forbid(clippy::borrow_pats)]
#[warn(clippy::borrow_pats)]
fn test(s1: String, s2: String, s3: String, pair: (String, String)) {
let _test = no_rep(1, s3);
let _test = direct_dep(&s1, 2);
let _test = lifetime_dep(&s1, &s2);
let _test = lifetime_dep_more(&s1, &s2);
let _test = lifetime_dep_prim(&1, &2);
let _test = lifetime_dep_const("In", "Put");
let _test = lifetime_outlives_middle_1(&s1, &s2);
let _test = lifetime_outlives_middle_2(&s1, &s2);
let _test = lifetime_outlives(&s1, &s2);
let _test = lifetime_outlives(&pair.0, &pair.1);
}
Expand Down
90 changes: 90 additions & 0 deletions tests/ui/thesis/fn_call_relations.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
DefId(0:3 ~ fn_call_relations[cfca]::no_rep)
FuncReals {
tcx: PrintPrevent,
multiverse: {},
sig: fn(u32, std::string::String) -> u16,
}
DefId(0:4 ~ fn_call_relations[cfca]::direct_dep)
FuncReals {
tcx: PrintPrevent,
multiverse: {},
sig: fn(&ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:29 ~ fn_call_relations[cfca]::direct_dep::'_), '_) }) std::string::String, u32) -> &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:29 ~ fn_call_relations[cfca]::direct_dep::'_), '_) }) std::string::String,
}
DefId(0:5 ~ fn_call_relations[cfca]::lifetime_dep)
FuncReals {
tcx: PrintPrevent,
multiverse: {},
sig: fn(&ReBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed(DefId(0:30 ~ fn_call_relations[cfca]::lifetime_dep::'_), '_) }) std::string::String, &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:6 ~ fn_call_relations[cfca]::lifetime_dep::'a), 'a) }) std::string::String) -> &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:6 ~ fn_call_relations[cfca]::lifetime_dep::'a), 'a) }) std::string::String,
}
DefId(0:7 ~ fn_call_relations[cfca]::lifetime_dep_more)
FuncReals {
tcx: PrintPrevent,
multiverse: {},
sig: fn(&ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:8 ~ fn_call_relations[cfca]::lifetime_dep_more::'a), 'a) }) std::string::String, &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:8 ~ fn_call_relations[cfca]::lifetime_dep_more::'a), 'a) }) std::string::String) -> &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:8 ~ fn_call_relations[cfca]::lifetime_dep_more::'a), 'a) }) std::string::String,
}
DefId(0:11 ~ fn_call_relations[cfca]::lifetime_dep_prim)
FuncReals {
tcx: PrintPrevent,
multiverse: {},
sig: fn(&ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:12 ~ fn_call_relations[cfca]::lifetime_dep_prim::'a), 'a) }) u32, &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:12 ~ fn_call_relations[cfca]::lifetime_dep_prim::'a), 'a) }) u32) -> &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:12 ~ fn_call_relations[cfca]::lifetime_dep_prim::'a), 'a) }) u32,
}
DefId(0:9 ~ fn_call_relations[cfca]::lifetime_dep_const)
FuncReals {
tcx: PrintPrevent,
multiverse: {},
sig: fn(&ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:10 ~ fn_call_relations[cfca]::lifetime_dep_const::'a), 'a) }) str, &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:10 ~ fn_call_relations[cfca]::lifetime_dep_const::'a), 'a) }) str) -> &ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:10 ~ fn_call_relations[cfca]::lifetime_dep_const::'a), 'a) }) str,
}
DefId(0:13 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1)
FuncReals {
tcx: PrintPrevent,
multiverse: {
ReEarlyParam(DefId(0:14 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'a), 0, 'a): {
ReEarlyParam(DefId(0:15 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'d), 1, 'd),
ReEarlyParam(DefId(0:16 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'b), 2, 'b),
ReEarlyParam(DefId(0:17 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'c), 3, 'c),
},
ReEarlyParam(DefId(0:15 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'d), 1, 'd): {
ReEarlyParam(DefId(0:16 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'b), 2, 'b),
ReEarlyParam(DefId(0:17 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'c), 3, 'c),
},
},
sig: fn(&ReEarlyParam(DefId(0:16 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'b), 2, 'b) std::string::String, &ReEarlyParam(DefId(0:17 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'c), 3, 'c) std::string::String) -> &ReEarlyParam(DefId(0:14 ~ fn_call_relations[cfca]::lifetime_outlives_middle_1::'a), 0, 'a) std::string::String,
}
DefId(0:18 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2)
FuncReals {
tcx: PrintPrevent,
multiverse: {
ReEarlyParam(DefId(0:19 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'a), 0, 'a): {
ReEarlyParam(DefId(0:20 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'b), 1, 'b),
ReEarlyParam(DefId(0:21 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'c), 2, 'c),
ReEarlyParam(DefId(0:22 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'d), 3, 'd),
},
ReEarlyParam(DefId(0:22 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'d), 3, 'd): {
ReEarlyParam(DefId(0:20 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'b), 1, 'b),
ReEarlyParam(DefId(0:21 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'c), 2, 'c),
},
},
sig: fn(&ReEarlyParam(DefId(0:20 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'b), 1, 'b) std::string::String, &ReEarlyParam(DefId(0:21 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'c), 2, 'c) std::string::String) -> &ReEarlyParam(DefId(0:19 ~ fn_call_relations[cfca]::lifetime_outlives_middle_2::'a), 0, 'a) std::string::String,
}
DefId(0:23 ~ fn_call_relations[cfca]::lifetime_outlives)
FuncReals {
tcx: PrintPrevent,
multiverse: {
ReEarlyParam(DefId(0:24 ~ fn_call_relations[cfca]::lifetime_outlives::'a), 0, 'a): {
ReEarlyParam(DefId(0:25 ~ fn_call_relations[cfca]::lifetime_outlives::'b), 1, 'b),
ReEarlyParam(DefId(0:26 ~ fn_call_relations[cfca]::lifetime_outlives::'c), 2, 'c),
},
},
sig: fn(&ReEarlyParam(DefId(0:25 ~ fn_call_relations[cfca]::lifetime_outlives::'b), 1, 'b) std::string::String, &ReEarlyParam(DefId(0:26 ~ fn_call_relations[cfca]::lifetime_outlives::'c), 2, 'c) std::string::String) -> &ReEarlyParam(DefId(0:24 ~ fn_call_relations[cfca]::lifetime_outlives::'a), 0, 'a) std::string::String,
}
DefId(0:23 ~ fn_call_relations[cfca]::lifetime_outlives)
FuncReals {
tcx: PrintPrevent,
multiverse: {
ReEarlyParam(DefId(0:24 ~ fn_call_relations[cfca]::lifetime_outlives::'a), 0, 'a): {
ReEarlyParam(DefId(0:25 ~ fn_call_relations[cfca]::lifetime_outlives::'b), 1, 'b),
ReEarlyParam(DefId(0:26 ~ fn_call_relations[cfca]::lifetime_outlives::'c), 2, 'c),
},
},
sig: fn(&ReEarlyParam(DefId(0:25 ~ fn_call_relations[cfca]::lifetime_outlives::'b), 1, 'b) std::string::String, &ReEarlyParam(DefId(0:26 ~ fn_call_relations[cfca]::lifetime_outlives::'c), 2, 'c) std::string::String) -> &ReEarlyParam(DefId(0:24 ~ fn_call_relations[cfca]::lifetime_outlives::'a), 0, 'a) std::string::String,
}
Loading

0 comments on commit 6fac5c8

Please sign in to comment.