Skip to content

Commit d41bd48

Browse files
philbertydkm
authored andcommitted
Initial Type resolution for closures
This adds the type checking for a HIR::ClosureExpr which specifies a TyTy::ClosureType whih inherits the FnOnce trait by default. The specialisation of the trait bound needs to be determined by the the mutability and argument capture and moveablity rules. The CallExpr is amended here so that we support CallExpr's for all receivers that implement any of the FnTraits. This means closures and generics that have the relevant type bound of FnTraits we get the same path of type checking. Addresses #195
1 parent 50b4b04 commit d41bd48

9 files changed

+479
-31
lines changed

gcc/rust/hir/tree/rust-hir-expr.h

+2
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,8 @@ class ClosureExpr : public ExprWithoutBlock
21312131
};
21322132
std::unique_ptr<Expr> &get_expr () { return expr; }
21332133

2134+
std::vector<ClosureParam> &get_params () { return params; }
2135+
21342136
void accept_vis (HIRFullVisitor &vis) override;
21352137
void accept_vis (HIRExpressionVisitor &vis) override;
21362138

gcc/rust/typecheck/rust-hir-type-check-expr.cc

+297-11
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,6 @@ TypeCheckExpr::visit (HIR::CallExpr &expr)
178178
{
179179
TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ());
180180

181-
bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::ADT
182-
|| function_tyty->get_kind () == TyTy::TypeKind::FNDEF
183-
|| function_tyty->get_kind () == TyTy::TypeKind::FNPTR;
184-
if (!valid_tyty)
185-
{
186-
rust_error_at (expr.get_locus (),
187-
"Failed to resolve expression of function call");
188-
return;
189-
}
190-
191181
rust_debug_loc (expr.get_locus (), "resolved_call_expr to: {%s}",
192182
function_tyty->get_name ().c_str ());
193183

@@ -214,6 +204,24 @@ TypeCheckExpr::visit (HIR::CallExpr &expr)
214204
rust_assert (adt->number_of_variants () == 1);
215205
variant = *adt->get_variants ().at (0);
216206
}
207+
208+
infered
209+
= TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
210+
return;
211+
}
212+
213+
bool resolved_fn_trait_call
214+
= resolve_fn_trait_call (expr, function_tyty, &infered);
215+
if (resolved_fn_trait_call)
216+
return;
217+
218+
bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::FNDEF
219+
|| function_tyty->get_kind () == TyTy::TypeKind::FNPTR;
220+
if (!valid_tyty)
221+
{
222+
rust_error_at (expr.get_locus (),
223+
"Failed to resolve expression of function call");
224+
return;
217225
}
218226

