From 2314ab75178704c8e74ea655775461beedc506b6 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 23 Jun 2022 11:16:05 +0200 Subject: [PATCH] feat: Allow features to enable builtin statements (#4773) * feat: Allow features to enable builtin statements * chore: make generate * chore: cargo clippy --- libflux/flux-core/src/semantic/bootstrap.rs | 6 +- libflux/flux-core/src/semantic/check.rs | 2 +- libflux/flux-core/src/semantic/convert.rs | 112 ++++++++++++++---- .../src/semantic/flatbuffers/tests.rs | 2 +- .../src/semantic/flatbuffers/types.rs | 6 +- libflux/flux-core/src/semantic/mod.rs | 2 +- libflux/flux-core/src/semantic/nodes.rs | 14 ++- libflux/flux-core/src/semantic/tests.rs | 34 +++++- libflux/flux-core/src/semantic/types.rs | 4 +- .../flux-core/src/semantic/walk/test_utils.rs | 2 +- libflux/flux/src/cffi.rs | 8 +- libflux/go/libflux/buildinfo.gen.go | 18 +-- 12 files changed, 154 insertions(+), 56 deletions(-) diff --git a/libflux/flux-core/src/semantic/bootstrap.rs b/libflux/flux-core/src/semantic/bootstrap.rs index b4d1919fe5..f04828d2bf 100644 --- a/libflux/flux-core/src/semantic/bootstrap.rs +++ b/libflux/flux-core/src/semantic/bootstrap.rs @@ -440,7 +440,7 @@ mod tests { if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for int. {:?}", err); } - convert_polytype(&typ_expr)? + convert_polytype(&typ_expr, &Default::default())? })]) .unwrap(); if want != types { @@ -460,7 +460,7 @@ mod tests { "TypeExpression parsing failed for int. {:?}", err ); } - convert_polytype(&typ_expr, )? + convert_polytype(&typ_expr, &Default::default())? }, String::from("b") => { let mut p = parser::Parser::new("{x: int , y: int}"); @@ -470,7 +470,7 @@ mod tests { "TypeExpression parsing failed for int. {:?}", err ); } - convert_polytype(&typ_expr, )? + convert_polytype(&typ_expr, &Default::default())? }, }; if want diff --git a/libflux/flux-core/src/semantic/check.rs b/libflux/flux-core/src/semantic/check.rs index 9df5dc4a15..4922353008 100644 --- a/libflux/flux-core/src/semantic/check.rs +++ b/libflux/flux-core/src/semantic/check.rs @@ -368,7 +368,7 @@ mod tests { ctr = ctr + 1; } let ast_pkg = merge_ast_files(ast_files); - let sem_pkg = convert::convert_package(&ast_pkg, &Default::default())?; + let sem_pkg = convert::convert_package(&ast_pkg, &Default::default(), &Default::default())?; Ok(sem_pkg) } diff --git a/libflux/flux-core/src/semantic/convert.rs b/libflux/flux-core/src/semantic/convert.rs index fd9404d200..de1c57c67a 100644 --- a/libflux/flux-core/src/semantic/convert.rs +++ b/libflux/flux-core/src/semantic/convert.rs @@ -18,6 +18,7 @@ use crate::{ env::Environment, nodes::*, types::{self, BuiltinType, MonoType, MonoTypeMap, SemanticMap}, + AnalyzerConfig, Feature, }, }; @@ -71,8 +72,12 @@ pub type Result = std::result::Result; /// overhead involved. /// /// [AST package]: ast::Package -pub fn convert_package(pkg: &ast::Package, env: &Environment) -> Result> { - let mut converter = Converter::with_env(env); +pub fn convert_package( + pkg: &ast::Package, + env: &Environment, + config: &AnalyzerConfig, +) -> Result> { + let mut converter = Converter::with_env(env, config); let r = converter.convert_package(pkg); converter.finish()?; Ok(r) @@ -84,8 +89,9 @@ pub fn convert_package(pkg: &ast::Package, env: &Environment) -> Result Result> { - let mut converter = Converter::new(); + let mut converter = Converter::new(config); let r = converter.convert_polytype(type_expression); converter.finish()?; Ok(r) @@ -95,8 +101,9 @@ pub fn convert_polytype( pub(crate) fn convert_monotype( ty: &ast::MonoType, tvars: &mut BTreeMap, + config: &AnalyzerConfig, ) -> Result> { - let mut converter = Converter::new(); + let mut converter = Converter::new(config); let r = converter.convert_monotype(&ty, tvars); converter.finish()?; Ok(r) @@ -257,6 +264,24 @@ pub struct SymbolInfo { #[allow(missing_docs)] pub type PackageInfo = HashMap; +fn get_attribute<'a>(comments: impl IntoIterator, attr: &str) -> Option<&'a str> { + comments.into_iter().find_map(|comment| { + // Remove the comment and any preceding whitespace + let comment = comment.trim_start_matches("//").trim_start(); + if let Some(content) = comment.strip_prefix('@') { + let mut iter = content.splitn(2, char::is_whitespace); + let name = iter.next().unwrap(); + if name == attr { + Some(iter.next().unwrap_or("").trim()) + } else { + None + } + } else { + None + } + }) +} + #[derive(Debug, Default)] struct Symbols<'a> { symbols: SymbolStack, @@ -266,9 +291,9 @@ struct Symbols<'a> { } impl<'a> Symbols<'a> { - fn with_env(env: &'a Environment) -> Self { + fn new(env: Option<&'a Environment>) -> Self { Symbols { - env: Some(env), + env, package_info: Default::default(), local_labels: Default::default(), symbols: SymbolStack::default(), @@ -287,6 +312,7 @@ impl<'a> Symbols<'a> { if package.is_none() && !self.local_labels.contains_key(&symbol[..]) { self.local_labels.insert(symbol.to_string(), symbol.clone()); } + symbol } @@ -326,20 +352,23 @@ impl<'a> Symbols<'a> { pub(crate) struct Converter<'a> { symbols: Symbols<'a>, errors: Errors, + config: &'a AnalyzerConfig, } impl<'a> Converter<'a> { - fn new() -> Self { + fn new(config: &'a AnalyzerConfig) -> Self { Converter { - symbols: Symbols::default(), + symbols: Symbols::new(None), errors: Errors::new(), + config, } } - pub(crate) fn with_env(env: &'a Environment) -> Self { + pub(crate) fn with_env(env: &'a Environment, config: &'a AnalyzerConfig) -> Self { Converter { - symbols: Symbols::with_env(env), + symbols: Symbols::new(Some(env)), errors: Errors::new(), + config, } } @@ -385,7 +414,7 @@ impl<'a> Converter<'a> { let body = file .body .iter() - .map(|s| self.convert_statement(package_name, s)) + .filter_map(|s| self.convert_statement(package_name, s)) .collect::>(); File { @@ -430,13 +459,13 @@ impl<'a> Converter<'a> { } } - fn convert_statement(&mut self, package: &str, stmt: &ast::Statement) -> Statement { - match stmt { + fn convert_statement(&mut self, package: &str, stmt: &ast::Statement) -> Option { + Some(match stmt { ast::Statement::Option(s) => { Statement::Option(Box::new(self.convert_option_statement(s))) } ast::Statement::Builtin(s) => { - Statement::Builtin(self.convert_builtin_statement(package, s)) + Statement::Builtin(self.convert_builtin_statement(package, s)?) } ast::Statement::Test(s) => Statement::Test(Box::new(self.convert_test_statement(s))), ast::Statement::TestCase(s) => { @@ -454,7 +483,7 @@ impl<'a> Converter<'a> { ast::Statement::Bad(s) => Statement::Error(BadStmt { loc: s.base.location.clone(), }), - } + }) } fn convert_assignment(&mut self, assign: &ast::Assignment) -> Assignment { @@ -473,12 +502,29 @@ impl<'a> Converter<'a> { } } - fn convert_builtin_statement(&mut self, package: &str, stmt: &ast::BuiltinStmt) -> BuiltinStmt { - BuiltinStmt { + fn convert_builtin_statement( + &mut self, + package: &str, + stmt: &ast::BuiltinStmt, + ) -> Option { + // Only include builtin statements that have the `feature` attribute if a matching + // feature is detected + if let Some(attr) = get_attribute( + stmt.base.comments.iter().map(|c| c.text.as_str()), + "feature", + ) + .and_then(|attr| attr.parse::().ok()) + { + if self.config.features.iter().all(|feature| *feature != attr) { + return None; + } + } + + Some(BuiltinStmt { loc: stmt.base.location.clone(), id: self.define_identifier(Some(package), &stmt.id, &stmt.base.comments), typ_expr: self.convert_polytype(&stmt.ty), - } + }) } fn convert_testcase(&mut self, package: &str, stmt: &ast::TestCaseStmt) -> TestCaseStmt { TestCaseStmt { @@ -492,7 +538,7 @@ impl<'a> Converter<'a> { .block .body .iter() - .map(|s| self.convert_statement(package, s)) + .filter_map(|s| self.convert_statement(package, s)) .collect(), } } @@ -1345,7 +1391,8 @@ mod tests { } fn test_convert(pkg: ast::Package) -> Result> { - let mut converter = Converter::new(); + let config = AnalyzerConfig::default(); + let mut converter = Converter::new(&config); let mut pkg = converter.convert_package(&pkg); converter.finish()?; @@ -2741,11 +2788,18 @@ mod tests { test_convert(pkg).unwrap(); } + fn convert_monotype_test( + ty: &ast::MonoType, + tvars: &mut BTreeMap, + ) -> Result> { + convert_monotype(ty, tvars, &Default::default()) + } + #[test] fn test_convert_monotype_int() { let monotype = Parser::new("int").parse_monotype(); let mut m = BTreeMap::new(); - let got = convert_monotype(&monotype, &mut m).unwrap(); + let got = convert_monotype_test(&monotype, &mut m).unwrap(); let want = MonoType::INT; assert_eq!(want, got); } @@ -2755,7 +2809,7 @@ mod tests { let monotype = Parser::new("{ A with b: int }").parse_monotype(); let mut m = BTreeMap::new(); - let got = convert_monotype(&monotype, &mut m).unwrap(); + let got = convert_monotype_test(&monotype, &mut m).unwrap(); let want = MonoType::from(types::Record::Extension { head: types::Property { k: types::RecordLabel::from("b"), @@ -2771,7 +2825,7 @@ mod tests { let monotype_ex = Parser::new("(?A: int) => int").parse_monotype(); let mut m = BTreeMap::new(); - let got = convert_monotype(&monotype_ex, &mut m).unwrap(); + let got = convert_monotype_test(&monotype_ex, &mut m).unwrap(); let mut opt = MonoTypeMap::new(); opt.insert(String::from("A"), MonoType::INT.into()); let want = MonoType::from(types::Function { @@ -2787,7 +2841,7 @@ mod tests { fn test_convert_polytype() { let type_exp = Parser::new("(A: T, B: S) => T where T: Addable, S: Divisible").parse_type_expression(); - let got = convert_polytype(&type_exp).unwrap(); + let got = convert_polytype(&type_exp, &Default::default()).unwrap(); let want = { let mut vars = Vec::::new(); @@ -2820,7 +2874,7 @@ mod tests { fn test_convert_polytype_2() { let type_exp = Parser::new("(A: T, B: S) => T where T: Addable").parse_type_expression(); - let got = convert_polytype(&type_exp).unwrap(); + let got = convert_polytype(&type_exp, &Default::default()).unwrap(); let mut vars = Vec::::new(); vars.push(types::BoundTvar(0)); vars.push(types::BoundTvar(1)); @@ -2841,4 +2895,12 @@ mod tests { let want = types::PolyType { vars, cons, expr }; assert_eq!(want, got); } + + #[test] + fn test_get_attribute() { + assert_eq!( + get_attribute(["// @feature labelPolymorphism\n"], "feature"), + Some("labelPolymorphism"), + ); + } } diff --git a/libflux/flux-core/src/semantic/flatbuffers/tests.rs b/libflux/flux-core/src/semantic/flatbuffers/tests.rs index 1b175fafc8..f294802e4f 100644 --- a/libflux/flux-core/src/semantic/flatbuffers/tests.rs +++ b/libflux/flux-core/src/semantic/flatbuffers/tests.rs @@ -94,7 +94,7 @@ re !~ /foo/ package: String::from("test"), files: f, }; - let mut pkg = match convert::convert_package(&pkg, &Default::default()) { + let mut pkg = match convert::convert_package(&pkg, &Default::default(), &Default::default()) { Ok(pkg) => pkg, Err(e) => { assert!(false, "{}", e); diff --git a/libflux/flux-core/src/semantic/flatbuffers/types.rs b/libflux/flux-core/src/semantic/flatbuffers/types.rs index dda69a79b8..8d37e953d0 100644 --- a/libflux/flux-core/src/semantic/flatbuffers/types.rs +++ b/libflux/flux-core/src/semantic/flatbuffers/types.rs @@ -731,7 +731,7 @@ mod tests { if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for {}. {:?}", expr, err); } - let want = convert_polytype(&typ_expr).unwrap(); + let want = convert_polytype(&typ_expr, &Default::default()).unwrap(); let mut builder = flatbuffers::FlatBufferBuilder::new(); let buf = serialize(&mut builder, want.clone(), build_polytype); @@ -746,14 +746,14 @@ mod tests { if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for bool. {:?}", err); } - let a = convert_polytype(&typ_expr).unwrap(); + let a = convert_polytype(&typ_expr, &Default::default()).unwrap(); let mut p = parser::Parser::new("time"); let typ_expr = p.parse_type_expression(); if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for time. {:?}", err); } - let b = convert_polytype(&typ_expr).unwrap(); + let b = convert_polytype(&typ_expr, &Default::default()).unwrap(); let want: PackageExports = vec![ (Symbol::from("a"), a.clone()), diff --git a/libflux/flux-core/src/semantic/mod.rs b/libflux/flux-core/src/semantic/mod.rs index 331a21445d..5823c42429 100644 --- a/libflux/flux-core/src/semantic/mod.rs +++ b/libflux/flux-core/src/semantic/mod.rs @@ -603,7 +603,7 @@ impl<'env, I: import::Importer> Analyzer<'env, I> { } let (mut sem_pkg, package_info) = { - let mut converter = convert::Converter::with_env(&self.env); + let mut converter = convert::Converter::with_env(&self.env, &self.config); let sem_pkg = converter.convert_package(ast_pkg); let package_info = converter.take_package_info(); diff --git a/libflux/flux-core/src/semantic/nodes.rs b/libflux/flux-core/src/semantic/nodes.rs index 2c67716c75..7013a8073c 100644 --- a/libflux/flux-core/src/semantic/nodes.rs +++ b/libflux/flux-core/src/semantic/nodes.rs @@ -139,6 +139,10 @@ struct InferState<'a, 'env> { } impl InferState<'_, '_> { + fn add(&mut self, name: Symbol, t: PolyType) { + self.env.add(name, t) + } + fn lookup(&mut self, loc: &ast::SourceLocation, name: &Symbol) -> PolyType { self.env.lookup(name).cloned().unwrap_or_else(|| { self.error( @@ -514,7 +518,7 @@ impl File { PolyType::error() }); - infer.env.add(name, poly); + infer.add(name, poly); } for node in &mut self.body { @@ -605,7 +609,7 @@ pub struct BuiltinStmt { impl BuiltinStmt { fn infer(&mut self, infer: &mut InferState<'_, '_>) -> std::result::Result<(), Error> { self.typ_expr.check_signture(&self.loc, infer); - infer.env.add(self.id.name.clone(), self.typ_expr.clone()); + infer.add(self.id.name.clone(), self.typ_expr.clone()); Ok(()) } fn apply(&mut self, _: &mut dyn Substituter) {} @@ -824,7 +828,7 @@ impl VariableAssgn { self.cons = p.cons.clone(); // Update the type environment - infer.env.add(self.id.name.clone(), p); + infer.add(self.id.name.clone(), p); Ok(()) } fn apply(&mut self, sub: &mut dyn Substituter) { @@ -1031,7 +1035,7 @@ impl FunctionExpr { cons: BoundTvarKinds::new(), expr: param_type.clone(), }; - infer.env.add(id.clone(), typ); + infer.add(id.clone(), typ); opt.insert(id.to_string(), param_type.into()); } None => { @@ -1044,7 +1048,7 @@ impl FunctionExpr { cons: BoundTvarKinds::new(), expr: MonoType::Var(ftvar), }; - infer.env.add(id.clone(), typ.clone()); + infer.add(id.clone(), typ.clone()); // Piped arguments cannot have a default value. // So check if this is a piped argument. if param.is_pipe { diff --git a/libflux/flux-core/src/semantic/tests.rs b/libflux/flux-core/src/semantic/tests.rs index 8d2dea3f27..b88d24d5d3 100644 --- a/libflux/flux-core/src/semantic/tests.rs +++ b/libflux/flux-core/src/semantic/tests.rs @@ -54,7 +54,8 @@ fn parse_map(package: Option<&str>, m: HashMap<&str, &str>) -> PolyTypeHashMap string + + // @feature labelPolymorphism + builtin x : (x: A) => string where A: Label + "#, + exp: map![ + "x" => "(x: string) => string", + ], + } + + test_infer! { + config: AnalyzerConfig{ + features: vec![Feature::LabelPolymorphism], + ..AnalyzerConfig::default() + }, + src: r#" + builtin x : (x: string) => string + + // @feature labelPolymorphism + builtin x : (x: A) => string where A: Label + "#, + exp: map![ + "x" => "(x: A) => string where A: Label", + ], + } +} diff --git a/libflux/flux-core/src/semantic/types.rs b/libflux/flux-core/src/semantic/types.rs index 5592f0c8cb..b7715e61d9 100644 --- a/libflux/flux-core/src/semantic/types.rs +++ b/libflux/flux-core/src/semantic/types.rs @@ -2311,7 +2311,7 @@ mod tests { if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for {}. {:?}", typ, err); } - convert_polytype(&typ_expr).unwrap() + convert_polytype(&typ_expr, &Default::default()).unwrap() } fn parse_type(expr: &str, tvars: &mut BTreeMap) -> MonoType { @@ -2321,7 +2321,7 @@ mod tests { if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed. {:?}", err); } - convert_monotype(&typ_expr.monotype, tvars).unwrap() + convert_monotype(&typ_expr.monotype, tvars, &Default::default()).unwrap() } #[test] diff --git a/libflux/flux-core/src/semantic/walk/test_utils.rs b/libflux/flux-core/src/semantic/walk/test_utils.rs index d837b5bad7..5ab10abde1 100644 --- a/libflux/flux-core/src/semantic/walk/test_utils.rs +++ b/libflux/flux-core/src/semantic/walk/test_utils.rs @@ -13,5 +13,5 @@ pub fn compile(source: &str) -> nodes::Package { package: "main".to_string(), files: vec![file], }; - convert::convert_package(&ast_pkg, &Default::default()).unwrap() + convert::convert_package(&ast_pkg, &Default::default(), &Default::default()).unwrap() } diff --git a/libflux/flux/src/cffi.rs b/libflux/flux/src/cffi.rs index a42290d6c1..423875636c 100644 --- a/libflux/flux/src/cffi.rs +++ b/libflux/flux/src/cffi.rs @@ -968,7 +968,7 @@ from(bucket: v.bucket) if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed. {:?}", err); } - let want = convert_polytype(&typ_expr).unwrap(); + let want = convert_polytype(&typ_expr, &Default::default()).unwrap(); assert_eq!(want, got.lookup("x").expect("'x' not found").clone()); } @@ -1004,7 +1004,7 @@ from(bucket: v.bucket) if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for {:?}", err); } - let want_a = convert_polytype(&typ_expr).unwrap(); + let want_a = convert_polytype(&typ_expr, &Default::default()).unwrap(); let code = "stream[{ D with _value: A @@ -1020,7 +1020,7 @@ from(bucket: v.bucket) if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for {:?}", err); } - let want_b = convert_polytype(&typ_expr).unwrap(); + let want_b = convert_polytype(&typ_expr, &Default::default()).unwrap(); let code = "stream[{ D with _value: A @@ -1036,7 +1036,7 @@ from(bucket: v.bucket) if let Err(err) = ast::check::check(ast::walk::Node::TypeExpression(&typ_expr)) { panic!("TypeExpression parsing failed for {:?}", err); } - let want_c = convert_polytype(&typ_expr).unwrap(); + let want_c = convert_polytype(&typ_expr, &Default::default()).unwrap(); assert_eq!(want_a, got.lookup("a").expect("'a' not found").clone()); assert_eq!(want_b, got.lookup("b").expect("'b' not found").clone()); diff --git a/libflux/go/libflux/buildinfo.gen.go b/libflux/go/libflux/buildinfo.gen.go index 840a6ffdf6..8887b8cfa8 100644 --- a/libflux/go/libflux/buildinfo.gen.go +++ b/libflux/go/libflux/buildinfo.gen.go @@ -35,32 +35,32 @@ var sourceHashes = map[string]string{ "libflux/flux-core/src/scanner/token.rs": "5090c2ac4b341566a85f7d9ed9b48746dd2084ec2f3effdb4a7dc16cf34d44b9", "libflux/flux-core/src/scanner/unicode.rl": "f923f3b385ddfa65c74427b11971785fc25ea806ca03d547045de808e16ef9a1", "libflux/flux-core/src/scanner/unicode.rl.COPYING": "6cf2d5d26d52772ded8a5f0813f49f83dfa76006c5f398713be3854fe7bc4c7e", - "libflux/flux-core/src/semantic/bootstrap.rs": "0d22aff61eca256a3922b5c1989d208f22f3c62b5bc19860d00b14ead0e09a7a", - "libflux/flux-core/src/semantic/check.rs": "4edce5a2acfe59b4b9f4921ba9f0f6b90c2831a891f2af95cd9ae777a054e6e4", - "libflux/flux-core/src/semantic/convert.rs": "5486cee013ae2a042fb1e7169baa2bb59451b37dad087d5651fbe6c5b00aac22", + "libflux/flux-core/src/semantic/bootstrap.rs": "6cdf0484bfe28092955c57dc5eefc34e8bea4226bc625d6a6370d97231c8bb22", + "libflux/flux-core/src/semantic/check.rs": "aba00a83db5302e0438d3d4093ca3c14a738eaac67e85fe6995055d602b55a27", + "libflux/flux-core/src/semantic/convert.rs": "b765b57058c61b274aed387dce874c47a91d84843aaeeeba2e213f3628d8cdd6", "libflux/flux-core/src/semantic/env.rs": "0d6295a88dae8eaaed12ee20a8d218616683e8d45a776966e0cab02be2760fd0", "libflux/flux-core/src/semantic/flatbuffers/mod.rs": "270671ffdcb1eb5308f9bbab0431c9464df264070a2deb05c526d182a6ec5585", "libflux/flux-core/src/semantic/flatbuffers/semantic_generated.rs": "beaaa6b08d8b56dba81153a58e440bbdc430b4eca0201a3431e90b793f1adbce", - "libflux/flux-core/src/semantic/flatbuffers/types.rs": "efb290c1b5aee9d16c3576eba6ab1dccefdc1b3bc3f727e3677cf87536ef2a9f", + "libflux/flux-core/src/semantic/flatbuffers/types.rs": "2c86f457dce786b4f3320870098ac017786c35db94732ee964a3715e50d4d647", "libflux/flux-core/src/semantic/formatter/mod.rs": "154c57a50af3c5e2f975f43cf1534864d4c81ca26e8d59b1f3c7d949fdac4ac4", "libflux/flux-core/src/semantic/fresh.rs": "18cb879ece9052682c9dda8117c23acd1f63c0325feaa1aef9a53db4f17d2d69", "libflux/flux-core/src/semantic/fs.rs": "f7f609bc8149769d99b737150e184a2d54029c0b768365dbcf08ff193b0e1f6f", "libflux/flux-core/src/semantic/import.rs": "184e955211db1ceb1be782b4daf75584b86907b1428e50015497909cfc2dd89a", "libflux/flux-core/src/semantic/infer.rs": "fc8e7ab4b1339c7f384e3b4223364bcc7b81e04eb01bbd4e02c5426392f5abab", - "libflux/flux-core/src/semantic/mod.rs": "c09e559d5495929a2f8aee9e513ec7a2a8f521456c13e50d5c1131bc101620ca", - "libflux/flux-core/src/semantic/nodes.rs": "c167257c25ea41e1f8ccfa6bb942cbb5c87bf392b5d2346f32e75ede13d38974", + "libflux/flux-core/src/semantic/mod.rs": "7dbc3bf025770b10b0bd97226055dda8eae8af8aeb8489243b869a5015bbccbb", + "libflux/flux-core/src/semantic/nodes.rs": "3a58c34ff9e17061711de626560d386768862ebc2f8faf7764fac0dd054dc4dd", "libflux/flux-core/src/semantic/sub.rs": "043d92de0ca86ffb93fc4fcb7256bcbba8026016f45792dcaf4597812e8b3b4c", "libflux/flux-core/src/semantic/symbols.rs": "ddbceca632ca384c6bb461a660e02781c43295025f2dd10f1ea997131dd5eb30", - "libflux/flux-core/src/semantic/types.rs": "2f2c791c585866f06912e417eb7df3f9efa2cbb129b87c61391df7b619a34051", + "libflux/flux-core/src/semantic/types.rs": "190ba7823fa0f42262747ca46eb7b43f2b24c8247c5e88ea67e3b735343ad9cf", "libflux/flux-core/src/semantic/vectorize.rs": "7024304c2f2accc57ef44cb4e739e184bee3d217bf2bf38a957edfdee935d6b4", "libflux/flux-core/src/semantic/walk/_walk.rs": "c3d04e72cfbe595d4919b84f4df4bc6c55297516cf8e12e6cb691f48be648291", "libflux/flux-core/src/semantic/walk/mod.rs": "f71aac086dd1d7b730e24bac690a3e47a9bfe575cd6cba499af67db9b920c726", - "libflux/flux-core/src/semantic/walk/test_utils.rs": "a4a594ed40ff8a8297752f1ba618abd8b94f30a649afa59bc7c26129dd192fd8", + "libflux/flux-core/src/semantic/walk/test_utils.rs": "043d5137ed626f1990c89f639e867e5b33a906f13c427313b497062f98215846", "libflux/flux-core/src/semantic/walk/walk_mut.rs": "b4518f0c777cdf46cf65c5aee9e8ae40be420bdbcb900e61b1581f924e0d786a", "libflux/flux/Cargo.toml": "9fae0ee886a5c5278ad6b4606c36d9e929ab9529995856a333a618caae35c210", "libflux/flux/FLUXDOC.md": "92e6dd8043bd87b4924e09aa28fb5346630aee1214de28ea2c8fc0687cad0785", "libflux/flux/build.rs": "6f2a4da51744a174ab13a1ebcb18ea39c0acfc2c720e7a61ea3e326fb0649529", - "libflux/flux/src/cffi.rs": "ceb834f6ee042671db9c418fbe67aa78a497447ce32ea2855352a9445c979832", + "libflux/flux/src/cffi.rs": "c5f9776c7274b0249916fafc725bbb48ab865fd857c1388f01c188877b58d25d", "libflux/flux/src/lib.rs": "3cd7dfcf7491f5797d501a647ee92a3f66b5790f6df7ed2238f1969b4bd929ed", "libflux/flux/templates/base.html": "a818747b9621828bb96b94291c60922db54052bbe35d5e354f8e589d2a4ebd02", "libflux/flux/templates/home.html": "f9927514dd42ca7271b4817ad1ca33ec79c03a77a783581b4dcafabd246ebf3f",