about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_passes/messages.ftl7
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs16
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs10
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl9
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/from.rs132
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/ty.rs11
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs18
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs1
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs4
11 files changed, 209 insertions, 2 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 28d7c6613c8..73cbcdd30ce 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -32,6 +32,13 @@ ast_passes_assoc_type_without_body =
     associated type in `impl` without body
     .suggestion = provide a definition for the type
 
+ast_passes_async_fn_in_const_trait_or_trait_impl =
+    async functions are not allowed in `const` {$in_impl ->
+        [true] trait impls
+        *[false] traits
+    }
+    .label = associated functions of `const` cannot be declared `async`
+
 ast_passes_at_least_one_trait = at least one trait must be specified
 
 ast_passes_auto_generic = auto traits cannot have generic parameters
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index f0b67755761..0c72f319007 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -293,6 +293,21 @@ impl<'a> AstValidator<'a> {
         });
     }
 
+    fn check_async_fn_in_const_trait_or_impl(&self, sig: &FnSig, parent: &TraitOrTraitImpl) {
+        let Some(const_keyword) = parent.constness() else { return };
+
+        let Some(CoroutineKind::Async { span: async_keyword, .. }) = sig.header.coroutine_kind
+        else {
+            return;
+        };
+
+        self.dcx().emit_err(errors::AsyncFnInConstTraitOrTraitImpl {
+            async_keyword,
+            in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
+            const_keyword,
+        });
+    }
+
     fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
         self.check_decl_num_args(fn_decl);
         self.check_decl_cvariadic_pos(fn_decl);
@@ -1578,6 +1593,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl);
             if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
                 self.check_trait_fn_not_const(sig.header.constness, parent);
+                self.check_async_fn_in_const_trait_or_impl(sig, parent);
             }
         }
 
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 1cb2493afe8..5ecc0d21411 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -63,6 +63,16 @@ pub(crate) struct TraitFnConst {
 }
 
 #[derive(Diagnostic)]
+#[diag(ast_passes_async_fn_in_const_trait_or_trait_impl)]
+pub(crate) struct AsyncFnInConstTraitOrTraitImpl {
+    #[primary_span]
+    pub async_keyword: Span,
+    pub in_impl: bool,
+    #[label]
+    pub const_keyword: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(ast_passes_forbidden_bound)]
 pub(crate) struct ForbiddenBound {
     #[primary_span]
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index eb3c40cc593..358c0d3db46 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -222,6 +222,15 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
 
 builtin_macros_format_use_positional = consider using a positional formatting argument instead
 
+builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind}
+
+builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields ->
+    [true] multiple fields
+    *[false] no fields
+}
+
+builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field
+
 builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
     .note = only one `#[default]` attribute is needed
     .label = `#[default]` used here
diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs
new file mode 100644
index 00000000000..ef0e6ca324a
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/from.rs
@@ -0,0 +1,132 @@
+use rustc_ast as ast;
+use rustc_ast::{ItemKind, VariantData};
+use rustc_errors::MultiSpan;
+use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
+use rustc_span::{Ident, Span, kw, sym};
+use thin_vec::thin_vec;
+
+use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty};
+use crate::deriving::generic::{
+    BlockOrExpr, FieldlessVariantsStrategy, MethodDef, SubstructureFields, TraitDef,
+    combine_substructure,
+};
+use crate::deriving::pathvec_std;
+use crate::errors;
+
+/// Generate an implementation of the `From` trait, provided that `item`
+/// is a struct or a tuple struct with exactly one field.
+pub(crate) fn expand_deriving_from(
+    cx: &ExtCtxt<'_>,
+    span: Span,
+    mitem: &ast::MetaItem,
+    annotatable: &Annotatable,
+    push: &mut dyn FnMut(Annotatable),
+    is_const: bool,
+) {
+    let Annotatable::Item(item) = &annotatable else {
+        cx.dcx().bug("derive(From) used on something else than an item");
+    };
+
+    // #[derive(From)] is currently usable only on structs with exactly one field.
+    let field = if let ItemKind::Struct(_, _, data) = &item.kind
+        && let [field] = data.fields()
+    {
+        Some(field.clone())
+    } else {
+        None
+    };
+
+    let from_type = match &field {
+        Some(field) => Ty::AstTy(field.ty.clone()),
+        // We don't have a type to put into From<...> if we don't have a single field, so just put
+        // unit there.
+        None => Ty::Unit,
+    };
+    let path =
+        Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
+
+    // Generate code like this:
+    //
+    // struct S(u32);
+    // #[automatically_derived]
+    // impl ::core::convert::From<u32> for S {
+    //     #[inline]
+    //     fn from(value: u32) -> S {
+    //         Self(value)
+    //     }
+    // }
+    let from_trait_def = TraitDef {
+        span,
+        path,
+        skip_path_as_bound: true,
+        needs_copy_as_bound_if_packed: false,
+        additional_bounds: Vec::new(),
+        supports_unions: false,
+        methods: vec![MethodDef {
+            name: sym::from,
+            generics: Bounds { bounds: vec![] },
+            explicit_self: false,
+            nonself_args: vec![(from_type, sym::value)],
+            ret_ty: Ty::Self_,
+            attributes: thin_vec![cx.attr_word(sym::inline, span)],
+            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
+            combine_substructure: combine_substructure(Box::new(|cx, span, substructure| {
+                let Some(field) = &field else {
+                    let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
+                    let err_span = MultiSpan::from_spans(vec![span, item_span]);
+                    let error = match &item.kind {
+                        ItemKind::Struct(_, _, data) => {
+                            cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
+                                span: err_span,
+                                multiple_fields: data.fields().len() > 1,
+                            })
+                        }
+                        ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
+                            cx.dcx().emit_err(errors::DeriveFromWrongTarget {
+                                span: err_span,
+                                kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
+                            })
+                        }
+                        _ => cx.dcx().bug("Invalid derive(From) ADT input"),
+                    };
+
+                    return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error)));
+                };
+
+                let self_kw = Ident::new(kw::SelfUpper, span);
+                let expr: Box<ast::Expr> = match substructure.fields {
+                    SubstructureFields::StaticStruct(variant, _) => match variant {
+                        // Self {
+                        //     field: value
+                        // }
+                        VariantData::Struct { .. } => cx.expr_struct_ident(
+                            span,
+                            self_kw,
+                            thin_vec![cx.field_imm(
+                                span,
+                                field.ident.unwrap(),
+                                cx.expr_ident(span, Ident::new(sym::value, span))
+                            )],
+                        ),
+                        // Self(value)
+                        VariantData::Tuple(_, _) => cx.expr_call_ident(
+                            span,
+                            self_kw,
+                            thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))],
+                        ),
+                        variant => {
+                            cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}"));
+                        }
+                    },
+                    _ => cx.dcx().bug("Invalid derive(From) ADT input"),
+                };
+                BlockOrExpr::new_expr(expr)
+            })),
+        }],
+        associated_types: Vec::new(),
+        is_const,
+        is_staged_api_crate: cx.ecfg.features.staged_api(),
+    };
+
+    from_trait_def.expand(cx, mitem, annotatable, push);
+}
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
index 00e70b21cf4..1458553d492 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
@@ -2,7 +2,7 @@
 //! when specifying impls to be derived.
 
 pub(crate) use Ty::*;
-use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind};
+use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind, TyKind};
 use rustc_expand::base::ExtCtxt;
 use rustc_span::source_map::respan;
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
@@ -65,7 +65,7 @@ impl Path {
     }
 }
 
-/// A type. Supports pointers, Self, and literals.
+/// A type. Supports pointers, Self, literals, unit or an arbitrary AST path.
 #[derive(Clone)]
 pub(crate) enum Ty {
     Self_,
@@ -76,6 +76,8 @@ pub(crate) enum Ty {
     Path(Path),
     /// For () return types.
     Unit,
+    /// An arbitrary type.
+    AstTy(Box<ast::Ty>),
 }
 
 pub(crate) fn self_ref() -> Ty {
@@ -101,6 +103,7 @@ impl Ty {
                 let ty = ast::TyKind::Tup(ThinVec::new());
                 cx.ty(span, ty)
             }
+            AstTy(ty) => ty.clone(),
         }
     }
 
