Diagnose attempts to copy a non-copyable type. (#3345)

For now, we treat class types and `String` as non-copyable, because we
don't know how to emit SemIR to copy them yet. This will change as we
add support for copying those types when appropriate.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2023-10-28 01:00:59 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent ae22338468
commit d42c1a3a7c
5 changed files with 103 additions and 20 deletions
+46 -9
View File
@@ -632,6 +632,36 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
return value_id;
}
// Given a value expression, form a corresponding initializer that copies from
// that value, if it is possible to do so.
static auto PerformCopy(Context& context, SemIR::NodeId expr_id)
-> SemIR::NodeId {
auto expr = context.nodes().Get(expr_id);
auto type_id = expr.type_id();
if (type_id == SemIR::TypeId::Error) {
return SemIR::NodeId::BuiltinError;
}
// TODO: Directly track on the value representation whether it's a copy of
// the object representation.
auto value_rep = SemIR::GetValueRepresentation(context.sem_ir(), type_id);
if (value_rep.kind == SemIR::ValueRepresentation::Copy &&
value_rep.aggregate_kind == SemIR::ValueRepresentation::NotAggregate &&
value_rep.type_id == type_id) {
// For by-value scalar types, no explicit action is required. Initializing
// from a value expression is treated as copying the value.
return expr_id;
}
// TODO: We don't yet have rules for whether and when a class type is
// copyable, or how to perform the copy.
CARBON_DIAGNOSTIC(CopyOfUncopyableType, Error,
"Cannot copy value of type `{0}`.", std::string);
context.emitter().Emit(expr.parse_node(), CopyOfUncopyableType,
context.sem_ir().StringifyType(type_id));
return SemIR::NodeId::BuiltinError;
}
auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
ConversionTarget target) -> SemIR::NodeId {
auto& sem_ir = context.sem_ir();
@@ -730,18 +760,25 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
[[fallthrough]];
case SemIR::ExpressionCategory::DurableReference:
case SemIR::ExpressionCategory::EphemeralReference: {
// If we have a reference and don't want one, form a value binding.
if (target.kind != ConversionTarget::ValueOrReference &&
target.kind != ConversionTarget::Discarded) {
// TODO: Support types with custom value representations.
expr_id = context.AddNode(
SemIR::BindValue{expr.parse_node(), expr.type_id(), expr_id});
case SemIR::ExpressionCategory::EphemeralReference:
// If a reference expression is an acceptable result, we're done.
if (target.kind == ConversionTarget::ValueOrReference ||
target.kind == ConversionTarget::Discarded) {
break;
}
break;
}
// If we have a reference and don't want one, form a value binding.
// TODO: Support types with custom value representations.
expr_id = context.AddNode(
SemIR::BindValue{expr.parse_node(), expr.type_id(), expr_id});
// We now have a value expression.
[[fallthrough]];
case SemIR::ExpressionCategory::Value:
// When initializing from a value, perform a copy.
if (target.is_initializer()) {
expr_id = PerformCopy(context, expr_id);
}
break;
}
+2 -4
View File
@@ -6,7 +6,7 @@
var test_i32: i32 = 0;
var test_f64: f64 = 0.1;
var test_str: String = "Test";
let test_str: String = "Test";
var test_type: type = i32;
// CHECK:STDOUT: file "builtin_types.carbon" {
@@ -19,10 +19,8 @@ var test_type: type = i32;
// CHECK:STDOUT: %.loc8: f64 = real_literal 1e-1
// CHECK:STDOUT: assign %test_f64.var, %.loc8
// CHECK:STDOUT: %.1: type = ptr_type String
// CHECK:STDOUT: %test_str.var: ref String = var "test_str"
// CHECK:STDOUT: %test_str: ref String = bind_name "test_str", %test_str.var
// CHECK:STDOUT: %.loc9: String = string_literal "Test"
// CHECK:STDOUT: assign %test_str.var, %.loc9
// CHECK:STDOUT: %test_str: String = bind_name "test_str", %.loc9
// CHECK:STDOUT: %test_type.var: ref type = var "test_type"
// CHECK:STDOUT: %test_type: ref type = bind_name "test_type", %test_type.var
// CHECK:STDOUT: assign %test_type.var, i32
+52
View File
@@ -0,0 +1,52 @@
// 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
class X {
}
fn F(x: X) {
// TODO: Strings should eventually be copyable, once we decide how to
// represent them.
// CHECK:STDERR: fail_not_copyable.carbon:[[@LINE+3]]:19: ERROR: Cannot copy value of type `String`.
// CHECK:STDERR: var s: String = "hello";
// CHECK:STDERR: ^
var s: String = "hello";
// TODO: Decide on rules for when classes are copyable.
// CHECK:STDERR: fail_not_copyable.carbon:[[@LINE+3]]:14: ERROR: Cannot copy value of type `X`.
// CHECK:STDERR: var y: X = x;
// CHECK:STDERR: ^
var y: X = x;
}
// CHECK:STDOUT: file "fail_not_copyable.carbon" {
// CHECK:STDOUT: class_declaration @X, ()
// CHECK:STDOUT: %X: type = class_type @X
// CHECK:STDOUT: %.loc8: type = struct_type {}
// CHECK:STDOUT: %F: <function> = fn_decl @F
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @X {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F(%x: X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc8: type = tuple_type ()
// CHECK:STDOUT: %.loc7: type = ptr_type {}
// CHECK:STDOUT: %.1: type = ptr_type String
// CHECK:STDOUT: %s.var: ref String = var "s"
// CHECK:STDOUT: %s: ref String = bind_name "s", %s.var
// CHECK:STDOUT: %.loc16: String = string_literal "hello"
// CHECK:STDOUT: assign %s.var, <error>
// CHECK:STDOUT: %X.ref: type = name_reference "X", file.%X
// CHECK:STDOUT: %y.var: ref X = var "y"
// CHECK:STDOUT: %y: ref X = bind_name "y", %y.var
// CHECK:STDOUT: %x.ref: X = name_reference "x", %x
// CHECK:STDOUT: assign %y.var, <error>
// CHECK:STDOUT: return
// CHECK:STDOUT: }
@@ -129,6 +129,7 @@ CARBON_DIAGNOSTIC_KIND(ClassPreviousDefinition)
CARBON_DIAGNOSTIC_KIND(ClassRedefinition)
CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition)
CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop)
CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType)
CARBON_DIAGNOSTIC_KIND(DereferenceOfNonPointer)
CARBON_DIAGNOSTIC_KIND(DereferenceOfType)
CARBON_DIAGNOSTIC_KIND(FunctionPreviousDefinition)
+2 -7
View File
@@ -9,10 +9,7 @@ class C {
var b: C*;
}
fn F(c: C) -> C {
// TODO: We should copy `c` into the return slot here.
return c;
}
fn F(c: C) -> C;
fn Run() {
var c: C;
@@ -22,9 +19,7 @@ fn Run() {
// CHECK:STDOUT: ; ModuleID = 'basic.carbon'
// CHECK:STDOUT: source_filename = "basic.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define void @F(ptr sret({ i32, ptr }) %return, ptr %c) {
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT: declare void @F(ptr sret({ i32, ptr }), ptr)
// CHECK:STDOUT:
// CHECK:STDOUT: define void @main() {
// CHECK:STDOUT: %c = alloca { i32, ptr }, align 8