diff --git a/Cargo.lock b/Cargo.lock index 31d3a6fbce..1712f4e1bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -323,6 +323,7 @@ dependencies = [ "itertools", "log", "serde", + "strum", ] [[package]] @@ -1026,6 +1027,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" @@ -1177,6 +1184,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" version = "2.0.29" diff --git a/crates/dfcheck/src/context.rs b/crates/dfcheck/src/context.rs index 6c96cad365..c30b44256d 100644 --- a/crates/dfcheck/src/context.rs +++ b/crates/dfcheck/src/context.rs @@ -40,7 +40,7 @@ impl Context { .iter() .flat_map(|(id, (annots, _))| { annots.iter().filter_map(move |annot| match annot { - Annotation::Label(MarkerAnnotation { marker, refinement }) => { + Annotation::Marker(MarkerAnnotation { marker, refinement }) => { Some((*marker, (*id, refinement.clone()))) } _ => None, @@ -305,7 +305,7 @@ fn test_happens_before() -> Result<()> { desc.annotations .get(&function.function) .map_or(false, |(anns, _)| { - anns.iter().filter_map(Annotation::as_label_ann).any(|ann| { + anns.iter().filter_map(Annotation::as_marker).any(|ann| { ann.marker == marker && (ann .refinement @@ -340,7 +340,7 @@ fn test_happens_before() -> Result<()> { DataSource::Argument(arg) => desc.annotations[&ctrl_name] .0 .iter() - .filter_map(Annotation::as_label_ann) + .filter_map(Annotation::as_marker) .any(|ann| { ann.marker == marker && (ann.refinement.on_self() @@ -353,7 +353,7 @@ fn test_happens_before() -> Result<()> { DataSource::FunctionCall(cs) => desc.annotations[&cs.function] .0 .iter() - .filter_map(Annotation::as_label_ann) + .filter_map(Annotation::as_marker) .any(|ann| { ann.marker == marker && (ann.refinement.on_self() || ann.refinement.on_return()) diff --git a/crates/dfgraph/Cargo.toml b/crates/dfgraph/Cargo.toml index e6589d3e76..8c55eba5be 100644 --- a/crates/dfgraph/Cargo.toml +++ b/crates/dfgraph/Cargo.toml @@ -15,3 +15,4 @@ log = "0.4" internment = { version = "0.7.1", features = ["serde"] } indexical = { workspace = true } itertools = "0.11.0" +strum = { version = "0.25", features = ["derive"] } \ No newline at end of file diff --git a/crates/dfgraph/src/lib.rs b/crates/dfgraph/src/lib.rs index e36be33e51..b6dba08da0 100644 --- a/crates/dfgraph/src/lib.rs +++ b/crates/dfgraph/src/lib.rs @@ -18,6 +18,8 @@ pub(crate) mod rustc { pub use middle::mir; } +extern crate strum; + pub mod global_location; #[cfg(feature = "rustc")] mod rustc_impls; @@ -29,7 +31,6 @@ use global_location::GlobalLocation; use indexical::define_index_type; use internment::Intern; use itertools::Itertools; -use log::warn; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{borrow::Cow, fmt, hash::Hash, iter}; @@ -43,31 +44,27 @@ pub type Function = Identifier; /// Types of annotations we support. /// /// Usually you'd expect one of those annotation types in any given situation. -/// For convenience the match methods [`Self::as_label_ann`], -/// [`Self::as_otype_ann`] and [`Self::as_exception_annotation`] are provided. These are particularly useful in conjunction with e.g. [`Iterator::filter_map`] -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize)] +/// For convenience the match methods [`Self::as_marker`], [`Self::as_otype`] +/// and [`Self::as_exception`] are provided. These are particularly useful in +/// conjunction with e.g. [`Iterator::filter_map`] +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, strum::EnumIs)] pub enum Annotation { - Label(MarkerAnnotation), + Marker(MarkerAnnotation), OType(Vec), Exception(ExceptionAnnotation), } impl Annotation { - /// If this is an [`Annotation::Label`], returns the underlying [`MarkerAnnotation`]. - pub fn as_label_ann(&self) -> Option<&MarkerAnnotation> { + /// If this is an [`Annotation::Marker`], returns the underlying [`MarkerAnnotation`]. + pub fn as_marker(&self) -> Option<&MarkerAnnotation> { match self { - Annotation::Label(l) => Some(l), + Annotation::Marker(l) => Some(l), _ => None, } } - /// Returns true if this is an [`Annotation::Label`]. - pub fn is_label_ann(&self) -> bool { - matches!(self, Annotation::Label(_)) - } - /// If this is an [`Annotation::OType`], returns the underlying [`TypeDescriptor`]. - pub fn as_otype_ann(&self) -> Option<&[TypeDescriptor]> { + pub fn as_otype(&self) -> Option<&[TypeDescriptor]> { match self { Annotation::OType(t) => Some(t), _ => None, @@ -75,7 +72,7 @@ impl Annotation { } /// If this is an [`Annotation::Exception`], returns the underlying [`ExceptionAnnotation`]. - pub fn as_exception_annotation(&self) -> Option<&ExceptionAnnotation> { + pub fn as_exception(&self) -> Option<&ExceptionAnnotation> { match self { Annotation::Exception(e) => Some(e), _ => None, @@ -92,10 +89,10 @@ pub struct ExceptionAnnotation { pub verification_hash: Option, } -/// A label annotation and its refinements. +/// A marker annotation and its refinements. #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Serialize, Deserialize)] pub struct MarkerAnnotation { - /// The (unchanged) name of the label as provided by the user + /// The (unchanged) name of the marker as provided by the user pub marker: Identifier, #[serde(flatten)] pub refinement: MarkerRefinement, @@ -105,7 +102,7 @@ fn const_false() -> bool { false } -/// Refinements in the label targeting. The default (no refinement provided) is +/// Refinements in the marker targeting. The default (no refinement provided) is /// `on_argument == vec![]` and `on_return == false`, which is also what is /// returned from [`Self::empty`]. #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize)] @@ -116,6 +113,8 @@ pub struct MarkerRefinement { on_return: bool, } +/// Disaggregated version of [`MarkerRefinement`]. Can be added to an existing +/// refinement [`MarkerRefinement::merge_kind`]. #[derive(Clone, Deserialize, Serialize)] pub enum MarkerRefinementKind { Argument(#[serde(with = "crate::tiny_bitset::pretty")] TinyBitSet), @@ -158,10 +157,12 @@ impl MarkerRefinement { } } + /// Get the refinements on arguments pub fn on_argument(&self) -> TinyBitSet { self.on_argument } + /// Is this refinement targeting the return value? pub fn on_return(&self) -> bool { self.on_return } @@ -173,7 +174,7 @@ impl MarkerRefinement { } } -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Serialize, Deserialize)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Serialize, Deserialize, strum::EnumIs)] pub enum ObjectType { Function(usize), Type, @@ -181,33 +182,13 @@ pub enum ObjectType { } impl ObjectType { - pub fn is_function(&self) -> Option { + /// If this is [`Self::Function`], then return the payload. + pub fn as_function(&self) -> Option { match self { ObjectType::Function(f) => Some(*f), _ => None, } } - pub fn merge(&mut self, other: &Self) { - if self != other { - warn!( - "Merging two different object types {:?} and {:?}!", - self, other - ); - match (self, other) { - (ObjectType::Function(ref mut i), ObjectType::Function(j)) => { - if j > i { - *i = *j - } - } - (slf, other) => { - panic!("Cannot merge two different object types {slf:?} and {other:?}") - } - } - } - } - pub fn is_type(&self) -> bool { - matches!(self, ObjectType::Type) - } } pub type AnnotationMap = HashMap, ObjectType)>; @@ -305,7 +286,7 @@ impl ProgramDescription { .chain( self.annotations .iter() - .filter(|f| f.1 .1.is_function().is_some()) + .filter(|f| f.1 .1.as_function().is_some()) .map(|f| f.0), ) .collect() diff --git a/crates/dfpp/src/ana/mod.rs b/crates/dfpp/src/ana/mod.rs index 97c0d4b526..7a8cd5d5ae 100644 --- a/crates/dfpp/src/ana/mod.rs +++ b/crates/dfpp/src/ana/mod.rs @@ -290,7 +290,7 @@ impl<'tcx> CollectingVisitor<'tcx> { .chain(self.marker_ctx.external_annotations().iter().map(|(did, anns)| { ( *did, - anns.iter().cloned().map(Annotation::Label).collect(), + anns.iter().cloned().map(Annotation::Marker).collect(), ) })) .map(|(did, anns)| { diff --git a/crates/dfpp/src/frg.rs b/crates/dfpp/src/frg.rs index 73a4becb9e..af3b91ed36 100644 --- a/crates/dfpp/src/frg.rs +++ b/crates/dfpp/src/frg.rs @@ -590,7 +590,7 @@ impl ProgramDescriptionExt for ProgramDescription { self.annotations .values() .flat_map(|v| v.0.iter()) - .filter_map(Annotation::as_label_ann) + .filter_map(Annotation::as_marker) .map(|a| a.marker) .chain(std::iter::once(Identifier::new_intern( name::EXCEPTIONS_LABEL, @@ -678,51 +678,52 @@ impl ProgramDescriptionExt for ProgramDescription { // Part of why we choose this behavior is because there is no // call-site-independent representation for arguments, so the // label has to be attached to the call site argument. - anns.iter() - .filter_map(Annotation::as_label_ann) - .map(move |a| { - ( - if a.refinement.on_return() { - Some(self.all_sources_with_ctrl().into_iter().filter(|(_, s)| { - s.as_function_call().map_or(false, |c| &c.function == id) - })) - } else { - None - } - .into_iter() - .flatten() - .map(|(ctrl, ds)| data_source_as_forge(ds, alloc, ctrl)) - .chain( - self.all_sinks() - .into_iter() - .filter(|s| { - matches!( - s, - DataSink::Argument{function, arg_slot} if - &function.function == id - && a.refinement - .on_argument() - .is_set(*arg_slot as u32) - ) - }) - .map(|s| s.build_forge(alloc)), - ) - .chain([id.build_forge(alloc)]) - .chain(a.refinement.on_argument().into_iter_set_in_domain().map( - |slot| { + anns.iter().filter_map(Annotation::as_marker).map(move |a| { + ( + if a.refinement.on_return() { + Some(self.all_sources_with_ctrl().into_iter().filter(|(_, s)| { + s.as_function_call().map_or(false, |c| &c.function == id) + })) + } else { + None + } + .into_iter() + .flatten() + .map(|(ctrl, ds)| data_source_as_forge(ds, alloc, ctrl)) + .chain( + self.all_sinks() + .into_iter() + .filter(|s| { + matches!( + s, + DataSink::Argument{function, arg_slot} if + &function.function == id + && a.refinement + .on_argument() + .is_set(*arg_slot as u32) + ) + }) + .map(|s| s.build_forge(alloc)), + ) + .chain([id.build_forge(alloc)]) + .chain( + a.refinement + .on_argument() + .into_iter_set_in_domain() + .map(|slot| { FormalParameter { function: *id, position: slot as u16, } .build_forge(alloc) - }, - )) - // This is necessary because otherwise captured variables escape - .collect::>() - .into_iter(), - std::iter::once(a.marker.build_forge(alloc)), + }), ) - }) + // This is necessary because otherwise captured variables escape + .collect::>() + .into_iter(), + std::iter::once(a.marker.build_forge(alloc)), + ) + }) })) .append(" +") .append(alloc.hardline()) @@ -730,7 +731,7 @@ impl ProgramDescriptionExt for ProgramDescription { alloc.forge_relation(self.annotations.iter().map(|(id, (anns, _))| { ( anns.iter() - .filter_map(Annotation::as_exception_annotation) + .filter_map(Annotation::as_exception) .next() .map(|_| id.build_forge(alloc)) .into_iter(), @@ -783,7 +784,7 @@ impl ProgramDescriptionExt for ProgramDescription { ( std::iter::once(o.build_forge(alloc)), anns.iter() - .filter_map(Annotation::as_otype_ann) + .filter_map(Annotation::as_otype) .flat_map(|v| v.iter()) .map(|t| t.build_forge(alloc)), ) @@ -1144,7 +1145,7 @@ impl ProgramDescriptionExt for ProgramDescription { alloc.forge_relation_with_arity(3, self.annotations.iter() .flat_map(|(ident, (anns, _))| - anns.iter().filter_map(Annotation::as_label_ann) + anns.iter().filter_map(Annotation::as_marker) .flat_map(|label| label.refinement.on_argument().into_iter_set_in_domain().map(|i| ( diff --git a/crates/dfpp/src/marker_db.rs b/crates/dfpp/src/marker_db.rs index bc6412751d..8753ef0ebd 100644 --- a/crates/dfpp/src/marker_db.rs +++ b/crates/dfpp/src/marker_db.rs @@ -81,7 +81,7 @@ impl<'tcx> MarkerCtx<'tcx> { .as_local() .map(|ldid| self.local_annotations(ldid)) .into_iter() - .flat_map(|anns| anns.iter().flat_map(Annotation::as_label_ann)) + .flat_map(|anns| anns.iter().flat_map(Annotation::as_marker)) .chain(self.external_markers(def_id).iter()) } @@ -94,7 +94,7 @@ impl<'tcx> MarkerCtx<'tcx> { pub fn is_locally_marked(&self, def_id: LocalDefId) -> bool { self.local_annotations(def_id) .iter() - .any(Annotation::is_label_ann) + .any(Annotation::is_marker) } /// Are there any markers (local or external) on this item? @@ -192,11 +192,11 @@ impl<'tcx> MarkerCtx<'tcx> { .iter() .filter_map(|a| { a.match_extract(&consts::MARKER_MARKER, |i| { - Annotation::Label(crate::ann_parse::ann_match_fn(i)) + Annotation::Marker(crate::ann_parse::ann_match_fn(i)) }).or_else(|| a.match_extract(&consts::LABEL_MARKER, |i| { warn!("The `dfpp::label` annotation is deprecated, use `dfpp::marker` instead"); - Annotation::Label(crate::ann_parse::ann_match_fn(i)) + Annotation::Marker(crate::ann_parse::ann_match_fn(i)) }) ) .or_else(|| { diff --git a/crates/dfpp/src/test_utils.rs b/crates/dfpp/src/test_utils.rs index 4dde7c8126..92f83f36b5 100644 --- a/crates/dfpp/src/test_utils.rs +++ b/crates/dfpp/src/test_utils.rs @@ -571,7 +571,7 @@ pub trait HasGraph<'g>: Sized + Copy { let marker = Identifier::new_intern(marker); self.graph().0.annotations.values().any(|v| { v.0.iter() - .filter_map(|a| a.as_label_ann()) + .filter_map(|a| a.as_marker()) .any(|m| m.marker == marker) }) }