Skip to content

Commit

Permalink
delegation: Support async, const, extern "ABI" and C-variadic functions
Browse files Browse the repository at this point in the history
Also allow `impl Trait` in delegated functions.
The delegation item will refer to the original opaque type from the callee, fresh opaque type won't be created.
  • Loading branch information
petrochenkov committed Mar 14, 2024
1 parent 78bf0c4 commit f0f0dae
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 161 deletions.
116 changes: 75 additions & 41 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::span_bug;
use rustc_middle::ty::ResolverAstLowering;
use rustc_middle::ty::{Asyncness, ResolverAstLowering};
use rustc_span::{symbol::Ident, Span};
use rustc_target::spec::abi;
use std::iter;
Expand All @@ -66,7 +66,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
return false;
};
if let Some(local_sig_id) = sig_id.as_local() {
self.resolver.has_self.contains(&local_sig_id)
self.resolver.delegation_fn_sigs[&local_sig_id].has_self
} else {
match self.tcx.def_kind(sig_id) {
DefKind::Fn => false,
Expand All @@ -81,13 +81,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
delegation: &Delegation,
item_id: NodeId,
) -> DelegationResults<'hir> {
let span = delegation.path.segments.last().unwrap().ident.span;
let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span);
let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span);
match sig_id {
Ok(sig_id) => {
let decl = self.lower_delegation_decl(sig_id, span);
let sig = self.lower_delegation_sig(span, decl);
let body_id = self.lower_delegation_body(sig.decl, delegation);
let (param_count, c_variadic) = self.param_count(sig_id);
let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span);
let sig = self.lower_delegation_sig(sig_id, decl, span);
let body_id = self.lower_delegation_body(delegation, param_count, span);

let generics = self.lower_delegation_generics(span);
DelegationResults { body_id, sig, generics }
Expand Down Expand Up @@ -122,70 +123,92 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}

fn param_count(&self, sig_id: DefId) -> (usize, bool /*c_variadic*/) {
if let Some(local_sig_id) = sig_id.as_local() {
// Map may be filled incorrectly due to recursive delegation.
// Error will be emmited later in astconv.
match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
Some(sig) => (sig.param_count, sig.c_variadic),
None => (0, false),
}
} else {
let sig = self.tcx.fn_sig(sig_id).skip_binder().skip_binder();
(sig.inputs().len(), sig.c_variadic)
}
}

