Skip to content

Commit 8834869

Browse files
emit View only for unconditional borrowers
1 parent 66594b0 commit 8834869

7 files changed

Lines changed: 69 additions & 279 deletions

File tree

xdr-generator-rust/generator/src/generator.rs

Lines changed: 36 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use std::collections::hash_map::Entry;
2-
use std::collections::{BTreeSet, HashMap, HashSet};
2+
use std::collections::{HashMap, HashSet};
33

44
use askama::Template;
55
use xdr_parser::ast::{
6-
CfgExpr, Const, Definition, Enum, Struct, StructMember, Type, Typedef, Union, UnionArm,
7-
XdrSpec,
6+
CfgExpr, Const, Definition, Enum, Struct, StructMember, Type, Typedef, Union, UnionArm, XdrSpec,
87
};
98
use xdr_parser::lexer::IntBase;
109
use xdr_parser::types::{is_builtin_type, is_fixed_array, is_fixed_opaque, is_var_array, TypeInfo};
@@ -60,7 +59,7 @@ impl RustGenerator {
6059
let borrow = self
6160
.def_borrow
6261
.get(&(name.to_string(), cfg.map(ToString::to_string)))
63-
.cloned()
62+
.copied()
6463
.unwrap_or(BorrowCfg::Never);
6564
view_emit(self.view_required.contains(name), &borrow, cfg)
6665
}
@@ -308,8 +307,6 @@ impl RustGenerator {
308307
member_names,
309308
emit_view: r.emit_view,
310309
view_cfg: r.view_cfg,
311-
emit_view_alias: r.emit_view_alias,
312-
view_alias_cfg: r.view_alias_cfg,
313310
cfg,
314311
}
315312
}
@@ -396,8 +393,6 @@ impl RustGenerator {
396393
arms,
397394
emit_view: r.emit_view,
398395
view_cfg: r.view_cfg,
399-
emit_view_alias: r.emit_view_alias,
400-
view_alias_cfg: r.view_alias_cfg,
401396
cfg,
402397
default_arm_cfg,
403398
}
@@ -458,8 +453,6 @@ impl RustGenerator {
458453
custom_schemars: is_fixed_opaque_type && !custom_str && !no_display_fromstr,
459454
emit_view: r.emit_view,
460455
view_cfg: r.view_cfg,
461-
emit_view_alias: r.emit_view_alias,
462-
view_alias_cfg: r.view_alias_cfg,
463456
view_type_ref: resolved.view_type_ref,
464457
from_view_expr: resolved.from_view_expr,
465458
cfg,
@@ -556,69 +549,45 @@ impl RustGenerator {
556549
}
557550
}
558551

559-
/// Combine a definition's cfg with an additional condition, rendered as the
560-
/// inside of a `#[cfg(...)]`.
561-
fn combine_cfg(def_cfg: Option<&str>, extra: &str) -> String {
562-
match def_cfg {
563-
Some(def) => format!("all({def}, {extra})"),
564-
None => extra.to_string(),
565-
}
566-
}
567-
568552
// =============================================================================
569553
// Borrow analysis
570554
// =============================================================================
571555

572-
/// The cfg condition under which a type's `View` form actually borrows, i.e.
573-
/// under which its `'a` lifetime is used.
556+
/// Whether a type holds heap-allocated data, and so whether its `View` form
557+
/// would use its `'a` lifetime.
574558
///
575-
/// A `View` type may borrow unconditionally, never, or only under some cfgs —
576-
/// when the heap-allocated data it holds sits behind a cfg-gated union arm, or
577-
/// is reached through a type that itself only holds heap data under a cfg.
578-
#[derive(Clone, Debug, PartialEq, Eq)]
559+
/// The distinction that matters is unconditional: only a type that borrows
560+
/// under every cfg gets a `View` form. One that borrows under some cfgs would
561+
/// need a cfg-gated `View`, which any unconditional container of it would name
562+
/// unconditionally and so reference where it does not exist.
563+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
579564
enum BorrowCfg {
580-
/// Holds no heap-allocated data under any cfg, so it needs no `View` form.
565+
/// Holds no heap-allocated data under any cfg.
581566
Never,
582-
/// Always borrows, so its `View` form is unconditional.
567+
/// Holds heap-allocated data under every cfg.
583568
Always,
584-
/// Borrows under the disjunction of these cfg predicates.
585-
When(BTreeSet<String>),
569+
/// Holds heap-allocated data only under some cfgs, because it sits behind a
570+
/// cfg-gated union arm or is reached through a type that does.
571+
Sometimes,
586572
}
587573

