Skip to content

Commit

Permalink
Implement hierarchical namespace lookups.
Browse files Browse the repository at this point in the history
  • Loading branch information
tritao committed Sep 9, 2024
1 parent 8232d42 commit dc00260
Show file tree
Hide file tree
Showing 12 changed files with 414 additions and 275 deletions.
29 changes: 14 additions & 15 deletions sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
ty::{self, TyDecl, TyImplItem, TyImplSelfOrTrait, TyTraitInterfaceItem, TyTraitItem},
*,
},
namespace::{IsExtendingExistingImpl, IsImplSelf, TryInsertingTraitImplOnFailure},
namespace::{IsExtendingExistingImpl, IsImplSelf, TraitMap, TryInsertingTraitImplOnFailure},
semantic_analysis::{
type_check_context::EnforceTypeArguments, AbiMode, ConstShadowingMode,
TyNodeDepGraphNodeId, TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext,
Expand Down Expand Up @@ -653,20 +653,19 @@ fn type_check_trait_implementation(
ctx.namespace_mut()
.module_mut(engines)
.write(engines, |m| {
m.current_items_mut()
.implemented_traits
.check_if_trait_constraints_are_satisfied_for_type(
handler,
implementing_for,
&trait_supertraits
.iter()
.map(|x| x.into())
.collect::<Vec<_>>(),
block_span,
engines,
TryInsertingTraitImplOnFailure::Yes,
code_block_first_pass.into(),
)
TraitMap::check_if_trait_constraints_are_satisfied_for_type(
m,
handler,
implementing_for,
&trait_supertraits
.iter()
.map(|x| x.into())
.collect::<Vec<_>>(),
block_span,
engines,
TryInsertingTraitImplOnFailure::Yes,
code_block_first_pass.into(),
)
})?;

for (type_arg, type_param) in trait_type_arguments.iter().zip(trait_type_parameters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use crate::{
language::{
parsed::*,
ty::{
self, GetDeclIdent, TyCodeBlock, TyDecl, TyExpression, TyExpressionVariant, TyImplItem,
TyReassignmentTarget, VariableMutability,
self, GetDeclIdent, StructAccessInfo, TyCodeBlock, TyDecl, TyExpression,
TyExpressionVariant, TyImplItem, TyReassignmentTarget, VariableMutability,
},
*,
},
Expand All @@ -39,12 +39,13 @@ use ast_node::declaration::{insert_supertraits_into_namespace, SupertraitOf};
use either::Either;
use indexmap::IndexMap;
use itertools::Itertools;
use namespace::{LexicalScope, Module, ResolvedDeclaration};
use rustc_hash::FxHashSet;
use std::collections::{HashMap, VecDeque};
use sway_ast::intrinsics::Intrinsic;
use sway_error::{
convert_parse_tree_error::ConvertParseTreeError,
error::CompileError,
error::{CompileError, StructFieldUsageContext},
handler::{ErrorEmitted, Handler},
warning::{CompileWarning, Warning},
};
Expand Down Expand Up @@ -2295,7 +2296,8 @@ impl ty::TyExpression {
let indices = indices.into_iter().rev().collect::<Vec<_>>();
let (ty_of_field, _ty_of_parent) =
ctx.namespace().program_id(engines).read(engines, |m| {
m.current_items().find_subfield_type(
Self::find_subfield_type(
m,
handler,
ctx.engines(),
ctx.namespace(),
Expand Down Expand Up @@ -2332,6 +2334,188 @@ impl ty::TyExpression {
})
}

pub fn find_subfield_type(
module: &Module,
handler: &Handler,
engines: &Engines,
namespace: &Namespace,
base_name: &Ident,
projections: &[ty::ProjectionKind],
) -> Result<(TypeId, TypeId), ErrorEmitted> {
let mut lexical_scope_opt = Some(module.current_lexical_scope());
while let Some(lexical_scope) = lexical_scope_opt {
let result = Self::find_subfield_type_helper(
lexical_scope,
handler,
engines,
namespace,
base_name,
projections,
)?;
if let Some(result) = result {
return Ok(result);
}
if let Some(parent_scope_id) = lexical_scope.parent {
lexical_scope_opt = module.get_lexical_scope(parent_scope_id);
} else {
lexical_scope_opt = None;
}
}

// Symbol not found
Err(handler.emit_err(CompileError::UnknownVariable {
var_name: base_name.clone(),
span: base_name.span(),
}))
}

/// Returns a tuple where the first element is the [TypeId] of the actual expression, and
/// the second is the [TypeId] of its parent.
fn find_subfield_type_helper(
lexical_scope: &LexicalScope,
handler: &Handler,
engines: &Engines,
namespace: &Namespace,
base_name: &Ident,
projections: &[ty::ProjectionKind],
) -> Result<Option<(TypeId, TypeId)>, ErrorEmitted> {
let type_engine = engines.te();
let decl_engine = engines.de();

let symbol = match lexical_scope.items.symbols.get(base_name).cloned() {
Some(s) => s,
None => {
return Ok(None);
}
};
let mut symbol = match symbol {
ResolvedDeclaration::Parsed(_) => unreachable!(),
ResolvedDeclaration::Typed(ty_decl) => ty_decl.return_type(handler, engines)?,
};
let mut symbol_span = base_name.span();
let mut parent_rover = symbol;
let mut full_span_for_error = base_name.span();
for projection in projections {
let resolved_type = match type_engine.to_typeinfo(symbol, &symbol_span) {
Ok(resolved_type) => resolved_type,
Err(error) => {
return Err(handler.emit_err(CompileError::TypeError(error)));
}
};
match (resolved_type, projection) {
(
TypeInfo::Struct(decl_ref),
ty::ProjectionKind::StructField { name: field_name },
) => {
let struct_decl = decl_engine.get_struct(&decl_ref);
let (struct_can_be_changed, is_public_struct_access) =
StructAccessInfo::get_info(engines, &struct_decl, namespace).into();

let field_type_id = match struct_decl.find_field(field_name) {
Some(struct_field) => {
if is_public_struct_access && struct_field.is_private() {
return Err(handler.emit_err(CompileError::StructFieldIsPrivate {
field_name: field_name.into(),
struct_name: struct_decl.call_path.suffix.clone(),
field_decl_span: struct_field.name.span(),
struct_can_be_changed,
usage_context: StructFieldUsageContext::StructFieldAccess,
}));
}

struct_field.type_argument.type_id
}
None => {
return Err(handler.emit_err(CompileError::StructFieldDoesNotExist {
field_name: field_name.into(),
available_fields: struct_decl
.accessible_fields_names(is_public_struct_access),
is_public_struct_access,
struct_name: struct_decl.call_path.suffix.clone(),
struct_decl_span: struct_decl.span(),
struct_is_empty: struct_decl.is_empty(),
usage_context: StructFieldUsageContext::StructFieldAccess,
}));
}
};
parent_rover = symbol;
symbol = field_type_id;
symbol_span = field_name.span().clone();
full_span_for_error = Span::join(full_span_for_error, &field_name.span());
}
(TypeInfo::Tuple(fields), ty::ProjectionKind::TupleField { index, index_span }) => {
let field_type_opt = {
fields
.get(*index)
.map(|TypeArgument { type_id, .. }| type_id)
};
let field_type = match field_type_opt {
Some(field_type) => field_type,
None => {
return Err(handler.emit_err(CompileError::TupleIndexOutOfBounds {
index: *index,
count: fields.len(),
tuple_type: engines.help_out(symbol).to_string(),
span: index_span.clone(),
prefix_span: full_span_for_error.clone(),
}));
}
};
parent_rover = symbol;
symbol = *field_type;
symbol_span = index_span.clone();
full_span_for_error = Span::join(full_span_for_error, index_span);
}
(
TypeInfo::Array(elem_ty, _),
ty::ProjectionKind::ArrayIndex { index_span, .. },
) => {
parent_rover = symbol;
symbol = elem_ty.type_id;
symbol_span = index_span.clone();
// `index_span` does not contain the enclosing square brackets.
// Which means, if this array index access is the last one before the
// erroneous expression, the `full_span_for_error` will be missing the
// closing `]`. We can live with this small glitch so far. To fix it,
// we would need to bring the full span of the index all the way from
// the parsing stage. An effort that doesn't pay off at the moment.
// TODO: Include the closing square bracket into the error span.
full_span_for_error = Span::join(full_span_for_error, index_span);
}
(actually, ty::ProjectionKind::StructField { name }) => {
return Err(handler.emit_err(CompileError::FieldAccessOnNonStruct {
actually: engines.help_out(actually).to_string(),
storage_variable: None,
field_name: name.into(),
span: full_span_for_error,
}));
}
(
actually,
ty::ProjectionKind::TupleField {
index, index_span, ..
},
) => {
return Err(
handler.emit_err(CompileError::TupleElementAccessOnNonTuple {
actually: engines.help_out(actually).to_string(),
span: full_span_for_error,
index: *index,
index_span: index_span.clone(),
}),
);
}
(actually, ty::ProjectionKind::ArrayIndex { .. }) => {
return Err(handler.emit_err(CompileError::NotIndexable {
actually: engines.help_out(actually).to_string(),
span: full_span_for_error,
}));
}
}
}
Ok(Some((symbol, parent_rover)))
}

fn type_check_ref(
handler: &Handler,
mut ctx: TypeCheckContext<'_>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,7 @@ fn collect_struct_constructors(
// but that would be a way too much of suggestions, and moreover, it is also not a design pattern/guideline
// that we wish to encourage.
namespace.program_id(engines).read(engines, |m| {
m.current_items()
.get_items_for_type(engines, struct_type_id)
m.get_items_for_type(engines, struct_type_id)
.iter()
.filter_map(|item| match item {
ResolvedTraitImplItem::Parsed(_) => unreachable!(),
Expand Down
Loading

0 comments on commit dc00260

Please sign in to comment.