From c8f18446f4ec162fc83171ec863d3db2c1b8cb4e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 19 Jan 2023 14:56:01 -0800 Subject: [PATCH] Support explicit conversion of tuples. (#2537) Allow explicit conversion from a tuple of `T1`, `T2`, ... to a tuple of `U1`, `U2`, ... if each element has an explicit conversion. Also add a comment to existing `ImplicitAs` impl for tuples explaining its purpose. --- explorer/data/prelude.carbon | 27 ++++++++++++ explorer/testdata/tuple/convert.carbon | 42 +++++++++++++++++++ .../fail_implicit_convert_with_as.carbon | 27 ++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 explorer/testdata/tuple/convert.carbon create mode 100644 explorer/testdata/tuple/fail_implicit_convert_with_as.carbon diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index 0200ac52f965..a3b089fafa27 100644 --- a/explorer/data/prelude.carbon +++ b/explorer/data/prelude.carbon @@ -43,6 +43,7 @@ __match_first { fn Convert[self: Self]() -> U { return __EqualConvert(self, U); } } + // A tuple implicitly converts to another tuple if all its elements do. // TODO: Simplify this once we have variadics. // TODO: Should these be final? impl forall [U1:! type, T1:! ImplicitAs(U1)] @@ -67,6 +68,32 @@ __match_first { return (v1.Convert(), v2.Convert(), v3.Convert()); } } + + // A tuple explicitly converts to another tuple if all its elements do. Note + // that this fully overlaps with the previous set of impls for the case where + // an implicit conversion is possible. + impl forall [U1:! type, T1:! As(U1)] + (T1,) as As((U1,)) { + fn Convert[self: Self]() -> (U1,) { + let (v1: T1,) = self; + return (v1.Convert(),); + } + } + impl forall [U1:! type, U2:! type, T1:! As(U1), T2:! As(U2)] + (T1, T2) as As((U1, U2)) { + fn Convert[self: Self]() -> (U1, U2) { + let (v1: T1, v2: T2) = self; + return (v1.Convert(), v2.Convert()); + } + } + impl forall [U1:! type, U2:! type, U3:! type, + T1:! As(U1), T2:! As(U2), T3:! As(U3)] + (T1, T2, T3) as As((U1, U2, U3)) { + fn Convert[self: Self]() -> (U1, U2, U3) { + let (v1: T1, v2: T2, v3: T3) = self; + return (v1.Convert(), v2.Convert(), v3.Convert()); + } + } } // ---------------------- diff --git a/explorer/testdata/tuple/convert.carbon b/explorer/testdata/tuple/convert.carbon new file mode 100644 index 000000000000..14e8d7ff22de --- /dev/null +++ b/explorer/testdata/tuple/convert.carbon @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{explorer-run} +// RUN: %{explorer-run-trace} +// CHECK:STDOUT: 2 +// CHECK:STDOUT: 3 +// CHECK:STDOUT: 4 +// CHECK:STDOUT: 5 +// CHECK:STDOUT: result: 0 + +package ExplorerTest api; + +class A { + var a: i32; +} +class B { + var b: i32; +} +class C { + var c: i32; +} + +external impl A as ImplicitAs(B) { + fn Convert[self: Self]() -> B { return {.b = self.a + 1}; } +} +external impl B as As(C) { + fn Convert[self: Self]() -> C { return {.c = self.b + 2}; } +} + +fn Main() -> i32 { + var a: (A, A) = ({.a = 1}, {.a = 2}); + var b: (B, B) = a; + Print("{0}", b[0].b); + Print("{0}", b[1].b); + var c: (C, C) = b as (C, C); + Print("{0}", c[0].c); + Print("{0}", c[1].c); + return 0; +} diff --git a/explorer/testdata/tuple/fail_implicit_convert_with_as.carbon b/explorer/testdata/tuple/fail_implicit_convert_with_as.carbon new file mode 100644 index 000000000000..16649c777462 --- /dev/null +++ b/explorer/testdata/tuple/fail_implicit_convert_with_as.carbon @@ -0,0 +1,27 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +class A { + var a: i32; +} +class B { + var b: i32; +} + +external impl A as As(B) { + fn Convert[self: Self]() -> B { return {.b = self.a}; } +} + +fn Main() -> i32 { + var a: (i32, A) = (1, {.a = 2}); + // CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/tuple/fail_implicit_convert_with_as.carbon:[[@LINE+1]]: type error in name binding: '(i32, class A)' is not implicitly convertible to '(i32, class B)' + var b: (i32, B) = a; + return 0; +}