Skip to content

Commit

Permalink
Adds support for where clauses in impl self methods. (FuelLabs#3836)
Browse files Browse the repository at this point in the history
When where clauses were used in impl self methods with a generic
parameter from
 impl<T> an error was thrown saying `cannot find type "T" in this scope`

Parent type parameters that are used in method where clauses are now
added to `FunctionDeclaration.type_parameters`.
This is needed for
`TypeParameter::gather_decl_mapping_from_trait_constraints` to work
properly as we need to have a type parameter with constraints. The
generated decl_mapping is used by `type_check_method_application` to
replace decls from trait constraints.

Closes FuelLabs#3333
  • Loading branch information
esdrubal authored Apr 26, 2023
1 parent f62148f commit e99b788
Show file tree
Hide file tree
Showing 18 changed files with 592 additions and 38 deletions.
11 changes: 5 additions & 6 deletions sway-core/src/language/call_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,29 +110,28 @@ impl CallPath {
// combination of the package name in which the symbol is defined and the path to the
// current submodule.
let mut synonym_prefixes = vec![];
let mut submodule_name_in_synonym_prefixes = false;
let mut is_external = false;

if let Some(use_synonym) = namespace.use_synonyms.get(&self.suffix) {
synonym_prefixes = use_synonym.0.clone();
let submodule = namespace.submodule(&[use_synonym.0[0].clone()]);
if let Some(submodule) = submodule {
if let Some(submodule_name) = submodule.name.clone() {
submodule_name_in_synonym_prefixes =
submodule_name.as_str() == synonym_prefixes[0].as_str();
}
is_external = submodule.is_external;
}
}

let mut prefixes: Vec<Ident> = vec![];

if synonym_prefixes.is_empty() || !submodule_name_in_synonym_prefixes {
if !is_external {
if let Some(pkg_name) = &namespace.root().module.name {
prefixes.push(pkg_name.clone());
}

for mod_path in namespace.mod_path() {
prefixes.push(mod_path.clone());
}
}

prefixes.extend(synonym_prefixes);

CallPath {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ impl ty::TyScrutinee {
name_ident: BaseIdent::new_with_override("_".into(), span.clone()),
trait_constraints: vec![],
trait_constraints_span: Span::dummy(),
is_from_parent: false,
};
let typed_scrutinee = ty::TyScrutinee {
variant: ty::TyScrutineeVariant::CatchAll,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
decl_engine::{DeclEngineInsert, DeclRefFunction, UpdateConstantExpression},
decl_engine::{DeclEngineInsert, DeclRefFunction, ReplaceDecls, UpdateConstantExpression},
error::*,
language::{parsed::*, ty, *},
semantic_analysis::*,
Expand Down Expand Up @@ -49,7 +49,7 @@ pub(crate) fn type_check_method_application(
warnings,
errors
);
let method = decl_engine.get_function(&decl_ref);
let mut method = decl_engine.get_function(&decl_ref);

// check the method visibility
if span.path() != method.span.path() && method.visibility.is_private() {
Expand Down Expand Up @@ -312,17 +312,36 @@ pub(crate) fn type_check_method_application(
ctx.namespace
.insert_trait_implementation_for_type(engines, method.return_type.type_id);

// Handle the trait constraints. This includes checking to see if the trait
// constraints are satisfied and replacing old decl ids based on the
// constraint with new decl ids based on the new type.
let decl_mapping = check!(
TypeParameter::gather_decl_mapping_from_trait_constraints(
ctx.by_ref(),
&method.type_parameters,
&call_path.span()
),
return err(warnings, errors),
warnings,
errors
);
method.replace_decls(&decl_mapping, ctx.engines());
let return_type = method.return_type.type_id;
let new_decl_ref = decl_engine
.insert(method)
.with_parent(decl_engine, (*decl_ref.id()).into());

let exp = ty::TyExpression {
expression: ty::TyExpressionVariant::FunctionApplication {
call_path,
contract_call_params: contract_call_params_map,
arguments: typed_arguments_with_names,
fn_ref: decl_ref,
fn_ref: new_decl_ref,
selector,
type_binding: Some(method_name_binding.strip_inner()),
call_path_typeid: Some(call_path_typeid),
},
return_type: method.return_type.type_id,
return_type,
span,
};

Expand Down
77 changes: 59 additions & 18 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,9 @@ fn item_to_ast_nodes(
item_enum_to_enum_declaration(context, handler, engines, item_enum, attributes)?,
)),
ItemKind::Fn(item_fn) => {
let function_declaration =
item_fn_to_function_declaration(context, handler, engines, item_fn, attributes)?;
let function_declaration = item_fn_to_function_declaration(
context, handler, engines, item_fn, attributes, None,
)?;
error_if_self_param_is_not_allowed(
context,
handler,
Expand Down Expand Up @@ -461,6 +462,7 @@ fn item_fn_to_function_declaration(
engines: Engines<'_>,
item_fn: ItemFn,
attributes: AttributesMap,
parent_generic_params_opt: Option<GenericParams>,
) -> Result<FunctionDeclaration, ErrorEmitted> {
let span = item_fn.span();
let return_type = match item_fn.fn_signature.return_type_opt {
Expand Down Expand Up @@ -491,11 +493,12 @@ fn item_fn_to_function_declaration(
)?,
span,
return_type,
type_parameters: generic_params_opt_to_type_parameters(
type_parameters: generic_params_opt_to_type_parameters_with_parent(
context,
handler,
engines,
item_fn.fn_signature.generics,
parent_generic_params_opt,
item_fn.fn_signature.where_clause_opt.clone(),
)?,
where_clause: item_fn
Expand Down Expand Up @@ -573,7 +576,7 @@ fn item_trait_to_trait_declaration(
context,
handler,
engines,
item_trait.generics,
item_trait.generics.clone(),
item_trait.where_clause_opt,
)?;
let interface_surface = item_trait
Expand Down Expand Up @@ -614,6 +617,7 @@ fn item_trait_to_trait_declaration(
engines,
item_fn.value,
attributes,
item_trait.generics.clone(),
)?))
})
.filter_map_ok(|fn_decl| fn_decl)
Expand Down Expand Up @@ -654,10 +658,15 @@ fn item_impl_to_declaration(
return Ok(None);
}
Ok(Some(match item.value {
sway_ast::ItemImplItem::Fn(fn_item) => {
item_fn_to_function_declaration(context, handler, engines, fn_item, attributes)
.map(ImplItem::Fn)
}
sway_ast::ItemImplItem::Fn(fn_item) => item_fn_to_function_declaration(
context,
handler,
engines,
fn_item,
attributes,
item_impl.generic_params_opt.clone(),
)
.map(ImplItem::Fn),
sway_ast::ItemImplItem::Const(const_item) => item_const_to_constant_declaration(
context, handler, engines, const_item, attributes, true,
)
Expand Down Expand Up @@ -798,6 +807,7 @@ fn item_abi_to_abi_declaration(
engines,
item_fn.value,
attributes,
None,
)?;
error_if_self_param_is_not_allowed(
context,
Expand Down Expand Up @@ -1013,6 +1023,24 @@ fn generic_params_opt_to_type_parameters(
engines: Engines<'_>,
generic_params_opt: Option<GenericParams>,
where_clause_opt: Option<WhereClause>,
) -> Result<Vec<TypeParameter>, ErrorEmitted> {
generic_params_opt_to_type_parameters_with_parent(
context,
handler,
engines,
generic_params_opt,
None,
where_clause_opt,
)
}

fn generic_params_opt_to_type_parameters_with_parent(
context: &mut Context,
handler: &Handler,
engines: Engines<'_>,
generic_params_opt: Option<GenericParams>,
parent_generic_params_opt: Option<GenericParams>,
where_clause_opt: Option<WhereClause>,
) -> Result<Vec<TypeParameter>, ErrorEmitted> {
let type_engine = engines.te();
let decl_engine = engines.de();
Expand All @@ -1026,7 +1054,8 @@ fn generic_params_opt_to_type_parameters(
None => Vec::new(),
};

let mut params = match generic_params_opt {
let generics_to_params = |generics: Option<GenericParams>, is_from_parent: bool| match generics
{
Some(generic_params) => generic_params
.parameters
.into_inner()
Expand All @@ -1045,26 +1074,35 @@ fn generic_params_opt_to_type_parameters(
name_ident: ident,
trait_constraints: Vec::new(),
trait_constraints_span: Span::dummy(),
is_from_parent,
}
})
.collect::<Vec<_>>(),
None => Vec::new(),
};

let mut params = generics_to_params(generic_params_opt, false);
let parent_params = generics_to_params(parent_generic_params_opt, true);

let mut errors = Vec::new();
for (ty_name, bounds) in trait_constraints.into_iter() {
let param_to_edit = match params
let param_to_edit = if let Some(o) = params
.iter_mut()
.find(|TypeParameter { name_ident, .. }| name_ident.as_str() == ty_name.as_str())
{
Some(o) => o,
None => {
errors.push(ConvertParseTreeError::ConstrainedNonExistentType {
ty_name: ty_name.clone(),
span: ty_name.span().clone(),
});
continue;
}
o
} else if let Some(o2) = parent_params
.iter()
.find(|TypeParameter { name_ident, .. }| name_ident.as_str() == ty_name.as_str())
{
params.push(o2.clone());
params.last_mut().unwrap()
} else {
errors.push(ConvertParseTreeError::ConstrainedNonExistentType {
ty_name: ty_name.clone(),
span: ty_name.span().clone(),
});
continue;
};

param_to_edit.trait_constraints_span = Span::join(ty_name.span(), bounds.span());
Expand Down Expand Up @@ -3247,6 +3285,7 @@ fn statement_let_to_ast_nodes(
),
trait_constraints: vec![],
trait_constraints_span: Span::dummy(),
is_from_parent: false,
};
let initial_type_id = engines.te().insert(
engines.de(),
Expand Down Expand Up @@ -3537,6 +3576,7 @@ fn ty_to_type_parameter(
name_ident: underscore_token.into(),
trait_constraints: Default::default(),
trait_constraints_span: Span::dummy(),
is_from_parent: false,
});
}
Ty::Tuple(..) => panic!("tuple types are not allowed in this position"),
Expand All @@ -3556,6 +3596,7 @@ fn ty_to_type_parameter(
name_ident,
trait_constraints: Vec::new(),
trait_constraints_span: Span::dummy(),
is_from_parent: false,
})
}

Expand Down
56 changes: 46 additions & 10 deletions sway-core/src/type_system/ast_elements/type_parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct TypeParameter {
pub name_ident: Ident,
pub(crate) trait_constraints: Vec<TraitConstraint>,
pub(crate) trait_constraints_span: Span,
pub(crate) is_from_parent: bool,
}

impl HashWithEngines for TypeParameter {
Expand All @@ -36,6 +37,7 @@ impl HashWithEngines for TypeParameter {
// reliable source of obj v. obj distinction
trait_constraints_span: _,
initial_type_id: _,
is_from_parent: _,
} = self;
let type_engine = engines.te();
type_engine.get(*type_id).hash(state, engines);
Expand Down Expand Up @@ -66,6 +68,7 @@ impl OrdWithEngines for TypeParameter {
// reliable source of obj v. obj distinction
trait_constraints_span: _,
initial_type_id: _,
is_from_parent: _,
} = self;
let TypeParameter {
type_id: rti,
Expand All @@ -75,6 +78,7 @@ impl OrdWithEngines for TypeParameter {
// reliable source of obj v. obj distinction
trait_constraints_span: _,
initial_type_id: _,
is_from_parent: _,
} = other;
ln.cmp(rn)
.then_with(|| engines.te().get(*lti).cmp(&engines.te().get(*rti), engines))
Expand Down Expand Up @@ -173,6 +177,7 @@ impl TypeParameter {
name_ident,
mut trait_constraints,
trait_constraints_span,
is_from_parent,
..
} = type_parameter;

Expand Down Expand Up @@ -206,23 +211,54 @@ impl TypeParameter {
);
}

// Insert the type parameter into the namespace as a dummy type
// declaration.
let type_parameter_decl =
ty::TyDecl::GenericTypeForFunctionScope(ty::GenericTypeForFunctionScope {
name: name_ident.clone(),
type_id,
});
ctx.namespace
.insert_symbol(name_ident.clone(), type_parameter_decl)
.ok(&mut warnings, &mut errors);
// When type parameter is from parent then it was already inserted.
// Instead of inserting a type with same name we unify them.
if is_from_parent {
if let Some(sy) = ctx.namespace.symbols.get(&name_ident) {
match sy {
ty::TyDecl::GenericTypeForFunctionScope(ty::GenericTypeForFunctionScope {
type_id: sy_type_id,
..
}) => {
append!(
ctx.engines().te().unify(
ctx.engines().de(),
type_id,
*sy_type_id,
&trait_constraints_span,
"",
None
),
warnings,
errors
);
}
_ => errors.push(CompileError::Internal(
"Unexpected TyDeclaration for TypeParameter.",
name_ident.span(),
)),
}
}
} else {
// Insert the type parameter into the namespace as a dummy type
// declaration.
let type_parameter_decl =
ty::TyDecl::GenericTypeForFunctionScope(ty::GenericTypeForFunctionScope {
name: name_ident.clone(),
type_id,
});
ctx.namespace
.insert_symbol(name_ident.clone(), type_parameter_decl)
.ok(&mut warnings, &mut errors);
}

let type_parameter = TypeParameter {
name_ident,
type_id,
initial_type_id,
trait_constraints,
trait_constraints_span,
is_from_parent,
};
ok(type_parameter, warnings, errors)
}
Expand Down
Loading

0 comments on commit e99b788

Please sign in to comment.