588574
impl BorrowCfg {
589575
fn or(self, other: BorrowCfg) -> BorrowCfg {
590576
match (self, other) {
591577
(BorrowCfg::Always, _) | (_, BorrowCfg::Always) => BorrowCfg::Always,
592578
(BorrowCfg::Never, o) | (o, BorrowCfg::Never) => o,
593-
(BorrowCfg::When(mut a), BorrowCfg::When(b)) => {
594-
a.extend(b);
595-
BorrowCfg::When(a)
596-
}
579+
(BorrowCfg::Sometimes, BorrowCfg::Sometimes) => BorrowCfg::Sometimes,
597580
}
598581
}
599582

600583
/// Restrict to also require `cfg`, as for a cfg-gated union arm.
601584
fn and_cfg(self, cfg: Option<&str>) -> BorrowCfg {
602-
let Some(cfg) = cfg else { return self };
603-
match self {
604-
BorrowCfg::Never => BorrowCfg::Never,
605-
BorrowCfg::Always => BorrowCfg::When(core::iter::once(cfg.to_string()).collect()),
606-
BorrowCfg::When(set) => {
607-
BorrowCfg::When(set.iter().map(|e| format!("all({cfg}, {e})")).collect())
608-
}
585+
if cfg.is_none() {
586+
return self;
609587
}
610-
}
611-
612-
/// The cfg predicate to gate the borrowing `View` form on, or `None` when it
613-
/// is unconditional.
614-
fn render(&self) -> Option<String> {
615588
match self {
616-
BorrowCfg::Never | BorrowCfg::Always => None,
617-
BorrowCfg::When(set) if set.len() == 1 => set.iter().next().cloned(),
618-
BorrowCfg::When(set) => Some(format!(
619-
"any({})",
620-
set.iter().cloned().collect::<Vec<_>>().join(", ")
621-
)),
589+
BorrowCfg::Never => BorrowCfg::Never,
590+
BorrowCfg::Always | BorrowCfg::Sometimes => BorrowCfg::Sometimes,
622591
}
623592
}
624593
}
@@ -653,11 +622,17 @@ impl<'a> BorrowAnalysis<'a> {
653622
analysis
654623
}
655624

656-
/// The names that have a `View` form at all, i.e. that borrow under some cfg.
625+
/// The names that get a `View` form, i.e. that borrow under every cfg.
626+
///
627+
/// A name that only borrows under some cfgs is excluded. Its `View` form
628+
/// would have to be cfg-gated, and an always-borrowing type containing it
629+
/// names that form unconditionally, so the reference would dangle wherever
630+
/// the cfg is off. Excluding it leaves containers holding the owned type in
631+
/// that position, which is correct under every cfg.
657632
fn view_required(&self) -> HashSet<String> {
658633
self.by_name
659634
.iter()
660-
.filter(|(_, b)| **b != BorrowCfg::Never)
635+
.filter(|(_, b)| **b == BorrowCfg::Always)
661636
.map(|(n, _)| n.clone())
662637
.collect()
663638
}
@@ -670,7 +645,7 @@ impl<'a> BorrowAnalysis<'a> {
670645
/// cycle always borrows.
671646
fn of_name(&mut self, name: &str) -> BorrowCfg {
672647
if let Some(b) = self.by_name.get(name) {
673-
return b.clone();
648+
return *b;
674649
}
675650
if self.stack.contains(name) {
676651
return BorrowCfg::Always;
@@ -685,7 +660,7 @@ impl<'a> BorrowAnalysis<'a> {
685660
borrow = borrow.or(self.of_def(def).and_cfg(def_cfg.as_deref()));
686661
}
687662
self.stack.remove(name);
688-
self.by_name.insert(name.to_string(), borrow.clone());
663+
self.by_name.insert(name.to_string(), borrow);
689664
borrow
690665
}
691666

@@ -736,15 +711,12 @@ impl<'a> BorrowAnalysis<'a> {
736711

737712
/// How a definition's borrowing `View` form is emitted.
738713
///
739-
/// Where the definition borrows, a real `{name}View<'a>` is emitted, so its `'a`
740-
/// is always used. Where it does not, `{name}View<'a>` is emitted as a
741-
/// transparent alias of the owned type, so types containing it can name it
742-
/// uniformly across cfgs instead of being cfg-split themselves.
714+
/// A `{name}View<'a>` is emitted only where the definition borrows, so its `'a`
715+
/// is always used. Where it does not borrow, nothing is emitted: the owned type
716+
/// is already the whole value, and a heap-free type has nothing to borrow.
743717
struct ViewEmit {
744718
emit_view: bool,
745719
view_cfg: Option<String>,
746-
emit_view_alias: bool,
747-
view_alias_cfg: Option<String>,
748720
}
749721

750722
/// Decide how to emit the `View` form of one definition.
@@ -755,32 +727,18 @@ fn view_emit(has_view: bool, borrow: &BorrowCfg, def_cfg: Option<&str>) -> ViewE
755727
let mut emit = ViewEmit {
756728
emit_view: false,
757729
view_cfg: None,
758-
emit_view_alias: false,
759-
view_alias_cfg: None,
760730
};
761731
if !has_view {
762732
return emit;
763733
}
764734
match borrow {
765-
// The name has a View form only because another cfg branch of it
766-
// borrows, so this definition contributes just the alias.
767-
BorrowCfg::Never => {
768-
emit.emit_view_alias = true;
769-
emit.view_alias_cfg = def_cfg.map(ToString::to_string);
770-
}
735+
// Nothing to borrow in this branch, or nothing to borrow under some
736+
// cfg. Either way this definition emits no View form.
737+
BorrowCfg::Never | BorrowCfg::Sometimes => {}
771738
BorrowCfg::Always => {
772739
emit.emit_view = true;
773740
emit.view_cfg = def_cfg.map(ToString::to_string);
774741
}
775-
BorrowCfg::When(_) => {
776-
let when = borrow
777-
.render()
778-
.expect("BorrowCfg::When always renders a predicate");
779-
emit.emit_view = true;
780-
emit.view_cfg = Some(combine_cfg(def_cfg, &when));
781-
emit.emit_view_alias = true;
782-
emit.view_alias_cfg = Some(combine_cfg(def_cfg, &format!("not({when})")));
783-
}
784742
}
785743
emit
786744
}

xdr-generator-rust/generator/src/output.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,6 @@ pub struct StructOutput {
5454
pub emit_view: bool,
5555
/// The full cfg for the real `View` struct, gating it to where it borrows.
5656
pub view_cfg: Option<String>,
57-
/// True when `{name}View<'a>` is emitted as a transparent alias of `{name}`,
58-
/// for the cfgs under which this definition does not borrow.
59-
pub emit_view_alias: bool,
60-
/// The full cfg for the alias.
61-
pub view_alias_cfg: Option<String>,
6257
pub cfg: Option<String>,
6358
}
6459

@@ -105,12 +100,6 @@ pub struct UnionOutput {
105100
/// a cfg, this is the union's cfg combined with the disjunction of those
106101
/// arm cfgs, so the enum only exists where its lifetime is actually used.
107102
pub view_cfg: Option<String>,
108-
/// True when `{name}View<'a>` is emitted as a transparent alias of `{name}`,
109-
/// for the cfgs under which no arm borrows.
110-
pub emit_view_alias: bool,
111-
/// The full cfg for the alias: the union's cfg combined with the negation
112-
/// of the borrowing arms' cfgs.
113-
pub view_alias_cfg: Option<String>,
114103
pub cfg: Option<String>,
115104
/// Cfg for the first arm, used to gate the Default impl when the
116105
/// default variant is behind a cfg.
@@ -159,11 +148,6 @@ pub struct TypedefNewtypeOutput {
159148
pub emit_view: bool,
160149
/// The full cfg for the real `View` newtype, gating it to where it borrows.
161150
pub view_cfg: Option<String>,
162-
/// True when `{name}View<'a>` is emitted as a transparent alias of `{name}`,
163-
/// for the cfgs under which this definition does not borrow.
164-
pub emit_view_alias: bool,
165-
/// The full cfg for the alias.
166-
pub view_alias_cfg: Option<String>,
167151
/// The inner type in the borrowing `View` form of the newtype.
168152
pub view_type_ref: String,
169153
/// Expression converting the inner value from `View` form to owned form.

0 commit comments

Comments
 (0)