Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bounds generation for generics in derive(Arbitrary) #260

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions proptest-derive/src/use_tracking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@
//! track uses of type variables that need `Arbitrary` bounds in our
//! impls.

// Perhaps ordermap would be better, but our maps are so small that we care
// much more about the increased compile times incured by including ordermap.
// We need to preserve insertion order in any case, so HashMap is not useful.
use std::borrow::Borrow;
use std::collections::{BTreeMap, HashSet};
use std::collections::HashSet;

use syn;

Expand All @@ -30,10 +27,13 @@ use crate::util;
/// or similar and thus needs an `Arbitrary` bound added to them.
pub struct UseTracker {
/// Tracks 'usage' of a type variable name.
/// Allocation of this map will happen at once and no further
/// Allocation of this "map" will happen at once and no further
/// allocation will happen after that. Only potential updates
/// will happen after initial allocation.
used_map: BTreeMap<syn::Ident, bool>,
/// We need to preserve insertion order, thus using Vec instead of BTreeMap
/// or HashMap. A potential alternative would be indexmap crate, but our
/// maps are so small that it would not bring any significant benefit.
used_map: Vec<(syn::Ident, bool)>,
/// Extra types to bound by `Arbitrary` in the `where` clause.
where_types: HashSet<syn::Type>,
/// The generics that we are doing this for.
Expand Down Expand Up @@ -76,15 +76,19 @@ impl UseTracker {
/// a type variable and this call has no effect.
fn use_tyvar(&mut self, tyvar: impl Borrow<syn::Ident>) {
if self.track {
if let Some(used) = self.used_map.get_mut(tyvar.borrow()) {
if let Some(used) = self
.used_map
.iter_mut()
.find_map(|(ty, used)| (ty == tyvar.borrow()).then(|| used))
{
*used = true;
}
}
}

/// Returns true iff the type variable given exists.
fn has_tyvar(&self, ty_var: impl Borrow<syn::Ident>) -> bool {
self.used_map.contains_key(ty_var.borrow())
self.used_map.iter().any(|(ty, _)| ty == ty_var.borrow())
}

/// Mark the type as used.
Expand All @@ -101,8 +105,11 @@ impl UseTracker {
for_not: Option<syn::TypeParamBound>,
) -> DeriveResult<()> {
{
let mut iter =
self.used_map.values().zip(self.generics.type_params_mut());
let mut iter = self
.used_map
.iter()
.map(|(_, used)| used)
.zip(self.generics.type_params_mut());
if let Some(for_not) = for_not {
iter.try_for_each(|(&used, tv)| {
// Steal the attributes:
Expand Down
30 changes: 30 additions & 0 deletions proptest-derive/tests/use_tracker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2018 The proptest developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::marker::PhantomData;

use proptest::prelude::Arbitrary;
use proptest_derive::Arbitrary;

#[derive(Debug)]
struct NotArbitrary;

#[derive(Debug, Arbitrary)]
// Generic types are not in alphabetical order on purpose.
struct Foo<V, T, U> {
v: V,
t: T,
u: PhantomData<U>,
}

#[test]
fn asserting_arbitrary() {
fn assert_arbitrary<T: Arbitrary>() {}

assert_arbitrary::<Foo<i32, i32, NotArbitrary>>();
}