Skip to content

Commit 12b5a85

Browse files
celinvaldanielsn
andauthored
Fix type mixmatch of types with projection (rust-lang#1125)
* Add tests to associated types * Fix type mistmatch of types with projection Structures with members that had type projections in their declaration were being handled incorrectly. We were only normalizing the types when the projection was visited, which could impact on how we identify whether this structure already existed or not. This change normalizes all types before starting to codegen the entire type. Co-authored-by: Daniel Schwartz-Narbonne <[email protected]>
1 parent 94e7818 commit 12b5a85

File tree

7 files changed

+148
-20
lines changed

7 files changed

+148
-20
lines changed

kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,12 @@ impl<'tcx> GotocCtx<'tcx> {
533533
/// also c.f. https://www.ralfj.de/blog/2020/04/04/layout-debugging.html
534534
/// c.f. https://rust-lang.github.io/unsafe-code-guidelines/introduction.html
535535
pub fn codegen_ty(&mut self, ty: Ty<'tcx>) -> Type {
536-
let goto_typ = self.codegen_ty_inner(ty);
536+
let normalized = self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty);
537+
let goto_typ = self.codegen_ty_inner(normalized);
537538
if let Some(tag) = goto_typ.tag() {
538539
if !self.type_map.contains_key(&tag) {
539-
self.type_map.insert(tag, ty);
540+
debug!(mir_type=?normalized, gotoc_name=?tag, ?goto_typ, "codegen_ty: new type");
541+
self.type_map.insert(tag, normalized);
540542
}
541543
}
542544
goto_typ
@@ -624,9 +626,7 @@ impl<'tcx> GotocCtx<'tcx> {
624626
}
625627
}
626628
ty::Projection(_) | ty::Opaque(_, _) => {
627-
// hidden types that can be revealed by the compiler via normalization
628-
let normalized = self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty);
629-
self.codegen_ty(normalized)
629+
unreachable!("Type should've been normalized already")
630630
}
631631

632632
// shouldn't come to here after mormomorphization
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Tests the behavior of associated types.
5+
//! See https://doc.rust-lang.org/book/ch19-03-advanced-traits.html for more details.
6+
7+
/// Covert Source -> Target
8+
trait Convert {
9+
type Source;
10+
type Target;
11+
12+
fn convert(&self) -> Self::Target;
13+
}
14+
15+
/// Dummy trait that returns an u8
16+
trait U8Wrapper {
17+
fn get(&self) -> u8;
18+
}
19+
20+
impl<T: U8Wrapper> Convert for T {
21+
type Source = T;
22+
type Target = u8;
23+
24+
fn convert(&self) -> u8 {
25+
self.get()
26+
}
27+
}
28+
29+
#[derive(Copy, Clone, PartialEq, Eq)]
30+
struct MyU8 {
31+
inner: u8,
32+
}
33+
34+
impl U8Wrapper for MyU8 {
35+
fn get(&self) -> u8 {
36+
self.inner
37+
}
38+
}
39+
40+
#[kani::proof]
41+
pub fn check_source_type() {
42+
let obj1 = Some(MyU8 { inner: 10 });
43+
let obj2: Option<<MyU8 as Convert>::Source> = obj1;
44+
assert_eq!(obj1.unwrap(), obj2.unwrap());
45+
}
46+
47+
#[kani::proof]
48+
pub fn check_fn_convert() {
49+
let val: u8 = kani::any();
50+
let obj1 = Some(MyU8 { inner: val });
51+
assert_eq!(obj1.unwrap().get(), val);
52+
assert_eq!(obj1.unwrap().convert(), val);
53+
}
54+
55+
#[kani::proof]
56+
pub fn check_dyn_convert() {
57+
let val: u8 = kani::any();
58+
let obj1 = MyU8 { inner: val };
59+
let con: &dyn Convert<Source = MyU8, Target = u8> = &obj1;
60+
assert_eq!(con.convert(), val);
61+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Tests the handling of IntoIter associated types for Iterators.
5+
//! For Iterators, the IntoIter associated type is the type of the iterator itself.
6+
//! In this case, Kani should treat the types as the same type.
7+
8+
use std::str::Chars;
9+
10+
struct MyStruct<'a> {
11+
copy1: Option<<Chars<'a> as IntoIterator>::IntoIter>,
12+
copy2: Option<Chars<'a>>,
13+
}
14+
15+
impl<'a> MyStruct<'a> {
16+
fn new(source: Chars<'a>) -> Self {
17+
MyStruct { copy1: Some(source.clone()), copy2: Some(source) }
18+
}
19+
}
20+
21+
#[kani::proof]
22+
#[kani::unwind(2)]
23+
pub fn check_into_iter_type() {
24+
let original = "h";
25+
let mut wrapper = MyStruct::new(original.chars());
26+
assert!(wrapper.copy1.unwrap().eq(wrapper.copy2.unwrap()));
27+
}

tests/kani/AssociatedTypes/type_of.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Tests the behavior of TypeId for structs with associated types in their member declaration.
5+
//!
6+
//! See https://github.com/model-checking/kani/issues/1124 for more details.
7+
use std::any::TypeId;
8+
9+
trait Associated {
10+
type Typ;
11+
}
12+
13+
impl<T> Associated for T {
14+
type Typ = T;
15+
}
16+
17+
struct Wrapper<T>(T);
18+
19+
struct MyStruct {
20+
first: Wrapper<u8>,
21+
second: Wrapper<<u8 as Associated>::Typ>,
22+
}
23+
24+
fn same_type<T: 'static, U: 'static>(_: T, _: U) -> bool {
25+
TypeId::of::<T>() == TypeId::of::<U>()
26+
}
27+
28+
#[kani::proof]
29+
fn check_type() {
30+
let mine = MyStruct { first: Wrapper(10), second: Wrapper(10) };
31+
assert!(same_type(mine.first, mine.second));
32+
}

tests/kani/Iterator/flat_map.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//
4+
// This test checks the result of using Iterator::flat_map. We had some projection
5+
// issues with this in the past.
6+
7+
#[kani::proof]
8+
#[kani::unwind(3)]
9+
pub fn check_flat_map_char() {
10+
let hi = ["H", "i"];
11+
let mut hi_flat = hi.iter().flat_map(|s| s.chars());
12+
assert_eq!(hi_flat.next(), Some('H'));
13+
assert_eq!(hi_flat.next(), Some('i'));
14+
assert_eq!(hi_flat.next(), None);
15+
}
16+
17+
#[kani::proof]
18+
#[kani::unwind(4)]
19+
fn check_flat_map_len() {
20+
let hello = ["Hi", "!"];
21+
let length = hello.iter().flat_map(|s| s.chars()).count();
22+
assert_eq!(length, 3);
23+
}

tests/ui/unsupported-features/proj_mismatch/expected

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/ui/unsupported-features/proj_mismatch/flat_map_fixme.rs

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)