219227
infered = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
@@ -1422,7 +1430,123 @@ TypeCheckExpr::visit (HIR::MatchExpr &expr)
14221430
void
14231431
TypeCheckExpr::visit (HIR::ClosureExpr &expr)
14241432
{
1425-
gcc_unreachable ();
1433+
TypeCheckContextItem &current_context = context->peek_context ();
1434+
TyTy::FnType *current_context_fndecl = current_context.get_context_type ();
1435+
1436+
HirId ref = expr.get_mappings ().get_hirid ();
1437+
DefId id = expr.get_mappings ().get_defid ();
1438+
RustIdent ident{current_context_fndecl->get_ident ().path, expr.get_locus ()};
1439+
1440+
// get from parent context
1441+
std::vector<TyTy::SubstitutionParamMapping> subst_refs
1442+
= current_context_fndecl->clone_substs ();
1443+
1444+
std::vector<TyTy::TyVar> parameter_types;
1445+
for (auto &p : expr.get_params ())
1446+
{
1447+
if (p.has_type_given ())
1448+
{
1449+
TyTy::BaseType *param_tyty
1450+
= TypeCheckType::Resolve (p.get_type ().get ());
1451+
TyTy::TyVar param_ty (param_tyty->get_ref ());
1452+
parameter_types.push_back (param_ty);
1453+
1454+
TypeCheckPattern::Resolve (p.get_pattern ().get (),
1455+
param_ty.get_tyty ());
1456+
}
1457+
else
1458+
{
1459+
TyTy::TyVar param_ty
1460+
= TyTy::TyVar::get_implicit_infer_var (p.get_locus ());
1461+
parameter_types.push_back (param_ty);
1462+
1463+
TypeCheckPattern::Resolve (p.get_pattern ().get (),
1464+
param_ty.get_tyty ());
1465+
}
1466+
}
1467+
1468+
// we generate an implicit hirid for the closure args
1469+
HirId implicit_args_id = mappings->get_next_hir_id ();
1470+
TyTy::TupleType *closure_args
1471+
= new TyTy::TupleType (implicit_args_id, expr.get_locus (),
1472+
parameter_types);
1473+
context->insert_implicit_type (closure_args);
1474+
1475+
Location result_type_locus = expr.has_return_type ()
1476+
? expr.get_return_type ()->get_locus ()
1477+
: expr.get_locus ();
1478+
TyTy::TyVar result_type
1479+
= expr.has_return_type ()
1480+
? TyTy::TyVar (
1481+
TypeCheckType::Resolve (expr.get_return_type ().get ())->get_ref ())
1482+
: TyTy::TyVar::get_implicit_infer_var (expr.get_locus ());
1483+
1484+
// resolve the block
1485+
Location closure_expr_locus = expr.get_expr ()->get_locus ();
1486+
TyTy::BaseType *closure_expr_ty
1487+
= TypeCheckExpr::Resolve (expr.get_expr ().get ());
1488+
coercion_site (expr.get_mappings ().get_hirid (),
1489+
TyTy::TyWithLocation (result_type.get_tyty (),
1490+
result_type_locus),
1491+
TyTy::TyWithLocation (closure_expr_ty, closure_expr_locus),
1492+
expr.get_locus ());
1493+
1494+
// generate the closure type
1495+
infered = new TyTy::ClosureType (ref, id, ident, closure_args, result_type,
1496+
subst_refs);
1497+
1498+
// FIXME
1499+
// all closures automatically inherit the appropriate fn trait. Lets just
1500+
// assume FnOnce for now. I think this is based on the return type of the
1501+
// closure
1502+
1503+
Analysis::RustLangItem::ItemType lang_item_type
1504+
= Analysis::RustLangItem::ItemType::FN_ONCE;
1505+
DefId respective_lang_item_id = UNKNOWN_DEFID;
1506+
bool lang_item_defined
1507+
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
1508+
if (!lang_item_defined)
1509+
{
1510+
// FIXME
1511+
// we need to have a unified way or error'ing when we are missing lang
1512+
// items that is useful
1513+
rust_fatal_error (
1514+
expr.get_locus (), "unable to find lang item: %<%s%>",
1515+
Analysis::RustLangItem::ToString (lang_item_type).c_str ());
1516+
}
1517+
rust_assert (lang_item_defined);
1518+
1519+
// these lang items are always traits
1520+
HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
1521+
rust_assert (item->get_item_kind () == HIR::Item::ItemKind::Trait);
1522+
HIR::Trait *trait_item = static_cast<HIR::Trait *> (item);
1523+
1524+
TraitReference *trait = TraitResolver::Resolve (*trait_item);
1525+
rust_assert (!trait->is_error ());
1526+
1527+
TyTy::TypeBoundPredicate predicate (*trait, expr.get_locus ());
1528+
1529+
// resolve the trait bound where the <(Args)> are the parameter tuple type
1530+
HIR::GenericArgs args = HIR::GenericArgs::create_empty (expr.get_locus ());
1531+
1532+
// lets generate an implicit Type so that it resolves to the implict tuple
1533+
// type we have created
1534+
auto crate_num = mappings->get_current_crate ();
1535+
Analysis::NodeMapping mapping (crate_num, expr.get_mappings ().get_nodeid (),
1536+
implicit_args_id, UNKNOWN_LOCAL_DEFID);
1537+
HIR::TupleType *implicit_tuple
1538+
= new HIR::TupleType (mapping,
1539+
{} // we dont need to fill this out because it will
1540+
// auto resolve because the hir id's match
1541+
,
1542+
expr.get_locus ());
1543+
args.get_type_args ().push_back (std::unique_ptr<HIR::Type> (implicit_tuple));
1544+
1545+
// apply the arguments
1546+
predicate.apply_generic_arguments (&args);
1547+
1548+
// finally inherit the trait bound
1549+
infered->inherit_bounds ({predicate});
14261550
}
14271551

