From cb693652beffb5dbcf86d6b654842104f49c9597 Mon Sep 17 00:00:00 2001 From: Ziru Niu Date: Wed, 9 Nov 2022 18:31:36 +0800 Subject: [PATCH] add parser for path related facts --- polonius-parser/src/ir.rs | 9 +++++ polonius-parser/src/lexer.rs | 20 +++++++++- polonius-parser/src/parser.rs | 74 +++++++++++++++++++++++++++++++++++ polonius-parser/src/tests.rs | 72 ++++++++++++++++++++++++++++++++++ polonius-parser/src/token.rs | 18 +++++++++ src/program.rs | 32 ++++++++++++++- 6 files changed, 223 insertions(+), 2 deletions(-) diff --git a/polonius-parser/src/ir.rs b/polonius-parser/src/ir.rs index ad21269a11..7b2e3e80b1 100644 --- a/polonius-parser/src/ir.rs +++ b/polonius-parser/src/ir.rs @@ -7,6 +7,8 @@ pub struct Input { pub blocks: Vec, pub use_of_var_derefs_origin: Vec<(String, String)>, pub drop_of_var_derefs_origin: Vec<(String, String)>, + pub child_path: Vec<(String, String)>, + pub path_is_var: Vec<(String, String)>, } impl Input { @@ -15,6 +17,8 @@ impl Input { known_subsets: Vec, use_of_var_derefs_origin: Vec<(String, String)>, drop_of_var_derefs_origin: Vec<(String, String)>, + child_path: Vec<(String, String)>, + path_is_var: Vec<(String, String)>, blocks: Vec, ) -> Input { // set-up placeholders as origins with a placeholder loan of the same name @@ -31,6 +35,8 @@ impl Input { known_subsets, use_of_var_derefs_origin, drop_of_var_derefs_origin, + child_path, + path_is_var, blocks, } } @@ -67,6 +73,9 @@ pub enum Fact { OriginLiveOnEntry { origin: String }, DefineVariable { variable: String }, UseVariable { variable: String }, + PathMovedAtBase { path: String }, + PathAssignedAtBase { path: String }, + PathAccessedAtBase { path: String }, } #[derive(Debug, PartialEq)] diff --git a/polonius-parser/src/lexer.rs b/polonius-parser/src/lexer.rs index a3c23473ad..da8d6ef545 100644 --- a/polonius-parser/src/lexer.rs +++ b/polonius-parser/src/lexer.rs @@ -53,7 +53,7 @@ impl<'input> Lexer<'input> { [b'{', ..] => (1, T!['{']), [b'}', ..] => (1, T!['}']), // parameters - [c @ b'\'' | c @ b'B' | c @ b'L' | c @ b'V', ..] => ( + [c @ b'\'' | c @ b'B' | c @ b'L' | c @ b'V' | c @ b'P', ..] => ( input .char_indices() .skip(1) @@ -67,6 +67,7 @@ impl<'input> Lexer<'input> { b'B' => T![Block], b'L' => T![loan], b'V' => T![variable], + b'P' => T![path], _ => unreachable!(), }, ), @@ -95,6 +96,12 @@ impl<'input> Lexer<'input> { kw if kw.starts_with("known_subsets".as_bytes()) => { ("known_subsets".len() as u32, T![known subsets]) } + kw if kw.starts_with("child_path".as_bytes()) => { + ("child_path".len() as u32, T![child_path]) + } + kw if kw.starts_with("path_is_var".as_bytes()) => { + ("path_is_var".len() as u32, T![path_is_var]) + } // CFG keywords kw if kw.starts_with("block".as_bytes()) => ("block".len() as u32, T![block]), kw if kw.starts_with("goto".as_bytes()) => ("goto".len() as u32, T![goto]), @@ -122,6 +129,17 @@ impl<'input> Lexer<'input> { kw if kw.starts_with("var_dropped_at".as_bytes()) => { ("var_dropped_at".len() as u32, T![var_dropped_at]) } + kw if kw.starts_with("path_moved_at_base".as_bytes()) => { + ("path_moved_at_base".len() as u32, T![path_moved_at_base]) + } + kw if kw.starts_with("path_assigned_at_base".as_bytes()) => ( + "path_assigned_at_base".len() as u32, + T![path_assigned_at_base], + ), + kw if kw.starts_with("path_accessed_at_base".as_bytes()) => ( + "path_accessed_at_base".len() as u32, + T![path_accessed_at_base], + ), // effect keywords - use kw if kw.starts_with("use".as_bytes()) => ("use".len() as u32, T![use]), _ => return None, diff --git a/polonius-parser/src/parser.rs b/polonius-parser/src/parser.rs index ee0c954959..5b8d24b244 100644 --- a/polonius-parser/src/parser.rs +++ b/polonius-parser/src/parser.rs @@ -98,12 +98,16 @@ where let known_subsets = self.parse_known_subsets().unwrap_or_default(); let use_of_var_derefs_origin = self.parse_use_of_var_derefs_origin().unwrap_or_default(); let drop_of_var_derefs_origin = self.parse_drop_of_var_derefs_origin().unwrap_or_default(); + let child_path = self.parse_child_path().unwrap_or_default(); + let path_is_var = self.parse_path_is_var().unwrap_or_default(); let blocks = self.parse_blocks()?; Ok(Input::new( placeholders, known_subsets, use_of_var_derefs_origin, drop_of_var_derefs_origin, + child_path, + path_is_var, blocks, )) } @@ -164,6 +168,52 @@ where Ok(var_region_mappings) } + pub fn parse_child_path(&mut self) -> Result> { + self.consume(T![child_path])?; + self.consume(T!['{'])?; + let path_path_mappings = self.parse_path_path_mappings()?; + self.consume(T!['}'])?; + Ok(path_path_mappings) + } + + pub fn parse_path_path_mappings(&mut self) -> Result> { + let mut path_var_mappings = Vec::new(); + while self.try_consume(T!['(']) { + let child = self.parse_parameter(T![path])?; + self.consume(T![,])?; + let parent = self.parse_parameter(T![path])?; + self.consume(T![')'])?; + path_var_mappings.push((child, parent)); + if !self.try_consume(T![,]) { + break; + } + } + Ok(path_var_mappings) + } + + pub fn parse_path_is_var(&mut self) -> Result> { + self.consume(T![path_is_var])?; + self.consume(T!['{'])?; + let path_var_mappings = self.parse_path_var_mappings()?; + self.consume(T!['}'])?; + Ok(path_var_mappings) + } + + pub fn parse_path_var_mappings(&mut self) -> Result> { + let mut path_var_mappings = Vec::new(); + while self.try_consume(T!['(']) { + let path = self.parse_parameter(T![path])?; + self.consume(T![,])?; + let variable = self.parse_parameter(T![variable])?; + self.consume(T![')'])?; + path_var_mappings.push((path, variable)); + if !self.try_consume(T![,]) { + break; + } + } + Ok(path_var_mappings) + } + pub fn parse_blocks(&mut self) -> Result> { let mut blocks = Vec::new(); while self.try_consume(T![block]) { @@ -293,6 +343,27 @@ where self.consume(T![')'])?; Ok(Fact::UseVariable { variable }) } + T![path_moved_at_base] => { + self.consume(T![path_moved_at_base])?; + self.consume(T!['('])?; + let path = self.parse_parameter(T![path])?; + self.consume(T![')'])?; + Ok(Fact::PathMovedAtBase { path }) + } + T![path_assigned_at_base] => { + self.consume(T![path_assigned_at_base])?; + self.consume(T!['('])?; + let path = self.parse_parameter(T![path])?; + self.consume(T![')'])?; + Ok(Fact::PathAssignedAtBase { path }) + } + T![path_accessed_at_base] => { + self.consume(T![path_accessed_at_base])?; + self.consume(T!['('])?; + let path = self.parse_parameter(T![path])?; + self.consume(T![')'])?; + Ok(Fact::PathAccessedAtBase { path }) + } found => Err(ParseError::UnexpectedToken { found, expected: vec![ @@ -304,6 +375,9 @@ where T![var_defined_at], T![origin_live_on_entry], T![var_dropped_at], + T![path_moved_at_base], + T![path_assigned_at_base], + T![path_accessed_at_base], ], position: self.position(), }), diff --git a/polonius-parser/src/tests.rs b/polonius-parser/src/tests.rs index b879e22d05..1f63e1c8e4 100644 --- a/polonius-parser/src/tests.rs +++ b/polonius-parser/src/tests.rs @@ -326,3 +326,75 @@ fn known_subsets() { ] ); } + +#[test] +fn path_effects() { + let program = r" + placeholders {} + + block B0 { + path_moved_at_base(P1); + path_assigned_at_base(P2); + path_accessed_at_base(P3); + } + "; + let input = parse_input(program).expect("Path Effects"); + let block = &input.blocks[0]; + assert_eq!(block.statements.len(), 3); + + let statement = &block.statements[0]; + assert_eq!( + statement.effects, + [Effect::Fact(Fact::PathMovedAtBase { + path: "P1".to_string() + })] + ); + + let statement = &block.statements[1]; + assert_eq!( + statement.effects, + [Effect::Fact(Fact::PathAssignedAtBase { + path: "P2".to_string() + })] + ); + + let statement = &block.statements[2]; + assert_eq!( + statement.effects, + [Effect::Fact(Fact::PathAccessedAtBase { + path: "P3".to_string() + })] + ); +} + +#[test] +fn child_path() { + let program = r" + placeholders {} + child_path {(P1, P2), (P2, P3)} + "; + let input = parse_input(program).expect("Child Path"); + assert_eq!( + input.child_path, + [ + ("P1".to_string(), "P2".to_string()), + ("P2".to_string(), "P3".to_string()) + ] + ); +} + +#[test] +fn path_is_var() { + let program = r" + placeholders {} + path_is_var {(P1, V1), (P2, V2)} + "; + let input = parse_input(program).expect("Path is Var"); + assert_eq!( + input.path_is_var, + [ + ("P1".to_string(), "V1".to_string()), + ("P2".to_string(), "V2".to_string()) + ] + ); +} diff --git a/polonius-parser/src/token.rs b/polonius-parser/src/token.rs index 4e2284217c..2f4f6742c9 100644 --- a/polonius-parser/src/token.rs +++ b/polonius-parser/src/token.rs @@ -35,6 +35,8 @@ pub enum TokenKind { KwDropOfVarDerefsOrigin, KwPlaceholders, KwKnownSubsets, + KwChildPath, + KwPathIsVar, // CFG keywords KwBlock, KwGoto, @@ -47,6 +49,9 @@ pub enum TokenKind { KwVarDefinedAt, KwOriginLiveOnEntry, KwVarDroppedAt, + KwPathMovedAtBase, + KwPathAssignedAtBase, + KwPathAccesssedAtBase, // effect keywords - use KwUse, // parameters @@ -54,6 +59,7 @@ pub enum TokenKind { Block, Loan, Variable, + Path, Comment, Whitespace, Error, @@ -102,6 +108,8 @@ macro_rules! T { [drop_of_var_derefs_origin] => { $crate::token::TokenKind::KwDropOfVarDerefsOrigin}; [placeholders] => { $crate::token::TokenKind::KwPlaceholders}; [known subsets] => { $crate::token::TokenKind::KwKnownSubsets}; + [child_path] => { $crate::token::TokenKind::KwChildPath}; + [path_is_var] => { $crate::token::TokenKind::KwPathIsVar}; // CFG keywords [block] => { $crate::token::TokenKind::KwBlock}; [goto] => { $crate::token::TokenKind::KwGoto}; @@ -114,6 +122,9 @@ macro_rules! T { [var_defined_at] => { $crate::token::TokenKind::KwVarDefinedAt}; [origin_live_on_entry] => { $crate::token::TokenKind::KwOriginLiveOnEntry}; [var_dropped_at] => { $crate::token::TokenKind::KwVarDroppedAt}; + [path_moved_at_base] => { $crate::token::TokenKind::KwPathMovedAtBase}; + [path_assigned_at_base] => { $crate::token::TokenKind::KwPathAssignedAtBase}; + [path_accessed_at_base] => { $crate::token::TokenKind::KwPathAccesssedAtBase}; // effect keywords - use [use] => { $crate::token::TokenKind::KwUse}; // parameters @@ -121,6 +132,7 @@ macro_rules! T { [Block] => { $crate::token::TokenKind::Block}; [loan] => { $crate::token::TokenKind::Loan}; [variable] => { $crate::token::TokenKind::Variable}; + [path] => { $crate::token::TokenKind::Path }; [comment] => { $crate::token::TokenKind::Comment}; [ws] => { $crate::token::TokenKind::Whitespace}; [error] => { $crate::token::TokenKind::Error}; @@ -158,6 +170,8 @@ impl fmt::Display for TokenKind { T![drop_of_var_derefs_origin] => write!(f, "drop_of_var_derefs_origin"), T![placeholders] => write!(f, "placeholders"), T![known subsets] => write!(f, "known_subsets"), + T![child_path] => write!(f, "child_path"), + T![path_is_var] => write!(f, "path_is_var"), T![block] => write!(f, "block"), T![goto] => write!(f, "goto"), T![outlives] => write!(f, "outlives"), @@ -168,11 +182,15 @@ impl fmt::Display for TokenKind { T![var_defined_at] => write!(f, "var_defined_at"), T![origin_live_on_entry] => write!(f, "origin_live_on_entry"), T![var_dropped_at] => write!(f, "var_dropped_at"), + T![path_moved_at_base] => write!(f, "path_moved_at_base"), + T![path_assigned_at_base] => write!(f, "path_assigned_at_base"), + T![path_accessed_at_base] => write!(f, "path_accessed_at_base"), T![use] => write!(f, "use"), T![origin] => write!(f, "Origin"), T![Block] => write!(f, "Block"), T![loan] => write!(f, "Loan"), T![variable] => write!(f, "Variable"), + T![path] => write!(f, "Path"), T![comment] => write!(f, "// Comment"), T![ws] => write!(f, ""), T![error] => write!(f, ""), diff --git a/src/program.rs b/src/program.rs index 8e0de05978..3f6523aafb 100644 --- a/src/program.rs +++ b/src/program.rs @@ -123,6 +123,18 @@ pub(crate) fn parse_from_program( }), ); + facts.child_path.extend( + input.child_path.iter().map(|(ref child, ref parent)| { + (tables.paths.intern(child), tables.paths.intern(parent)) + }), + ); + + facts + .path_is_var + .extend(input.path_is_var.iter().map(|(ref path, ref variable)| { + (tables.paths.intern(path), tables.variables.intern(variable)) + })); + for block in &input.blocks { let block_name = &block.name; @@ -252,7 +264,25 @@ fn emit_fact(facts: &mut Facts, fact: &Fact, point: Point, tables: &mut Interner facts.var_used_at.insert((variable, point)); } - _ => {} + // facts: path_moved_at_base(Path, Point) + Fact::PathMovedAtBase { ref path } => { + let path = tables.paths.intern(path); + facts.path_moved_at_base.insert((path, point)); + } + + // facts: path_assigned_at_base(Path, Point) + Fact::PathAssignedAtBase { ref path } => { + let path = tables.paths.intern(path); + facts.path_assigned_at_base.insert((path, point)); + } + + // facts: path_accessed_at_base(Path, Point) + Fact::PathAccessedAtBase { ref path } => { + let path = tables.paths.intern(path); + facts.path_accessed_at_base.insert((path, point)); + } + + Fact::OriginLiveOnEntry { origin: _ } => {} }; }