From 24c879c3e59be4d1c8775e295ad0df409a3ebdbc Mon Sep 17 00:00:00 2001 From: Emily Herbert <17410721+emilyaherbert@users.noreply.github.com> Date: Wed, 23 Feb 2022 17:51:54 -0600 Subject: [PATCH] Allow prefixes in any call path. (#472) * Test case and parsing. * Better test case. * First pass as structs. * CallPath keeps track of if its an absolute path or not. * Update license. * Better test case. * Implement for enums. * Fix bug created during merge with master. * Better test case. * Fix clippy error. * Implement for functions. * Fix merge mistake. * Fix bug. * Fix cargo suggestion. * Remove part of test case due to OOM error. * Turn silent mode back on. * Clippy suggestions. * Pin lib version (#689) Depends on #683 * Add clippy allow. * Clippy suggestions. * Clippy suggestions. * Clippy suggestions. * PR review feedback. * Add verbose back. * Add unintended test. * Update test case. Co-authored-by: John Adler Co-authored-by: John Adler --- .../src/asm_generation/expression/mod.rs | 3 +- .../dead_code_analysis.rs | 6 +- sway-core/src/error.rs | 3 + sway-core/src/parse_tree/call_path.rs | 48 ++- .../src/parse_tree/declaration/impl_trait.rs | 1 - .../src/parse_tree/declaration/struct.rs | 1 + .../src/parse_tree/expression/method_name.rs | 1 - sway-core/src/parse_tree/expression/mod.rs | 84 +++-- .../src/parse_tree/expression/unary_op.rs | 1 + .../expression/func_app_instantiation.rs | 119 +++++++ .../ast_node/expression/mod.rs | 2 + .../ast_node/expression/typed_expression.rs | 293 ++++++++---------- .../typed_expression/method_application.rs | 4 +- .../semantic_analysis/ast_node/impl_trait.rs | 1 + .../src/semantic_analysis/ast_node/mod.rs | 2 + sway-core/src/semantic_analysis/namespace.rs | 1 + .../semantic_analysis/node_dependencies.rs | 5 +- sway-core/src/sway.pest | 44 ++- sway-server/src/utils/common.rs | 2 +- sway-utils/src/helpers.rs | 1 + test/src/e2e_vm_tests/mod.rs | 3 +- .../test_programs/dependencies/src/main.sw | 2 +- .../Forc.toml | 0 .../json_abi_oracle.json | 0 .../src/a_dependency.sw | 0 .../src/bar.sw | 0 .../src/inner/bar.sw | 0 .../src/inner/double_inner/double_bar.sw | 0 .../src/main.sw | 0 .../src/nested_dependency/bar/bar.sw | 0 .../use_full_path_names/Forc.toml | 5 + .../use_full_path_names/json_abi_oracle.json | 1 + .../use_full_path_names/src/a_dependency.sw | 5 + .../use_full_path_names/src/b_dependency.sw | 5 + .../use_full_path_names/src/c_dependency.sw | 5 + .../use_full_path_names/src/main.sw | 14 + 36 files changed, 429 insertions(+), 233 deletions(-) create mode 100644 sway-core/src/semantic_analysis/ast_node/expression/func_app_instantiation.rs rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/Forc.toml (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/json_abi_oracle.json (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/src/a_dependency.sw (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/src/bar.sw (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/src/inner/bar.sw (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/src/inner/double_inner/double_bar.sw (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/src/main.sw (100%) rename test/src/e2e_vm_tests/test_programs/{dependencies_parsing_error => dependency_parsing_error}/src/nested_dependency/bar/bar.sw (100%) create mode 100644 test/src/e2e_vm_tests/test_programs/use_full_path_names/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/use_full_path_names/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/use_full_path_names/src/a_dependency.sw create mode 100644 test/src/e2e_vm_tests/test_programs/use_full_path_names/src/b_dependency.sw create mode 100644 test/src/e2e_vm_tests/test_programs/use_full_path_names/src/c_dependency.sw create mode 100644 test/src/e2e_vm_tests/test_programs/use_full_path_names/src/main.sw diff --git a/sway-core/src/asm_generation/expression/mod.rs b/sway-core/src/asm_generation/expression/mod.rs index 60f5a468edf..5d528dc256c 100644 --- a/sway-core/src/asm_generation/expression/mod.rs +++ b/sway-core/src/asm_generation/expression/mod.rs @@ -361,8 +361,7 @@ pub(crate) fn convert_expression_to_asm( } // ABI casts are purely compile-time constructs and generate no corresponding bytecode TypedExpressionVariant::AbiCast { .. } => ok(vec![], warnings, errors), - a => { - println!("unimplemented: {:?}", a); + _ => { errors.push(CompileError::Unimplemented( "ASM generation has not yet been implemented for this.", exp.span.clone(), diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 4cddc1f5c40..698ede53a3a 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -491,8 +491,9 @@ fn connect_trait_declaration( ) { graph.namespace.add_trait( CallPath { - suffix: decl.name.clone(), prefixes: vec![], + suffix: decl.name.clone(), + is_absolute: false, }, entry_node, ); @@ -506,8 +507,9 @@ fn connect_abi_declaration( ) { graph.namespace.add_trait( CallPath { - suffix: decl.name.clone(), prefixes: vec![], + suffix: decl.name.clone(), + is_absolute: false, }, entry_node, ); diff --git a/sway-core/src/error.rs b/sway-core/src/error.rs index 7f65e61dc21..cb522703a68 100644 --- a/sway-core/src/error.rs +++ b/sway-core/src/error.rs @@ -390,6 +390,8 @@ pub enum CompileError { span: Span, err: pest::error::Error, }, + #[error("Error parsing input: {err:?}")] + ParseError { span: Span, err: String }, #[error( "Invalid top-level item: {0:?}. A program should consist of a contract, script, or \ predicate at the top level." @@ -929,6 +931,7 @@ impl CompileError { Unimplemented(_, span) => span, TypeError(err) => err.internal_span(), ParseFailure { span, .. } => span, + ParseError { span, .. } => span, InvalidTopLevelItem(_, span) => span, Internal(_, span) => span, InternalOwned(_, span) => span, diff --git a/sway-core/src/parse_tree/call_path.rs b/sway-core/src/parse_tree/call_path.rs index a71e0ee2b4f..b805d248e78 100644 --- a/sway-core/src/parse_tree/call_path.rs +++ b/sway-core/src/parse_tree/call_path.rs @@ -9,6 +9,9 @@ use pest::iterators::Pair; pub struct CallPath { pub prefixes: Vec, pub suffix: Ident, + // If `is_absolute` is true, then this call path is an absolute path from + // the project root namespace. If not, then it is relative to the current namespace. + pub(crate) is_absolute: bool, } impl std::convert::From for CallPath { @@ -16,6 +19,7 @@ impl std::convert::From for CallPath { CallPath { prefixes: vec![], suffix: other, + is_absolute: false, } } } @@ -66,10 +70,31 @@ impl CallPath { pair: Pair, config: Option<&BuildConfig>, ) -> CompileResult { + assert!(pair.as_rule() == Rule::call_path || pair.as_rule() == Rule::call_path_); let mut warnings = vec![]; let mut errors = vec![]; + let span = Span { + span: pair.as_span(), + path: config.map(|c| c.path()), + }; + if !(pair.as_rule() == Rule::call_path || pair.as_rule() == Rule::call_path_) { + errors.push(CompileError::ParseError { + span, + err: "expected call path here".to_string(), + }); + return err(warnings, errors); + } let mut pairs_buf = vec![]; - for pair in pair.into_inner() { + let stmt = pair.into_inner().next().unwrap(); + let is_absolute = stmt.as_rule() == Rule::absolute_call_path + || stmt.as_rule() == Rule::absolute_call_path_; + let stmt = stmt.into_inner(); + let it = if is_absolute { + stmt.skip(1) + } else { + stmt.skip(0) + }; + for pair in it { if pair.as_rule() != Rule::path_separator { pairs_buf.push(check!( ident::parse_from_pair(pair, config), @@ -83,7 +108,24 @@ impl CallPath { let suffix = pairs_buf.pop().unwrap(); let prefixes = pairs_buf; - // TODO eventually we want to be able to call methods with colon-delineated syntax - ok(CallPath { prefixes, suffix }, warnings, errors) + ok( + CallPath { + prefixes, + suffix, + is_absolute, + }, + warnings, + errors, + ) + } + + pub(crate) fn friendly_name(&self) -> String { + let mut buf = String::new(); + for prefix in self.prefixes.iter() { + buf.push_str(prefix.as_str()); + buf.push_str("::"); + } + buf.push_str(self.suffix.as_str()); + buf } } diff --git a/sway-core/src/parse_tree/declaration/impl_trait.rs b/sway-core/src/parse_tree/declaration/impl_trait.rs index 52fe6b8c94e..18824835a4d 100644 --- a/sway-core/src/parse_tree/declaration/impl_trait.rs +++ b/sway-core/src/parse_tree/declaration/impl_trait.rs @@ -51,7 +51,6 @@ impl ImplTrait { let impl_keyword = iter.next().unwrap(); assert_eq!(impl_keyword.as_str(), "impl"); let trait_name = iter.next().unwrap(); - assert_eq!(trait_name.as_rule(), Rule::trait_name); let trait_name = check!( CallPath::parse_from_pair(trait_name, config), return err(warnings, errors), diff --git a/sway-core/src/parse_tree/declaration/struct.rs b/sway-core/src/parse_tree/declaration/struct.rs index a2f12768ce6..5328009de95 100644 --- a/sway-core/src/parse_tree/declaration/struct.rs +++ b/sway-core/src/parse_tree/declaration/struct.rs @@ -86,6 +86,7 @@ impl StructDeclaration { span: name.as_span(), path, }; + let name = check!( ident::parse_from_pair(name, config), return err(warnings, errors), diff --git a/sway-core/src/parse_tree/expression/method_name.rs b/sway-core/src/parse_tree/expression/method_name.rs index 232fccc75af..93fa6d9d048 100644 --- a/sway-core/src/parse_tree/expression/method_name.rs +++ b/sway-core/src/parse_tree/expression/method_name.rs @@ -9,7 +9,6 @@ pub enum MethodName { call_path: CallPath, // if this is `None`, then use the first argument to determine the type type_name: Option, - is_absolute: bool, }, /// Represents a method lookup that does not contain any types in the path FromModule { method_name: Ident }, diff --git a/sway-core/src/parse_tree/expression/mod.rs b/sway-core/src/parse_tree/expression/mod.rs index 74b252d3fc5..a3c34c15fa0 100644 --- a/sway-core/src/parse_tree/expression/mod.rs +++ b/sway-core/src/parse_tree/expression/mod.rs @@ -73,7 +73,7 @@ pub enum Expression { span: Span, }, StructExpression { - struct_name: Ident, + struct_name: CallPath, fields: Vec, span: Span, }, @@ -224,9 +224,9 @@ impl Expression { span: span.clone(), } .to_var_name(), + is_absolute: true, }, type_name: None, - is_absolute: true, }, arguments, span, @@ -242,9 +242,9 @@ impl Expression { Ident::new_with_override("ops", span.clone()), ], suffix: op.to_var_name(), + is_absolute: true, }, type_name: None, - is_absolute: true, }, arguments, span, @@ -401,17 +401,13 @@ impl Expression { }; let mut func_app_parts = expr.into_inner(); let first_part = func_app_parts.next().unwrap(); - assert!(first_part.as_rule() == Rule::ident); - let suffix = check!( - ident::parse_from_pair(first_part, config), + assert!(first_part.as_rule() == Rule::call_path); + let name = check!( + CallPath::parse_from_pair(first_part, config), return err(warnings, errors), warnings, errors ); - let name = CallPath { - prefixes: vec![], - suffix, - }; let (arguments, type_args) = { let maybe_type_args = func_app_parts.next().unwrap(); match maybe_type_args.as_rule() { @@ -538,7 +534,7 @@ impl Expression { let mut expr_iter = expr.into_inner(); let struct_name = expr_iter.next().unwrap(); let struct_name = check!( - ident::parse_from_pair(struct_name, config), + CallPath::parse_from_pair(struct_name, config), return err(warnings, errors), warnings, errors @@ -751,16 +747,16 @@ impl Expression { } } Rule::fully_qualified_method => { - let mut path_parts_buf = vec![]; + let mut call_path = None; let mut type_name = None; let mut method_name = None; let mut arguments = None; for pair in pair.into_inner() { match pair.as_rule() { Rule::path_separator => (), - Rule::path_ident => { - path_parts_buf.push(check!( - ident::parse_from_pair(pair, config), + Rule::call_path => { + call_path = Some(check!( + CallPath::parse_from_pair(pair, config), continue, warnings, errors @@ -788,22 +784,48 @@ impl Expression { errors ); - // parse the method name into a call path - let method_name = MethodName::FromType { - call_path: CallPath { - prefixes: path_parts_buf, - suffix: check!( - ident::parse_from_pair( - method_name.expect("guaranteed by grammar"), - config - ), - return err(warnings, errors), - warnings, - errors - ), - }, - type_name: Some(type_name), - is_absolute: false, + let method_name = match call_path { + Some(call_path) => { + let mut call_path_buf = call_path.prefixes; + call_path_buf.push(call_path.suffix); + + // parse the method name into a call path + MethodName::FromType { + call_path: CallPath { + prefixes: call_path_buf, + suffix: check!( + ident::parse_from_pair( + method_name.expect("guaranteed by grammar"), + config + ), + return err(warnings, errors), + warnings, + errors + ), + is_absolute: call_path.is_absolute, //is_absolute: false, + }, + type_name: Some(type_name), + } + } + None => { + // parse the method name into a call path + MethodName::FromType { + call_path: CallPath { + prefixes: vec![], + suffix: check!( + ident::parse_from_pair( + method_name.expect("guaranteed by grammar"), + config + ), + return err(warnings, errors), + warnings, + errors + ), + is_absolute: false, //is_absolute: false, + }, + type_name: Some(type_name), + } + } }; let mut arguments_buf = vec![]; diff --git a/sway-core/src/parse_tree/expression/unary_op.rs b/sway-core/src/parse_tree/expression/unary_op.rs index f844509fc99..16ccc06c6f7 100644 --- a/sway-core/src/parse_tree/expression/unary_op.rs +++ b/sway-core/src/parse_tree/expression/unary_op.rs @@ -55,6 +55,7 @@ impl UnaryOp { Ident::new_with_override("ops", op_span.clone()), ], suffix: Ident::new_with_override(self.to_var_name(), op_span), + is_absolute: false, }, arguments: vec![arg], span, diff --git a/sway-core/src/semantic_analysis/ast_node/expression/func_app_instantiation.rs b/sway-core/src/semantic_analysis/ast_node/expression/func_app_instantiation.rs new file mode 100644 index 00000000000..b4ee949fb74 --- /dev/null +++ b/sway-core/src/semantic_analysis/ast_node/expression/func_app_instantiation.rs @@ -0,0 +1,119 @@ +use crate::build_config::BuildConfig; +use crate::control_flow_analysis::ControlFlowGraph; +use crate::error::*; +use crate::semantic_analysis::{ast_node::*, TCOpts, TypeCheckArguments}; +use crate::type_engine::TypeId; +use std::cmp::Ordering; + +#[allow(clippy::too_many_arguments)] +pub(crate) fn instantiate_function_application( + typed_function_decl: TypedFunctionDeclaration, + name: CallPath, + arguments: Vec, + namespace: crate::semantic_analysis::NamespaceRef, + crate_namespace: NamespaceRef, + self_type: TypeId, + build_config: &BuildConfig, + dead_code_graph: &mut ControlFlowGraph, + opts: TCOpts, +) -> CompileResult { + let mut warnings = vec![]; + let mut errors = vec![]; + let TypedFunctionDeclaration { + parameters, + return_type, + body, + span, + purity, + .. + } = typed_function_decl; + + if opts.purity != purity { + errors.push(CompileError::PureCalledImpure { span: name.span() }); + } + + match arguments.len().cmp(¶meters.len()) { + Ordering::Greater => { + let arguments_span = arguments.iter().fold( + arguments + .get(0) + .map(|x| x.span()) + .unwrap_or_else(|| name.span()), + |acc, arg| join_spans(acc, arg.span()), + ); + errors.push(CompileError::TooManyArgumentsForFunction { + span: arguments_span, + method_name: name.suffix.clone(), + expected: parameters.len(), + received: arguments.len(), + }); + } + Ordering::Less => { + let arguments_span = arguments.iter().fold( + arguments + .get(0) + .map(|x| x.span()) + .unwrap_or_else(|| name.span()), + |acc, arg| join_spans(acc, arg.span()), + ); + errors.push(CompileError::TooFewArgumentsForFunction { + span: arguments_span, + method_name: name.suffix.clone(), + expected: parameters.len(), + received: arguments.len(), + }); + } + Ordering::Equal => {} + } + // type check arguments in function application vs arguments in function + // declaration. Use parameter type annotations as annotations for the + // arguments + // + let typed_call_arguments = arguments + .into_iter() + .zip(parameters.iter()) + .map(|(arg, param)| { + ( + param.name.clone(), + TypedExpression::type_check(TypeCheckArguments { + checkee: arg.clone(), + namespace, + crate_namespace, + return_type_annotation: param.r#type, + help_text: "The argument that has been provided to this function's type does \ + not match the declared type of the parameter in the function \ + declaration.", + self_type, + build_config, + dead_code_graph, + mode: Mode::NonAbi, + opts, + }) + .unwrap_or_else(&mut warnings, &mut errors, || { + error_recovery_expr(arg.span()) + }), + ) + }) + .collect(); + + ok( + TypedExpression { + return_type, + // now check the function call return type + // FEATURE this IsConstant can be true if the function itself is + // constant-able const functions would be an + // advanced feature and are not supported right + // now + is_constant: IsConstant::No, + expression: TypedExpressionVariant::FunctionApplication { + arguments: typed_call_arguments, + name, + function_body: body, + selector: None, // regular functions cannot be in a contract call; only methods + }, + span, + }, + warnings, + errors, + ) +} diff --git a/sway-core/src/semantic_analysis/ast_node/expression/mod.rs b/sway-core/src/semantic_analysis/ast_node/expression/mod.rs index 17084921c84..c88ce1befd7 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/mod.rs @@ -1,8 +1,10 @@ mod enum_instantiation; +mod func_app_instantiation; mod struct_expr_field; mod typed_expression; mod typed_expression_variant; pub(crate) use enum_instantiation::instantiate_enum; +pub(crate) use func_app_instantiation::instantiate_function_application; pub(crate) use struct_expr_field::TypedStructExpressionField; pub(crate) use typed_expression::{error_recovery_expr, TypedExpression}; pub(crate) use typed_expression_variant::*; diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 9e7a12bd746..ae6f2d5f9d4 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -6,10 +6,6 @@ use crate::{ semantic_analysis::ast_node::*, type_engine::{insert_type, IntegerBits}, }; -use sway_types::join_spans; - -use either::Either; -use std::cmp::Ordering; mod method_application; use crate::type_engine::TypeId; @@ -450,123 +446,38 @@ impl TypedExpression { warnings, errors ); - let TypedFunctionDeclaration { - parameters, - return_type, - body, - span, - purity, - .. - } = if let TypedDeclaration::FunctionDeclaration(decl) = function_declaration { - // if this is a generic function, monomorphize its internal types and insert the resulting - // declaration into the namespace. Then, use that instead. - if decl.type_parameters.is_empty() { - decl + let typed_function_decl = + if let TypedDeclaration::FunctionDeclaration(decl) = function_declaration { + // if this is a generic function, monomorphize its internal types and insert the resulting + // declaration into the namespace. Then, use that instead. + if decl.type_parameters.is_empty() { + decl + } else { + check!( + decl.monomorphize(type_arguments, self_type), + return err(warnings, errors), + warnings, + errors + ) + } } else { - check!( - decl.monomorphize(type_arguments, self_type), - return err(warnings, errors), - warnings, - errors - ) - } - } else { - errors.push(CompileError::NotAFunction { - name: name.span().as_str().to_string(), - span: name.span(), - what_it_is: function_declaration.friendly_name(), - }); - return err(warnings, errors); - }; - - if opts.purity != purity { - errors.push(CompileError::PureCalledImpure { span: name.span() }); - } - - match arguments.len().cmp(¶meters.len()) { - Ordering::Greater => { - let arguments_span = arguments.iter().fold( - arguments - .get(0) - .map(|x| x.span()) - .unwrap_or_else(|| name.span()), - |acc, arg| join_spans(acc, arg.span()), - ); - errors.push(CompileError::TooManyArgumentsForFunction { - span: arguments_span, - method_name: name.suffix.clone(), - expected: parameters.len(), - received: arguments.len(), - }); - } - Ordering::Less => { - let arguments_span = arguments.iter().fold( - arguments - .get(0) - .map(|x| x.span()) - .unwrap_or_else(|| name.span()), - |acc, arg| join_spans(acc, arg.span()), - ); - errors.push(CompileError::TooFewArgumentsForFunction { - span: arguments_span, - method_name: name.suffix.clone(), - expected: parameters.len(), - received: arguments.len(), + errors.push(CompileError::NotAFunction { + name: name.span().as_str().to_string(), + span: name.span(), + what_it_is: function_declaration.friendly_name(), }); - } - Ordering::Equal => {} - } - // type check arguments in function application vs arguments in function - // declaration. Use parameter type annotations as annotations for the - // arguments - // - let typed_call_arguments = arguments - .into_iter() - .zip(parameters.iter()) - .map(|(arg, param)| { - ( - param.name.clone(), - TypedExpression::type_check(TypeCheckArguments { - checkee: arg.clone(), - namespace, - crate_namespace, - return_type_annotation: param.r#type, - help_text: - "The argument that has been provided to this function's type does \ - not match the declared type of the parameter in the function \ - declaration.", - self_type, - build_config, - dead_code_graph, - mode: Mode::NonAbi, - opts, - }) - .unwrap_or_else(&mut warnings, &mut errors, || { - error_recovery_expr(arg.span()) - }), - ) - }) - .collect(); - - ok( - TypedExpression { - return_type, - // now check the function call return type - // FEATURE this IsConstant can be true if the function itself is - // constant-able const functions would be an - // advanced feature and are not supported right - // now - is_constant: IsConstant::No, - expression: TypedExpressionVariant::FunctionApplication { - arguments: typed_call_arguments, - name, - function_body: body, - selector: None, // regular functions cannot be in a contract call; only methods - }, - span, - }, - warnings, - errors, + return err(warnings, errors); + }; + instantiate_function_application( + typed_function_decl, + name, + arguments, + namespace, + crate_namespace, + self_type, + build_config, + dead_code_graph, + opts, ) } @@ -879,7 +790,7 @@ impl TypedExpression { #[allow(clippy::too_many_arguments)] fn type_check_struct_expression( span: Span, - struct_name: Ident, + call_path: CallPath, fields: Vec, namespace: crate::semantic_analysis::NamespaceRef, crate_namespace: NamespaceRef, @@ -890,26 +801,36 @@ impl TypedExpression { ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; + let module_result = namespace + .find_module_relative(&call_path.prefixes) + .ok(&mut warnings, &mut errors); let mut typed_fields_buf = vec![]; - - let definition: TypedStructDeclaration = - match namespace.clone().get_symbol(&struct_name).value { + let definition = match module_result { + Some(module) => match module.clone().get_symbol(&call_path.suffix).value { Some(TypedDeclaration::StructDeclaration(st)) => st, Some(_) => { errors.push(CompileError::DeclaredNonStructAsStruct { - name: struct_name.clone(), + name: call_path.suffix.clone(), span, }); return err(warnings, errors); } None => { errors.push(CompileError::StructNotFound { - name: struct_name.clone(), + name: call_path.suffix.clone(), span, }); return err(warnings, errors); } - }; + }, + None => { + errors.push(CompileError::StructNotFound { + name: call_path.suffix, + span, + }); + return err(warnings, errors); + } + }; // if this is a generic struct, i.e. it has some type // parameters, monomorphize it before unifying the // types @@ -1218,11 +1139,11 @@ impl TypedExpression { ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - // The first step is to determine if the call path refers to a module or an enum. + // The first step is to determine if the call path refers to a module, enum, or function. // We could rely on the capitalization convention, where modules are lowercase // and enums are uppercase, but this is not robust in the long term. - // Instead, we try to resolve both paths. - // If only one exists, then we use that one. Otherwise, if both exist, it is + // Instead, we try to resolve all paths. + // If only one exists, then we use that one. Otherwise, if more than one exist, it is // an ambiguous reference error. let mut probe_warnings = Vec::new(); let mut probe_errors = Vec::new(); @@ -1240,59 +1161,91 @@ impl TypedExpression { }; // now we can see if this thing is a symbol (typed declaration) or reference to an - // enum instantiation - let this_thing: Either = - match (module_result, enum_module_combined_result) { - (Some(_module), Some(_enum_res)) => { - errors.push(CompileError::AmbiguousPath { span }); - return err(warnings, errors); - } - (Some(module), None) => match module.get_symbol(&call_path.suffix).value { - Some(decl) => Either::Left(decl), - None => { - errors.push(CompileError::SymbolNotFound { - name: call_path.suffix.as_str().to_string(), - span: call_path.suffix.span().clone(), + // enum instantiation, and if it is not either of those things, then it might be a + // function application + let exp: TypedExpression = match (module_result, enum_module_combined_result) { + (Some(_module), Some(_enum_res)) => { + errors.push(CompileError::AmbiguousPath { span }); + return err(warnings, errors); + } + (Some(module), None) => match module.get_symbol(&call_path.suffix).value { + Some(decl) => match decl { + TypedDeclaration::EnumDeclaration(enum_decl) => { + check!( + instantiate_enum( + enum_decl, + call_path.suffix, + args, + namespace, + crate_namespace, + self_type, + build_config, + dead_code_graph, + opts, + ), + return err(warnings, errors), + warnings, + errors + ) + } + TypedDeclaration::FunctionDeclaration(func_decl) => check!( + instantiate_function_application( + func_decl, + call_path, + args, + namespace, + crate_namespace, + self_type, + build_config, + dead_code_graph, + opts, + ), + return err(warnings, errors), + warnings, + errors + ), + a => { + errors.push(CompileError::NotAnEnum { + name: call_path.friendly_name(), + span, + actually: a.friendly_name().to_string(), }); return err(warnings, errors); } }, - (None, Some(enum_decl)) => Either::Right(check!( - instantiate_enum( - enum_decl, - call_path.suffix, - args, - namespace, - crate_namespace, - self_type, - build_config, - dead_code_graph, - opts, - ), - return err(warnings, errors), - warnings, - errors - )), - (None, None) => { + None => { errors.push(CompileError::SymbolNotFound { - span, name: call_path.suffix.as_str().to_string(), + span: call_path.suffix.span().clone(), }); return err(warnings, errors); } - }; - - let exp = match this_thing { - Either::Left(_) => { - errors.push(CompileError::Unimplemented( - "Unable to refer to declarations in other modules directly. Try \ - importing it instead.", + }, + (None, Some(enum_decl)) => check!( + instantiate_enum( + enum_decl, + call_path.suffix, + args, + namespace, + crate_namespace, + self_type, + build_config, + dead_code_graph, + opts, + ), + return err(warnings, errors), + warnings, + errors + ), + (None, None) => { + errors.push(CompileError::SymbolNotFound { span, - )); + name: call_path.suffix.as_str().to_string(), + }); return err(warnings, errors); } - Either::Right(expr) => expr, }; + ok(exp, warnings, errors) } @@ -1572,9 +1525,9 @@ impl TypedExpression { Ident::new_with_override("ops", span.clone()), ], suffix: Ident::new_with_override("index", span.clone()), + is_absolute: true, }, type_name: None, - is_absolute: true, }; type_check_method_application( method_name, diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 3ec281ad837..4cc07f1e62e 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -46,7 +46,6 @@ pub(crate) fn type_check_method_application( MethodName::FromType { ref type_name, ref call_path, - is_absolute, } => { let ty = match type_name { Some(name) => { @@ -61,7 +60,7 @@ pub(crate) fn type_check_method_application( .map(|x| x.return_type) .unwrap_or_else(|| insert_type(TypeInfo::Unknown)), }; - let from_module = if is_absolute { + let from_module = if call_path.is_absolute { Some(crate_namespace) } else { None @@ -154,6 +153,7 @@ pub(crate) fn type_check_method_application( name: CallPath { prefixes: vec![], suffix: method_name, + is_absolute: false, }, arguments: args_and_names, function_body: method.body.clone(), diff --git a/sway-core/src/semantic_analysis/ast_node/impl_trait.rs b/sway-core/src/semantic_analysis/ast_node/impl_trait.rs index e83ff4737aa..a242906b63c 100644 --- a/sway-core/src/semantic_analysis/ast_node/impl_trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/impl_trait.rs @@ -351,6 +351,7 @@ fn type_check_trait_implementation( CallPath { prefixes: vec![], suffix: trait_name.clone(), + is_absolute: false, }, match resolve_type(type_implementing_for, type_implementing_for_span) { Ok(o) => o, diff --git a/sway-core/src/semantic_analysis/ast_node/mod.rs b/sway-core/src/semantic_analysis/ast_node/mod.rs index 30a6e962d21..5ccba0e7f22 100644 --- a/sway-core/src/semantic_analysis/ast_node/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/mod.rs @@ -415,6 +415,7 @@ impl TypedAstNode { CallPath { prefixes: vec![], suffix: name.clone(), + is_absolute: false, }, TypeInfo::SelfType, interface_surface @@ -543,6 +544,7 @@ impl TypedAstNode { let trait_name = CallPath { prefixes: vec![], suffix: Ident::new_with_override("r#Self", block_span.clone()), + is_absolute: false, }; namespace.insert_trait_implementation( trait_name.clone(), diff --git a/sway-core/src/semantic_analysis/namespace.rs b/sway-core/src/semantic_analysis/namespace.rs index 689b712bc2c..37467e1a326 100644 --- a/sway-core/src/semantic_analysis/namespace.rs +++ b/sway-core/src/semantic_analysis/namespace.rs @@ -85,6 +85,7 @@ impl Namespace { let trait_name = CallPath { suffix: trait_name.suffix, prefixes: new_prefixes, + is_absolute: trait_name.is_absolute, }; if self .implemented_traits diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index f9fd68d51d5..809261b47af 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -373,8 +373,9 @@ impl Dependencies { fields, .. } => { - self.deps - .insert(DependentSymbol::Symbol(struct_name.as_str().to_string())); + self.deps.insert(DependentSymbol::Symbol( + struct_name.suffix.as_str().to_string(), + )); self.gather_from_iter(fields.iter(), |deps, field| { deps.gather_from_expr(&field.value) }) diff --git a/sway-core/src/sway.pest b/sway-core/src/sway.pest index 42e3733272f..150289d93e9 100644 --- a/sway-core/src/sway.pest +++ b/sway-core/src/sway.pest @@ -41,13 +41,30 @@ predicate = {"predicate" ~ ";" ~ (non_var_decl|use_statement|include_stateme file_path = { ident ~ ("/" ~ ident)* } include_statement = { include_keyword ~ file_path ~ alias? ~ ";"} alias = { as_keyword ~ ident } + +// this call path is used when there are 0+ prefixes in the call path +// examples: +// thing +// thing::thing2 +// thing::thing2::thing3 +call_path = {relative_call_path|absolute_call_path} +relative_call_path = {ident ~ (path_separator ~ ident)*} +absolute_call_path = {path_separator ~ ident ~ (path_separator ~ ident)*} +// this call path is used when there are 1+ prefixes in the call path +// examples: +// thing::thing2 +// thing::thing2::thing3 +call_path_ = {relative_call_path_|absolute_call_path_} +relative_call_path_ = {ident ~ (path_separator ~ ident)+} +absolute_call_path_ = {path_separator ~ ident ~ (path_separator ~ ident)+} + // expressions -expr_inner = _{unary_op_expr|asm_expression|match_expression|abi_cast|if_exp|code_block|func_app|literal_value|struct_expression|method_exp|tuple_index|struct_field_access|delineated_path|array_index|var_exp|array_exp|parenthesized_expression|tuple_expr} +expr_inner = _{unary_op_expr|asm_expression|match_expression|abi_cast|if_exp|code_block|tuple_index|struct_expression|delineated_path|func_app|literal_value|method_exp|struct_field_access|array_index|var_exp|array_exp|parenthesized_expression|tuple_expr} parenthesized_expression = {"(" ~ expr ~ ")"} unary_op_expr = { unary_op ~ expr_inner } // // op exps built in to expr to prevent left recursion expr = {expr_inner ~ (op ~ expr_inner)*} -func_app = {ident ~ type_args? ~ fn_args} +func_app = {call_path ~ type_args? ~ fn_args} type_args = {path_separator ~ type_params} fn_args = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } var_exp = {var_name_ident} @@ -55,18 +72,15 @@ var_name_ident = {ident} struct_field_access = {subfield_path} method_exp = {subfield_exp | fully_qualified_method} subfield_exp = {subfield_path ~ fn_args} -// TODO subfield path should allow parenthesized expressions _or_ just idents subfield_path = {(sub_subfield_path ~ ".")+ ~ call_item} sub_subfield_path = {array_index|call_item} -fully_qualified_method = {path_separator? ~ (path_ident ~ path_separator)* ~ "~" ~ type_name ~ path_separator ~ call_item ~ fn_args} +fully_qualified_method = {call_path? ~ "~" ~ type_name ~ path_separator ~ call_item ~ fn_args} call_item = {ident | "(" ~ expr ~ ")" } -delineated_path = {path_component ~ fn_args?} -path_component = {path_ident ~ (path_separator ~ path_ident)+} -path_ident = {ident} +delineated_path = {call_path_ ~ fn_args?} array_index = {call_item ~ "[" ~ expr ~ "]" ~ ("[" ~ expr ~ "]")*} // abi blocks and abi casting -abi_cast = {abi_keyword ~ "(" ~ trait_name ~ "," ~ expr ~ ")"} +abi_cast = {abi_keyword ~ "(" ~ call_path ~ "," ~ expr ~ ")"} abi_decl = {abi_keyword ~ abi_name ~ trait_methods} abi_name = {ident} @@ -99,15 +113,14 @@ struct_scrutinee = {struct_name ~ "{" ~ struct_scrutinee_fields ~"}"} struct_scrutinee_fields = {struct_scrutinee_field ~ ("," ~ struct_scrutinee_field)* ~ ","?} struct_scrutinee_field = {ident ~ field_scrutinee?} field_scrutinee = {":" ~ scrutinee} -enum_scrutinee = {enum_scrutinee_component ~ fn_args_scrutinee?} -enum_scrutinee_component = {path_ident ~ (path_separator ~ path_ident)+} +enum_scrutinee = {call_path_ ~ fn_args_scrutinee?} fn_args_scrutinee = { "(" ~ (scrutinee ~ ("," ~ scrutinee)*)? ~ ")" } tuple_scrutinee = { "(" ~ (scrutinee ~ ("," ~ scrutinee)* ~ ","?)? ~ ")" } code_block = {"{" ~ (declaration|control_flow|expr_statement)* ~ (expr)? ~ "}"} -struct_expression = {struct_name ~ "{" ~ struct_expr_fields ~ "}"} +struct_expression = {call_path ~ "{" ~ struct_expr_fields ~ "}"} struct_expr_fields = {(struct_field_name ~ ":" ~ expr ~ ("," ~ struct_field_name ~ ":" ~ expr)* ~ ","?)?} array_exp = {"[" ~ array_elems? ~ "]"} // Strictly speaking the [val; count] initialiser for a static array can have any constant expression @@ -155,7 +168,7 @@ fn_decl_param_name = {ident} fn_decl_name = {ident} type_name = {str_type|ident ~ type_params?|tuple_type|array_type} str_type = { "str" ~ "[" ~ basic_integer ~ "]" } -trait_bounds = {"where" ~ (generic_type_param ~ ":" ~ trait_name) ~ ("," ~ generic_type_param ~ ":" ~ trait_name)*} +trait_bounds = {"where" ~ (generic_type_param ~ ":" ~ call_path) ~ ("," ~ generic_type_param ~ ":" ~ call_path)*} generic_type_param = {ident} // Array size can be any constant u64 expression, but we don't properly support constant expressions. See `array_elems rule above. array_type = {"[" ~ type_name ~ ";" ~ basic_integer ~ "]"} @@ -166,12 +179,11 @@ return_statement = {return_keyword ~ expr? ~ ";"} expr_statement = {expr ~ ";"} // traits -trait_decl = {visibility ~ trait_decl_keyword ~ trait_name ~ type_params? ~ supertraits? ~ trait_bounds? ~ trait_methods} +trait_decl = {visibility ~ trait_decl_keyword ~ call_path ~ type_params? ~ supertraits? ~ trait_bounds? ~ trait_methods} trait_methods = {"{" ~ (fn_signature ~ ";")* ~ "}" ~ ("{" ~ fn_decl* ~ "}")*} -trait_name = {ident ~ (path_separator ~ ident)*} supertraits = {":" ~ supertrait ~ ("+" ~ supertrait)*} -supertrait = {trait_name ~ type_params?} -impl_trait = {impl_keyword ~ trait_name ~ type_params? ~ "for" ~ type_name ~ type_params? ~ trait_bounds? ~ ("{" ~ fn_decl* ~ "}")} +supertrait = {call_path ~ type_params?} +impl_trait = {impl_keyword ~ call_path ~ type_params? ~ "for" ~ type_name ~ type_params? ~ trait_bounds? ~ ("{" ~ fn_decl* ~ "}")} // imports use_statement = {relative_use_statement | absolute_use_statement} diff --git a/sway-server/src/utils/common.rs b/sway-server/src/utils/common.rs index e17d7fba7c9..47ef44a3445 100644 --- a/sway-server/src/utils/common.rs +++ b/sway-server/src/utils/common.rs @@ -16,7 +16,7 @@ pub(crate) fn extract_var_body(var_dec: &VariableDeclaration) -> VarBody { VarBody::FunctionCall(name.suffix.as_str().into()) } Expression::StructExpression { struct_name, .. } => { - VarBody::Type(struct_name.as_str().into()) + VarBody::Type(struct_name.suffix.as_str().into()) } Expression::Literal { value, .. } => match value { Literal::U8(_) => VarBody::Type("u8".into()), diff --git a/sway-utils/src/helpers.rs b/sway-utils/src/helpers.rs index a1af0ee0ecb..a90d8e54fcc 100644 --- a/sway-utils/src/helpers.rs +++ b/sway-utils/src/helpers.rs @@ -5,6 +5,7 @@ use std::fs; use std::path::{Path, PathBuf}; /// Continually go up in the file tree until a manifest (Forc.toml) is found. +#[allow(clippy::branches_sharing_code)] pub fn find_manifest_dir(starter_path: &Path) -> Option { let mut path = std::fs::canonicalize(starter_path).ok()?; let empty_path = PathBuf::from("/"); diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 25f0e932779..c6820d63c93 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -79,6 +79,7 @@ pub fn run(filter_regex: Option) { ("trait_import_with_star", ProgramState::Return(0)), ("tuple_desugaring", ProgramState::Return(9)), ("multi_item_import", ProgramState::Return(0)), // false + ("use_full_path_names", ProgramState::Return(1)), ("tuple_indexing", ProgramState::Return(1)), ("tuple_access", ProgramState::Return(42)), ("funcs_with_generic_types", ProgramState::Return(1)), // true @@ -108,7 +109,7 @@ pub fn run(filter_regex: Option) { // when that is re-implemented we should reenable this test //"infinite_dependencies", "top_level_vars", - "dependencies_parsing_error", + "dependency_parsing_error", "disallowed_gm", "bad_generic_annotation", "bad_generic_var_annotation", diff --git a/test/src/e2e_vm_tests/test_programs/dependencies/src/main.sw b/test/src/e2e_vm_tests/test_programs/dependencies/src/main.sw index bc1c29e71d2..695dea1cc05 100644 --- a/test/src/e2e_vm_tests/test_programs/dependencies/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/dependencies/src/main.sw @@ -11,7 +11,7 @@ fn main() -> bool { let foo = Foo { foo: "foo", }; - let db = DoubleBar { + let db = ::foo::bar::double_bar::DoubleBar { a: 5u32, }; let bar = Bar { diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/Forc.toml b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/Forc.toml similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/Forc.toml rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/Forc.toml diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/json_abi_oracle.json similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/json_abi_oracle.json rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/json_abi_oracle.json diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/a_dependency.sw b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/a_dependency.sw similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/a_dependency.sw rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/a_dependency.sw diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/bar.sw b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/bar.sw similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/bar.sw rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/bar.sw diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/inner/bar.sw b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/inner/bar.sw similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/inner/bar.sw rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/inner/bar.sw diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/inner/double_inner/double_bar.sw b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/inner/double_inner/double_bar.sw similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/inner/double_inner/double_bar.sw rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/inner/double_inner/double_bar.sw diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/main.sw b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/main.sw similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/main.sw rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/main.sw diff --git a/test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/nested_dependency/bar/bar.sw b/test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/nested_dependency/bar/bar.sw similarity index 100% rename from test/src/e2e_vm_tests/test_programs/dependencies_parsing_error/src/nested_dependency/bar/bar.sw rename to test/src/e2e_vm_tests/test_programs/dependency_parsing_error/src/nested_dependency/bar/bar.sw diff --git a/test/src/e2e_vm_tests/test_programs/use_full_path_names/Forc.toml b/test/src/e2e_vm_tests/test_programs/use_full_path_names/Forc.toml new file mode 100644 index 00000000000..afe26ad01ea --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/use_full_path_names/Forc.toml @@ -0,0 +1,5 @@ +[project] +author = "Fuel Labs " +license = "Apache-2.0" +name = "use_full_path_names" +entry = "main.sw" diff --git a/test/src/e2e_vm_tests/test_programs/use_full_path_names/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/use_full_path_names/json_abi_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/use_full_path_names/json_abi_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/a_dependency.sw b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/a_dependency.sw new file mode 100644 index 00000000000..7baf46e46b4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/a_dependency.sw @@ -0,0 +1,5 @@ +library foo; + +pub struct Foo { + foo: u32, +} diff --git a/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/b_dependency.sw b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/b_dependency.sw new file mode 100644 index 00000000000..85a073fee35 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/b_dependency.sw @@ -0,0 +1,5 @@ +library bar; + +enum Bar { + Baz: bool, +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/c_dependency.sw b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/c_dependency.sw new file mode 100644 index 00000000000..cbbea5a0867 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/c_dependency.sw @@ -0,0 +1,5 @@ +library baz; + +fn return_1() -> u32 { + 1u32 +} diff --git a/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/main.sw b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/main.sw new file mode 100644 index 00000000000..4f3495401b5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/use_full_path_names/src/main.sw @@ -0,0 +1,14 @@ +script; + +dep a_dependency; +dep b_dependency; +dep c_dependency; + +fn main() -> u64 { + let x = foo::Foo { + foo: 1u32, + }; + let y = bar::Bar::Baz(true); + let z = ::bar::Bar::Baz(false); + baz::return_1() +}