Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
19 changes: 16 additions & 3 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
e.id,
expr_hir_id,
*coroutine_kind,
*constness,
fn_decl,
body,
*fn_decl_span,
Expand Down Expand Up @@ -1060,7 +1061,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
constness: Const,
mut constness: Const,
movability: Movability,
decl: &FnDecl,
body: &Expr,
Expand All @@ -1070,11 +1071,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
let closure_def_id = self.local_def_id(closure_id);
let (binder_clause, generic_params) = self.lower_closure_binder(binder);

if let Const::Yes(span) = constness {
if !self.is_in_const_context {
self.dcx().span_err(span, "cannot use `const` closures outside of const contexts");
constness = Const::No;
}
}

let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
let mut coroutine_kind = find_attr!(attrs, Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable));

// FIXME(contracts): Support contracts on closures?
let body_id = this.lower_fn_body(decl, None, |this| {
let body_id = this.lower_fn_body(decl, None, constness, |this| {
this.coroutine_kind = coroutine_kind;
let e = this.lower_expr_mut(body);
coroutine_kind = this.coroutine_kind;
Expand Down Expand Up @@ -1157,6 +1165,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
closure_id: NodeId,
closure_hir_id: HirId,
coroutine_kind: CoroutineKind,
constness: Const,
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
Expand Down Expand Up @@ -1203,6 +1212,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
let fn_decl =
self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);

if let Const::Yes(span) = constness {
self.dcx().span_err(span, "const coroutines are not supported");
}

let c = self.arena.alloc(hir::Closure {
def_id: closure_def_id,
binder: binder_clause,
Expand All @@ -1216,7 +1229,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// knows that a `FnDecl` output type like `-> &str` actually means
// "coroutine that returns &str", rather than directly returning a `&str`.
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
constness: hir::Constness::NotConst,
constness: self.lower_constness(constness),
});
hir::ExprKind::Closure(c)
}
Expand Down
24 changes: 20 additions & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::mem;

use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
Expand Down Expand Up @@ -345,6 +347,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
body.as_deref(),
attrs,
contract.as_deref(),
header.constness,
);

let itctx = ImplTraitContext::Universal;
Expand Down Expand Up @@ -1024,6 +1027,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
Some(body),
attrs,
contract.as_deref(),
sig.header.constness,
);
let (generics, sig) = self.lower_method_sig(
generics,
Expand Down Expand Up @@ -1217,6 +1221,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
body.as_deref(),
attrs,
contract.as_deref(),
sig.header.constness,
);
let (generics, sig) = self.lower_method_sig(
generics,
Expand Down Expand Up @@ -1346,11 +1351,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>),
) -> hir::BodyId {
let prev_coroutine_kind = self.coroutine_kind.take();
let prev_is_in_const_context = mem::take(&mut self.is_in_const_context);
let task_context = self.task_context.take();
let (parameters, result) = f(self);
let body_id = self.record_body(parameters, result);
self.task_context = task_context;
self.coroutine_kind = prev_coroutine_kind;
self.is_in_const_context = prev_is_in_const_context;
body_id
}

Expand All @@ -1369,9 +1376,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
decl: &FnDecl,
contract: Option<&FnContract>,
constness: Const,
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
) -> hir::BodyId {
self.lower_body(|this| {
if let Const::Yes(_) = constness {
this.is_in_const_context = true;
}
let params =
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));

Expand All @@ -1389,16 +1400,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
decl: &FnDecl,
body: &Block,
contract: Option<&FnContract>,
constness: Const,
) -> hir::BodyId {
self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body))
}

pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
self.lower_body(|this| {
(
&[],
match expr {
Some(expr) => this.lower_expr_mut(expr),
Some(expr) => {
this.is_in_const_context = true;
this.lower_expr_mut(expr)
}
None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
},
)
Expand All @@ -1417,12 +1432,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: Option<&Block>,
attrs: &'hir [hir::Attribute],
contract: Option<&FnContract>,
constness: Const,
) -> hir::BodyId {
let Some(body) = body else {
// Functions without a body are an error, except if this is an intrinsic. For those we
// create a fake body so that the entire rest of the compiler doesn't have to deal with
// this as a special case.
return self.lower_fn_body(decl, contract, |this| {
return self.lower_fn_body(decl, contract, constness, |this| {
if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() {
let span = this.lower_span(span);
let empty_block = hir::Block {
Expand All @@ -1447,7 +1463,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
};
let Some(coroutine_kind) = coroutine_kind else {
// Typical case: not a coroutine.
return self.lower_fn_body_block(decl, body, contract);
return self.lower_fn_body_block(decl, body, contract, constness);
};
// FIXME(contracts): Support contracts on async fn.
self.lower_body(|this| {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct LoweringContext<'a, 'hir> {
loop_scope: Option<HirId>,
is_in_loop_condition: bool,
is_in_dyn_type: bool,
is_in_const_context: bool,

current_hir_id_owner: hir::OwnerId,
item_local_id_counter: hir::ItemLocalId,
Expand Down Expand Up @@ -190,6 +191,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
loop_scope: None,
is_in_loop_condition: false,
is_in_dyn_type: false,
is_in_const_context: false,
coroutine_kind: None,
task_context: None,
current_item: None,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ declare_features! (
/// Allows defining and calling c-variadic functions in const contexts.
(unstable, const_c_variadic, "1.95.0", Some(151787)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
(unstable, const_closures, "1.68.0", Some(106003)),
/// Allows using `[const] Destruct` bounds and calling drop impls in const contexts.
(unstable, const_destruct, "1.85.0", Some(133214)),
/// Allows `for _ in _` loops in const contexts.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/src/collect/predicates_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,9 @@ pub(super) fn const_conditions<'tcx>(
},
// N.B. Tuple ctors are unconditionally constant.
Node::Ctor(hir::VariantData::Tuple { .. }) => return Default::default(),
Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_), .. }) => {
(hir::Generics::empty(), None, tcx.is_conditionally_const(tcx.local_parent(def_id)))
}
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
};

Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_middle/src/hir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,18 @@ impl<'tcx> TyCtxt<'tcx> {
/// This should only be used for determining the context of a body, a return
/// value of `Some` does not always suggest that the owner of the body is `const`,
/// just that it has to be checked as if it were.
pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option<ConstContext> {
let def_id = def_id.into();
pub fn hir_body_const_context(self, local_def_id: LocalDefId) -> Option<ConstContext> {
let def_id = local_def_id.into();
let ccx = match self.hir_body_owner_kind(def_id) {
BodyOwnerKind::Const { inline } => ConstContext::Const { inline },
BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability),

BodyOwnerKind::Fn if self.is_constructor(def_id) => return None,
BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
ConstContext::ConstFn
// Const closures use their parent's const context
BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
return self.hir_body_const_context(self.local_parent(local_def_id));
}
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
};

Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_middle/src/query/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl<'tcx> QueryJob<'tcx> {

#[derive(Debug)]
pub struct QueryWaiter<'tcx> {
pub query: Option<QueryJobId>,
pub parent: Option<QueryJobId>,
pub condvar: Condvar,
pub span: Span,
pub cycle: Mutex<Option<CycleError<'tcx>>>,
Expand Down Expand Up @@ -94,8 +94,12 @@ impl<'tcx> QueryLatch<'tcx> {
return Ok(()); // already complete
};

let waiter =
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
let waiter = Arc::new(QueryWaiter {
parent: query,
span,
cycle: Mutex::new(None),
condvar: Condvar::new(),
});

// We push the waiter on to the `waiters` list. It can be accessed inside
// the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.is_conditionally_const(def_id)
}

fn closure_is_const(self, def_id: DefId) -> bool {
debug_assert_matches!(self.def_kind(def_id), DefKind::Closure);
self.constness(def_id) == hir::Constness::Const
}

fn alias_has_const_conditions(self, def_id: DefId) -> bool {
debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::OpaqueTy);
self.is_conditionally_const(def_id)
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,11 +2118,7 @@ impl<'tcx> TyCtxt<'tcx> {
// FIXME(const_trait_impl): ATPITs could be conditionally const?
hir::OpaqueTyOrigin::TyAlias { .. } => false,
},
DefKind::Closure => {
// Closures and RPITs will eventually have const conditions
// for `[const]` bounds.
false
}
DefKind::Closure => self.constness(def_id) == hir::Constness::Const,
DefKind::Ctor(_, CtorKind::Const)
| DefKind::Mod
| DefKind::Struct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,10 +661,11 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
///
/// Doing so on all calls to `extract_tupled_inputs_and_output_from_callable`
/// would be wasteful.
#[instrument(level = "trace", skip(cx), ret)]
pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
cx: I,
self_ty: I::Ty,
) -> Result<(ty::Binder<I, (I::Ty, I::Ty)>, I::FunctionId, I::GenericArgs), NoSolution> {
) -> Result<(ty::Binder<I, (I::Ty, I::Ty)>, I::DefId, I::GenericArgs), NoSolution> {
match self_ty.kind() {
ty::FnDef(def_id, args) => {
let sig = cx.fn_sig(def_id);
Expand All @@ -675,7 +676,7 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
Ok((
sig.instantiate(cx, args)
.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
def_id,
def_id.into(),
args,
))
} else {
Expand All @@ -686,9 +687,19 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
ty::FnPtr(..) => {
return Err(NoSolution);
}
// `Closure`s are not const for now.
ty::Closure(..) => {
return Err(NoSolution);
ty::Closure(def, args) => {
if cx.closure_is_const(def) {
let closure_args = args.as_closure();
Ok((
closure_args
.sig()
.map_bound(|sig| (sig.inputs().get(0).unwrap(), sig.output())),
def.into(),
args,
))
} else {
return Err(NoSolution);
}
}
// `CoroutineClosure`s are not const for now.
ty::CoroutineClosure(..) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ where
todo!("Fn* are not yet const")
}

#[instrument(level = "trace", skip_all, ret)]
fn consider_builtin_fn_trait_candidates(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
Expand All @@ -289,7 +290,7 @@ where
let output_is_sized_pred =
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);
let requirements = cx
.const_conditions(def_id.into())
.const_conditions(def_id)
.iter_instantiated(cx, args)
.map(|trait_ref| {
(
Expand Down
15 changes: 0 additions & 15 deletions compiler/rustc_query_impl/src/from_cycle_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,6 @@ use rustc_span::{ErrorGuaranteed, Span};
use crate::job::report_cycle;

pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) {
vtables.type_of.value_from_cycle_error = |tcx, _, _, err| {
let guar = err.emit();
erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar)))
};

vtables.type_of_opaque_hir_typeck.value_from_cycle_error = |tcx, _, _, err| {
let guar = err.emit();
erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar)))
};

vtables.erase_and_anonymize_regions_ty.value_from_cycle_error = |tcx, _, _, err| {
let guar = err.emit();
erase_val(Ty::new_error(tcx, guar))
};

vtables.fn_sig.value_from_cycle_error = |tcx, key, _, err| {
let guar = err.delay_as_bug();
erase_val(fn_sig(tcx, key, guar))
Expand Down
Loading
Loading