Skip to content

Commit

Permalink
Adds support for Enum::<T>::Variant. (FuelLabs#3795)
Browse files Browse the repository at this point in the history
This commit changes `TypeBinding` to contain also
`prefix_type_arguments` this allows us to have both `Enum::<T>::Variant`
compiled with `prefix_type_arguments` and `Enum::Variant::<T>` compiled
with `type_arguments`. In case both are used an error is thrown.

Errors are also thrown when `prefix_type_arguments` is used with
constants or functions. Tests included for this.

Also added an error when `type_arguments` is used with constants.

Closes FuelLabs#3585

---------

Co-authored-by: Mohammad Fawaz <[email protected]>
  • Loading branch information
esdrubal and mohammadfawaz authored Feb 11, 2023
1 parent c6e35fb commit ba1300d
Show file tree
Hide file tree
Showing 26 changed files with 325 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl ty::TyIntrinsicFunctionKind {
type_arguments,
..
} = kind_binding;
let type_arguments = type_arguments.to_vec();
match kind {
Intrinsic::SizeOfVal => {
type_check_size_of_val(ctx, kind, arguments, type_arguments, span)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl ty::TyExpression {
inner: MethodName::FromTrait {
call_path: call_path.clone(),
},
type_arguments: vec![],
type_arguments: TypeArgs::Regular(vec![]),
span: call_path.span(),
};
let arguments = VecDeque::from(arguments);
Expand Down Expand Up @@ -1048,6 +1048,21 @@ impl ty::TyExpression {
};
type_check_method_application(ctx.by_ref(), method_name_binding, Vec::new(), args, span)
} else {
let mut type_arguments = type_arguments;
if let TypeArgs::Regular(vec) = before.type_arguments {
if !vec.is_empty() {
if !type_arguments.to_vec().is_empty() {
return err(
vec![],
vec![
ConvertParseTreeError::MultipleGenericsNotSupported { span }.into()
],
);
}
type_arguments = TypeArgs::Prefix(vec)
}
}

let call_path_binding = TypeBinding {
inner: CallPath {
prefixes: path,
Expand All @@ -1057,36 +1072,7 @@ impl ty::TyExpression {
type_arguments,
span: path_span,
};
let mut res =
Self::type_check_delineated_path(ctx, call_path_binding, span, Some(args));

// In case `before` has type args, this would be e.g., `foo::bar::<TyArgs>::baz(...)`.
// So, we would need, but don't have, parametric modules to apply arguments to.
// Emit an error and ignore the type args.
//
// TODO: This also bans `Enum::<TyArgs>::Variant` but there's no good reason to ban that.
// Instead, we should allow this but ban `Enum::Variant::<TyArgs>`, which Rust does allow,
// but shouldn't, because with GADTs, we could ostensibly have the equivalent of:
// ```haskell
// {-# LANGUAGE GADTs, RankNTypes #-}
// data Foo where Bar :: forall a. Show a => a -> Foo
// ```
// or to illustrate with Sway-ish syntax:
// ```rust
// enum Foo {
// Bar<A: Debug>: A, // Let's ignore memory representation, etc.
// }
// ```
if !before.type_arguments.is_empty() {
res.errors.push(
ConvertParseTreeError::GenericsNotSupportedHere {
span: Span::join_all(before.type_arguments.iter().map(|t| t.span())),
}
.into(),
);
}

res
Self::type_check_delineated_path(ctx, call_path_binding, span, Some(args))
}
}

Expand Down Expand Up @@ -1145,6 +1131,7 @@ impl ty::TyExpression {
let enum_name = call_path_binding.inner.prefixes[0].clone();
let variant_name = call_path_binding.inner.suffix.clone();
let enum_call_path = call_path_binding.inner.rshift();

let mut call_path_binding = TypeBinding {
inner: enum_call_path,
type_arguments: call_path_binding.type_arguments,
Expand Down Expand Up @@ -1194,6 +1181,15 @@ impl ty::TyExpression {
(false, Some((decl_id, call_path_binding)), None, None) => {
warnings.append(&mut function_probe_warnings);
errors.append(&mut function_probe_errors);
// In case `foo::bar::<TyArgs>::baz(...)` throw an error.
if let TypeArgs::Prefix(_) = call_path_binding.type_arguments {
errors.push(
ConvertParseTreeError::GenericsNotSupportedHere {
span: call_path_binding.type_arguments.span(),
}
.into(),
);
}
check!(
instantiate_function_application(ctx, decl_id, call_path_binding, args, span),
return err(warnings, errors),
Expand All @@ -1211,6 +1207,16 @@ impl ty::TyExpression {
(false, None, None, Some((const_decl, call_path_binding))) => {
warnings.append(&mut const_probe_warnings);
errors.append(&mut const_probe_errors);
if !call_path_binding.type_arguments.to_vec().is_empty() {
// In case `foo::bar::CONST::<TyArgs>` throw an error.
// In case `foo::bar::<TyArgs>::CONST` throw an error.
errors.push(
ConvertParseTreeError::GenericsNotSupportedHere {
span: unknown_call_path_binding.type_arguments.span(),
}
.into(),
);
}
check!(
instantiate_constant_decl(const_decl, call_path_binding),
return err(warnings, errors),
Expand Down Expand Up @@ -1557,7 +1563,7 @@ impl ty::TyExpression {
is_absolute: true,
},
},
type_arguments: vec![],
type_arguments: TypeArgs::Regular(vec![]),
span: span.clone(),
};
type_check_method_application(ctx, method_name, vec![], vec![prefix, index], span)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ pub(crate) fn resolve_method_name(
check!(
ctx.monomorphize(
&mut func_decl,
&mut method_name.type_arguments,
method_name.type_arguments.to_vec_mut(),
EnforceTypeArguments::No,
&method_name_span,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ pub(crate) fn struct_instantiation(
span: inner_span,
} = call_path_binding.clone();

if let TypeArgs::Prefix(_) = type_arguments {
errors.push(CompileError::DoesNotTakeTypeArgumentsAsPrefix {
name: suffix,
span: type_arguments.span(),
});
return err(warnings, errors);
}

let type_arguments = type_arguments.to_vec();

let type_info = match (suffix.as_str(), type_arguments.is_empty()) {
("Self", true) => TypeInfo::SelfType,
("Self", false) => {
Expand Down
26 changes: 19 additions & 7 deletions sway-core/src/semantic_analysis/node_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,10 @@ impl Dependencies {
arguments,
} = &**function_application_expression;
self.gather_from_call_path(&call_path_binding.inner, false, true)
.gather_from_type_arguments(type_engine, &call_path_binding.type_arguments)
.gather_from_type_arguments(
type_engine,
&call_path_binding.type_arguments.to_vec(),
)
.gather_from_iter(arguments.iter(), |deps, arg| {
deps.gather_from_expr(type_engine, arg)
})
Expand Down Expand Up @@ -458,7 +461,10 @@ impl Dependencies {
fields,
} = &**struct_expression;
self.gather_from_call_path(&call_path_binding.inner, false, false)
.gather_from_type_arguments(type_engine, &call_path_binding.type_arguments)
.gather_from_type_arguments(
type_engine,
&call_path_binding.type_arguments.to_vec(),
)
.gather_from_iter(fields.iter(), |deps, field| {
deps.gather_from_expr(type_engine, &field.value)
})
Expand All @@ -480,10 +486,13 @@ impl Dependencies {
call_path_binding.inner.suffix.before.inner.clone(),
));
}
this.gather_from_type_arguments(type_engine, &call_path_binding.type_arguments)
.gather_from_iter(args.iter(), |deps, arg| {
deps.gather_from_expr(type_engine, arg)
})
this.gather_from_type_arguments(
type_engine,
&call_path_binding.type_arguments.to_vec(),
)
.gather_from_iter(args.iter(), |deps, arg| {
deps.gather_from_expr(type_engine, arg)
})
}
ExpressionKind::DelineatedPath(delineated_path_expression) => {
let DelineatedPathExpression {
Expand All @@ -495,7 +504,10 @@ impl Dependencies {
// variant name.
let args_vec = args.clone().unwrap_or_default();
self.gather_from_call_path(&call_path_binding.inner, true, false)
.gather_from_type_arguments(type_engine, &call_path_binding.type_arguments)
.gather_from_type_arguments(
type_engine,
&call_path_binding.type_arguments.to_vec(),
)
.gather_from_iter(args_vec.iter(), |deps, arg| {
deps.gather_from_expr(type_engine, arg)
})
Expand Down
55 changes: 43 additions & 12 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1329,7 +1329,7 @@ fn method_call_fields_to_method_application_expression(

let method_name_binding = TypeBinding {
inner: MethodName::FromModule { method_name },
type_arguments,
type_arguments: TypeArgs::Regular(type_arguments),
span,
};
let contract_call_params = match contract_args_opt {
Expand Down Expand Up @@ -1435,7 +1435,7 @@ fn expr_func_app_to_expression_kind(
name: call_seg.name,
kind_binding: TypeBinding {
inner: intrinsic,
type_arguments,
type_arguments: TypeArgs::Regular(type_arguments),
span: name_args_span(span, type_arguments_span),
},
arguments,
Expand All @@ -1460,7 +1460,7 @@ fn expr_func_app_to_expression_kind(
};
let call_path_binding = TypeBinding {
inner: call_path,
type_arguments,
type_arguments: TypeArgs::Regular(type_arguments),
span,
};
return Ok(ExpressionKind::FunctionApplication(Box::new(
Expand All @@ -1478,7 +1478,7 @@ fn expr_func_app_to_expression_kind(
let before = TypeBinding {
span: name_args_span(last.name.span(), last_ty_args_span),
inner: last.name,
type_arguments: last_ty_args,
type_arguments: TypeArgs::Regular(last_ty_args),
};
let suffix = AmbiguousSuffix {
before,
Expand All @@ -1492,7 +1492,7 @@ fn expr_func_app_to_expression_kind(
let call_path_binding = TypeBinding {
span: name_args_span(call_path.span(), type_arguments_span),
inner: call_path,
type_arguments,
type_arguments: TypeArgs::Regular(type_arguments),
};
Ok(ExpressionKind::AmbiguousPathExpression(Box::new(
AmbiguousPathExpression {
Expand Down Expand Up @@ -2029,7 +2029,7 @@ fn op_call(
is_absolute: true,
},
},
type_arguments: vec![],
type_arguments: TypeArgs::Regular(vec![]),
span: op_span,
};
Ok(Expression {
Expand Down Expand Up @@ -2614,11 +2614,31 @@ fn path_expr_to_call_path_binding(
..
} = path_expr;
let is_absolute = path_root_opt_to_bool(context, handler, root_opt)?;
let (prefixes, suffix, span, type_arguments) = match suffix.pop() {
let (prefixes, suffix, span, regular_type_arguments, prefix_type_arguments) = match suffix.pop()
{
Some((_, call_path_suffix)) => {
let mut prefixes = vec![path_expr_segment_to_ident(context, handler, &prefix)?];
for (_, call_path_prefix) in suffix {
let ident = path_expr_segment_to_ident(context, handler, &call_path_prefix)?;
let (prefix_ident, mut prefix_type_arguments) = if suffix.is_empty() {
path_expr_segment_to_ident_or_type_argument(context, handler, engines, prefix)?
} else {
(
path_expr_segment_to_ident(context, handler, &prefix)?,
vec![],
)
};
let mut prefixes = vec![prefix_ident];
for (i, (_, call_path_prefix)) in suffix.iter().enumerate() {
let ident = if i == suffix.len() - 1 {
let (prefix, prefix_ty_args) = path_expr_segment_to_ident_or_type_argument(
context,
handler,
engines,
call_path_prefix.clone(),
)?;
prefix_type_arguments = prefix_ty_args;
prefix
} else {
path_expr_segment_to_ident(context, handler, call_path_prefix)?
};
// note that call paths only support one set of type arguments per call path right
// now
prefixes.push(ident);
Expand All @@ -2630,15 +2650,26 @@ fn path_expr_to_call_path_binding(
engines,
call_path_suffix,
)?;
(prefixes, suffix, span, ty_args)
(prefixes, suffix, span, ty_args, prefix_type_arguments)
}
None => {
let span = prefix.span();
let (suffix, ty_args) =
path_expr_segment_to_ident_or_type_argument(context, handler, engines, prefix)?;
(vec![], suffix, span, ty_args)
(vec![], suffix, span, ty_args, vec![])
}
};

let type_arguments = if !regular_type_arguments.is_empty() && !prefix_type_arguments.is_empty()
{
let error = ConvertParseTreeError::MultipleGenericsNotSupported { span };
return Err(handler.emit_err(error.into()));
} else if !prefix_type_arguments.is_empty() {
TypeArgs::Prefix(prefix_type_arguments)
} else {
TypeArgs::Regular(regular_type_arguments)
};

Ok(TypeBinding {
inner: CallPath {
prefixes,
Expand Down
Loading

0 comments on commit ba1300d

Please sign in to comment.