@@ -132,6 +135,10 @@ impl Ty {
                 cx.path_all(span, false, vec![self_ty], params)
             }
             Path(p) => p.to_path(cx, span, self_ty, generics),
+            AstTy(ty) => match &ty.kind {
+                TyKind::Path(_, path) => path.clone(),
+                _ => cx.dcx().span_bug(span, "non-path in a path in generic `derive`"),
+            },
             Ref(..) => cx.dcx().span_bug(span, "ref in a path in generic `derive`"),
             Unit => cx.dcx().span_bug(span, "unit in a path in generic `derive`"),
         }
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index 1edc2965def..cee6952fa34 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -23,6 +23,7 @@ pub(crate) mod clone;
 pub(crate) mod coerce_pointee;
 pub(crate) mod debug;
 pub(crate) mod default;
+pub(crate) mod from;
 pub(crate) mod hash;
 
 #[path = "cmp/eq.rs"]
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index bb520db75b9..54e8f750337 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -447,6 +447,24 @@ pub(crate) struct DefaultHasArg {
 }
 
 #[derive(Diagnostic)]
+#[diag(builtin_macros_derive_from_wrong_target)]
+#[note(builtin_macros_derive_from_usage_note)]
+pub(crate) struct DeriveFromWrongTarget<'a> {
+    #[primary_span]
+    pub(crate) span: MultiSpan,
+    pub(crate) kind: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_derive_from_wrong_field_count)]
+#[note(builtin_macros_derive_from_usage_note)]
+pub(crate) struct DeriveFromWrongFieldCount {
+    #[primary_span]
+    pub(crate) span: MultiSpan,
+    pub(crate) multiple_fields: bool,
+}
+
+#[derive(Diagnostic)]
 #[diag(builtin_macros_derive_macro_call)]
 pub(crate) struct DeriveMacroCall {
     #[primary_span]
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 7bc448a9acb..86a4927f390 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -139,6 +139,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         PartialEq: partial_eq::expand_deriving_partial_eq,
         PartialOrd: partial_ord::expand_deriving_partial_ord,
         CoercePointee: coerce_pointee::expand_deriving_coerce_pointee,
+        From: from::expand_deriving_from,
     }
 
     let client = rustc_proc_macro::bridge::client::Client::expand1(rustc_proc_macro::quote);
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 87ecc7b41e2..07f928b8c88 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -470,6 +470,8 @@ declare_features! (
     (unstable, deprecated_suggestion, "1.61.0", Some(94785)),
     /// Allows deref patterns.
     (incomplete, deref_patterns, "1.79.0", Some(87121)),
+    /// Allows deriving the From trait on single-field structs.
+    (unstable, derive_from, "CURRENT_RUSTC_VERSION", Some(144889)),
     /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
     (unstable, doc_auto_cfg, "1.58.0", Some(43781)),
     /// Allows `#[doc(cfg(...))]`.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index f7a8258a9d8..8e26aa9e77f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -392,6 +392,7 @@ symbols! {
         __D,
         __H,
         __S,
+        __T,
         __awaitee,
         __try_var,
         _t,
@@ -746,6 +747,7 @@ symbols! {
         contracts_ensures,
         contracts_internals,
         contracts_requires,
+        convert,
         convert_identity,
         copy,
         copy_closures,
@@ -847,6 +849,7 @@ symbols! {
         derive_const,
         derive_const_issue: "118304",
         derive_default_enum,
+        derive_from,
         derive_smart_pointer,
         destruct,
         destructuring_assignment,
@@ -2331,6 +2334,7 @@ symbols! {
         va_start,
         val,
         validity,
+        value,
         values,
         var,
         variant_count,