From nobody Sun Sep 27 05:28:31 2026 Received: from CWXP265CU008.outbound.protection.outlook.com (mail-ukwestazon11020101.outbound.protection.outlook.com [52.101.195.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EA08F4ACC75; Fri, 4 Sep 2026 14:12:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.195.101 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531170; cv=fail; b=aV9r/XW3a/7UgtHxyMmSI3/ifgE8I2i/7uubX+Eq3Ep0R0r/Y1cC6Ygx9zVetFyKOJ1PeWJGJT3Za9zQataNrK6id8kgDA5DyGhqFt5HndpUqDESHDLAP31J6t1in3lDR2PiAH6flVsplNO/pTlKkU48sTt8D4zFu9GnHZqm4R0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531170; c=relaxed/simple; bh=z2m2pH5QymFTxl9lX2nrBsBHr933wTMg3TZl5cpaDys=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=i675B6Mo+3BusLI/j/lzVaqcxTtjXovjxlpGVgiu/RbNIyyVuY5VGfxBBk66f7U+U1SxdpI7brDxxFsHAd4YgVKMMCAQjbeKf1gm33uIXK0ytg2v+pA2rr2BpS6hENIMtoJT0tTgI6kpajcJSE2vFUAXAQvQXIfooUN0eEEDjBo= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net; spf=pass smtp.mailfrom=garyguo.net; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b=AK6xyRmX; arc=fail smtp.client-ip=52.101.195.101 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=garyguo.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b="AK6xyRmX" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=oRg+hWmZtsFDEmwjkp933E500XrRy/+orbsECJVz/UfrD7vK7sQO9uf/BZfJAFcPBojAqtT9HNr56Fbv4+EyoxfURwKP0DjiCTyrm83oT42Cwdp5thzvSxyxJwQgDenOXzKjgKlyIKhieDKE2rOyuM7FhB059bbNP+Vl3/jpKeV4ZqJisPOmSgOETpQ3TJ4/wVGSpQuk7RJcmIGDlnk5+k1oOaDrfyr/QtVW7D+SQG5IfArIMNbe2jImdKyZd3LnAGt2ehWE82f1UBh5yBNwXQXNyZDR3bEH0F1uETiXFphL4eAwsOOXq+j880LabdQhtTzhqB7J+erB5SpW55zi4g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=acBuirXj+oy1DXSEQu8Obok8b81IDGs9J8W2HTwHLXI=; b=LhQFeEzhMgWMPzovNw2Pjeeo9Irjk7qMTPIJBpvLBZSW/7SLWwNMnEIkGsarpJv+GcItPcc6x8VGWQGK7Buh8acghy5nDIuUh2ENmpCufGPg4Iw6oxZkhDtdeWLnBocmCTmIbH6QN+xtK6WVlNsMKWzv8U1K+eP02c6QKBTv+o43NRL7jBf7/tRRlvnpmUBZ8IX4yrwLzdn2vT3tB6dZ9+3DVBhYbQBriBFtMZCPVy65Lfui5Ure56XhizIozdzF5nw2L0mOoxZZM3EFj8shRIiUomwDnk2xow7x38iWoV7JmEY8gxGsF+CEEamMJGUDWJzeKOB7nx2CxQIapsbIig== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=garyguo.net; dmarc=pass action=none header.from=garyguo.net; dkim=pass header.d=garyguo.net; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=garyguo.net; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=acBuirXj+oy1DXSEQu8Obok8b81IDGs9J8W2HTwHLXI=; b=AK6xyRmX8sE2WUCsBHiFB+c7ym8tRl+RzGb00230GE0lXuhfUfXWSS2rKpno2lGr/yksQ8I+olTcz0qBV89zjnYldRgBH+Q3o0Bb8W0LQbofzMA1oDKg7/uY4LI8IX0FeC1ZHAg/61LcidqRjknWOW50PfIHRJARPBMd3QDKwxg= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=garyguo.net; Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) by CWLP265MB6690.GBRP265.PROD.OUTLOOK.COM (2603:10a6:400:1ec::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Fri, 4 Sep 2026 14:12:36 +0000 Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1]) by LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1%4]) with mapi id 15.21.0360.008; Fri, 4 Sep 2026 14:12:36 +0000 From: Gary Guo Date: Fri, 04 Sep 2026 15:12:29 +0100 Subject: [PATCH 1/4] rust: pin-init: internal: extract utility code to new module Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260904-tuple-struct-v1-1-72c50bd037fd@garyguo.net> References: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> In-Reply-To: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> To: Benno Lossin , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Gary Guo X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788531156; l=3058; i=gary@garyguo.net; s=20221204; h=from:subject:message-id; bh=z2m2pH5QymFTxl9lX2nrBsBHr933wTMg3TZl5cpaDys=; b=WztIzjsmkoQ9CSm5blXwjwVUfATXW+7ROKOGarQzGm4ewW1YKu/IF1yyZk3YTUiYEQ3eL240J dusG6KKkklvADKpRLjggDsDvi1MngddWMhmy2cethp7qzrb6n8p9dir X-Developer-Key: i=gary@garyguo.net; a=ed25519; pk=vB3uIX95SM4eVrIqo1DWNWKDKD2xzB+yLLLr0yOPYMo= X-ClientProxiedBy: LO4P123CA0690.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:37b::7) To LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: LOAP265MB8560:EE_|CWLP265MB6690:EE_ X-MS-Office365-Filtering-Correlation-Id: 6f5da472-047e-47e3-f61d-08df0a8e926e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|23010399003|10070799003|7416014|376014|10067099003|3023799007|18002099003|56012099006|22082099003|921020; X-Microsoft-Antispam-Message-Info: 6xedqbNqIGq0t4Tu8EzJAzEr7SXp8Ny5hXqwFsjRsW/S2eM+0E+LMiqgrq8WjqIcUOCDPEDfckevve3fyR0mgJVEBt8sVeZ1T0pWGkZOOAikhCRpIkpCijLd0EPMKlJH4M3JFGiOqAbljX+B77JhpiEo0YDo4APyHNXy4dwPJriGDU+T8/RBXyKQMjC3cMNjsIuJVLGQ2eNNndGbrTvz/0UuALd1Yqj4U/yXjyrJ9rhlG10dctzvh5AnBH1g0IFlY3s3B16zHJyu52u3lBo9y/jF95HZHASOwVlvpQPUcrno8jG3+PdFxfNQK91W7L2BJhlsZ2EePODrgrfCwQ6vJk+WSkEr+zpshvXBEVrEhPsJa4eVgoQmGSeRk7ARmWRC/BFTUrrbS/ravbkgI8aSDS7GyJpHO8Fq24UcIxj8Pu3PXHCBkl7bsEAC8Ue2y5oKTynUXob1F9K1ypeQO6/QQXNfdk2OF4Q9e+CMYljW7isznt92Bt1B5JhQWlFW3H3jc7HWl74QGo7aW4NRfokkpbHVgCyOGX8uT+G57cfTY06kFOvGACEU4v3N91DoHI5hMs+PMrZrUXlWxxXbiJzXc6GfWMQE/bJjgAlzxPY2CqzMjzIZIKRk8+yvf65nygLVjJ05VqeNd2Fu91BFraeABwpU+kHba7KllMqQHZTHxJW/0721ldR5p27QjGqcA6nKiprcJMZ90hcLp8LYVrfulQ== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(23010399003)(10070799003)(7416014)(376014)(10067099003)(3023799007)(18002099003)(56012099006)(22082099003)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?R1VCNktqUiticnhzSHhtd0Foa0c3c0VEbzM1d24wWlo5M2ZhZGwySUdLVFdF?= =?utf-8?B?MjhjUDBYUkhESE1lck13SDVJRmloMThUSktUK05FSXhuWUpHbFZiS28rRit2?= =?utf-8?B?T002Z0ZzWWhPTU1ZSDdhZHdHMzdRVmlJVloyMWQxSUJyc096WmtvelpGckN4?= =?utf-8?B?amtJUGwvSjIreHE0MU9pWjdwUSttLzgyMUN4RXBUdnBaZThkV0M3RGw5S3NH?= =?utf-8?B?Smc2UWszdDhOcTZ2Y1pBd1FyeFFGVGgveWtlWTdyc0t0TThUR08yOFN3L2oy?= =?utf-8?B?UVFXWjNuUzZVbHZualVpNlBydHo4S0RvQS9sU0lvMmlsNXJKeGJ1bHRXOG9Z?= =?utf-8?B?ek91RUpmL3N5clpPMWxYUlgvazNvYmtORHg0TUl1VkdDVDhqcVNVaFZMaXlF?= =?utf-8?B?RFZXck93UDF2aXo2V2FscjdDZk1Pcm12N1NEUThpaUdLdzdDVTc3SWZ3T3o3?= =?utf-8?B?cUZKdnQ2RVQwQnJHUkZYOTZDaGl1RFg4TkZmTlAwbGVMS1IrdnhpNVE1SHpD?= =?utf-8?B?b2FwY0ZqSFhjdjFPSnRoeE9SRUZoTnZrekRobnVXQ2RxOHVqZ2tEVDM4dnZw?= =?utf-8?B?S21lUUxjNy9iL2JBaHNhT3NNVitUbzV0YXpzRVJmYTdKMThoZHVpNGt5VWNN?= =?utf-8?B?M0Y2bWNMeEFhVEt5YkN0blN5Q1l1bkdFV2tTNkVaOUZENS9kQ1BMMFd5eTBG?= =?utf-8?B?K2FITXNZQldJalRidEtnMEgvZStJaDg2cTJMUUJidDYzejlkY0RuTG1zbUpS?= =?utf-8?B?MWFyMldYMHR3Vk94cFBkL0FpaXpUN1VOa2JGTnhNWk0rZTNvKzFCWDduSG1l?= =?utf-8?B?NFYwdFNVUjB1Q0E3RGVQZnZ0Qkd5ajBnd216VDEzRDZ5ZzZrMkd4WUdmUzlT?= =?utf-8?B?TGEydm1LVUhobnJNRUZIQUp3UndzckRhamVaWXZvRGRja2p2empFcVV3MkEx?= =?utf-8?B?dGpUc0d6b25RbzRVWE0vOG90dEtHaHVJVzZnWWdTbFQ5TlRGdDVwMTZqbGFQ?= =?utf-8?B?SjczWUxPSGYwRUNRT1ByZ3hKSkNSSVRCaDV4V3d1TW5MNHN6WjhDZW9ScUdW?= =?utf-8?B?OE5GTC9FMkVLMHdBeW9lR1I2YlNnVHpaeExmcWF5dUp4UU13L2VMQzlMVEhL?= =?utf-8?B?MTgrTzVVUldROEprU2tKeFg2RnlITG9kREt0MEc2bmZsZER3a2xJMXFlaWo0?= =?utf-8?B?WStjQTZxbHVhNjNGNkNmUUplMHQ0Q3N2bG5EV2xjdE1SUndDN2NmWlQ4T3R6?= =?utf-8?B?RjNWbXZUQlpYZ0tRbEtpcEsvWWlSeXNrckdybzNvYkprbDdhZ1pHRWdNaTQ4?= =?utf-8?B?NzFIZHMyMTNjZnJpbmc4NnZkQWJpbUhjM1g2cE1GU0RyNld2Unc4dWhmdzlC?= =?utf-8?B?WFpjUGtPMnNnbUEyQWJIbGU0UzZXaldUUktyYWNoNmlWOEthbW50ZEduUUs5?= =?utf-8?B?YlZJVFFaUjQzTkMwbVUzakxZNjg1VHFBL2JJMFlBZGphdkF0dVQ3NU9BcTdp?= =?utf-8?B?SWNoUXh1VXo2UXRYS2tQbVl3UFVNMU1vSFcwUXNrVi93MlNNeStFMDhSNVVL?= =?utf-8?B?MUc5ckVTT1ZISmh2WVpjNnloUTdIT1FiSzloVXFLVGxuSWFjOWMyaXhnV1VY?= =?utf-8?B?SndTN2xNU3UwY2czcHhiR1B1MnU2Rmtmc2MwZmdBY2FPakk5eTFndE45VHpP?= =?utf-8?B?MDkvdnFoUWp5YkJWVENHbGdoQkNuSkk2UEtsMDR0NXhWNUFtcnl0a0FYYkJC?= =?utf-8?B?elR1dFJIa1kweGF1MWNNWkFWaDdsMkNCVHRjUCtCcExRNnFlcUIzSitrdDNY?= =?utf-8?B?TjZ0S0h3VlBiU1paanJIbGpiaEVDVWhZdEFsUnZkNTY1a1VaL3FKTkZtUkI4?= =?utf-8?B?cHBscnVuTnBXU0Y4UTE4ajZWZ2ZzY2hKQVYzSlFvTEQrNHcyNG5ZSFVSTUdL?= =?utf-8?B?N1AwV0FLS0ErVDV3dnNzRmZFMkRVTEFoQjZQS3NBbUJ3Nkpwb3lqem1KTE1o?= =?utf-8?B?L1dlaWFGLzZpWTU0OThzYU11S0dOTE5HREJkUVVkekNqeTh5OWs0K2prYUdC?= =?utf-8?B?V1l0ZXVQdk9MYVZ1UmE4bkVIYy9XVWRWNGZOdXgrbUFhaE5VSUhSM3cwRUZk?= =?utf-8?B?T3FSR3Ryc1Y1RiszYzdGVFJPeUYzc2IvNEljOWVkcFJZWlY4NFZmeTZNa0hs?= =?utf-8?B?UDkxc1ArVUxTWW0xRDRUWmJOVXBBS09Kc2ErWUh5YUU1eDhJaStvODJWMS9S?= =?utf-8?B?UEU5ZVRGOG9QV21QYmJhd21hbS9PSGxta2pOOW10UjRjR1IzZHNCTlgvQWR6?= =?utf-8?B?VWEvdHEraTdlNFVueVpHRWQxSzJaVkY3QXdzU0pBQkVaUzQ3bVpkZz09?= X-OriginatorOrg: garyguo.net X-MS-Exchange-CrossTenant-Network-Message-Id: 6f5da472-047e-47e3-f61d-08df0a8e926e X-MS-Exchange-CrossTenant-AuthSource: LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Sep 2026 14:12:36.9063 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: bbc898ad-b10f-4e10-8552-d9377b823d45 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 045afwZ9x6+frZJGT34nalboTB7UCXraheGfsfaQfAWDhqF0QMAZDSYOeD0BY/mYk0GCawLrGg6eot+44pe5xQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP265MB6690 Create a new `util.rs` to host utility code that are generic and can be shared by multiple macros. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/lib.rs | 1 + rust/pin-init/internal/src/pin_data.rs | 17 +++++------------ rust/pin-init/internal/src/util.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src= /lib.rs index 60d5093f3128..4d8ff86484b6 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -18,6 +18,7 @@ mod init; mod pin_data; mod pinned_drop; +mod util; mod zeroable; =20 #[proc_macro_attribute] diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/interna= l/src/pin_data.rs index ff194d27565e..074bc6b3091a 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -10,7 +10,10 @@ Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Vis= ibility, WhereClause, }; =20 -use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; +use crate::{ + diagnostics::{DiagCtxt, ErrorGuaranteed}, + util::*, +}; =20 pub(crate) mod kw { syn::custom_keyword!(PinnedDrop); @@ -81,21 +84,11 @@ pub(crate) fn pin_data( // // We need to perform this after parsing so we can reliably detect fie= ld cfgs. for (field_idx, field) in struct_.fields.iter_mut().enumerate() { - let cfg: Vec<_> =3D field - .attrs - .iter() - .filter(|a| a.path().is_ident("cfg")) - .map(|a| { - a.parse_args::() - .expect("parse as token stream cannot fail") - }) - .collect(); - + let cfg =3D field.attrs.extract_cfg_attrs(); if cfg.is_empty() { continue; } =20 - field.attrs.retain(|a| !a.path().is_ident("cfg")); let cfg_true_struct =3D quote!(#struct_); =20 let punctuated =3D match &mut struct_.fields { diff --git a/rust/pin-init/internal/src/util.rs b/rust/pin-init/internal/sr= c/util.rs new file mode 100644 index 000000000000..ed18ab7d45e6 --- /dev/null +++ b/rust/pin-init/internal/src/util.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use proc_macro2::TokenStream; +use syn::Attribute; + +pub(crate) trait AttrListExt { + fn extract_cfg_attrs(&mut self) -> Vec; +} + +impl AttrListExt for Vec { + fn extract_cfg_attrs(&mut self) -> Vec { + let cfg: Vec<_> =3D self + .iter() + .filter(|a| a.path().is_ident("cfg")) + .map(|a| { + a.parse_args::() + .expect("parse as token stream cannot fail") + }) + .collect(); + + if !cfg.is_empty() { + self.retain(|a| !a.path().is_ident("cfg")); + } + + cfg + } +} --=20 2.54.0 From nobody Sun Sep 27 05:28:31 2026 Received: from CWXP265CU008.outbound.protection.outlook.com (mail-ukwestazon11020101.outbound.protection.outlook.com [52.101.195.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5E9504AA41B; Fri, 4 Sep 2026 14:12:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.195.101 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531167; cv=fail; b=UQojp6c6uTHLthTjwfv7P4S09kNkXQpwUe7IZgAJc5/0IuS72yWMSKytJs7akq6anFXkHwysYYWj7XILpx4KVsp9rXKhAkw+5FcqOnnyf8aW0RGo8pgvLPCUWxUFUINA+59gmG2u7jKtq75dTymQpAHcMNXkrxG9A6Sh9P+L2GA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531167; c=relaxed/simple; bh=0/6ZQKHQs9GqOQ5aPinMsD6dxy14kMOQl/17CjeeVr4=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=IsNLDPTxOlw4/akMwoJT1FpghKQQNvG5ybvFcxguxtauIahiEvKZJI7cP5RR9dHt95Zd1aKsGGqEpSM5CZ3DWd4LC4pjbI0VoaQ3eUuYdEQws5uUb+kO3qQw/mh1sgJvrKO/KzOzLfuF4nKFdoy2lkgpMb12Ulk7XlvVtqmMdSE= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net; spf=pass smtp.mailfrom=garyguo.net; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b=iqI8LKtY; arc=fail smtp.client-ip=52.101.195.101 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=garyguo.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b="iqI8LKtY" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=sBVyH1RyIyEFG5g+MZFWLMaatHVdam0n6HrlHb5YLlpnX/Vw/8Nu4+IXfiuSN0HigQ3iiVtDwbsGcTfkmWmZUNqNA3fwvJKbv+/enuyQktsdI4GypA8sqGMKaEpRf4MvEhqoOR8oqIuGyD/zhOumrUnN6SwMLN7vEspNLZFc/Q6mRPKQTCZqLMmvYIQWnmtXvPdNWcImCqLGVhP8NBLukOZqjiDNULAiWwh2xgmTy5/deJqC/6zLU6BdQKEMwMbzQMP1CboxCeiTpZNcMTa0AHZUXoxgH4JsrmMvB2nJlkI1C6CSAqJj27PprPnhhNkWuS7nRiebXGiEPsVWvEJMfQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=MxcYGtn3GQa+3v3Cnvogr0xErRrUz0uD3139vMcqGb0=; b=TsnkBcvzqThDJP+VIzHL4OiCMNrSGnI+IeFr8HGvPm5zhiCV3Lt5AcAOwGPAlYmQsj9ECjl4j1xYRlc2iDCPlslOt7Y7uiLS4nHT30OBuYmFXrEnRbyNqYUCasMk8rMlclyJgS0+HUDFaDaFffv3ODmj3jrYfqJjgNNO+WHR+LV4g6Iyjfh1ykjsYjdNyONqYAJB3Q+n2dvck7+bXjg8Ey/Uh848lpzPxQx/bvKvRw1a7OVwpIjCaNO+xzUV8lQLpdvvkPPeddKYjwKVGvhOyBUXV75wX40P0NdQlzVi+vB7iNL79e6go47sO+FqVjORAIG85KD2TxrXYEgc6BjrTQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=garyguo.net; dmarc=pass action=none header.from=garyguo.net; dkim=pass header.d=garyguo.net; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=garyguo.net; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=MxcYGtn3GQa+3v3Cnvogr0xErRrUz0uD3139vMcqGb0=; b=iqI8LKtY7IAQz0GjCv45Hwetx+Tl3+tEEOMEmCtRUvLkqM0TxCgYSBHky28qtaLDPOjuLBZR1cEtsEieihue5vy98hDESw4WDCHIJBKgnzAbw7aaxgL8Nk3vIUs2FLqw9fyBIQ/iq2Uy5a7JBCiEdzW39VFVgnP1Mdne7qg6paM= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=garyguo.net; Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) by CWLP265MB6690.GBRP265.PROD.OUTLOOK.COM (2603:10a6:400:1ec::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Fri, 4 Sep 2026 14:12:37 +0000 Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1]) by LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1%4]) with mapi id 15.21.0360.008; Fri, 4 Sep 2026 14:12:37 +0000 From: Gary Guo Date: Fri, 04 Sep 2026 15:12:30 +0100 Subject: [PATCH 2/4] rust: pin-init: internal: pin_data: support tuple struct projections Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260904-tuple-struct-v1-2-72c50bd037fd@garyguo.net> References: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> In-Reply-To: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> To: Benno Lossin , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Gary Guo , Mohamad Alsadhan X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788531156; l=13236; i=gary@garyguo.net; s=20221204; h=from:subject:message-id; bh=53EC9ELB0uhcu9+blKC+zESsQ9pYPaki2Z2Lyl9cmnc=; b=biBrjWv2qAL0NWDtK8Ee/IO7o87wupqGTnEFrBS6fgQojMc6Ia88kFHrK7ka7yA5q5J/FKLCB oHc9ebLQzZ9CKDGUBadCUYoFSRVFVecA2rCWX7QTDrrRitfX65LYpL/ X-Developer-Key: i=gary@garyguo.net; a=ed25519; pk=vB3uIX95SM4eVrIqo1DWNWKDKD2xzB+yLLLr0yOPYMo= X-ClientProxiedBy: LO4P123CA0690.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:37b::7) To LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: LOAP265MB8560:EE_|CWLP265MB6690:EE_ X-MS-Office365-Filtering-Correlation-Id: ee7018e7-03e5-405f-296d-08df0a8e92b4 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|23010399003|10070799003|7416014|376014|10067099003|6133799003|3023799007|18002099003|56012099006|5023799004|22082099003|921020; X-Microsoft-Antispam-Message-Info: rZzoS+nSQd49tZfM2IZzQd0XSqOSJG5TmckNdS9T9s25eN7JFyUR4kECPNu58tulHZZB0LEV5cB5qvD6XS3encMJte5+XLJ/feRUr569bgBmkSzU8iSla7OMM+AAM6VW7lpx6nEnV8sIweaPBy4Zyk9v3ofIHV8DDlKW/6BLWD9bqbeDRl4nhR3CFhTAr4pP6sG6iSXlqhPTVBUSG16pu7BxJn0gjflr3XndTwmrRx805dwitFfC1OZiXKbd0BSX9a/lwVyJJ/Y4mryYs5Txsww/zCTSBEqywY+p3EGRnwLTC1t6LZ0gkDWnSzxBhTFK0LE1Zc9qqZ9hLpWEXU6TzGw6+r/PlxPt39HhSdPun0tXFlBIRu/TKzAK+boC77iZebSdVm7koiB6Eim/16/1dL2MH8LXoFA7y3mkB/XJq0Hc6WHMqEhzDeVShGo/xrvPjY9L50YlvNfViO6rkRk6yEzNdu5vGOU4ZeOTgtHxEuu/m0GmX2KuBaEuEil5d7hM7w1tVSdui5RpxiWofschtLX3Em0aS/rSDq2aZdmr7tLwArxacEX75Xpv+NAYROKK/AOdRXYbvFb21mFzJ4kaj6uecY0v05ii7SV2YqtYuwCxJ9bCp68EQAwvQv7UOys0FvLMAyChO+Uy1Bs4jdii/TxXQNEy4QAmim1cbGmc1u0jWb9KehwCoKM44P7OIZRA0OYOdHmT1PvhgE1D1Cbb9w== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(23010399003)(10070799003)(7416014)(376014)(10067099003)(6133799003)(3023799007)(18002099003)(56012099006)(5023799004)(22082099003)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?Qmd2VkN5aHhVY2V6dlBoZE1TSlcxNWNBd1Iycndhc1E1Sng3V1l6eUNPeVhJ?= =?utf-8?B?ZVpVUk8raktNejNIb0VzTmdOYVJ2TkF1bzBDU0ZqbVRlR2s4cVR6UGRtVkdk?= =?utf-8?B?ZnNoYkV6WVE5ZzR1QXByL1FPV3ZwNlpERW50VGR4MHFzdDhrSjZQUzNqQU5I?= =?utf-8?B?OXIvSkVDUmloSDBaUjRBbzl5MytrM1ZGcGZxbWs2VXRYYjRtVXFEbm5vU2pq?= =?utf-8?B?WjF6TUF3K1lSTUIzV0Zhb0hGUGszZ2VsOTIrU0JVeEFxYzlEVFpGMWFMVWhY?= =?utf-8?B?UkJ3QmtEYW10Q1dXeXdndld3ZllBRGFJaEdjZW5pSWxvcXE3bFpUM0NKampH?= =?utf-8?B?YlQxSHpaY1RYWkhrWnRoTUJJQ1BwUGoxc3daQm1CZ3dmVHVuQUd2Z3JPSE9L?= =?utf-8?B?Zk1zcFozVmF3dnc4eVZMZ3ZNSW83VHc0MUtiS0JzbWFWVW1nckI5ZHZZTnN5?= =?utf-8?B?emJPbXdqWWs0eHhoR3UvczZIZFNHZTc2QWR0YWF3aHFPV1FpTGpnYzlmZVFa?= =?utf-8?B?ejFCdFBsazVvMU5LTFJLNXJCYTZCT0ZodUJXenl1eGlKMzNxb3JvV0swanNS?= =?utf-8?B?em1EK0I0MFVzRG1LYjN0bXZ4R0N2TFpMOE9hOEhSWmZhSkR1a1FlNkovNHlG?= =?utf-8?B?ZkF1VUhzbitrbnMrdGRSVDhWYStkZWJPMkUvdUVGQ0U2UjFhbllNMXlLY3lX?= =?utf-8?B?ZUUrSlBDN05WNFkxcC9KQ3BWeVZTWDhoNEdLT0MzUnAzRkkxWWRwNG9rUmFk?= =?utf-8?B?dzFhOVZzL0NuQytqZVpURmlWU243QThJUzhkT2kwMStaRnIrK25VaS9lYmY0?= =?utf-8?B?d29Qc0pKLzZoWEp1dkE2bmIwWGhzSFJ4NnBPQWFoS1lCaXp5aFlxTDZCT0hI?= =?utf-8?B?K2dyZzlNR041TngybC9FNG55YnFpK01QRmk3ZjI5K0lEcXBlbDg1aTRxSG1X?= =?utf-8?B?ZjRQQmprZURrV013OW91VkJTWFFTbUhyazF6YnRHenRyeU9yMFFqTjV5WERk?= =?utf-8?B?cXZQVjgwQzlUTVZ0Z3ZBUllXREIyeHcvQk1SNEtqaVI2K05ZS1lDdUNFOVpy?= =?utf-8?B?TndkTzNqNDA0S2lLVmFsa3BEY1FncG1jSytYUmpyMW5CVmZWcWNoQkQzdytB?= =?utf-8?B?czBFcW8yODdIRmNJUzZ4RTdRWmpiaGV3K2J5Z29pUTlZYjRYMU9Ea3hoM0Zr?= =?utf-8?B?Nk5kTlk4Q2FRWlNPQmxJTjltR1ZkeHMvdmpqaGtqQ0hxU0RTVGNSQkRyK1dw?= =?utf-8?B?TXNlVys5VXl2clZLeUR3dklXMld0N2VKVE5FdkxJaEI3TUpnakZOcEwwY3B0?= =?utf-8?B?OFNmS2lRRVEwOGQvNkpXb0tWMzE2RXBzQTM3SE1LQUJyaDU1T1JKN3k0MVB3?= =?utf-8?B?U3Y4dkNyRkxLWllSeVBWK0dTaUdGeUpHaUNvaklVd2RzNlBHZkdCMlNaUE5W?= =?utf-8?B?RWwrbXBDY0lOY0xZdGNWbEY3S1pmazZLdWlBTjkrcnBwNTNSNnlBMy9tNmRi?= =?utf-8?B?OFViNzUzMzZRZGRWUHZIZUZoWHZiZDNpQkQwbTRPdkxjMFF5REpFWFpROGt5?= =?utf-8?B?TkQwc1lPVU81U2VrdHBrbldJcHdFLzJtRFdGMER1TzRaRzBaeno3U3NRa21q?= =?utf-8?B?Vk9DV3l5S3lxTEFJNlBVNThWdDErVmYwbVk2S25JR3BoMDFIOCtOWGx6YWZh?= =?utf-8?B?UWs3dTBOOUNaSlBmOEF0OE9aQ2x3NXQwRzYrT0lNLzhyUkdaeU1YcTRMSVNz?= =?utf-8?B?TlFwMVBFSml3OE8zby9YaUIzNDJUbHEyS3hoQWJxVUFXdW0zRUxHVStCWjNp?= =?utf-8?B?UTRsUjF0YkFNUmpHYVRnKy9lM0laQjNKczlUOGtyaW45NVYrb295K1JSTktH?= =?utf-8?B?ODMwMXlmT0kzOFo1bnhSVUswWEJod0VzRFhnUkU5VzZUUis3WDE1M0xPY1c4?= =?utf-8?B?emVvYTVZTXNIOGxHbFp6aVMrbXVLd1ZKdlg3RzBrS0hJWUFwK1h2VHJJZzU3?= =?utf-8?B?dndxRHpRTlR1NDIyWHpvMGU3OWNNZStvSXcvZmkwcFRWOEhsZThncUZzTDRQ?= =?utf-8?B?QjRnTWh6eEpRb0dTK1EzK1c1dk1pQlJEWHBad2phWkkwVFVnWVVyZ0RYVngr?= =?utf-8?B?UlVnY1lFeEZlWDh3T1FQZStPN0RreEpLcmNpaXhiRmR1QmthZ2RSYkx5VVhu?= =?utf-8?B?aEpBWlZqQ0VrcEVnZ3U4S1N5czRrMlFabmN3cTBoblVqZjZXODh5cHd5UzhS?= =?utf-8?B?UmN3VEg0ZWNaeGNtRmxETjRNVVNQU2Y2VTJOdmo4Qk1OQ3BvNmpkaFdoZXk1?= =?utf-8?B?Y1VDT2c2dVZvRERQQ3QwUHNRUGFoSzJibExvTTJnTzNUZ2FZM0hIUT09?= X-OriginatorOrg: garyguo.net X-MS-Exchange-CrossTenant-Network-Message-Id: ee7018e7-03e5-405f-296d-08df0a8e92b4 X-MS-Exchange-CrossTenant-AuthSource: LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Sep 2026 14:12:37.3539 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: bbc898ad-b10f-4e10-8552-d9377b823d45 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: I6derTBu5Ge2daLgCicz/utMN22JrSjMLY9ncXB6StjsC4oYFpgtGYjNfl7j+f94PWYKsNAdj0hIq0HP8t1y1A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP265MB6690 From: Mohamad Alsadhan `#[pin_data]` rejects tuple structs because it assumes every field has a name, which it uses for the projection field, the `__Unpin` field and the pin-data accessor. Identify fields by `syn::Member` instead, so that tuple fields are referred to by their index in generated field accesses. The names that generated items still need are derived from the index as `_0`, `_1`, etc. The projection of a tuple struct is a tuple struct itself, so projected fields are accessed with the same `.0`, `.1` syntax as on the input type rather than through synthesised names. Signed-off-by: Mohamad Alsadhan [ Moved utility code to util.rs as extension trait - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 112 +++++++++++++++++++++++------= ---- rust/pin-init/internal/src/util.rs | 31 ++++++++- rust/pin-init/src/lib.rs | 23 +++++++ 3 files changed, 130 insertions(+), 36 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/interna= l/src/pin_data.rs index 074bc6b3091a..8cd9bf139567 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -7,7 +7,8 @@ parse_quote, parse_quote_spanned, spanned::Spanned, visit_mut::VisitMut, - Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Vis= ibility, WhereClause, + Field, Fields, Generics, Ident, Index, Item, Member, PathSegment, Type= , TypePath, Visibility, + WhereClause, }; =20 use crate::{ @@ -49,6 +50,7 @@ fn to_tokens(&self, tokens: &mut TokenStream) { =20 struct FieldInfo<'a> { field: &'a Field, + member: Member, pinned: bool, } =20 @@ -129,10 +131,12 @@ pub(crate) fn pin_data( replacer.visit_generics_mut(&mut struct_.generics); replacer.visit_fields_mut(&mut struct_.fields); =20 + let is_tuple_struct =3D matches!(struct_.fields, Fields::Unnamed(_)); let fields: Vec> =3D struct_ .fields .iter_mut() - .map(|field| { + .enumerate() + .map(|(index, field)| { let len =3D field.attrs.len(); field.attrs.retain(|a| !a.path().is_ident("pin")); let pinned_count =3D len - field.attrs.len(); @@ -144,23 +148,30 @@ pub(crate) fn pin_data( !field.attrs.iter().any(|a| a.path().is_ident("cfg")), "cfgs should be all resolved at this point" ); + let member =3D match &field.ident { + Some(ident) =3D> Member::Named(ident.clone()), + None =3D> Member::Unnamed(Index { + index: index as u32, + span: field.span(), + }), + }; =20 FieldInfo { field: &*field, + member, pinned: pinned_count !=3D 0, } }) .collect(); =20 for field in &fields { - let ident =3D field.field.ident.as_ref().unwrap(); - if !field.pinned && is_phantom_pinned(&field.field.ty) { dcx.warn( field.field, format!( - "The field `{ident}` of type `PhantomPinned` only has = an effect \ + "The field {} of type `PhantomPinned` only has an effe= ct \ if it has the `#[pin]` attribute", + field.member.display_name(), ), ); } @@ -168,8 +179,13 @@ pub(crate) fn pin_data( =20 let unpin_impl =3D generate_unpin_impl(&struct_.ident, &struct_.generi= cs, &fields); let drop_impl =3D generate_drop_impl(&struct_.ident, &struct_.generics= , args); - let projections =3D - generate_projections(&struct_.vis, &struct_.ident, &struct_.generi= cs, &fields); + let projections =3D generate_projections( + &struct_.vis, + &struct_.ident, + &struct_.generics, + is_tuple_struct, + &fields, + ); let the_pin_data =3D generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.gener= ics, &fields); =20 @@ -231,7 +247,7 @@ fn generate_unpin_impl( unreachable!() }; let pinned_fields =3D fields.iter().filter(|f| f.pinned).map(|f| { - let ident =3D f.field.ident.as_ref().unwrap(); + let ident =3D f.member.as_ident(); let ty =3D &f.field.ty; quote!( #ident: #ty @@ -313,6 +329,7 @@ fn generate_projections( vis: &Visibility, ident: &Ident, generics: &Generics, + is_tuple_struct: bool, fields: &[FieldInfo<'_>], ) -> TokenStream { let (impl_generics, ty_generics, _) =3D generics.split_for_impl(); @@ -325,28 +342,32 @@ fn generate_projections( let (fields_decl, fields_proj): (Vec<_>, Vec<_>) =3D fields .iter() .map(|field| { - let Field { vis, ident, ty, .. } =3D &field.field; + let Field { vis, ty, .. } =3D &field.field; + let member =3D &field.member; + // The projection of a tuple struct is a tuple struct itself, = so its fields are + // positional and must not be named. + let name =3D (!is_tuple_struct).then(|| { + let ident =3D field.member.as_ident(); + quote!(#ident:) + }); =20 - let ident =3D ident - .as_ref() - .expect("only structs with named fields are supported"); if field.pinned { ( quote!( - #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, + #vis #name ::core::pin::Pin<&'__pin mut #ty>, ), quote!( // SAFETY: this field is structurally pinned. - #ident: unsafe { ::core::pin::Pin::new_unchecked(&= mut #this.#ident) }, + #name unsafe { ::core::pin::Pin::new_unchecked(&mu= t #this.#member) }, ), ) } else { ( quote!( - #vis #ident: &'__pin mut #ty, + #vis #name &'__pin mut #ty, ), quote!( - #ident: &mut #this.#ident, + #name &mut #this.#member, ), ) } @@ -355,24 +376,52 @@ fn generate_projections( let structurally_pinned_fields_docs =3D fields .iter() .filter(|f| f.pinned) - .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); + .map(|f| format!(" - {}", f.member.display_name())); let not_structurally_pinned_fields_docs =3D fields .iter() .filter(|f| !f.pinned) - .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); + .map(|f| format!(" - {}", f.member.display_name())); let docs =3D format!(" Pin-projections of [`{ident}`]"); + let (projection_def, projection_init) =3D if is_tuple_struct { + ( + quote! { + #vis struct #projection #generics_with_pin_lt ( + #(#fields_decl)* + ::core::marker::PhantomData<&'__pin mut ()>, + ) #whr; + }, + quote! { + #projection( + #(#fields_proj)* + ::core::marker::PhantomData, + ) + }, + ) + } else { + ( + quote! { + #vis struct #projection #generics_with_pin_lt + #whr + { + #(#fields_decl)* + ___pin_phantom_data: ::core::marker::PhantomData<&'__p= in mut ()>, + } + }, + quote! { + #projection { + #(#fields_proj)* + ___pin_phantom_data: ::core::marker::PhantomData, + } + }, + ) + }; quote! { #[doc =3D #docs] // Allow `non_snake_case` since the same warning will be emitted on // the struct definition. #[allow(dead_code, non_snake_case)] #[doc(hidden)] - #vis struct #projection #generics_with_pin_lt - #whr - { - #(#fields_decl)* - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut (= )>, - } + #projection_def =20 impl #impl_generics #ident #ty_generics #whr @@ -390,10 +439,7 @@ impl #impl_generics #ident #ty_generics ) -> #projection #ty_generics_with_pin_lt { // SAFETY: we only give access to `&mut` for fields not st= ructurally pinned. let #this =3D unsafe { ::core::pin::Pin::get_unchecked_mut= (self) }; - #projection { - #(#fields_proj)* - ___pin_phantom_data: ::core::marker::PhantomData, - } + #projection_init } } } @@ -414,11 +460,9 @@ fn generate_the_pin_data( let field_accessors =3D fields .iter() .map(|f| { - let Field { vis, ident, ty, .. } =3D f.field; - - let field_name =3D ident - .as_ref() - .expect("only structs with named fields are supported"); + let Field { vis, ty, .. } =3D f.field; + let field_name =3D f.member.as_ident(); + let member =3D &f.member; let pin_marker =3D if f.pinned { quote!(Pinned) } else { @@ -443,7 +487,7 @@ fn generate_the_pin_data( // - If `#pin_marker` is `Pinned`, the corresponding f= ield is structurally // pinned. // - Other safety requirements follows the safety requ= irement. - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*= slot).#field_name) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*= slot).#member) } } } }) diff --git a/rust/pin-init/internal/src/util.rs b/rust/pin-init/internal/sr= c/util.rs index ed18ab7d45e6..ed2c78f0658f 100644 --- a/rust/pin-init/internal/src/util.rs +++ b/rust/pin-init/internal/src/util.rs @@ -1,7 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT =20 -use proc_macro2::TokenStream; -use syn::Attribute; +use proc_macro2::{Ident, TokenStream}; +use quote::format_ident; +use syn::{Attribute, Index, Member}; =20 pub(crate) trait AttrListExt { fn extract_cfg_attrs(&mut self) -> Vec; @@ -25,3 +26,29 @@ fn extract_cfg_attrs(&mut self) -> Vec { cfg } } + +pub(crate) trait MemberExt { + /// Returns an identifier for the member. + /// + /// Tuple fields have no name of their own, so they are named `_0`, `_= 1`, ... instead. + fn as_ident(&self) -> Ident; + + /// Obtain a display name for the member in diagnostics. + fn display_name(&self) -> String; +} + +impl MemberExt for Member { + fn as_ident(&self) -> Ident { + match self { + Member::Named(ident) =3D> ident.clone(), + Member::Unnamed(Index { index, .. }) =3D> format_ident!("_{ind= ex}"), + } + } + + fn display_name(&self) -> String { + match self { + Member::Named(ident) =3D> format!("`{ident}`"), + Member::Unnamed(Index { index, .. }) =3D> format!("index `{ind= ex}`"), + } + } +} diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 7600cdbbbf98..bf77b76c43c8 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -304,6 +304,9 @@ /// This macro enables the use of the [`pin_init!`] macro. When pin-initia= lizing a `struct`, /// then `#[pin]` directs the type of initializer that is required. /// +/// Tuple structs are supported as well. Their fields have no names, so th= e generated projection +/// is a tuple struct too and its fields are accessed by index. +/// /// If your `struct` implements `Drop`, then you need to add `PinnedDrop` = as arguments to this /// macro, and change your `Drop` implementation to `PinnedDrop` annotated= with /// `#[`[`macro@pinned_drop`]`]`, since dropping pinned values requires ex= tra care. @@ -327,6 +330,26 @@ /// } /// ``` /// +/// The same as a tuple struct, projected by index: +/// +/// ``` +/// # #![feature(allocator_api)] +/// # #[path =3D "../examples/mutex.rs"] mod mutex; use mutex::*; +/// use core::pin::Pin; +/// use pin_init::pin_data; +/// +/// enum Command { +/// /* ... */ +/// } +/// +/// #[pin_data] +/// struct DriverData(#[pin] CMutex>, Box<[u8; 1024 * 1024]>); +/// +/// fn queue(data: Pin<&mut DriverData>) -> Pin<&mut CMutex>>= { +/// data.project().0 +/// } +/// ``` +/// /// ``` /// # #![feature(allocator_api)] /// # #[path =3D "../examples/mutex.rs"] mod mutex; use mutex::*; --=20 2.54.0 From nobody Sun Sep 27 05:28:31 2026 Received: from CWXP265CU008.outbound.protection.outlook.com (mail-ukwestazon11020101.outbound.protection.outlook.com [52.101.195.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AAA514AA006; Fri, 4 Sep 2026 14:12:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.195.101 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531165; cv=fail; b=Aud+9uWRZmWP6N7iqzQudGARYy2bwxWmycytiA6mWeJfyZQmnKsuHj1dxkPXpy6VCXYN3UD+cPJe/bBW848OqDveqbCkwdhQO1m5c6UNlHvuFY1hQ3HV7JYU72zUCdBYcTQ+F/upsaSIh51w2ebjw2KUlmKMfFYJvyxbevT86tA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531165; c=relaxed/simple; bh=djkkMmraWl4WThEKEaE91m6SpYS6HRLs7EldNtzvEOc=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=hquG/s+9uhWy8M3R71UzH4vLTn5qxEwY5aiRoOuJ28zgfFDMW2PAEBVTrYhLYtS3PGhqxYLvi5nWGh1SgbFz7y2/4cD1Xa6OQr0ITfXd/OPr4k1SoyrQO9AANhgsUikjp7Lxxra5j0Poacr3nV6nPmJ2qM7sxf6yQKwv175qp6E= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net; spf=pass smtp.mailfrom=garyguo.net; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b=DRywfOCu; arc=fail smtp.client-ip=52.101.195.101 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=garyguo.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b="DRywfOCu" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=MjVGO69xDYfOlvPIWXDtQjvF72mA+6pM/IDvUUbtkyEOnpTdXQJIEl5HfNplM1QulMaPbdoOaI0lgk18BPvaJk2TpDUHeyQpEzbSBbmRTj1MFgQYR0y9OzK3I2TF4f2MOtsj/DWRmxar5fGhZyF6Fk6flcSS/oEyvZEKtBoe5n/2VgSHHkAmT651hB+tMICVaAVDvmlEY06YghNPJ4DFOGZlKYRT7ofzxLNYBDV5ZyS5l94dnFs67esB2WEUPVeRey6juCv6Lbsvw04xogiOwqQv8MD9mohAsAdkIaH7HC9iKt9cxzvyQ6d+ijXR5FmVnW51E1vdm/qENrYUU5jqtA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=0vHiSxYq+BCBFy6DR4TMZGRcSBhwOZcWcO8GHEwqbzs=; b=d3gkmHGkRAVuN1ovxKvcBprfd7aM+JpNcp1Z2bt8s0zpiepTz+vtLJGL9905+fK+tr3meEZcpyEut5NQXq3dAfWfVDzLf3I5w7xhvC9GB8Ml8pQz2vAMjq78EsmlDXWcEb63ByA3mGNCBKAc38blzB6rl12umSMV6UWKVm8dDaqLaaP8Fue1m/x8wN9anAbDjp5fuY2HiDjzqI1+BEG3f1Fl2j4PyfJIAROjSs+dMuK86Vjl8MZ29sjU4iW8OnCctIRmPqAsg1gHGdcB19P3MFKJhmNXSWnK4YEjXDWSBUPqoO6Zb0uu92ZjsY8qMYSf0lhX8oUzx9eQAhwp1Muh9w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=garyguo.net; dmarc=pass action=none header.from=garyguo.net; dkim=pass header.d=garyguo.net; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=garyguo.net; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=0vHiSxYq+BCBFy6DR4TMZGRcSBhwOZcWcO8GHEwqbzs=; b=DRywfOCuL8Rpo/NryQo/knminOmcPk5HLDk3qZiXlx1xiQxkvQJPyX3r42QNrp5W7xr5WXORnJxo6E/9goHOQa5Y6xP6nCHajwZ5dv/CiTl5GsHX9iYNSu9VwHtvzCIWcAj/r2UD7WZBjK8a2BKDQlpUrEW3f9BhdF6kTmTwv5c= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=garyguo.net; Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) by CWLP265MB6690.GBRP265.PROD.OUTLOOK.COM (2603:10a6:400:1ec::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Fri, 4 Sep 2026 14:12:37 +0000 Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1]) by LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1%4]) with mapi id 15.21.0360.008; Fri, 4 Sep 2026 14:12:37 +0000 From: Gary Guo Date: Fri, 04 Sep 2026 15:12:31 +0100 Subject: [PATCH 3/4] rust: pin-init: internal: init: support tuple structs in `[pin_]init!` Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260904-tuple-struct-v1-3-72c50bd037fd@garyguo.net> References: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> In-Reply-To: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> To: Benno Lossin , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Gary Guo , Mohamad Alsadhan X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788531156; l=12396; i=gary@garyguo.net; s=20221204; h=from:subject:message-id; bh=LyHwjjF7Z5aEqauFHUSqo3KF4drjz/2/gytD/pO8hzU=; b=p7na7fxZtXzOmdZ7D+uGiNnhYqkxKtMNUI6+j1lxL7mC16puacJA+XvyGLeTRgteRh/0n/MNj 02TO0x0LIAhCR3daUCeW7IG2jFjo2O1EQr270oB166U/cQeuWHpDW8L X-Developer-Key: i=gary@garyguo.net; a=ed25519; pk=vB3uIX95SM4eVrIqo1DWNWKDKD2xzB+yLLLr0yOPYMo= X-ClientProxiedBy: LO4P123CA0690.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:37b::7) To LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: LOAP265MB8560:EE_|CWLP265MB6690:EE_ X-MS-Office365-Filtering-Correlation-Id: 39cc9fcf-3755-4f31-2f27-08df0a8e92fa X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|23010399003|10070799003|7416014|376014|10067099003|6133799003|18002099003|56012099006|22082099003|921020; X-Microsoft-Antispam-Message-Info: MQzAmzr3eau3hPkWWK6NDYvmIf06oCsYS2VGMrPBIkrVPFNZ6KLbk/6WZd2+W3b8k5+xnpqjp6iaJ9c/WftBWjqskVCHKGUO3CmBuvJvoG2kR0c8/EsQKEOXZF2fPCTHssshvk06903Q+/5C7S1THR73RmuRGC9w4gkciTMlwBXREbPtuftm67WuV8V+X37/z8eF8GQ8BsJ9B4BFbHRPXMHLUiH+tgTyS53rXZkplwB6ZwaQerTvdFa26C8jGBRp1a9NgCUHZUEnBTXtULpMHYlhv9QLtNWKSkWR2BMPrfMRBm8GWtZ9KxyDPlFDF4g5/V1CpfeD/cCDeBqevH4PQRqVdFuDEgyzNCS56UI/eY/Q65UQpbHtNb4tDVWKN8dPDzpxx7RicXLAbhPCi7wKdkkODs5bAeRz0SvvJ0iZu4AhB/y89OaTiDh2ngTLXNSzbbGwLlwUSfYeGL5ieQ03pdmj8ZfQ5edNIx4lszHKdl6e4Zijjr62ee7xzOhEBKVt3SB9wb38t8NI2XbPuIYpBVgvxY8PywncnKweoJKhMoCd32eEMBdXljMZRZv4qppWucMGZJkQIa+EwW0+zthGTrUxTdo4Dd4mrInXBWcldsp/IGmO6Vnc8/laNrTczTDDaoxfTvLJF9icPgQ/nOwtNRaKo94t+fCz0S4+aueFVvelV5RId+JpBMyGYYDe04xUt/69WDLxwhQjGwEB/EZfuw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(23010399003)(10070799003)(7416014)(376014)(10067099003)(6133799003)(18002099003)(56012099006)(22082099003)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?VVFoM1hsVUFSTDVKOWY3bFZndzcvTFlkT255TW1yaTdNN3ExeUhXVDlYOVZP?= =?utf-8?B?dEVTVCs0d3M2bjN2NUdmVXUyV0tud1NQUE56Wis4ZHQrUGNDY1p5SDNzaVl2?= =?utf-8?B?SHRHSG41RUFMMHlOTjljeVhJczl0UFAxQUd6eGYxeTJsYXhBdmdMU2c2WWZl?= =?utf-8?B?c24rLzlUQVYwNFlVbW1kb2lvTXRQbS9uaDJhbEdISDhyS05KQkVKQjlDSjMx?= =?utf-8?B?V3BxV3RzYWZXL3ZJOXVoU2lzT0FndmdUU2YvR2YyQVVVQndtb01Ub3ZDL1l6?= =?utf-8?B?YjJ5SjV5SDl3OUZlUkdGVUJGeGxvQk1yZ3NTWHRQMEFBcWF3NjJ1UE0vc1lW?= =?utf-8?B?eFBUVWE0TjJPcDI3OUJNdGE1dVNpZDdmUHYyb2tFcWowUjVwR3g1UkhScUti?= =?utf-8?B?SGNaQjB1Y0IrZERjc1BPOVFNQzh1L2RKUGRremdraEN4TkF0WmRoVXdEeGQw?= =?utf-8?B?bnB2bmFGd2lDWFFkV3hvUDBYUCs0cFFTRS8wZjlYMzZYckU3UVpKNFhYR3F0?= =?utf-8?B?ZWR0ak8vRFRveHJBU2JDM0FXMmdrTWpmZk1zQlRFUFNlb1ROYkxXSDZzNnVK?= =?utf-8?B?ajQ4NXpRZzVOTVh3UkNjNnNVR0dVY2EvWUxxUG14S2F1N3k4ZUkyQXhJTkZ3?= =?utf-8?B?d092Vm9FTXJnVXg4cmxGbCsxWDBLRlZ4Y1FtUWd3aGZTRFllRXk0eERJQThW?= =?utf-8?B?dTVwV0xOUTJvWDBTd1hWWTNNclAyaG04SkIrdzdMb1pjSWJNT1FqZUpaQmRX?= =?utf-8?B?ckprblhKdTB1OUJGU3hpM2I0VzlyQ3l5amh2S2ZVVDNtYzhmRFErNWpCUUF3?= =?utf-8?B?ZXR2bG9iNFh5OWsrYWNJQXRWTlY5eVZOcVhiTWdqUlprTW53MU1Pd1RxZ1Jj?= =?utf-8?B?RzA3MFE4RVZiRXA2anFWVzVJaWRFQjR6VkJlYmxPVHVTN3ZrSjJZWHpxU2Rr?= =?utf-8?B?bDZHSjI4b1ZCMUxoTVI2Q0dLRTJPd2JXTmFQRnBadUUzSThEVWdFWWVmSElR?= =?utf-8?B?eWJhZWJRdXdEb0RFbXQxTjlrYlBoV2ZKSktpRUF6QmxTZnRVU1lYQjVnZVc0?= =?utf-8?B?cnpKVmVrdm5CTFd1R1RNOHM2T0pwaitUMDBGVXF3elFCbjdFYkFmOFJ2Umky?= =?utf-8?B?RUxoS0VxSjIrZzU5SlphWGhraFhpU2RoSnhmdjdvT1lCQUttL0VSYWk1bi95?= =?utf-8?B?ZUF3Z25meExsU0lqNlIvRFNYSkpDSzR5dERkQkVldnd6N1g3Ykt0Q1ZheUFU?= =?utf-8?B?dzlqVUlhbG5TcUxaSEFOU3U0RmdxY3c2cHprZFRXSTJvdUxkUll0ZHZjM3pG?= =?utf-8?B?UWwzaUlkRUFZZ29pZ1NaVVp1RjUrMjF5a01iNlJnN3ZOOW9waUxDQnpENFRY?= =?utf-8?B?MHVsc0Nyd3JkV3I0Y3krUGtkTXhiY3k2OHBhY2IrMm41SGU0ei81aEEzcENx?= =?utf-8?B?MEplTEwwY1RMSk1GU0QxaTZZVTQyb1RWVHlQZnBkNDdCbVd6MkthZzdxR1Ax?= =?utf-8?B?SktBQUE3SVUrNldJMm1VYVF2dHhtOC9FdkFRdmh0VVlJTy82KzRLdVM5OUpW?= =?utf-8?B?Q1Z6RVBiczJ0L25pVGRKZkxnaTAyWi80czBRb2FGYzBNQXEvdVNFN3JjeGZp?= =?utf-8?B?dThsZkYxMkVKbE8rMDVML1JxTEVZZWJrWklQTkVlREprb3lsWW5jSFdGSDlx?= =?utf-8?B?Mm5qeFgwMUJHWXBETGNkWW8yd1VxMTMwZU1BTlNUYXBSMU1sRXlHRGxoallj?= =?utf-8?B?TGExelNCZzl6UDMrV3pUMlNyWG1Ub1pUNUpmS1lvbUtnSG5qdVVEcVBsUnRW?= =?utf-8?B?akRacnVXa2tQKzVCUHhFZlVWaVFGSXZFYWpYZ0psRmprYmtDZmZzUXo3eUJN?= =?utf-8?B?UkxKaE5FTkxKT2p0eHNEN0tCZk9sMDBDbEFwcHZpTE02bVYwU2hYT1VZSEN4?= =?utf-8?B?ZSswOS9DZWVuelZST24xc3l5ZStGWCt1RkZ1b1l0dVV0ZDM5SmxvUW9BMHJI?= =?utf-8?B?RTNaR05kZnBWaWdJdGZVQTJ5WmZkSzVuUE9uVytvRnlJcnMra01BdkxZT3VI?= =?utf-8?B?Ti9YQ0MyS0tZY0dwMVV4T08zUHlicEVOaGpmc3k4aEtIZXQrckZCUVVqTDZH?= =?utf-8?B?eXpjNk5mQ2w2Qm1rZDg1QVJnMk1TYUJ3eDZ1ZnhqUHFMUkRIeThicU5Ka1Ar?= =?utf-8?B?TzlZUVc5YjJsSmNUNXdXYTZXZnJTbVNZL240N1c4aVpEMnVHMy9CdXgzNnpG?= =?utf-8?B?dzJibnRNalNUN2JteGN0aktuMHp0YVdsQllzSnM5UXRKcVpKSGpwdzFyc2to?= =?utf-8?B?VUNIS2VSQ0E3K05HOTNKRFljYTlLOWE4bG5GZGJJaTNVUFB0eXhndz09?= X-OriginatorOrg: garyguo.net X-MS-Exchange-CrossTenant-Network-Message-Id: 39cc9fcf-3755-4f31-2f27-08df0a8e92fa X-MS-Exchange-CrossTenant-AuthSource: LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Sep 2026 14:12:37.8077 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: bbc898ad-b10f-4e10-8552-d9377b823d45 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: hsUqgFfyqgkj6J+r9z0s8te+FNPkVk1WfMuQ4qxOmsk5XaP2PQKsGJ3R4AkjOEdOcRxTCTPirYOnM9YwOQbRTQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP265MB6690 From: Mohamad Alsadhan Extend the initializer syntax so that a field can be named by an index, addressing tuple struct fields the same way a struct expression does: pin_init!(Foo { 0: value, 1 <- initializer }) Tuple fields are not exposed by a `let` binding to the fields after them, since they have no name to bind; `_0` would shadow a user variable. Signed-off-by: Mohamad Alsadhan [ Fixed incorrect index calculation and cleaned up the code - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 115 +++++++++++++++++++++------------= ---- rust/pin-init/src/lib.rs | 32 +++++++++-- 2 files changed, 95 insertions(+), 52 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/sr= c/init.rs index fd0b5ea4a0a3..5920bb281a50 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -8,10 +8,13 @@ parse_quote, punctuated::Punctuated, spanned::Spanned, - token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token,= Type, + token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, LitInt, Memb= er, Path, Token, Type, }; =20 -use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; +use crate::{ + diagnostics::{DiagCtxt, ErrorGuaranteed}, + util::*, +}; =20 pub(crate) struct Initializer { attrs: Vec, @@ -36,11 +39,11 @@ struct InitializerField { =20 enum InitializerKind { Value { - ident: Ident, + member: Member, value: Option<(Token![:], Expr)>, }, Init { - ident: Ident, + member: Member, _left_arrow_token: Token![<-], value: Expr, }, @@ -52,9 +55,9 @@ enum InitializerKind { } =20 impl InitializerKind { - fn ident(&self) -> Option<&Ident> { + fn member(&self) -> Option<&Member> { match self { - Self::Value { ident, .. } | Self::Init { ident, .. } =3D> Some= (ident), + Self::Value { member, .. } | Self::Init { member, .. } =3D> So= me(member), Self::Code { .. } =3D> None, } } @@ -229,9 +232,9 @@ fn init_fields( cfgs }; =20 - let ident =3D match kind { - InitializerKind::Value { ident, .. } =3D> ident, - InitializerKind::Init { ident, .. } =3D> ident, + let member =3D match kind { + InitializerKind::Value { member, .. } =3D> member, + InitializerKind::Init { member, .. } =3D> member, InitializerKind::Code { block, .. } =3D> { let stmt =3D &block.stmts; res.extend(quote! { @@ -243,27 +246,28 @@ fn init_fields( continue; } }; + let ident =3D member.as_ident(); =20 let slot =3D if pinned { quote! { // SAFETY: // - `slot` is valid and properly aligned. - // - `make_field_check` checks that `&raw mut (*slot).#ide= nt` is properly aligned. - // - `make_field_check` prevents `#ident` from being used = twice, therefore - // `(*slot).#ident` is exclusively accessed and has not = been initialized. + // - `make_field_check` checks that `&raw mut (*slot).#mem= ber` is properly aligned. + // - `make_field_check` prevents `#member` from being used= twice, therefore + // `(*slot).#member` is exclusively accessed and has not= been initialized. (unsafe { #data.#ident(#slot) }) } } else { quote! { // For `init!()` macro, everything is unpinned. // SAFETY: - // - `&raw mut (*slot).#ident` is valid. - // - `make_field_check` checks that `&raw mut (*slot).#ide= nt` is properly aligned. - // - `make_field_check` prevents `#ident` from being used = twice, therefore - // `(*slot).#ident` is exclusively accessed and has not = been initialized. + // - `&raw mut (*slot).#member` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#mem= ber` is properly aligned. + // - `make_field_check` prevents `#member` from being used= twice, therefore + // `(*slot).#member` is exclusively accessed and has not= been initialized. (unsafe { ::pin_init::__internal::Slot::<::pin_init::__internal:= :Unpinned, _>::new( - &raw mut (*#slot).#ident + &raw mut (*#slot).#member ) }) } @@ -273,11 +277,11 @@ fn init_fields( let guard =3D format_ident!("__{ident}_guard", span =3D Span::mixe= d_site()); =20 let init =3D match kind { - InitializerKind::Value { ident, value } =3D> { + InitializerKind::Value { value, .. } =3D> { let value =3D value .as_ref() .map(|(_, value)| quote!(#value)) - .unwrap_or_else(|| quote!(#ident)); + .unwrap_or_else(|| quote!(#member)); =20 quote! { #(#attrs)* @@ -294,14 +298,23 @@ fn init_fields( InitializerKind::Code { .. } =3D> unreachable!(), }; =20 + // A tuple field has no name that could be bound here (the `_0` id= entifiers are considered + // implementation detail and not user-facing). + let binding =3D match member { + Member::Named(ident) =3D> quote! { + #(#cfgs)* + // Allow `non_snake_case` since the same warning is going = to be reported for the + // struct field. + #[allow(unused_variables, non_snake_case)] + let #ident =3D #guard.let_binding(); + }, + Member::Unnamed(_) =3D> quote!(), + }; + res.extend(quote! { #init =20 - #(#cfgs)* - // Allow `non_snake_case` since the same warning is going to b= e reported for the struct - // field. - #[allow(unused_variables, non_snake_case)] - let #ident =3D #guard.let_binding(); + #binding }); =20 guards.push(guard); @@ -326,9 +339,9 @@ fn make_field_check( ) -> TokenStream { let field_attrs: Vec<_> =3D fields .iter() - .filter_map(|f| f.kind.ident().map(|_| &f.attrs)) + .filter_map(|f| f.kind.member().map(|_| &f.attrs)) .collect(); - let field_name: Vec<_> =3D fields.iter().filter_map(|f| f.kind.ident()= ).collect(); + let field_name: Vec<_> =3D fields.iter().filter_map(|f| f.kind.member(= )).collect(); let zeroing_trailer =3D match init_kind { InitKind::Normal =3D> None, InitKind::Zeroing =3D> Some(quote! { @@ -376,7 +389,8 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Re= sult { let lh =3D content.lookahead1(); if lh.peek(End) || lh.peek(Token![..]) { break; - } else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Toke= n![#]) { + } else if lh.peek(Ident) || lh.peek(LitInt) || lh.peek(Token![= _]) || lh.peek(Token![#]) + { fields.push_value(content.parse()?); let lh =3D content.lookahead1(); if lh.peek(End) { @@ -450,31 +464,36 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::= Result { impl Parse for InitializerKind { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let lh =3D input.lookahead1(); - if lh.peek(Token![_]) { - Ok(Self::Code { + let member =3D if lh.peek(Token![_]) { + return Ok(Self::Code { _underscore_token: input.parse()?, _colon_token: input.parse()?, block: input.parse()?, + }); + } else if lh.peek(Ident) || lh.peek(LitInt) { + input.parse::()? + } else { + return Err(lh.error()); + }; + + let lh =3D input.lookahead1(); + if lh.peek(Token![<-]) { + Ok(Self::Init { + member, + _left_arrow_token: input.parse()?, + value: input.parse()?, + }) + } else if lh.peek(Token![:]) { + Ok(Self::Value { + member, + value: Some((input.parse()?, input.parse()?)), + }) + } else if matches!(member, Member::Named(_)) && (lh.peek(Token![,]= ) || lh.peek(End)) { + // Short-hand syntax, available for named fields only. + Ok(Self::Value { + member, + value: None, }) - } else if lh.peek(Ident) { - let ident =3D input.parse()?; - let lh =3D input.lookahead1(); - if lh.peek(Token![<-]) { - Ok(Self::Init { - ident, - _left_arrow_token: input.parse()?, - value: input.parse()?, - }) - } else if lh.peek(Token![:]) { - Ok(Self::Value { - ident, - value: Some((input.parse()?, input.parse()?)), - }) - } else if lh.peek(Token![,]) || lh.peek(End) { - Ok(Self::Value { ident, value: None }) - } else { - Err(lh.error()) - } } else { Err(lh.error()) } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index bf77b76c43c8..f020b383851e 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -598,7 +598,7 @@ macro_rules! stack_try_pin_init { }; } =20 -/// Construct an in-place, fallible pinned initializer for `struct`s. +/// Construct an in-place, fallible pinned initializer for structs, includ= ing tuple structs. /// /// The error type defaults to [`Infallible`]; if you need a different one= , write `? Error` at the /// end, after the struct initializer. @@ -632,6 +632,28 @@ macro_rules! stack_try_pin_init { /// # Box::pin_init(demo()).unwrap(); /// ``` /// +/// The fields of a tuple struct are addressed by their index: +/// +/// ```rust +/// # use pin_init::*; +/// # use core::pin::Pin; +/// #[pin_data] +/// struct Pair(usize, Bar); +/// +/// #[pin_data] +/// struct Bar { +/// x: u32, +/// } +/// +/// # fn demo() -> impl PinInit { +/// let initializer =3D pin_init!(Pair { +/// 0: 42, +/// 1 <- Bar { x: 64 }, +/// }); +/// # initializer } +/// # Box::pin_init(demo()).unwrap(); +/// ``` +/// /// Arbitrary Rust expressions can be used to set the value of a variable. /// /// The fields are initialized in the order that they appear in the initia= lizer. So it is possible @@ -750,9 +772,11 @@ macro_rules! stack_try_pin_init { /// /// # Syntax /// -/// As already mentioned in the examples above, inside of `pin_init!` a `s= truct` initializer with -/// the following modifications is expected: +/// As already mentioned in the examples above, inside of `pin_init!` a st= ruct initializer with the +/// following modifications is expected: /// - Fields that you want to initialize in-place have to use `<-` instead= of `:`. +/// - Tuple struct fields are named by their index, as in `0: value` or `0= <- initializer`. They +/// are not exposed by a `let` binding, since they have no name to bind. /// - You can use `_: { /* run any user-code here */ },` anywhere where yo= u can place fields in /// order to run arbitrary code. /// - In front of the initializer you can write `&this in` to have access = to a [`NonNull`] @@ -791,7 +815,7 @@ macro_rules! stack_try_pin_init { /// [`NonNull`]: core::ptr::NonNull pub use pin_init_internal::pin_init; =20 -/// Construct an in-place, fallible initializer for `struct`s. +/// Construct an in-place, fallible initializer for structs, including tup= le structs. /// /// This macro defaults the error to [`Infallible`]; if you need a differe= nt one, write `? Error` /// at the end, after the struct initializer. --=20 2.54.0 From nobody Sun Sep 27 05:28:31 2026 Received: from CWXP265CU008.outbound.protection.outlook.com (mail-ukwestazon11020101.outbound.protection.outlook.com [52.101.195.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B413D4BAA0B; Fri, 4 Sep 2026 14:12:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.195.101 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531173; cv=fail; b=d4/1t96Sb9GLWkNNh1qGBrFvTt5vSpY/dQqxeAmPO07d9UrFtuvvQ1FU2R5SBzpmEBHt5av4xDT7DQz5AiKmudtNmJLWkURwnjKPOUt/DAvqDdDVh2GOIxgS7Op2ak9p4dpr/yhxbTzUl0aa6xJLFgecPXw2UpAJHWFkuHZtaIE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788531173; c=relaxed/simple; bh=RLK0b8eB9ZSo1/0R+Ph8XQcJ2dU5Aqv20n2lULZrxVU=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=t1BfzIgDs9lZJ91E4f3rQPK6EqqsYzn/wRph2nqsD+kFRd5nY8wMZYmCRvMIc/ydPcXXJ1S2UN/BDtMhKpnCQD8gIkUQK7vffgAcu1npF/ZqN4FV/aEsxLrzyNCxCx6elWzn4nBDVbX83ks3B3foroInVFpq/k3NyDe5wdCD3c8= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net; spf=pass smtp.mailfrom=garyguo.net; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b=n2Gdaqt7; arc=fail smtp.client-ip=52.101.195.101 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=garyguo.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=garyguo.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net header.b="n2Gdaqt7" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=PU739MBfk/stZujTEaWMtQRFfuWb07y5sILdYwgyc/mxuue5Ty3HfJ7Ed9yVKLg8MD+6agvs2ayHAT9c34xY+S/IKCs5yKM0sf/BtJiMbKf8qrcASHvaZrEVi+qJf9v8LpAlFNW/7KHWfPCeITREOImgRY+MdVKTtYgslTHdHRMpGyWprrle5q4VoNbOlJe9gdlxiLCmlIsv2z/jY6CUfsWq2WvUvBKu9mtGVSl1+0p1BeT+lO54SJ7Ah3MfQRTIgkTaZt1ZAd8D/MjcSZKxQAnYkiqxaztIDI7R8pb/SVlgtUEZFp+Z1DbZpnArAicRoIl8UH20G2z5BthIbkLhRQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=M7cGaE+NoD13JUaFexXjy2Fl/jVhHJUuwRfTIbRVki0=; b=KDjlfocMq1Z66PEsjL9DFfJge9WeKH+lomCMSsohxPMPBno5QG/shh/hZ4G12AVuVWuYSqUhsDDnsWlPs1OQQ70i4aJtb2LxBmvovljp+AiLEzkS5nA1PAMlRq4BPQ+t3MpZaV3HcqoJFMX9NPY/ZwycDqItxdVKRTQfG1/TFSF0kwDuuYFVPxRvTFsBOK6X2frKWKk1KRt5vjh9f7qhLal/Kul2FLsL71Rj4kEGmliejI9aN6cIhDXpFsR79nuSXXiBWkPPL0EYWJ+qFiZsIOsCIgYkNnZjtZWE4Xeu+PdzcfCoiO4Rd/oC04tKM/i56kE9WTkDENSxMjEm39gx3w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=garyguo.net; dmarc=pass action=none header.from=garyguo.net; dkim=pass header.d=garyguo.net; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=garyguo.net; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=M7cGaE+NoD13JUaFexXjy2Fl/jVhHJUuwRfTIbRVki0=; b=n2Gdaqt7NoVTSA1LdgPL5QgsdrOHv3t/OfoDZixUuu9lPzG8AR+Ujg5YoZ4lUg+qKM9NLtPcQlRk74wuqXyPghkJE70iymmlFfQQQx6f4G1JKvWHm5wYURD3DYkaE5Rt58/kGn3ztRbvaMCqDlnMuwPM56oy6sPWhtI3YlMrl5k= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=garyguo.net; Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) by CWLP265MB6690.GBRP265.PROD.OUTLOOK.COM (2603:10a6:400:1ec::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Fri, 4 Sep 2026 14:12:38 +0000 Received: from LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1]) by LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM ([fe80::f60b:1537:68d7:4fc1%4]) with mapi id 15.21.0360.008; Fri, 4 Sep 2026 14:12:38 +0000 From: Gary Guo Date: Fri, 04 Sep 2026 15:12:32 +0100 Subject: [PATCH 4/4] rust: pin-init: internal: init: support tuple struct constructor syntax Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260904-tuple-struct-v1-4-72c50bd037fd@garyguo.net> References: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> In-Reply-To: <20260904-tuple-struct-v1-0-72c50bd037fd@garyguo.net> To: Benno Lossin , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Gary Guo , Mohamad Alsadhan X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788531156; l=19350; i=gary@garyguo.net; s=20221204; h=from:subject:message-id; bh=+RadHJsY5Xbi85OLnSHLYUzQBC08O4r6yyxtXElk2nk=; b=gtn1twVzcrRKD/CsMSZKuJuUNeT9ffZblZVS4RjpsWrizBNgQrFnoE3UcDbeIrA/nHpYE6rzQ oDevjvZYrSaDMybeaywH90wtyvfjEwf7upp297h3+iiJXN1fiLLpw+7 X-Developer-Key: i=gary@garyguo.net; a=ed25519; pk=vB3uIX95SM4eVrIqo1DWNWKDKD2xzB+yLLLr0yOPYMo= X-ClientProxiedBy: LO4P123CA0690.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:37b::7) To LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:4ab::19) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: LOAP265MB8560:EE_|CWLP265MB6690:EE_ X-MS-Office365-Filtering-Correlation-Id: 0368b0e2-b1a4-4404-934a-08df0a8e933e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|23010399003|10070799003|7416014|376014|10067099003|6133799003|3023799007|18002099003|56012099006|5023799004|22082099003|921020; X-Microsoft-Antispam-Message-Info: i7ZBLxhCePS7hV9psBOnM6xBdKKL50zMzANbavQklgnCiEW9ACZUcRR451344OWposYX2f6hvp58wcgJg5mSwVUiu40CAWBNoUpyRVLJCS/QqbRb7i75LqHvaJZ3y9E8m4D0ndOsYmp9sY2BDyP/NAmnsnYgG/Z2RJXjpalNsO8NiLMG129mHq7GgMQ1u+RaVFqVofp0LISfnQOBF2n+BKOFNdaZvESSVl5GzSeV011vsu6mypBRMvoNTxY6syyt+JmJV++SzK5OkJNbuOsCs9QJsURHv/XkrmnyngDX1xkZBfCQ8vhjFykYLJ6YxQX07pZy49A03xl5Nc+He6pBLztc8aB0xnNuo74jBkh7ZyuLGbaaZ2uZ1BMeZ6osiJudO3Vys7tljgNWE8yRhiQ7fmxNc3Zmi6AHc2ze+/VgDc8IjW1ihHB86pUPjvhRFLAfwZZhqNgs9fHUYZTa46lxRECzEFe6QT5tv8vz7ywDTrzZsM/98SEDwHyyQBMjPPevfgvyOYMqsy28fBBtHAqfXyx9S084NlOdyQkeXi5fdcuSR/ACCPNMEbJ7NXR2oWsi9vT/oxqf49pkYNmTs70UMLdu8IszZvqZ6hrDpqStb2DNvWGPeS80b9KoZkRPfLd4ovMWnuymG1kbWawvgAUDFSV70xdRgQtVZZl0PSNZStdQbqdilGgkDWJS+E79USSR6VVTvtM54cBDrpOZLx9L+w== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(23010399003)(10070799003)(7416014)(376014)(10067099003)(6133799003)(3023799007)(18002099003)(56012099006)(5023799004)(22082099003)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?NlFNWFdzcXFhTE1uUW8vMnVXaitybUZqa1N6ZlpMRWp4R0drdmFmRWZyV21C?= =?utf-8?B?M2VwQm05TUNlb3FFdHJHUUpud2ZObWZTTDIwRUVQNEtYVWdzanNHRlc2a2pC?= =?utf-8?B?dkpBckVvSlRSK3NQSmZKUFpmcHpLQWVQTW5mY3dwMGNKK1I0YmxXMW9jOHpq?= =?utf-8?B?b0JpempOeExsaG00cTdyNnpWQVNQMVhDVys1dDRwVVZzV2VVZXQ4TFpMaHpm?= =?utf-8?B?NXo1WFpxZFR5WGdFbTc5L0Q0OHZOcm1NRUpRTS9wYkhWb0FTNXhudndWNGJz?= =?utf-8?B?REJlZUE2bnJEeGlWalh6cDlldUtlc0doQ1JzbmRCK2ZvUGFzU3dIQ0lEcVg0?= =?utf-8?B?YzRuaXp0T2k1ZW5zNU9JYUJEdnNWNDlCTnpNd2txaW1Gd0hVWjI0dS92UmVF?= =?utf-8?B?bzZkTitoNmhCUVFNV1ZRMktFUnBoQkRqbktPTVorbVpzeVQrVThIZWFiK2hI?= =?utf-8?B?U0RoY1J3dGJMVW5YSDNPRnNQdjBhZUxJZFF1YjhFcXJUeHFXcThWK2laelNL?= =?utf-8?B?emZUdjhLUUc0T1ZvNjJJNm5XMENSZWFSNG00aVF6YWw3dldhZFFrVUFvaVV5?= =?utf-8?B?RkY1RW5PU2wyejd6ZXQ3N29lVVg2TGxhUnBMenZSRldoVVhWOTRhSmVFbm8r?= =?utf-8?B?bTlybmFyZlY0aU5RNGVSS2kxeWJTa2puV05kYU1KTHhrVzFqWEVMWWRPTEFW?= =?utf-8?B?Y3RrMXczQ3o0b0g2NUdJclliNlY3K0QyUHY5V0J5R2NaTkpFUWw3Q3FrVGZY?= =?utf-8?B?M0V1VW1UVEVXN1A1b09GWTFIZGNheUlnckVRUmQ3NXpTbDJlVFFCcVg3R3h2?= =?utf-8?B?ZzRhYlZCWG5yOU9SOFQxYllMclNuM1d3WjdUNnloMWhSN1BPMkpnano0bUE3?= =?utf-8?B?bEhmS1l2Mll4TWdsMjVMT0VFVWhtTGVpRjg2UUJvUktCR1A0VWxMZ0dZQXdD?= =?utf-8?B?RGFJekl2R3pKcGlFVnEwdmQ5SFRsWFhJTTFJNjBRTC94WjZqSUVMa2Jkemc4?= =?utf-8?B?U3QxbEpHcUV2eFZ1cGo5TkdvWjJPazNncWtpZ0s4LzRWKzYxaUFZUVhpcU9D?= =?utf-8?B?a3FRVG5aaHl1UWgwcHdFdFBmV2trMjMrdjhPVXpuaGtyclNnRzIyM3JVbHNS?= =?utf-8?B?VUVEUHdIRkwwaTRXZG5weTcxNVpXeWQ2TzVKRXhGdHIvaDljdHFKWS9EaGRV?= =?utf-8?B?WW1wTmFCUnZvdVV2bHFkZWxSK0M1b1VvVGVJMHVIRC9xY0RFWENDZHI2UmJK?= =?utf-8?B?RmVBZ1k2YXJWemhBV0cvUGY0TG5tem1pZWQ1Z28vS1NnQ1ZxZjlRTWhXZnN5?= =?utf-8?B?VnY3RGMycXFtYVlDSXVUWUlQUzdIdDc4eXdGU3pPdjQvbmx2Z1MvUnhVSTZE?= =?utf-8?B?L01NblRQeEJmYTN6MkNkUEdadVYrWUx1TUIyQ2ZaV1BmR3VCWm1OWGtuVEww?= =?utf-8?B?djRYZG5JNDl5VU5VMHF0V01FMVFDbkNIZ3BvcFhQc3hyZldPclRHOCswVTh0?= =?utf-8?B?Sk5adDVrVk1vbzhvSDhiMEZoa2VWWmN1NFFZNUViUGcybWZjTkREMWZVMnd6?= =?utf-8?B?SlMyMUxNYlRUR2wvZkZ0RncwZDVtaWpwSEVlMUdRUHVSU1lUTjlldzBmbnlJ?= =?utf-8?B?ZFVtU1VNUHM3a2JFUTZJdVRLdEdtbjQ5azdiZEVCSVQ1UVk0YUV2UXpVVWxO?= =?utf-8?B?N1BCeEMxSFlXQmIwSzFRQUlMalk4NDl1NnFCZlBOUkM4STIxYk42UXBGdnla?= =?utf-8?B?L0RGY3JtNHhjbmhDN1NQeFhwcUNtY2g2bFZvL2cvTlZmZWdZM1lrQlovZ25y?= =?utf-8?B?V09KN0hMRXJLTXJRa1ZQampmNE5jc2hkajdISFlVQjE2bFJJa0d4VHFiczFi?= =?utf-8?B?WG5IOFpTbmx5TTRtOXpuZU9DY1h4M0tuSFpxVkhKcS8wVGJPOXBkRmFPUUsr?= =?utf-8?B?cjg1ejMzZ2VnUzJVeU9nQ3F2N0huZGJ5WmsyU0FiVk1rVXFFNDZnYndwdHhu?= =?utf-8?B?Zk0zM2tVZkVXa2Rud2p5RDFDU2RGOXh3SjdNamhqMmVWZlZKanZJSFRjRDhL?= =?utf-8?B?d29CSGRXWWFkYUtSOEpQZzI3WmJWTzVkdG5RN2I3ZnY0d3FSWTVJVS9LMmt0?= =?utf-8?B?K2lpNUc1NCswL3ZTL1VOMGsybVFDVFE4cW5OSFVYdkFtanQ1SDYvUXpxRkdH?= =?utf-8?B?V1R2akg2YUs4NE5EbFpVOWIvU1B4RWRhYmwySFZObk5CdEZsSXo2aS8rZE1Q?= =?utf-8?B?L1pIaE1RYm9BVUErbzdCUXdXdksxcmxsdE83OG1mdVpPc0lDMkh1Y2doQzVE?= =?utf-8?B?bVpFeW1BQkkrVi9YTGsvTmJXTUo1a2xGQzVUQXo0bGZrRmpWdm5kUT09?= X-OriginatorOrg: garyguo.net X-MS-Exchange-CrossTenant-Network-Message-Id: 0368b0e2-b1a4-4404-934a-08df0a8e933e X-MS-Exchange-CrossTenant-AuthSource: LOAP265MB8560.GBRP265.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Sep 2026 14:12:38.5688 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: bbc898ad-b10f-4e10-8552-d9377b823d45 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: IQ9ORLdU67znpAmqPW5isNNwua6ZVj43HVsoi3c2BGoH7oJ+xZ4FnM1tpGO7HC+kCYvrbu3zT7vqOkAqquanRQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP265MB6690 From: Mohamad Alsadhan A tuple struct whose fields are all set to a value reads better written like a call to its constructor than with the indices spelled out: pin_init!(Foo(value, value)) Parse the two forms into separate types and rewrite the constructor arguments into the indexed fields they are shorthand for, so that only the parser has to know about the second form. The arguments have no names, so they cannot use `<-`. Parse it anyway and reject it afterwards, which reports the position of every offending `<-` rather than stopping at the first one. `cfg` needs different treatment for tuple constructor syntax. As non-derive proc macros are invoked before cfg is resolved, the macro cannot know whether a field survives, and dropping a tuple field renumbers every field after it. That cannot be expressed by attaching a `cfg` attribute to the initializer of a single field. Thus, resolve tuple field cfgs up front instead, by generating two cfg-gated invocations of the macro with one field resolved in each. This is the approach of commit 5bbf2b2deb94 ("rust: pin-init: internal: rework how `#[pin_data]` handles cfg"), and it is linear time because only one of the two branches is ever expanded. Struct expression syntax do not renumber, so using tuple structs with struct syntax can keep using the existing attribute-based handling. Suggested-by: Gary Guo Link: https://github.com/Rust-for-Linux/pin-init/pull/165 Signed-off-by: Mohamad Alsadhan [ Pre-expand cfgs for tuple init syntax. Use generics instead of separate types for normalization - Gary ] Co-developed-by: Gary Guo Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 381 +++++++++++++++++++++++++++++++++= +--- rust/pin-init/internal/src/lib.rs | 11 +- rust/pin-init/src/lib.rs | 16 ++ 3 files changed, 383 insertions(+), 25 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/sr= c/init.rs index 5920bb281a50..dc5dfb2ca114 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -1,14 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT =20 use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens, TokenStreamExt}; use syn::{ - braced, + braced, parenthesized, parse::{End, Parse}, parse_quote, - punctuated::Punctuated, + punctuated::{Pair, Punctuated}, spanned::Spanned, - token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, LitInt, Memb= er, Path, Token, Type, + token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Index, LitIn= t, Member, Path, Token, + Type, }; =20 use crate::{ @@ -16,14 +17,87 @@ util::*, }; =20 -pub(crate) struct Initializer { +pub(crate) struct Initializer { attrs: Vec, this: Option, + kind: Kind, + error: Option<(Token![?], Type)>, +} + +pub(crate) struct InitExprStruct { path: Path, brace_token: token::Brace, fields: Punctuated, rest: Option<(Token![..], Expr)>, - error: Option<(Token![?], Type)>, +} + +pub(crate) struct InitExprTuple { + path: Path, + paren_token: token::Paren, + fields: Punctuated, +} + +pub(crate) enum InitExprKind { + Struct(InitExprStruct), + Tuple(InitExprTuple), +} + +struct InitTupleField { + attrs: Vec, + /// `<-` is not valid in constructor syntax; it is parsed anyway so th= at it can be rejected + /// with a proper diagnostic instead of a parse error. + left_arrow_token: Option, + value: Expr, +} + +impl InitExprTuple { + fn normalize(self) -> InitExprStruct { + let InitExprTuple { + path, + paren_token, + fields, + } =3D self; + InitExprStruct { + path, + brace_token: token::Brace { + span: paren_token.span, + }, + fields: fields + .into_pairs() + .enumerate() + .map(|(index, pair)| { + let (field, comma) =3D pair.into_tuple(); + let span =3D field.value.span(); + let field =3D InitializerField { + attrs: field.attrs, + kind: InitializerKind::Value { + member: Member::Unnamed(Index { + index: index.try_into().unwrap(), + span, + }), + value: Some((Token![:](span), field.value)), + }, + }; + Pair::new(field, comma) + }) + .collect(), + rest: None, + } + } + + fn validate(&self, dcx: &mut DiagCtxt) -> Result<(), ErrorGuaranteed> { + let mut result =3D Ok(()); + for field in &self.fields { + if let Some(left_arrow_token) =3D &field.left_arrow_token { + result =3D Err(dcx.error( + left_arrow_token, + "`<-` is not supported in tuple constructor syntax; na= me the fields by index \ + instead, e.g. `Type { 0 <- initializer, 1: value }`", + )); + } + } + result + } } =20 struct This { @@ -71,16 +145,103 @@ struct DefaultErrorAttribute { ty: Box, } =20 -pub(crate) fn expand( +pub(crate) fn expand_with_cfg( + initializer: Initializer, + default_error: Option<&'static str>, + pinned: bool, + dcx: &mut DiagCtxt, +) -> Result { + let initializer =3D match initializer.kind { + InitExprKind::Tuple(expr) =3D> { + expr.validate(dcx)?; + + let mut initializer =3D Initializer { + attrs: initializer.attrs, + this: initializer.this, + kind: expr, + error: initializer.error, + }; + + // Removing a tuple field renumbers every field after it, whic= h cannot be expressed with + // a `cfg` attribute on the initializer of a single field. The= refore, resolve tuple + // field cfgs before continuing. Struct expression syntax uses= explicit numbers, so + // there is no need to pre-expand them and we only need to emi= t their cfgs on generated + // code. + for (field_idx, field) in initializer.kind.fields.iter_mut().e= numerate() { + let cfg =3D field.attrs.extract_cfg_attrs(); + + if cfg.is_empty() { + continue; + } + + let true_initializer =3D initializer.to_token_stream(); + initializer.kind.fields =3D initializer + .kind + .fields + .into_pairs() + .enumerate() + .filter(|&(index, _)| index !=3D field_idx) + .map(|(_, pair)| pair) + .collect(); + + let false_initializer =3D &initializer; + + let macro_name =3D if pinned { + quote!(::pin_init::pin_init) + } else { + quote!(::pin_init::init) + }; + + // Resolve one field at a time until we've got no more tup= le field cfgs. + // + // This is linear time because macro invocations with fals= e cfg will not be + // expanded. + return Ok(quote! { + { + // Use `{}` delimiter here so semicolon is not req= uired, otherwise the + // expression becomes unit type. + #[cfg(all(#(#cfg,)*))] + #macro_name! { #true_initializer } + + #[cfg(not(all(#(#cfg,)*)))] + #macro_name! { #false_initializer } + } + }); + } + + // No cfgs left, we can normalize the initializer to the struc= t kind. + Initializer { + attrs: initializer.attrs, + this: initializer.this, + kind: initializer.kind.normalize(), + error: initializer.error, + } + } + + InitExprKind::Struct(expr) =3D> Initializer { + attrs: initializer.attrs, + this: initializer.this, + kind: expr, + error: initializer.error, + }, + }; + + expand(initializer, default_error, pinned, dcx) +} + +fn expand( Initializer { attrs, this, - path, - brace_token, - fields, - rest, + kind: + InitExprStruct { + path, + brace_token, + fields, + rest, + }, error, - }: Initializer, + }: Initializer, default_error: Option<&'static str>, pinned: bool, dcx: &mut DiagCtxt, @@ -99,7 +260,10 @@ pub(crate) fn expand( } else if let Some(default_error) =3D default_error { syn::parse_str(default_error).unwrap() } else { - dcx.error(brace_token.span.close(), "expected `? ` a= fter `}`"); + dcx.error( + brace_token.span.close(), + "expected `? ` after initializer", + ); parse_quote!(::core::convert::Infallible) } }, @@ -377,11 +541,8 @@ fn make_field_check( } } =20 -impl Parse for Initializer { - fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { - let attrs =3D input.call(Attribute::parse_outer)?; - let this =3D input.peek(Token![&]).then(|| input.parse()).transpos= e()?; - let path =3D input.parse()?; +impl InitExprStruct { + fn parse_with_path(path: Path, input: syn::parse::ParseStream<'_>) -> = syn::Result { let content; let brace_token =3D braced!(content in input); let mut fields =3D Punctuated::new(); @@ -408,6 +569,51 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { .peek(Token![..]) .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse(= )?))) .transpose()?; + Ok(Self { + path, + brace_token, + fields, + rest, + }) + } +} + +impl InitExprTuple { + fn parse_with_path(path: Path, input: syn::parse::ParseStream<'_>) -> = syn::Result { + let content; + let paren_token =3D parenthesized!(content in input); + let mut fields =3D Punctuated::new(); + while !content.is_empty() { + fields.push_value(InitTupleField { + attrs: content.call(Attribute::parse_outer)?, + left_arrow_token: content.parse()?, + value: content.parse()?, + }); + if content.is_empty() { + break; + } + fields.push_punct(content.parse()?); + } + Ok(InitExprTuple { + path, + paren_token, + fields, + }) + } +} + +impl Parse for Initializer { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let attrs =3D input.call(Attribute::parse_outer)?; + let this =3D input.peek(Token![&]).then(|| input.parse()).transpos= e()?; + let path =3D input.parse()?; + let kind =3D if input.peek(token::Brace) { + InitExprKind::Struct(InitExprStruct::parse_with_path(path, inp= ut)?) + } else if input.peek(token::Paren) { + InitExprKind::Tuple(InitExprTuple::parse_with_path(path, input= )?) + } else { + return Err(input.error("expected curly braces or parentheses")= ); + }; let error =3D input .peek(Token![?]) .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?))) @@ -426,10 +632,7 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { Ok(Self { attrs, this, - path, - brace_token, - fields, - rest, + kind, error, }) } @@ -499,3 +702,137 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::= Result { } } } + +impl ToTokens for Initializer { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + this, + kind, + error, + } =3D self; + tokens.append_all(attrs); + this.to_tokens(tokens); + kind.to_tokens(tokens); + if let Some((question, ty)) =3D error { + question.to_tokens(tokens); + ty.to_tokens(tokens); + } + } +} + +impl ToTokens for InitExprKind { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Struct(init) =3D> init.to_tokens(tokens), + Self::Tuple(init) =3D> init.to_tokens(tokens), + } + } +} + +impl ToTokens for InitExprStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + path, + brace_token, + fields, + rest, + } =3D self; + path.to_tokens(tokens); + brace_token.surround(tokens, |tokens| { + fields.to_tokens(tokens); + if let Some((dotdot, expr)) =3D rest { + dotdot.to_tokens(tokens); + expr.to_tokens(tokens); + } + }); + } +} + +impl ToTokens for InitExprTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + path, + paren_token, + fields, + } =3D self; + path.to_tokens(tokens); + paren_token.surround(tokens, |tokens| fields.to_tokens(tokens)); + } +} + +impl ToTokens for InitTupleField { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + left_arrow_token, + value, + } =3D self; + tokens.append_all(attrs); + left_arrow_token.to_tokens(tokens); + value.to_tokens(tokens); + } +} + +impl ToTokens for InitializerAttribute { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::DefaultError(DefaultErrorAttribute { ty }) =3D> { + quote!(#[default_error(#ty)]).to_tokens(tokens); + } + } + } +} + +impl ToTokens for This { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + _and_token, + ident, + _in_token, + } =3D self; + _and_token.to_tokens(tokens); + ident.to_tokens(tokens); + _in_token.to_tokens(tokens); + } +} + +impl ToTokens for InitializerField { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { attrs, kind } =3D self; + tokens.append_all(attrs); + kind.to_tokens(tokens); + } +} + +impl ToTokens for InitializerKind { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Value { member, value } =3D> { + member.to_tokens(tokens); + if let Some((colon, expr)) =3D value { + colon.to_tokens(tokens); + expr.to_tokens(tokens); + } + } + Self::Init { + member, + _left_arrow_token, + value, + } =3D> { + member.to_tokens(tokens); + _left_arrow_token.to_tokens(tokens); + value.to_tokens(tokens); + } + Self::Code { + _underscore_token, + _colon_token, + block, + } =3D> { + _underscore_token.to_tokens(tokens); + _colon_token.to_tokens(tokens); + block.to_tokens(tokens); + } + } + } +} diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src= /lib.rs index 4d8ff86484b6..c488019d6250 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -49,12 +49,17 @@ pub fn maybe_derive_zeroable(input: TokenStream) -> Tok= enStream { #[proc_macro] pub fn init(input: TokenStream) -> TokenStream { let input =3D parse_macro_input!(input); - DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infall= ible"), false, dcx)) - .into() + DiagCtxt::with(|dcx| { + init::expand_with_cfg(input, Some("::core::convert::Infallible"), = false, dcx) + }) + .into() } =20 #[proc_macro] pub fn pin_init(input: TokenStream) -> TokenStream { let input =3D parse_macro_input!(input); - DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infall= ible"), true, dcx)).into() + DiagCtxt::with(|dcx| { + init::expand_with_cfg(input, Some("::core::convert::Infallible"), = true, dcx) + }) + .into() } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index f020b383851e..4a1368741329 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -654,6 +654,20 @@ macro_rules! stack_try_pin_init { /// # Box::pin_init(demo()).unwrap(); /// ``` /// +/// A tuple struct whose fields are all set to a value can also be written= like a call to its +/// constructor: +/// +/// ```rust +/// # use pin_init::*; +/// #[pin_data] +/// struct Pair(usize, usize); +/// +/// # fn demo() -> impl PinInit { +/// let initializer =3D pin_init!(Pair(42, 64)); +/// # initializer } +/// # Box::pin_init(demo()).unwrap(); +/// ``` +/// /// Arbitrary Rust expressions can be used to set the value of a variable. /// /// The fields are initialized in the order that they appear in the initia= lizer. So it is possible @@ -777,6 +791,8 @@ macro_rules! stack_try_pin_init { /// - Fields that you want to initialize in-place have to use `<-` instead= of `:`. /// - Tuple struct fields are named by their index, as in `0: value` or `0= <- initializer`. They /// are not exposed by a `let` binding, since they have no name to bind. +/// - A tuple struct can also be initialized with constructor syntax, as i= n `Type(value, value)`. +/// Since its arguments are not named, they cannot use `<-`; write them = out by index instead. /// - You can use `_: { /* run any user-code here */ },` anywhere where yo= u can place fields in /// order to run arbitrary code. /// - In front of the initializer you can write `&this in` to have access = to a [`NonNull`] --=20 2.54.0