Skip to content

Commit

Permalink
Try to progress a bit on generic types in the middle of a path
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Oct 21, 2024
1 parent 33a1e7d commit af6a4e4
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 175 deletions.
3 changes: 0 additions & 3 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,6 @@ impl<'context> Elaborator<'context> {
last_segment.generics = Some(generics.ordered_args);
}

let exclude_last_segment = true;
self.check_unsupported_turbofish_usage(&path, exclude_last_segment);

let last_segment = path.last_segment();
let is_self_type = last_segment.ident.is_self_type_name();

Expand Down
20 changes: 14 additions & 6 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,11 +653,15 @@ impl<'context> Elaborator<'context> {

pub fn resolve_module_by_path(&mut self, path: Path) -> Option<ModuleId> {
match self.resolve_path(path.clone()) {
Ok(PathResolution { module_def_id: ModuleDefId::ModuleId(module_id), error }) => {
if error.is_some() {
None
} else {
Ok(PathResolution {
module_def_id: ModuleDefId::ModuleId(module_id),
generic_type_in_path: _,
errors,
}) => {
if errors.is_empty() {
Some(module_id)
} else {
None
}
}
_ => None,
Expand All @@ -666,8 +670,12 @@ impl<'context> Elaborator<'context> {

fn resolve_trait_by_path(&mut self, path: Path) -> Option<TraitId> {
let error = match self.resolve_path(path.clone()) {
Ok(PathResolution { module_def_id: ModuleDefId::TraitId(trait_id), error }) => {
if let Some(error) = error {
Ok(PathResolution {
module_def_id: ModuleDefId::TraitId(trait_id),
generic_type_in_path: _,
errors,
}) => {
for error in errors {
self.push_err(error);
}
return Some(trait_id);
Expand Down
61 changes: 38 additions & 23 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
},
hir::{
def_collector::dc_crate::CompilationError,
resolution::errors::ResolverError,
resolution::{errors::ResolverError, import::GenericTypeInPath},
type_check::{Source, TypeCheckError},
},
hir_def::{
Expand Down Expand Up @@ -178,9 +178,6 @@ impl<'context> Elaborator<'context> {
mutable: Option<Span>,
new_definitions: &mut Vec<HirIdent>,
) -> HirPattern {
let exclude_last_segment = true;
self.check_unsupported_turbofish_usage(&name, exclude_last_segment);

let last_segment = name.last_segment();
let name_span = last_segment.ident.span();
let is_self_type = last_segment.ident.is_self_type_name();
Expand All @@ -195,7 +192,7 @@ impl<'context> Elaborator<'context> {
};

let (struct_type, generics) = match self.lookup_type_or_error(name) {
Some(Type::Struct(struct_type, generics)) => (struct_type, generics),
Some(Type::Struct(struct_type, struct_generics)) => (struct_type, struct_generics),
None => return error_identifier(self),
Some(typ) => {
let typ = typ.to_string();
Expand Down Expand Up @@ -468,15 +465,20 @@ impl<'context> Elaborator<'context> {
}

pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) {
let exclude_last_segment = true;
self.check_unsupported_turbofish_usage(&variable, exclude_last_segment);

let unresolved_turbofish = variable.segments.last().unwrap().generics.clone();

let span = variable.span;
let expr = self.resolve_variable(variable);
let (expr, generic_type_in_path) = self.resolve_variable(variable);
let definition_id = expr.id;

// TODO: generic_type_in_path might be Some.
// In that case, the variable looks like this:
//
// foo::Bar::<i32>::baz
//
// That is, the type in the path before the function has some generics on it.
// How to handle this?

let definition_kind =
self.interner.try_definition(definition_id).map(|definition| definition.kind.clone());

Expand All @@ -498,24 +500,30 @@ impl<'context> Elaborator<'context> {
(id, typ)
}

fn resolve_variable(&mut self, path: Path) -> HirIdent {
fn resolve_variable(&mut self, path: Path) -> (HirIdent, Option<GenericTypeInPath>) {
if let Some(trait_path_resolution) = self.resolve_trait_generic_path(&path) {
if let Some(error) = trait_path_resolution.error {
for error in trait_path_resolution.errors {
self.push_err(error);
}

HirIdent {
location: Location::new(path.span, self.file),
id: self.interner.trait_method_id(trait_path_resolution.method.method_id),
impl_kind: ImplKind::TraitMethod(trait_path_resolution.method),
}
// TODO: GenericTypeInPath
let generic_type_in_path = None;
(
HirIdent {
location: Location::new(path.span, self.file),
id: self.interner.trait_method_id(trait_path_resolution.method.method_id),
impl_kind: ImplKind::TraitMethod(trait_path_resolution.method),
},
generic_type_in_path,
)
} else {
// If the Path is being used as an Expression, then it is referring to a global from a separate module
// Otherwise, then it is referring to an Identifier
// This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10;
// If the expression is a singular indent, we search the resolver's current scope as normal.
let span = path.span();
let (hir_ident, var_scope_index) = self.get_ident_from_path(path);
let ((hir_ident, var_scope_index), generic_type_in_path) =
self.get_ident_from_path(path);

if hir_ident.id != DefinitionId::dummy_id() {
match self.interner.definition(hir_ident.id).kind {
Expand Down Expand Up @@ -557,7 +565,7 @@ impl<'context> Elaborator<'context> {
}
}

hir_ident
(hir_ident, generic_type_in_path)
}
}

Expand Down Expand Up @@ -668,24 +676,31 @@ impl<'context> Elaborator<'context> {
}
}

pub fn get_ident_from_path(&mut self, path: Path) -> (HirIdent, usize) {
pub fn get_ident_from_path(
&mut self,
path: Path,
) -> ((HirIdent, usize), Option<GenericTypeInPath>) {
let location = Location::new(path.last_ident().span(), self.file);

let error = match path.as_ident().map(|ident| self.use_variable(ident)) {
Some(Ok(found)) => return found,
Some(Ok(found)) => return (found, None),
// Try to look it up as a global, but still issue the first error if we fail
Some(Err(error)) => match self.lookup_global(path) {
Ok(id) => return (HirIdent::non_trait_method(id, location), 0),
Ok((id, generic_type_in_path)) => {
return ((HirIdent::non_trait_method(id, location), 0), generic_type_in_path)
}
Err(_) => error,
},
None => match self.lookup_global(path) {
Ok(id) => return (HirIdent::non_trait_method(id, location), 0),
Ok((id, generic_type_in_path)) => {
return ((HirIdent::non_trait_method(id, location), 0), generic_type_in_path)
}
Err(error) => error,
},
};
self.push_err(error);
let id = DefinitionId::dummy_id();
(HirIdent::non_trait_method(id, location), 0)
((HirIdent::non_trait_method(id, location), 0), None)
}

pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) {
Expand Down
52 changes: 31 additions & 21 deletions compiler/noirc_frontend/src/elaborator/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use noirc_errors::{Location, Spanned};

use crate::ast::{Ident, Path, PathKind, ERROR_IDENT};
use crate::hir::def_map::{LocalModuleId, ModuleId};
use crate::hir::resolution::import::{PathResolution, PathResolutionResult};
use crate::hir::resolution::import::{GenericTypeInPath, PathResolution, PathResolutionResult};
use crate::hir::resolution::path_resolver::{PathResolver, StandardPathResolver};
use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree};
use crate::{
Expand All @@ -26,13 +26,18 @@ type Scope = GenericScope<String, ResolverMeta>;
type ScopeTree = GenericScopeTree<String, ResolverMeta>;

impl<'context> Elaborator<'context> {
pub(super) fn lookup<T: TryFromModuleDefId>(&mut self, path: Path) -> Result<T, ResolverError> {
pub(super) fn lookup<T: TryFromModuleDefId>(
&mut self,
path: Path,
) -> Result<(T, Option<GenericTypeInPath>), ResolverError> {
let span = path.span();
let id = self.resolve_path_or_error(path)?;
T::try_from(id).ok_or_else(|| ResolverError::Expected {
expected: T::description(),
got: id.as_str().to_owned(),
span,
let (id, generic_type_in_path) = self.resolve_path_or_error(path)?;
T::try_from(id).map(|id| (id, generic_type_in_path)).ok_or_else(|| {
ResolverError::Expected {
expected: T::description(),
got: id.as_str().to_owned(),
span,
}
})
}

Expand All @@ -53,14 +58,14 @@ impl<'context> Elaborator<'context> {
pub(super) fn resolve_path_or_error(
&mut self,
path: Path,
) -> Result<ModuleDefId, ResolverError> {
) -> Result<(ModuleDefId, Option<GenericTypeInPath>), ResolverError> {
let path_resolution = self.resolve_path(path)?;

if let Some(error) = path_resolution.error {
for error in path_resolution.errors {
self.push_err(error);
}

Ok(path_resolution.module_def_id)
Ok((path_resolution.module_def_id, path_resolution.generic_type_in_path))
}

pub(super) fn resolve_path(&mut self, path: Path) -> PathResolutionResult {
Expand All @@ -71,9 +76,11 @@ impl<'context> Elaborator<'context> {
if let Some(Type::Struct(struct_type, _)) = &self.self_type {
let struct_type = struct_type.borrow();
if path.segments.len() == 1 {
// TODO: handle generics
return Ok(PathResolution {
module_def_id: ModuleDefId::TypeId(struct_type.id),
error: None,
generic_type_in_path: None,
errors: Vec::new(),
});
}

Expand Down Expand Up @@ -183,17 +190,20 @@ impl<'context> Elaborator<'context> {
}
}

pub(super) fn lookup_global(&mut self, path: Path) -> Result<DefinitionId, ResolverError> {
pub(super) fn lookup_global(
&mut self,
path: Path,
) -> Result<(DefinitionId, Option<GenericTypeInPath>), ResolverError> {
let span = path.span();
let id = self.resolve_path_or_error(path)?;
let (id, generic_type_in_path) = self.resolve_path_or_error(path)?;

if let Some(function) = TryFromModuleDefId::try_from(id) {
return Ok(self.interner.function_definition_id(function));
return Ok((self.interner.function_definition_id(function), generic_type_in_path));
}

if let Some(global) = TryFromModuleDefId::try_from(id) {
let global = self.interner.get_global(global);
return Ok(global.definition_id);
return Ok((global.definition_id, generic_type_in_path));
}

let expected = "global variable".into();
Expand Down Expand Up @@ -240,7 +250,7 @@ impl<'context> Elaborator<'context> {
/// Lookup a given trait by name/path.
pub fn lookup_trait_or_error(&mut self, path: Path) -> Option<&mut Trait> {
match self.lookup(path) {
Ok(trait_id) => Some(self.get_trait_mut(trait_id)),
Ok((trait_id, _generic_type_in_path)) => Some(self.get_trait_mut(trait_id)),
Err(error) => {
self.push_err(error);
None
Expand All @@ -251,7 +261,7 @@ impl<'context> Elaborator<'context> {
/// Lookup a given struct type by name.
pub fn lookup_struct_or_error(&mut self, path: Path) -> Option<Shared<StructType>> {
match self.lookup(path) {
Ok(struct_id) => Some(self.get_struct(struct_id)),
Ok((struct_id, _generic_type_in_path)) => Some(self.get_struct(struct_id)),
Err(error) => {
self.push_err(error);
None
Expand All @@ -270,10 +280,10 @@ impl<'context> Elaborator<'context> {
}

match self.lookup(path) {
Ok(struct_id) => {
Ok((struct_id, _generic_type_in_path)) => {
let struct_type = self.get_struct(struct_id);
let generics = struct_type.borrow().instantiate(self.interner);
Some(Type::Struct(struct_type, generics))
let struct_generics = struct_type.borrow().instantiate(self.interner);
Some(Type::Struct(struct_type, struct_generics))
}
Err(error) => {
self.push_err(error);
Expand All @@ -283,6 +293,6 @@ impl<'context> Elaborator<'context> {
}

pub fn lookup_type_alias(&mut self, path: Path) -> Option<Shared<TypeAlias>> {
self.lookup(path).ok().map(|id| self.interner.get_type_alias(id))
self.lookup(path).ok().map(|(id, _generic_type_in_path)| self.interner.get_type_alias(id))
}
}
4 changes: 3 additions & 1 deletion compiler/noirc_frontend/src/elaborator/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,11 @@ impl<'context> Elaborator<'context> {
let mut mutable = true;
let span = ident.span();
let path = Path::from_single(ident.0.contents, span);
let (ident, scope_index) = self.get_ident_from_path(path);
let ((ident, scope_index), generic_type_in_path) = self.get_ident_from_path(path);
self.resolve_local_variable(ident.clone(), scope_index);

// TODO: generic_type_in_path

let typ = if ident.id == DefinitionId::dummy_id() {
Type::Error
} else {
Expand Down
23 changes: 5 additions & 18 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub const WILDCARD_TYPE: &str = "_";

pub(super) struct TraitPathResolution {
pub(super) method: TraitMethod,
pub(super) error: Option<PathResolutionError>,
pub(super) errors: Vec<PathResolutionError>,
}

impl<'context> Elaborator<'context> {
Expand Down Expand Up @@ -404,7 +404,7 @@ impl<'context> Elaborator<'context> {

// If we cannot find a local generic of the same name, try to look up a global
match self.resolve_path_or_error(path.clone()) {
Ok(ModuleDefId::GlobalId(id)) => {
Ok((ModuleDefId::GlobalId(id), _generic_type_in_path)) => {
if let Some(current_item) = self.current_item {
self.interner.add_global_dependency(current_item, id);
}
Expand Down Expand Up @@ -551,7 +551,7 @@ impl<'context> Elaborator<'context> {
let constraint = the_trait.as_constraint(path.span);
return Some(TraitPathResolution {
method: TraitMethod { method_id: method, constraint, assumed: true },
error: None,
errors: Vec::new(),
});
}
}
Expand All @@ -572,7 +572,7 @@ impl<'context> Elaborator<'context> {
let constraint = the_trait.as_constraint(path.span);
Some(TraitPathResolution {
method: TraitMethod { method_id: method, constraint, assumed: false },
error: path_resolution.error,
errors: path_resolution.errors,
})
}

Expand Down Expand Up @@ -600,7 +600,7 @@ impl<'context> Elaborator<'context> {
if let Some(method) = the_trait.find_method(path.last_name()) {
return Some(TraitPathResolution {
method: TraitMethod { method_id: method, constraint, assumed: true },
error: None,
errors: Vec::new(),
});
}
}
Expand Down Expand Up @@ -1826,19 +1826,6 @@ impl<'context> Elaborator<'context> {
context.trait_constraints.push((constraint, expr_id));
}

pub fn check_unsupported_turbofish_usage(&mut self, path: &Path, exclude_last_segment: bool) {
for (index, segment) in path.segments.iter().enumerate() {
if exclude_last_segment && index == path.segments.len() - 1 {
continue;
}

if segment.generics.is_some() {
let span = segment.turbofish_span();
self.push_err(TypeCheckError::UnsupportedTurbofishUsage { span });
}
}
}

pub fn bind_generics_from_trait_constraint(
&self,
constraint: &TraitConstraint,
Expand Down
Loading

0 comments on commit af6a4e4

Please sign in to comment.