Skip to content

currency: Implement runtime fallback for missing patterns and remove constructor validation#8066

Open
younies wants to merge 8 commits into
unicode-org:mainfrom
younies:fix-r3396052964
Open

currency: Implement runtime fallback for missing patterns and remove constructor validation#8066
younies wants to merge 8 commits into
unicode-org:mainfrom
younies:fix-r3396052964

Conversation

@younies

@younies younies commented Jun 11, 2026

Copy link
Copy Markdown
Member

This PR implements runtime fallback for currency patterns in CurrencyEssentials and relaxes validation in formatters to support custom data that may lack some patterns.

Changelog

  • Runtime Fallback:
    • Positive pattern getters in CurrencyEssentials now return non-optional &DoublePlaceholderPattern. If a pattern index is out of bounds (representing a missing pattern in custom data), they fall back according to the hierarchy, with FALLBACK_PATTERN ("{1}{0}") as the ultimate fallback.
    • Implemented positive pattern fallback hierarchies:
      • standard_alpha_next_to_number_positivestandard
      • accounting_positivestandard
      • accounting_alpha_next_to_number_positivestandard_alpha_next_to_numberstandard
    • Negative pattern getters return Option<&DoublePlaceholderPattern>, with accounting_negative falling back to standard_negative if missing (None).
  • Relaxed Validation:
    • Removed standard pattern validation from CurrencyFormatter and CompactCurrencyFormatter constructors. They now initialize successfully even if the standard pattern is missing in the data, relying on runtime fallback.
  • Documentation:
    • Added comments detailing the fallback hierarchies for all pattern getters.
    • Added comments in datagen explaining the datagen-time fallback resolution.
  • Data:
    • Regenerated experimental baked data.

…ternIndices

Make standard_alpha_next_to_number, accounting_positive, and
accounting_alpha_next_to_number_positive optional (Option<u8>) in
PatternIndices to support true runtime fallback and avoid datagen-time
fallback duplication.

Also updated getters in CurrencyEssentials to implement the fallback
hierarchies at runtime and added documentation comments for them.
Regenerated experimental test data.

TAG=agy
CONV=7de8b65e-8ff1-43aa-9dbd-1a73ee7b45ff
Comment on lines +182 to +187
debug_assert!(
(self.indices.standard as usize) < self.patterns.len(),
"Standard pattern index {} is out of bounds for patterns of length {}",
self.indices.standard,
self.patterns.len()
);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robertbastian robertbastian Jun 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please see my comment on the other PR

add by @younies #8050 (comment)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@robertbastian robertbastian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, for several reasons:

  • an Option<u8> takes more space than a u8
  • it shifts work that can be done at datagen time to runtime

younies added 6 commits June 11, 2026 15:28
…alidation

Revert the changes that made positive patterns optional in PatternIndices.
Positive patterns are now u8 (non-optional) again, and fallback is resolved
at datagen time.

Added runtime fallback to FALLBACK_PATTERN in CurrencyEssentials getters
for custom data support.

Removed standard pattern validation from CurrencyFormatter and
CompactCurrencyFormatter constructors, allowing them to succeed and use
runtime fallback if the pattern is missing.

Regenerated experimental baked data.

TAG=agy
CONV=2329ca53-2c21-4bb8-bc21-4b69db92e9cc
- Remove `.unwrap()` from positive pattern getters in `icu_provider_source` tests,
  as positive patterns are now non-optional.
- Add `#[cfg(feature = "chrono")]` to `test_against_chrono` in `zoneinfo64` to
  fix compilation failure when `chrono` feature is not enabled.

TAG=agy
CONV=2329ca53-2c21-4bb8-bc21-4b69db92e9cc
@younies younies changed the title currency: Make positive pattern variants optional and implement runtime fallback currency: Implement runtime fallback for missing patterns and remove constructor validation Jun 15, 2026
@younies

younies commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

I have made all pattern indices u8 except for the negative ones. At runtime, we need to know if a negative pattern does not exist so we can fall back to the decimal formatter.

Regarding the struct size, we will address that when we add all the patterns and implement the ULE.

@younies younies requested a review from robertbastian June 15, 2026 13:56
Comment on lines +147 to +148
// we have the fallback here for users feeding their own data without
// handling the fallback logic in their data generation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should document the invariant that the standard pattern always exists, and phrase any case where it doesn't as a condition that we don't support.

Suggested change
// we have the fallback here for users feeding their own data without
// handling the fallback logic in their data generation.
// we have the fallback here in case the data struct is corrupted.

Comment on lines +198 to +206
debug_assert!(
(self.indices.standard as usize) < self.patterns.len(),
"Standard pattern index {} is out of bounds for patterns of length {}",
self.indices.standard,
self.patterns.len()
);
self.patterns
.get(self.indices.standard as usize)
.or_else(|| self.patterns.get(0))
.unwrap_or(FALLBACK_PATTERN)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
debug_assert!(
(self.indices.standard as usize) < self.patterns.len(),
"Standard pattern index {} is out of bounds for patterns of length {}",
self.indices.standard,
self.patterns.len()
);
self.patterns
.get(self.indices.standard as usize)
.or_else(|| self.patterns.get(0))
.unwrap_or(FALLBACK_PATTERN)
self.patterns
.get(self.indices.standard as usize)
.unwrap_or_else(|| {
debug_assert!(
false,
"Standard pattern index {} is out of bounds for patterns of length {}",
self.indices.standard,
self.patterns.len()
);
// GIGO
FALLBACK_PATTERN
})

}

#[test]
#[cfg(feature = "chrono")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't make unrelated changes.

Comment on lines +194 to +196
/// Even though the baked data handles the fallback at data generation time,
/// we have the fallback here for users feeding their own data without
/// handling the fallback logic in their data generation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no fallback here

self.patterns
.get(self.indices.standard_alpha_next_to_number as usize)
.or_else(|| self.standard_pattern())
.unwrap_or_else(|| self.standard_pattern())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.unwrap_or_else(|| self.standard_pattern())
.unwrap_or_else(|| {
debug_assert!(
false,
"Standard-alpha-next-to-number pattern index {} is out of bounds for patterns of length {}",
self.indices.standard,
self.patterns.len()
);
// GIGO
FALLBACK_PATTERN
})

Comment on lines +218 to +223
/// Fallback hierarchy:
/// `standard_alpha_next_to_number` -> `standard`
///
/// Even though the baked data handles the fallback at data generation time,
/// we have the fallback here for users feeding their own data without
/// handling the fallback logic in their data generation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't document like this. we should treat the fallback as a datagen concern, here we just assume we have a value for each type

Suggested change
/// Fallback hierarchy:
/// `standard_alpha_next_to_number` -> `standard`
///
/// Even though the baked data handles the fallback at data generation time,
/// we have the fallback here for users feeding their own data without
/// handling the fallback logic in their data generation.


/// Returns the `standard_alpha_next_to_number` negative pattern if specified, falling back to standard negative.
///
/// Fallback hierarchy:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: handle the negative fallbacks at datagen time as well. they can still be Option<u8>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants