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

Support fundamental types with multiple type parameters #616

Merged
merged 2 commits into from
Oct 3, 2020
Merged
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
2 changes: 1 addition & 1 deletion chalk-integration/src/lowering.rs
Original file line number Diff line number Diff line change
@@ -274,7 +274,7 @@ impl LowerWithEnv for (&AdtDefn, chalk_ir::AdtId<ChalkIr>) {
fn lower(&self, env: &Env) -> LowerResult<Self::Lowered> {
let (adt_defn, adt_id) = self;

if adt_defn.flags.fundamental && adt_defn.all_parameters().len() != 1 {
if adt_defn.flags.fundamental && adt_defn.all_parameters().len() < 1 {
Err(RustIrError::InvalidFundamentalTypesParameters(
adt_defn.name.clone(),
))?;
67 changes: 28 additions & 39 deletions chalk-solve/src/clauses/program_clauses.rs
Original file line number Diff line number Diff line change
@@ -361,18 +361,20 @@ impl<I: Interner> ToProgramClauses<I> for AdtDatum<I> {
/// ```notrust
/// #[upstream]
/// #[fundamental]
/// struct Box<T> {}
/// struct Box<T, U> {}
/// ```
///
/// We generate the following clauses:
///
/// ```notrust
/// forall<T> { IsLocal(Box<T>) :- IsLocal(T). }
/// forall<T, U> { IsLocal(Box<T, U>) :- IsLocal(T). }
/// forall<T, U> { IsLocal(Box<T, U>) :- IsLocal(U). }
///
/// forall<T> { IsUpstream(Box<T>) :- IsUpstream(T). }
/// forall<T, U> { IsUpstream(Box<T, U>) :- IsUpstream(T), IsUpstream(U). }
///
/// // Generated for both upstream and local fundamental types
/// forall<T> { DownstreamType(Box<T>) :- DownstreamType(T). }
/// forall<T, U> { DownstreamType(Box<T, U>) :- DownstreamType(T). }
/// forall<T, U> { DownstreamType(Box<T, U>) :- DownstreamType(U). }
/// ```
///
#[instrument(level = "debug", skip(builder))]
@@ -395,38 +397,6 @@ impl<I: Interner> ToProgramClauses<I> for AdtDatum<I> {
let self_appl_ty = application_ty(builder, id);
let self_ty = self_appl_ty.clone().intern(interner);

// Fundamental types often have rules in the form of:
// Goal(FundamentalType<T>) :- Goal(T)
// This macro makes creating that kind of clause easy
macro_rules! fundamental_rule {
($goal:ident) => {
// Fundamental types must always have at least one
// type parameter for this rule to make any
// sense. We currently do not have have any
// fundamental types with more than one type
// parameter, nor do we know what the behaviour
// for that should be. Thus, we are asserting here
// that there is only a single type parameter
// until the day when someone makes a decision
// about how that should behave.
assert_eq!(
self_appl_ty.len_type_parameters(interner),
1,
"Only fundamental types with a single parameter are supported"
);

builder.push_clause(
DomainGoal::$goal(self_ty.clone()),
Some(DomainGoal::$goal(
// This unwrap is safe because we asserted
// above for the presence of a type
// parameter
self_appl_ty.first_type_parameter(interner).unwrap(),
)),
);
};
}

// Types that are not marked `#[upstream]` satisfy IsLocal(TypeName)
if !self.flags.upstream {
// `IsLocalTy(Ty)` depends *only* on whether the type
@@ -436,15 +406,34 @@ impl<I: Interner> ToProgramClauses<I> for AdtDatum<I> {
// If a type is `#[upstream]`, but is also
// `#[fundamental]`, it satisfies IsLocal if and only
// if its parameters satisfy IsLocal
fundamental_rule!(IsLocal);
fundamental_rule!(IsUpstream);
for type_param in self_appl_ty.type_parameters(interner) {
builder.push_clause(
DomainGoal::IsLocal(self_ty.clone()),
Some(DomainGoal::IsLocal(type_param)),
);
}
builder.push_clause(
DomainGoal::IsUpstream(self_ty.clone()),
self_appl_ty
.type_parameters(interner)
.map(|type_param| DomainGoal::IsUpstream(type_param)),
);
} else {
// The type is just upstream and not fundamental
builder.push_fact(DomainGoal::IsUpstream(self_ty.clone()));
}

if self.flags.fundamental {
fundamental_rule!(DownstreamType);
assert!(
self_appl_ty.len_type_parameters(interner) >= 1,
"Only fundamental types with type parameters are supported"
);
for type_param in self_appl_ty.type_parameters(interner) {
builder.push_clause(
DomainGoal::DownstreamType(self_ty.clone()),
Some(DomainGoal::DownstreamType(type_param)),
);
}
}
});
}
14 changes: 0 additions & 14 deletions tests/lowering/mod.rs
Original file line number Diff line number Diff line change
@@ -454,20 +454,6 @@ fn upstream_items() {
}
}

#[test]
fn fundamental_multiple_type_parameters() {
lowering_error! {
program {
#[fundamental]
struct Boxes<T, U> { }
}

error_msg {
"only a single parameter supported for fundamental type `Boxes`"
}
}
}

#[test]
fn tuples() {
lowering_success! {
71 changes: 71 additions & 0 deletions tests/test/coherence.rs
Original file line number Diff line number Diff line change
@@ -516,3 +516,74 @@ fn orphan_check() {
}
}
}

#[test]
fn fundamental_type_multiple_parameters() {
// Test that implementing a local trait on a fundamental
// type with multiple parameters is allowed
lowering_success! {
program {
#[upstream]
#[fundamental]
struct Box<T, U> { }

trait Local { }

impl<T, U> Local for Box<T, U> { }
}
}

// Test that implementing a remote trait on a fundamental
// type with multiple parameters is rejected
lowering_error! {
program {
#[upstream]
#[fundamental]
struct Box<T, U> { }

#[upstream]
trait Remote { }

impl<T, U> Remote for Box<T, U> { }
} error_msg {
"impl for trait `Remote` violates the orphan rules"
}
}

// Test that implementing a remote trait on a fundamental type
// with one local type parameter is allowed
lowering_success! {
program {
#[upstream]
#[fundamental]
struct Box<T, U> { }

struct Local { }

#[upstream]
trait Remote { }

impl<T> Remote for Box<T, Local> { }
}
}

// Test that implementing a remote trait on a fundamental type
// with one concrete remote type parameter is rejected
lowering_error! {
program {
#[upstream]
#[fundamental]
struct Box<T, U> { }

#[upstream]
struct Up { }

#[upstream]
trait Remote { }

impl<T> Remote for Box<T, Up> { }
} error_msg {
"impl for trait `Remote` violates the orphan rules"
}
}
}