Skip to content

Support fundamental types with multiple type parameters #616

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

Merged
merged 2 commits into from
Oct 3, 2020
Merged
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
2 changes: 1 addition & 1 deletion chalk-integration/src/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
))?;
Expand Down
67 changes: 28 additions & 39 deletions chalk-solve/src/clauses/program_clauses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand All @@ -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
Expand All @@ -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)),
);
}
}
});
}
Expand Down
14 changes: 0 additions & 14 deletions tests/lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down
71 changes: 71 additions & 0 deletions tests/test/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}