Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify functions have sized parameters/return values #2738

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gcc/rust/Make-lang.in
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ GRS_OBJS = \
rust/rust-dir-owner.o \
rust/rust-unicode.o \
rust/rust-punycode.o \
rust/rust-sized-checker.o \
$(END)
# removed object files from here

Expand Down
133 changes: 133 additions & 0 deletions gcc/rust/checks/errors/rust-sized-checker.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (C) 2023 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#include "rust-sized-checker.h"
#include "rust-hir.h"
#include "rust-hir-type-bounds.h"
#include "rust-hir-trait-resolve.h"

namespace Rust {
namespace HIR {

// utility functions

// set in SizedChecker::go
Resolver::TraitReference *
SizedChecker::sized_trait_cache;

bool
SizedChecker::is_sized (TyTy::BaseType *type)
{
return Resolver::TypeBoundsProbe::is_bound_satisfied_for_type (type, sized_trait_cache);
}

// items containing other items

void
SizedChecker::go (Crate &crate)
{
if (!sized_trait_cache)
{
DefId item_defid = UNKNOWN_DEFID;
if (!mappings.lookup_lang_item (Analysis::RustLangItem::ItemType::SIZED, &item_defid))
return;
Item *item = mappings.lookup_defid (item_defid);
rust_assert (item);
rust_assert (item->get_item_kind () == Item::ItemKind::Trait);
sized_trait_cache = Resolver::TraitResolver::Resolve (static_cast<Trait &> (*item));
}

for (auto &item : crate.get_items ())
item->accept_vis (*this);
}

void
SizedChecker::visit (Module &module)
{
for (auto &item : module.get_items ())
item->accept_vis (*this);
}

void
SizedChecker::visit (Trait &trait)
{
for (auto &trait_item : trait.get_trait_items ())
trait_item->accept_vis (*this);
}

void
SizedChecker::visit (ImplBlock &impl)
{
for (auto &impl_item : impl.get_impl_items ())
impl_item->accept_vis (*this);
}

void
SizedChecker::visit (ExternBlock &block)
{
for (auto &extern_item : block.get_extern_items ())
extern_item->accept_vis (*this);
}

// function-like items

void
SizedChecker::visit (Function &function)
{
TyTy::BaseType *fntype_base;
rust_assert (tyctx->lookup_type (function.get_mappings ().get_hirid (), &fntype_base));
rust_assert (fntype_base->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fntype = static_cast<TyTy::FnType *> (fntype_base);

for (auto &param : fntype->get_params ())
if (!is_sized (param.second))
rust_error_at (param.first->get_locus (), "function parameter type %<%s%> is dynamically sized", param.second->as_string ().c_str ());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rust_error_at (param.first->get_locus (), "function parameter type %<%s%> is dynamically sized", param.second->as_string ().c_str ());
rust_error_at (param.first->get_locus (), "function parameter type %qs is dynamically sized", param.second->as_string ().c_str ());


TyTy::BaseType *ret_type = fntype->get_return_type ();
if (!is_sized (ret_type))
// TODO: give this a better location_t
rust_error_at (function.get_locus (), "function return type %<%s%> is dynamically sized", ret_type->as_string ().c_str ());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}

void
SizedChecker::visit (TraitItemFunc &function)
{
// ignore functions with no body
if (!function.has_definition ()) return;

TyTy::BaseType *fntype_base;
rust_assert (tyctx->lookup_type (function.get_mappings ().get_hirid (), &fntype_base));
rust_assert (fntype_base->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fntype = static_cast<TyTy::FnType *> (fntype_base);

for (auto &param : fntype->get_params ())
if (!is_sized (param.second))
rust_error_at (param.first->get_locus (), "function parameter type %<%s%> is dynamically sized", param.second->as_string ().c_str ());

TyTy::BaseType *ret_type = fntype->get_return_type ();
if (!is_sized (ret_type))
// TODO: give this a better location_t
rust_error_at (function.get_locus (), "function return type %<%s%> is dynamically sized", ret_type->as_string ().c_str ());
}

// ignore extern functions

// TODO: visit items inside block expressions

} // namespace HIR
} // namespace Rust
62 changes: 62 additions & 0 deletions gcc/rust/checks/errors/rust-sized-checker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (C) 2023 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#ifndef RUST_SIZED_CHECKER_H
#define RUST_SIZED_CHECKER_H

#include "rust-hir-visitor.h"
#include "rust-tyty.h"
#include "rust-hir-trait-reference.h"
#include "rust-hir-type-check.h"

namespace Rust {
namespace HIR {

class SizedChecker : public HIRFullVisitorBase
{
static Resolver::TraitReference *sized_trait_cache;

static bool is_sized (TyTy::BaseType *);

Analysis::Mappings &mappings;
Resolver::TypeCheckContext *tyctx;

public:
SizedChecker () : mappings (*Analysis::Mappings::get ()), tyctx (Resolver::TypeCheckContext::get ()) {}

void go (Crate &crate);

// all items which could contain variables/functions

using HIRFullVisitorBase::visit;

void visit (Module &) override;
void visit (Function &) override;
void visit (Trait &) override;
void visit (ImplBlock &) override;
void visit (ExternBlock &) override;

void visit (TraitItemFunc &) override;

// void visit (ExternalFunctionItem &) override;
};

} // namespace HIR
} // namespace Rust

#endif /* !RUST_SIZED_CHECKER_H */
10 changes: 10 additions & 0 deletions gcc/rust/rust-session-manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "rust-attribute-values.h"
#include "rust-borrow-checker.h"
#include "rust-ast-validation.h"
#include "rust-sized-checker.h"

#include "input.h"
#include "selftest.h"
Expand Down Expand Up @@ -652,6 +653,15 @@ Session::compile_crate (const char *filename)
// type resolve
Resolver::TypeResolution::Resolve (hir);

if (saw_errors ())
return;

if (last_step == CompileOptions::CompileStep::SizedCheck)
return;

// verify that variables/function parameters/return values/etc. are sized
HIR::SizedChecker ().go (hir);

if (saw_errors ())
return;

Expand Down
1 change: 1 addition & 0 deletions gcc/rust/rust-session-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ struct CompileOptions
NameResolution,
Lowering,
TypeCheck,
SizedCheck,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you'll need to update gcc/rust/lang.opt as well to add that option

Privacy,
Unsafety,
Const,
Expand Down
4 changes: 2 additions & 2 deletions gcc/rust/typecheck/rust-tyty-bounds.cc
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ TypeBoundsProbe::assemble_sized_builtin ()
case TyTy::ARRAY:
case TyTy::REF:
case TyTy::POINTER:
case TyTy::PARAM:
case TyTy::FNDEF:
case TyTy::FNPTR:
case TyTy::BOOL:
Expand All @@ -136,14 +135,15 @@ TypeBoundsProbe::assemble_sized_builtin ()

// FIXME str and slice need to be moved and test cases updated
case TyTy::SLICE:
case TyTy::STR:
case TyTy::ADT:
case TyTy::TUPLE:
// FIXME add extra checks
assemble_builtin_candidate (Analysis::RustLangItem::SIZED);
break;

case TyTy::DYNAMIC:
case TyTy::STR:
case TyTy::PARAM:
case TyTy::ERROR:
break;
}
Expand Down
49 changes: 49 additions & 0 deletions gcc/testsuite/rust/compile/sized-check-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#[lang = "sized"]
pub trait Sized {}

mod encase {

// regular functions

pub fn foo0() -> str { // { dg-error {return [^\n]* dynamically} }
// TODO: this should produce other
// errors at some point
*""
}

pub fn foo1(_: str) {} // { dg-error {parameter [^\n]* dynamically} }

// trait functions

pub trait Foo {
fn undef(self, _: str); // this is ok
fn method(self) {} // { dg-error {parameter [^\n]* dynamically} }

fn foo0() -> str { // { dg-error {return [^\n]* dynamically} }
// TODO: this should produce other
// errors at some point
*""
}

fn foo1(_: str) {} // { dg-error {parameter [^\n]* dynamically} }
}

// impl functions

struct S;

impl S {
fn foo0(_: str) {} // { dg-error {parameter [^\n]* dynamically} }

fn foo1() -> str { // { dg-error {return [^\n]* dynamically} }
// TODO: this should produce other
// errors at some point
*""
}
}

} // mod encase

pub fn main() -> i32 {
0
}
Loading