fn lower_delegation_decl(
&mut self,
sig_id: DefId,
param_span: Span,
param_count: usize,
c_variadic: bool,
span: Span,
) -> &'hir hir::FnDecl<'hir> {
let args_count = if let Some(local_sig_id) = sig_id.as_local() {
// Map may be filled incorrectly due to recursive delegation.
// Error will be emmited later in astconv.
self.resolver.fn_parameter_counts.get(&local_sig_id).cloned().unwrap_or_default()
} else {
self.tcx.fn_arg_names(sig_id).len()
};
let inputs = self.arena.alloc_from_iter((0..args_count).map(|arg| hir::Ty {
// The last parameter in C variadic functions is skipped in the signature,
// like during regular lowering.
let decl_param_count = param_count - c_variadic as usize;
let inputs = self.arena.alloc_from_iter((0..decl_param_count).map(|arg| hir::Ty {
hir_id: self.next_id(),
kind: hir::TyKind::InferDelegation(sig_id, hir::InferDelegationKind::Input(arg)),
span: self.lower_span(param_span),
span,
}));

let output = self.arena.alloc(hir::Ty {
hir_id: self.next_id(),
kind: hir::TyKind::InferDelegation(sig_id, hir::InferDelegationKind::Output),
span: self.lower_span(param_span),
span,
});

self.arena.alloc(hir::FnDecl {
inputs,
output: hir::FnRetTy::Return(output),
c_variadic: false,
c_variadic,
lifetime_elision_allowed: true,
implicit_self: hir::ImplicitSelfKind::None,
})
}

fn lower_delegation_sig(
&mut self,
span: Span,
sig_id: DefId,
decl: &'hir hir::FnDecl<'hir>,
span: Span,
) -> hir::FnSig<'hir> {
hir::FnSig {
decl,
header: hir::FnHeader {
unsafety: hir::Unsafety::Normal,
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
abi: abi::Abi::Rust,
},
span: self.lower_span(span),
}
let header = if let Some(local_sig_id) = sig_id.as_local() {
match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
Some(sig) => self.lower_fn_header(sig.header),
None => self.generate_header_error(),
}
} else {
let sig = self.tcx.fn_sig(sig_id).skip_binder().skip_binder();
let asyncness = match self.tcx.asyncness(sig_id) {
Asyncness::Yes => hir::IsAsync::Async(span),
Asyncness::No => hir::IsAsync::NotAsync,
};
hir::FnHeader {
unsafety: sig.unsafety,
constness: self.tcx.constness(sig_id),
asyncness,
abi: sig.abi,
}
};
hir::FnSig { decl, header, span }
}

fn generate_param(&mut self, ty: &'hir hir::Ty<'hir>) -> (hir::Param<'hir>, NodeId) {
fn generate_param(&mut self, span: Span) -> (hir::Param<'hir>, NodeId) {
let pat_node_id = self.next_node_id();
let pat_id = self.lower_node_id(pat_node_id);
let pat = self.arena.alloc(hir::Pat {
hir_id: pat_id,
kind: hir::PatKind::Binding(hir::BindingAnnotation::NONE, pat_id, Ident::empty(), None),
span: ty.span,
span,
default_binding_modes: false,
});

(hir::Param { hir_id: self.next_id(), pat, ty_span: ty.span, span: ty.span }, pat_node_id)
(hir::Param { hir_id: self.next_id(), pat, ty_span: span, span }, pat_node_id)
}

fn generate_arg(&mut self, ty: &'hir hir::Ty<'hir>, param_id: HirId) -> hir::Expr<'hir> {
fn generate_arg(&mut self, param_id: HirId, span: Span) -> hir::Expr<'hir> {
let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment {
ident: Ident::empty(),
hir_id: self.next_id(),
Expand All @@ -194,20 +217,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
infer_args: false,
}));

let path =
self.arena.alloc(hir::Path { span: ty.span, res: Res::Local(param_id), segments });
let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments });

hir::Expr {
hir_id: self.next_id(),
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
span: ty.span,
span,
}
}

fn lower_delegation_body(
&mut self,
decl: &'hir hir::FnDecl<'hir>,
delegation: &Delegation,
param_count: usize,
span: Span,
) -> BodyId {
let path = self.lower_qpath(
delegation.id,
Expand All @@ -223,8 +246,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
let mut args: Vec<hir::Expr<'hir>> = Vec::new();

for (idx, param_ty) in decl.inputs.iter().enumerate() {
let (param, pat_node_id) = this.generate_param(param_ty);
for idx in 0..param_count {
let (param, pat_node_id) = this.generate_param(span);
parameters.push(param);

let arg = if let Some(block) = block
Expand All @@ -244,7 +267,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
} else {
let pat_hir_id = this.lower_node_id(pat_node_id);
this.generate_arg(param_ty, pat_hir_id)
this.generate_arg(pat_hir_id, span)
};
args.push(arg);
}
Expand Down Expand Up @@ -303,14 +326,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
implicit_self: hir::ImplicitSelfKind::None,
});

let sig = self.lower_delegation_sig(span, decl);
let header = self.generate_header_error();
let sig = hir::FnSig { decl, header, span };

let body_id = self.lower_body(|this| {
let expr =
hir::Expr { hir_id: this.next_id(), kind: hir::ExprKind::Err(err), span: span };
(&[], expr)
});
DelegationResults { generics, body_id, sig }
}

fn generate_header_error(&self) -> hir::FnHeader {
hir::FnHeader {
unsafety: hir::Unsafety::Normal,
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
abi: abi::Abi::Rust,
}
}
}

