Skip to content

Enforce tail call type is related to body return type in borrowck #144917

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
110 changes: 54 additions & 56 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,9 +769,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
TerminatorKind::Call { func, args, .. }
| TerminatorKind::TailCall { func, args, .. } => {
let call_source = match term.kind {
TerminatorKind::Call { call_source, .. } => call_source,
TerminatorKind::TailCall { .. } => CallSource::Normal,
let (call_source, destination, is_diverging) = match term.kind {
TerminatorKind::Call { call_source, destination, target, .. } => {
(call_source, destination, target.is_none())
}
TerminatorKind::TailCall { .. } => {
(CallSource::Normal, RETURN_PLACE.into(), false)
}
_ => unreachable!(),
};

Expand Down Expand Up @@ -845,9 +849,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}

if let TerminatorKind::Call { destination, target, .. } = term.kind {
self.check_call_dest(term, &sig, destination, target, term_location);
}
self.check_call_dest(term, &sig, destination, is_diverging, term_location);

// The ordinary liveness rules will ensure that all
// regions in the type of the callee are live here. We
Expand Down Expand Up @@ -1874,65 +1876,61 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
destination: Place<'tcx>,
target: Option<BasicBlock>,
is_diverging: bool,
term_location: Location,
) {
let tcx = self.tcx();
match target {
Some(_) => {
let dest_ty = destination.ty(self.body, tcx).ty;
let dest_ty = self.normalize(dest_ty, term_location);
let category = match destination.as_local() {
Some(RETURN_PLACE) => {
if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
self.universal_regions.defining_ty
{
if tcx.is_static(def_id) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
}
if is_diverging {
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't really care about the ordering here but the true branch seemed easier to put first

// The signature in this call can reference region variables,
// so erase them before calling a query.
let output_ty = self.tcx().erase_regions(sig.output());
if !output_ty
.is_privately_uninhabited(self.tcx(), self.infcx.typing_env(self.infcx.param_env))
{
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
} else {
let dest_ty = destination.ty(self.body, tcx).ty;
let dest_ty = self.normalize(dest_ty, term_location);
let category = match destination.as_local() {
Some(RETURN_PLACE) => {
if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
self.universal_regions.defining_ty
{
if tcx.is_static(def_id) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::Return(ReturnConstraint::Normal)
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return(ReturnConstraint::Normal)
}
Some(l) if !self.body.local_decls[l].is_user_variable() => {
ConstraintCategory::Boring
}
// The return type of a call is interesting for diagnostics.
_ => ConstraintCategory::Assignment,
};

let locations = term_location.to_locations();

if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
span_mirbug!(
self,
term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty,
sig.output(),
terr
);
}

// When `unsized_fn_params` is not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
Some(l) if !self.body.local_decls[l].is_user_variable() => {
ConstraintCategory::Boring
}
// The return type of a call is interesting for diagnostics.
_ => ConstraintCategory::Assignment,
};

let locations = term_location.to_locations();

if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
span_mirbug!(
self,
term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty,
sig.output(),
terr
);
}
None => {
// The signature in this call can reference region variables,
// so erase them before calling a query.
let output_ty = self.tcx().erase_regions(sig.output());
if !output_ty.is_privately_uninhabited(
self.tcx(),
self.infcx.typing_env(self.infcx.param_env),
) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}

// When `unsized_fn_params` is not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/explicit-tail-calls/ret-ty-borrowck-constraints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(explicit_tail_calls)]
#![expect(incomplete_features)]

fn link(x: &str) -> &'static str {
become passthrough(x);
//~^ ERROR lifetime may not live long enough
}

fn passthrough<T>(t: T) -> T { t }

fn main() {
let x = String::from("hello, world");
let s = link(&x);
drop(x);
println!("{s}");
}
10 changes: 10 additions & 0 deletions tests/ui/explicit-tail-calls/ret-ty-borrowck-constraints.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: lifetime may not live long enough
--> $DIR/ret-ty-borrowck-constraints.rs:5:5
|
LL | fn link(x: &str) -> &'static str {
| - let's call the lifetime of this reference `'1`
LL | become passthrough(x);
| ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`

error: aborting due to 1 previous error

Loading