14281552
bool
@@ -1630,6 +1754,168 @@ TypeCheckExpr::resolve_operator_overload (
16301754
return true;
16311755
}
16321756

1757+
HIR::PathIdentSegment
1758+
TypeCheckExpr::resolve_possible_fn_trait_call_method_name (
1759+
const TyTy::BaseType &receiver)
1760+
{
1761+
// Question
1762+
// do we need to probe possible bounds here? I think not, i think when we
1763+
// support Fn traits they are explicitly specified
1764+
1765+
// FIXME
1766+
// the logic to map the FnTrait to their respective call trait-item is
1767+
// duplicated over in the backend/rust-compile-expr.cc
1768+
for (const auto &bound : receiver.get_specified_bounds ())
1769+
{
1770+
bool found_fn = bound.get_name ().compare ("Fn") == 0;
1771+
bool found_fn_mut = bound.get_name ().compare ("FnMut") == 0;
1772+
bool found_fn_once = bound.get_name ().compare ("FnOnce") == 0;
1773+
1774+
if (found_fn)
1775+
{
1776+
return HIR::PathIdentSegment ("call");
1777+
}
1778+
else if (found_fn_mut)
1779+
{
1780+
return HIR::PathIdentSegment ("call_mut");
1781+
}
1782+
else if (found_fn_once)
1783+
{
1784+
return HIR::PathIdentSegment ("call_once");
1785+
}
1786+
}
1787+
1788+
// nothing
1789+
return HIR::PathIdentSegment ("");
1790+
}
1791+
1792+
bool
1793+
TypeCheckExpr::resolve_fn_trait_call (HIR::CallExpr &expr,
1794+
TyTy::BaseType *receiver_tyty,
1795+
TyTy::BaseType **result)
1796+
{
1797+
// we turn this into a method call expr
1798+
HIR::PathIdentSegment method_name
1799+
= resolve_possible_fn_trait_call_method_name (*receiver_tyty);
1800+
if (method_name.is_error ())
1801+
return false;
1802+
1803+
auto candidates = MethodResolver::Probe (receiver_tyty, method_name);
1804+
if (candidates.empty ())
1805+
return false;
1806+
1807+
if (candidates.size () > 1)
1808+
{
1809+
RichLocation r (expr.get_locus ());
1810+
for (auto &c : candidates)
1811+
r.add_range (c.candidate.locus);
1812+
1813+
rust_error_at (
1814+
r, "multiple candidates found for function trait method call %<%s%>",
1815+
method_name.as_string ().c_str ());
1816+
return false;
1817+
}
1818+
1819+
if (receiver_tyty->get_kind () == TyTy::TypeKind::CLOSURE)
1820+
{
1821+
const TyTy::ClosureType &closure
1822+
= static_cast<TyTy::ClosureType &> (*receiver_tyty);
1823+
closure.setup_fn_once_output ();
1824+
}
1825+
1826+
auto candidate = *candidates.begin ();
1827+
rust_debug_loc (expr.get_locus (),
1828+
"resolved call-expr to fn trait: {%u} {%s}",
1829+
candidate.candidate.ty->get_ref (),
1830+
candidate.candidate.ty->debug_str ().c_str ());
1831+
1832+
// Get the adjusted self
1833+
Adjuster adj (receiver_tyty);
1834+
TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
1835+
1836+
// store the adjustments for code-generation to know what to do which must be
1837+
// stored onto the receiver to so as we don't trigger duplicate deref mappings
1838+
// ICE when an argument is a method call
1839+
HirId autoderef_mappings_id = expr.get_mappings ().get_hirid ();
1840+
context->insert_autoderef_mappings (autoderef_mappings_id,
1841+
std::move (candidate.adjustments));
1842+
context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty);
1843+
1844+
PathProbeCandidate &resolved_candidate = candidate.candidate;
1845+
TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
1846+
NodeId resolved_node_id
1847+
= resolved_candidate.is_impl_candidate ()
1848+
? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
1849+
.get_nodeid ()
1850+
: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
1851+
1852+
if (lookup_tyty->get_kind () != TyTy::TypeKind::FNDEF)
1853+
{
1854+
RichLocation r (expr.get_locus ());
1855+
r.add_range (resolved_candidate.locus);
1856+
rust_error_at (r, "associated impl item is not a method");
1857+
return false;
1858+
}
1859+
1860+
TyTy::BaseType *lookup = lookup_tyty;
1861+
TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
1862+
if (!fn->is_method ())
1863+
{
1864+
RichLocation r (expr.get_locus ());
1865+
r.add_range (resolved_candidate.locus);
1866+
rust_error_at (r, "associated function is not a method");
1867+
return false;
1868+
}
1869+
1870+
// fn traits only support tuple argument passing so we need to implicitly set
1871+
// this up to get the same type checking we get in the rest of the pipeline
1872+
1873+
std::vector<TyTy::TyVar> call_args;
1874+
for (auto &arg : expr.get_arguments ())
1875+
{
1876+
TyTy::BaseType *a = TypeCheckExpr::Resolve (arg.get ());
1877+
call_args.push_back (TyTy::TyVar (a->get_ref ()));
1878+
}
1879+
1880+
// crate implicit tuple
1881+
HirId implicit_arg_id = mappings->get_next_hir_id ();
1882+
Analysis::NodeMapping mapping (mappings->get_current_crate (), UNKNOWN_NODEID,
1883+
implicit_arg_id, UNKNOWN_LOCAL_DEFID);
1884+
1885+
TyTy::TupleType *tuple
1886+
= new TyTy::TupleType (implicit_arg_id, expr.get_locus (), call_args);
1887+
context->insert_implicit_type (implicit_arg_id, tuple);
1888+
1889+
std::vector<TyTy::Argument> args;
1890+
TyTy::Argument a (mapping, tuple,
1891+
expr.get_locus () /*FIXME is there a better location*/);
1892+
args.push_back (std::move (a));
1893+
1894+
TyTy::BaseType *function_ret_tyty
1895+
= TyTy::TypeCheckMethodCallExpr::go (fn, expr.get_mappings (), args,
1896+
expr.get_locus (), expr.get_locus (),
1897+
adjusted_self, context);
1898+
if (function_ret_tyty == nullptr
1899+
|| function_ret_tyty->get_kind () == TyTy::TypeKind::ERROR)
1900+
{
1901+
rust_error_at (expr.get_locus (),
1902+
"failed check fn trait call-expr MethodCallExpr");
1903+
return false;
1904+
}
1905+
1906+
// store the expected fntype
1907+
context->insert_operator_overload (expr.get_mappings ().get_hirid (), fn);
1908+
1909+
// set up the resolved name on the path
1910+
resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
1911+
resolved_node_id);
1912+
1913+
// return the result of the function back
1914+
*result = function_ret_tyty;
1915+
1916+
return true;
1917+
}
1918+
16331919
bool
16341920
TypeCheckExpr::validate_arithmetic_type (
16351921
const TyTy::BaseType *tyty, HIR::ArithmeticOrLogicalExpr::ExprType expr_type)

gcc/rust/typecheck/rust-hir-type-check-expr.h

+7
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ class TypeCheckExpr : private TypeCheckBase, private HIR::HIRExpressionVisitor
103103
HIR::OperatorExprMeta expr, TyTy::BaseType *lhs,
104104
TyTy::BaseType *rhs);
105105

106+
bool resolve_fn_trait_call (HIR::CallExpr &expr,
107+
TyTy::BaseType *function_tyty,
108+
TyTy::BaseType **result);
109+
110+
HIR::PathIdentSegment
111+
resolve_possible_fn_trait_call_method_name (const TyTy::BaseType &receiver);
112+
106113
private:
107114
TypeCheckExpr ();
108115

gcc/rust/typecheck/rust-hir-type-check.h

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class TypeCheckContextItem
7070
return item.trait_item;
7171
}
7272

73+
TyTy::FnType *get_context_type ();
74+
7375
private:
7476
union Item
7577
{

0 commit comments

Comments
 (0)