struct SelfResolver<'a> {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}

fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
pub(super) fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
hir::IsAsync::Async(span)
} else {
Expand Down
28 changes: 0 additions & 28 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2281,11 +2281,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
try_emit("recursive delegation");
}

let sig = self.tcx().fn_sig(sig_id).instantiate_identity();
if sig.output().has_opaque_types() {
try_emit("delegation to a function with opaque type");
}

let sig_generics = self.tcx().generics_of(sig_id);
let parent = self.tcx().parent(self.item_def_id());
let parent_generics = self.tcx().generics_of(parent);
Expand All @@ -2297,29 +2292,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
try_emit("delegation with early bound generics");
}

if self.tcx().asyncness(sig_id) == ty::Asyncness::Yes {
try_emit("delegation to async functions");
}

if self.tcx().constness(sig_id) == hir::Constness::Const {
try_emit("delegation to const functions");
}

if sig.c_variadic() {
try_emit("delegation to variadic functions");
// variadic functions are also `unsafe` and `extern "C"`.
// Do not emit same error multiple times.
return error_occured;
}

if let hir::Unsafety::Unsafe = sig.unsafety() {
try_emit("delegation to unsafe functions");
}

if abi::Abi::Rust != sig.abi() {
try_emit("delegation to non Rust ABI functions");
}

error_occured
}

Expand Down
13 changes: 10 additions & 3 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use rustc_data_structures::unord::UnordMap;
use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
use rustc_index::IndexVec;
use rustc_macros::HashStable;
use rustc_query_system::ich::StableHashingContext;
Expand Down Expand Up @@ -224,8 +224,15 @@ pub struct ResolverAstLowering {
pub lint_buffer: Steal<LintBuffer>,

/// Information about functions signatures for delegation items expansion
pub has_self: LocalDefIdSet,
pub fn_parameter_counts: LocalDefIdMap<usize>,
pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
}

#[derive(Debug)]
pub struct DelegationFnSig {
pub header: ast::FnHeader,
pub param_count: usize,
pub has_self: bool,
pub c_variadic: bool,
}

#[derive(Clone, Copy, Debug)]
Expand Down
14 changes: 8 additions & 6 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, Par
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::{PrimTy, TraitCandidate};
use rustc_middle::middle::resolve_bound_vars::Set1;
use rustc_middle::ty::DelegationFnSig;
use rustc_middle::{bug, span_bug};
use rustc_session::config::{CrateType, ResolveDocLinks};
use rustc_session::lint;
Expand Down Expand Up @@ -4680,12 +4681,13 @@ struct ItemInfoCollector<'a, 'b, 'tcx> {

impl ItemInfoCollector<'_, '_, '_> {
fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) {
let def_id = self.r.local_def_id(id);
self.r.fn_parameter_counts.insert(def_id, sig.decl.inputs.len());

if sig.decl.has_self() {
self.r.has_self.insert(def_id);
}
let sig = DelegationFnSig {
header: sig.header,
param_count: sig.decl.inputs.len(),
has_self: sig.decl.has_self(),
c_variadic: sig.decl.c_variadic(),
};
self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig);
}
}

Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2046,7 +2046,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called },
ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType,
ast::AssocItemKind::Delegation(..)
if self.r.has_self.contains(&self.r.local_def_id(assoc_item.id)) =>
if self.r.delegation_fn_sigs[&self.r.local_def_id(assoc_item.id)]
.has_self =>
{
AssocSuggestion::MethodWithSelf { called }
}
Expand All @@ -2069,7 +2070,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
if filter_fn(res) {
let def_id = res.def_id();
let has_self = match def_id.as_local() {
Some(def_id) => self.r.has_self.contains(&def_id),
Some(def_id) => {
self.r.delegation_fn_sigs.get(&def_id).map_or(false, |sig| sig.has_self)
}
None => self
.r
.tcx
Expand Down
Loading

0 comments on commit f0f0dae

Please sign in to comment.