diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 6283737a..d3882d51 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1656,7 +1656,7 @@ impl TypedPattern { #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Namespace { Module(String), - Type(String), + Type(Option, String), } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 64d7c876..09b184a8 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -1217,9 +1217,15 @@ impl<'comments> Formatter<'comments> { } let name = match module { - Some(Namespace::Module(m)) | Some(Namespace::Type(m)) => { + Some(Namespace::Module(m)) | Some(Namespace::Type(None, m)) => { m.to_doc().append(".").append(name) } + Some(Namespace::Type(Some(m), c)) => m + .to_doc() + .append(".") + .append(c.as_str()) + .append(".") + .append(name), None => name.to_doc(), }; diff --git a/crates/aiken-lang/src/parser/pattern/constructor.rs b/crates/aiken-lang/src/parser/pattern/constructor.rs index ebfa681e..d3ddfe9e 100644 --- a/crates/aiken-lang/src/parser/pattern/constructor.rs +++ b/crates/aiken-lang/src/parser/pattern/constructor.rs @@ -9,6 +9,27 @@ pub fn parser( pattern: Recursive<'_, Token, UntypedPattern, ParseError>, ) -> impl Parser + '_ { choice(( + select! { Token::Name { name } => name } + .then(just(Token::Dot).ignore_then(select! {Token::UpName { name } => name})) + .then( + just(Token::Dot).ignore_then( + select! {Token::UpName { name } => name}.then(args(pattern.clone())), + ), + ) + .map_with_span( + |((module, namespace), (name, (arguments, spread_location, is_record))), span| { + UntypedPattern::Constructor { + is_record, + location: span, + name, + arguments, + module: Some(Namespace::Type(Some(module), namespace)), + constructor: (), + spread_location, + tipo: (), + } + }, + ), select! { Token::UpName { name } => name } .then( just(Token::Dot).ignore_then( @@ -22,7 +43,7 @@ pub fn parser( location: span, name, arguments, - module: Some(Namespace::Type(namespace)), + module: Some(Namespace::Type(None, namespace)), constructor: (), spread_location, tipo: (), diff --git a/crates/aiken-lang/src/parser/pattern/mod.rs b/crates/aiken-lang/src/parser/pattern/mod.rs index 97c6be58..e0c64cfd 100644 --- a/crates/aiken-lang/src/parser/pattern/mod.rs +++ b/crates/aiken-lang/src/parser/pattern/mod.rs @@ -27,9 +27,9 @@ pub use var::parser as var; pub fn parser() -> impl Parser { recursive(|pattern| { choice(( - var(pattern.clone()), pair(pattern.clone()), constructor(pattern.clone()), + var(pattern.clone()), discard(), int(), bytearray(), diff --git a/crates/aiken-lang/src/parser/pattern/snapshots/constructor_type_select.snap b/crates/aiken-lang/src/parser/pattern/snapshots/constructor_type_select.snap index 468e6816..8eb4d3cb 100644 --- a/crates/aiken-lang/src/parser/pattern/snapshots/constructor_type_select.snap +++ b/crates/aiken-lang/src/parser/pattern/snapshots/constructor_type_select.snap @@ -9,6 +9,7 @@ Constructor { arguments: [], module: Some( Type( + None, "Foo", ), ), diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 790b625d..77017615 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -2328,6 +2328,31 @@ fn use_type_as_namespace_for_patterns() { assert!(matches!(result, Ok(..)), "{result:#?}"); } +#[test] +fn use_nested_type_as_namespace_for_patterns() { + let dependency = r#" + pub type Foo { + A + B + } + "#; + + let source_code = r#" + use foo.{Foo} + + fn bar(x: Foo) { + when x is { + foo.Foo.A -> True + foo.Foo.B -> False + } + } + "#; + + let result = check_with_deps(parse(source_code), vec![(parse_as(dependency, "foo"))]); + + assert!(matches!(result, Ok(..)), "{result:#?}"); +} + #[test] fn use_opaque_type_as_namespace_for_patterns_fails() { let dependency = r#" diff --git a/crates/aiken-lang/src/tipo/environment.rs b/crates/aiken-lang/src/tipo/environment.rs index e814c45f..dafa622a 100644 --- a/crates/aiken-lang/src/tipo/environment.rs +++ b/crates/aiken-lang/src/tipo/environment.rs @@ -529,7 +529,42 @@ impl<'a> Environment<'a> { constructors: self.local_constructor_names(), }), - Some(Namespace::Type(t)) => { + Some(Namespace::Type(Some(module_name), t)) => { + let module_location = location.map(|start, _| (start, start + module_name.len())); + + // Lookup the module using the declared name (which may have been rebind with + // 'as'), to obtain its _full unambiguous name_. + let (_, module) = + self.imported_modules + .get(module_name) + .ok_or_else(|| Error::UnknownModule { + location: module_location, + name: module_name.to_string(), + known_modules: self + .importable_modules + .keys() + .map(|t| t.to_string()) + .collect(), + })?; + + let type_location = Span::create(module_location.end + 1, t.len()); + + let parent_type = module.types.get(t).ok_or_else(|| Error::UnknownType { + location: type_location, + name: t.to_string(), + types: self.known_type_names(), + })?; + + self.unused_modules.remove(&parent_type.module); + + self.get_fully_qualified_value_constructor( + (parent_type.module.as_str(), module_location), + (t, type_location), + (name, location.map(|_, end| (type_location.end + 1, end))), + ) + } + + Some(Namespace::Type(None, t)) => { let type_location = location.map(|start, _| (start, start + t.len())); let parent_type = self.module_types.get(t).ok_or_else(|| Error::UnknownType { @@ -558,7 +593,7 @@ impl<'a> Environment<'a> { .keys() .map(|t| t.to_string()) .collect(), - location, + location: Span::create(location.start, m.len()), })?; self.unused_modules.remove(m); diff --git a/crates/aiken-lang/src/tipo/pattern.rs b/crates/aiken-lang/src/tipo/pattern.rs index d3a23afc..b9dc0b1c 100644 --- a/crates/aiken-lang/src/tipo/pattern.rs +++ b/crates/aiken-lang/src/tipo/pattern.rs @@ -573,7 +573,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { // NOTE: // Type namespaces are completely erased during type-check. module: match module { - None | Some(Namespace::Type(_)) => None, + None | Some(Namespace::Type(..)) => None, Some(Namespace::Module(m)) => Some(m), }, name, @@ -609,7 +609,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { // NOTE: // Type namespaces are completely erased during type-check. module: match module { - None | Some(Namespace::Type(_)) => None, + None | Some(Namespace::Type(..)) => None, Some(Namespace::Module(m)) => Some(m), }, name,