Fix crash on use of uninitialized array element, and improve unformed checking (#2862)

Per #257, we should be treating unformedness as all-or-nothing, rather than being a per-field or per-array-element property. Previously we initialized an array with no explicit initializer as containing a sequence of uninitialized values, but that led to crashes when attempting to access those values, as the checks for reading an uninitialized value only expected values to be uninitialized at the top level.

Also, we had existing tests that attempt to store to an element of an uninitialized array. We now detect that and treat it as UB during evaluation, rather than crashing due to trying to perform field access into an uninitialized value.

Finally, many of these problems can be detected statically, but the resolve_unformed pass wasn't catching them because it missed a few expression and declaration forms. Support for those cases has been added too. This causes the pass to recurse more often, and in particular our existing recursion test started hitting a stack overflow after this, so resolve_unformed now uses `RunWithExtraStack`. In passing, remove the need to explicitly tell `RunWithExtraStack` the return type, and infer it as the return type of the callable instead.
This commit is contained in:
Richard Smith
2023-05-31 16:35:21 -07:00
committed by GitHub
parent 2e4beaf8f0
commit 81e53886a8
25 changed files with 319 additions and 89 deletions
+2 -3
View File
@@ -712,9 +712,8 @@ class Optional(T:! type) {
}
}
Assert(false, "Attempted to unwrap empty Optional");
// TODO: Drop uninitialized variable & return when we can flag unreachable paths.
var y: T;
return y;
// TODO: Drop return when we can flag unreachable paths.
return self.Get();
}
var element: OptionalElement(T);
+1
View File
@@ -219,6 +219,7 @@ cc_library(
"resolve_unformed.h",
],
deps = [
":stack_space",
"//common:check",
"//explorer/ast",
"//explorer/ast:static_scope",
+5
View File
@@ -38,6 +38,11 @@ auto Heap::Write(const Address& a, Nonnull<const Value*> v,
SourceLocation source_loc) -> ErrorOr<Success> {
CARBON_RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc));
if (states_[a.allocation_.index_] == ValueState::Uninitialized) {
if (!a.element_path_.IsEmpty()) {
return ProgramError(source_loc)
<< "undefined behavior: store to subobject of uninitialized value "
<< *values_[a.allocation_.index_];
}
states_[a.allocation_.index_] = ValueState::Alive;
}
CARBON_ASSIGN_OR_RETURN(values_[a.allocation_.index_],
-33
View File
@@ -1596,10 +1596,6 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
*print_stream_ << llvm::formatv(format_string);
break;
case 1: {
if ((*args[1]).kind() == Value::Kind::UninitializedValue) {
return ProgramError(exp.source_loc())
<< "Printing uninitialized value";
}
*print_stream_ << llvm::formatv(format_string,
cast<IntValue>(*args[1]).value());
break;
@@ -2134,18 +2130,6 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
if (definition.has_init()) {
CARBON_ASSIGN_OR_RETURN(
v, Convert(act.results()[0], dest_type, stmt.source_loc()));
} else if (dest_type->kind() == Value::Kind::StaticArrayType) {
const auto& array = cast<StaticArrayType>(dest_type);
CARBON_CHECK(array->has_size());
const auto& element_type = array->element_type();
const auto size = array->size();
std::vector<Nonnull<const Value*>> elements;
elements.reserve(size);
for (size_t i = 0; i < size; i++) {
elements.push_back(arena_->New<UninitializedValue>(&element_type));
}
v = arena_->New<TupleValueBase>(Value::Kind::TupleValue, elements);
} else {
v = arena_->New<UninitializedValue>(p);
}
@@ -2290,7 +2274,6 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
switch (decl.kind()) {
case DeclarationKind::VariableDeclaration: {
const auto& var_decl = cast<VariableDeclaration>(decl);
const auto* var_type = &var_decl.binding().static_type();
if (var_decl.has_initializer()) {
if (act.pos() == 0) {
return todo_.Spawn(
@@ -2303,22 +2286,6 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
todo_.Initialize(&var_decl.binding(), v);
return todo_.FinishAction();
}
} else if (var_type->kind() == Value::Kind::StaticArrayType) {
const auto& array = cast<StaticArrayType>(var_type);
CARBON_CHECK(array->has_size());
const auto& element_type = array->element_type();
const auto size = array->size();
std::vector<Nonnull<const Value*>> elements;
elements.reserve(size);
for (size_t i = 0; i < size; i++) {
elements.push_back(arena_->New<UninitializedValue>(&element_type));
}
Nonnull<const Value*> v =
arena_->New<TupleValueBase>(Value::Kind::TupleValue, elements);
todo_.Initialize(&var_decl.binding(), v);
return todo_.FinishAction();
} else {
Nonnull<const Value*> v =
arena_->New<UninitializedValue>(&var_decl.binding().value());
+6 -6
View File
@@ -276,7 +276,7 @@ auto NameResolver::AddExposedNames(const Declaration& declaration,
auto NameResolver::ResolveNames(Expression& expression,
const StaticScope& enclosing_scope)
-> ErrorOr<std::optional<ValueNodeView>> {
return RunWithExtraStack<ErrorOr<std::optional<ValueNodeView>>>(
return RunWithExtraStack(
[&]() { return ResolveNamesImpl(expression, enclosing_scope); });
}
@@ -463,7 +463,7 @@ auto NameResolver::ResolveNamesImpl(Expression& expression,
auto NameResolver::ResolveNames(WhereClause& clause,
const StaticScope& enclosing_scope)
-> ErrorOr<Success> {
return RunWithExtraStack<ErrorOr<Success>>(
return RunWithExtraStack(
[&]() { return ResolveNamesImpl(clause, enclosing_scope); });
}
@@ -499,7 +499,7 @@ auto NameResolver::ResolveNamesImpl(WhereClause& clause,
auto NameResolver::ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
-> ErrorOr<Success> {
return RunWithExtraStack<ErrorOr<Success>>(
return RunWithExtraStack(
[&]() { return ResolveNamesImpl(pattern, enclosing_scope); });
}
@@ -560,7 +560,7 @@ auto NameResolver::ResolveNamesImpl(Pattern& pattern,
auto NameResolver::ResolveNames(Statement& statement,
StaticScope& enclosing_scope)
-> ErrorOr<Success> {
return RunWithExtraStack<ErrorOr<Success>>(
return RunWithExtraStack(
[&]() { return ResolveNamesImpl(statement, enclosing_scope); });
}
@@ -703,7 +703,7 @@ auto NameResolver::ResolveNames(Declaration& declaration,
StaticScope& enclosing_scope,
ResolveFunctionBodies bodies)
-> ErrorOr<Success> {
return RunWithExtraStack<ErrorOr<Success>>(
return RunWithExtraStack(
[&]() { return ResolveNamesImpl(declaration, enclosing_scope, bodies); });
}
@@ -922,7 +922,7 @@ auto NameResolver::ResolveNamesImpl(Declaration& declaration,
}
auto ResolveNames(AST& ast) -> ErrorOr<Success> {
return RunWithExtraStack<ErrorOr<Success>>([&]() -> ErrorOr<Success> {
return RunWithExtraStack([&]() -> ErrorOr<Success> {
NameResolver resolver;
StaticScope file_scope;
+95 -29
View File
@@ -11,6 +11,7 @@
#include "explorer/ast/expression.h"
#include "explorer/ast/pattern.h"
#include "explorer/common/nonnull.h"
#include "explorer/interpreter/stack_space.h"
using llvm::cast;
@@ -54,22 +55,35 @@ auto FlowFacts::TakeAction(Nonnull<const AstNode*> node, ActionType action,
return Success();
}
// Traverses the sub-AST rooted at the given node, resolving the formed/unformed
// states of local variables within it and updating the flow facts.
static auto ResolveUnformed(Nonnull<const Expression*> expression,
FlowFacts& flow_facts, FlowFacts::ActionType action)
static auto ResolveUnformedImpl(Nonnull<const Expression*> expression,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Pattern*> pattern,
FlowFacts& flow_facts, FlowFacts::ActionType action)
static auto ResolveUnformedImpl(Nonnull<const Pattern*> pattern,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Statement*> statement,
FlowFacts& flow_facts, FlowFacts::ActionType action)
static auto ResolveUnformedImpl(Nonnull<const Statement*> statement,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
static auto ResolveUnformedImpl(Nonnull<const Expression*> expression,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Expression*> expression,
FlowFacts& flow_facts, FlowFacts::ActionType action)
// Traverses the sub-AST rooted at the given node, resolving the formed/unformed
// states of local variables within it and updating the flow facts.
template <typename T>
static auto ResolveUnformed(Nonnull<const T*> expression, FlowFacts& flow_facts,
FlowFacts::ActionType action) -> ErrorOr<Success> {
return RunWithExtraStack(
[&] { return ResolveUnformedImpl(expression, flow_facts, action); });
}
static auto ResolveUnformedImpl(Nonnull<const Expression*> expression,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success> {
switch (expression->kind()) {
case ExpressionKind::IdentifierExpression: {
@@ -85,6 +99,12 @@ static auto ResolveUnformed(Nonnull<const Expression*> expression,
ResolveUnformed(&call.argument(), flow_facts, action));
break;
}
case ExpressionKind::IntrinsicExpression: {
const auto& intrin = cast<IntrinsicExpression>(*expression);
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&intrin.args(), flow_facts, action));
break;
}
case ExpressionKind::TupleLiteral:
for (Nonnull<const Expression*> field :
cast<TupleLiteral>(*expression).fields()) {
@@ -100,6 +120,9 @@ static auto ResolveUnformed(Nonnull<const Expression*> expression,
// When a variable is taken address of, defer the unformed check to
// runtime. A more sound analysis can be implemented when a
// points-to analysis is available.
// TODO: This isn't enough to permit &x.y or &x[i] when x is
// uninitialized, because x.y and x[i] both require x to be
// initialized.
ResolveUnformed(opt_exp.arguments().front(), flow_facts,
FlowFacts::ActionType::Form));
} else {
@@ -117,15 +140,35 @@ static auto ResolveUnformed(Nonnull<const Expression*> expression,
}
break;
case ExpressionKind::SimpleMemberAccessExpression:
CARBON_RETURN_IF_ERROR(ResolveUnformed(
&cast<SimpleMemberAccessExpression>(*expression).object(), flow_facts,
FlowFacts::ActionType::Check));
case ExpressionKind::CompoundMemberAccessExpression:
case ExpressionKind::BaseAccessExpression:
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&cast<MemberAccessExpression>(*expression).object(),
flow_facts, FlowFacts::ActionType::Check));
break;
case ExpressionKind::BuiltinConvertExpression:
CARBON_RETURN_IF_ERROR(ResolveUnformed(
cast<BuiltinConvertExpression>(*expression).source_expression(),
flow_facts, FlowFacts::ActionType::Check));
break;
case ExpressionKind::IndexExpression:
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&cast<IndexExpression>(*expression).object(),
flow_facts, FlowFacts::ActionType::Check));
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&cast<IndexExpression>(*expression).offset(),
flow_facts, FlowFacts::ActionType::Check));
break;
case ExpressionKind::IfExpression: {
const auto& if_exp = cast<IfExpression>(*expression);
CARBON_RETURN_IF_ERROR(ResolveUnformed(&if_exp.condition(), flow_facts,
FlowFacts::ActionType::Check));
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&if_exp.then_expression(), flow_facts, action));
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&if_exp.else_expression(), flow_facts, action));
break;
}
case ExpressionKind::DotSelfExpression:
case ExpressionKind::IntLiteral:
case ExpressionKind::BoolLiteral:
@@ -135,13 +178,8 @@ static auto ResolveUnformed(Nonnull<const Expression*> expression,
case ExpressionKind::StringTypeLiteral:
case ExpressionKind::TypeTypeLiteral:
case ExpressionKind::ValueLiteral:
case ExpressionKind::IndexExpression:
case ExpressionKind::CompoundMemberAccessExpression:
case ExpressionKind::BaseAccessExpression:
case ExpressionKind::IfExpression:
case ExpressionKind::WhereExpression:
case ExpressionKind::StructTypeLiteral:
case ExpressionKind::IntrinsicExpression:
case ExpressionKind::UnimplementedExpression:
case ExpressionKind::FunctionTypeLiteral:
case ExpressionKind::ArrayTypeLiteral:
@@ -150,8 +188,9 @@ static auto ResolveUnformed(Nonnull<const Expression*> expression,
return Success();
}
static auto ResolveUnformed(Nonnull<const Pattern*> pattern,
FlowFacts& flow_facts, FlowFacts::ActionType action)
static auto ResolveUnformedImpl(Nonnull<const Pattern*> pattern,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success> {
switch (pattern->kind()) {
case PatternKind::BindingPattern: {
@@ -178,8 +217,9 @@ static auto ResolveUnformed(Nonnull<const Pattern*> pattern,
return Success();
}
static auto ResolveUnformed(Nonnull<const Statement*> statement,
FlowFacts& flow_facts, FlowFacts::ActionType action)
static auto ResolveUnformedImpl(Nonnull<const Statement*> statement,
FlowFacts& flow_facts,
FlowFacts::ActionType action)
-> ErrorOr<Success> {
switch (statement->kind()) {
case StatementKind::Block: {
@@ -280,15 +320,36 @@ static auto ResolveUnformed(Nonnull<const Statement*> statement,
}
break;
}
case StatementKind::For: {
const auto& for_stmt = cast<For>(*statement);
CARBON_RETURN_IF_ERROR(ResolveUnformed(
&for_stmt.loop_target(), flow_facts, FlowFacts::ActionType::Check));
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&for_stmt.body(), flow_facts, action));
break;
}
case StatementKind::Break:
case StatementKind::Continue:
case StatementKind::For:
// do nothing
break;
}
return Success();
}
static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
-> ErrorOr<Success>;
static auto ResolveUnformed(
llvm::ArrayRef<Nonnull<const Declaration*>> declarations)
-> ErrorOr<Success> {
return RunWithExtraStack([declarations]() -> ErrorOr<Success> {
for (Nonnull<const Declaration*> declaration : declarations) {
CARBON_RETURN_IF_ERROR(ResolveUnformed(declaration));
}
return Success();
});
}
static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
-> ErrorOr<Success> {
switch (declaration->kind()) {
@@ -306,12 +367,7 @@ static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
break;
}
case DeclarationKind::NamespaceDeclaration:
case DeclarationKind::ClassDeclaration:
case DeclarationKind::MixDeclaration:
case DeclarationKind::MixinDeclaration:
case DeclarationKind::InterfaceDeclaration:
case DeclarationKind::ConstraintDeclaration:
case DeclarationKind::ImplDeclaration:
case DeclarationKind::MatchFirstDeclaration:
case DeclarationKind::ChoiceDeclaration:
case DeclarationKind::VariableDeclaration:
@@ -322,6 +378,16 @@ static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
case DeclarationKind::AliasDeclaration:
// do nothing
break;
case DeclarationKind::ClassDeclaration:
return ResolveUnformed(cast<ClassDeclaration>(declaration)->members());
case DeclarationKind::MixinDeclaration:
return ResolveUnformed(cast<MixinDeclaration>(declaration)->members());
case DeclarationKind::InterfaceDeclaration:
case DeclarationKind::ConstraintDeclaration:
return ResolveUnformed(
cast<ConstraintTypeDeclaration>(declaration)->members());
case DeclarationKind::ImplDeclaration:
return ResolveUnformed(cast<ImplDeclaration>(declaration)->members());
}
return Success();
}
+5 -3
View File
@@ -27,11 +27,13 @@ auto RunWithExtraStackHelper(llvm::function_ref<void()> fn) -> void;
// create the current thread.
//
// Usage:
// return RunWithExtraStack<ReturnType>([&]() -> ReturnType {
// return RunWithExtraStack([&]() -> ReturnType {
// <function body>
// });
template <typename ReturnType>
auto RunWithExtraStack(llvm::function_ref<ReturnType()> fn) -> ReturnType {
template <typename Fn>
auto RunWithExtraStack(Fn fn) -> decltype(fn()) {
using ReturnType = decltype(fn());
static_assert(!std::is_reference_v<ReturnType>);
if (Internal::IsStackSpaceNearlyExhausted()) {
std::optional<ReturnType> result;
Internal::RunWithExtraStackHelper([&] { result = fn(); });
+1 -2
View File
@@ -2742,8 +2742,7 @@ auto TypeChecker::CheckAddrMeAccess(
auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
const ImplScope& impl_scope)
-> ErrorOr<Success> {
return RunWithExtraStack<ErrorOr<Success>>(
[&]() { return TypeCheckExpImpl(e, impl_scope); });
return RunWithExtraStack([&]() { return TypeCheckExpImpl(e, impl_scope); });
}
// NOLINTNEXTLINE(readability-function-size)
@@ -40,7 +40,7 @@ static auto ParseAndExecuteHelper(std::function<ErrorOr<AST>(Arena*)> parse,
Nonnull<TraceStream*> trace_stream,
Nonnull<llvm::raw_ostream*> print_stream)
-> ErrorOr<int> {
return RunWithExtraStack<ErrorOr<int>>([&]() -> ErrorOr<int> {
return RunWithExtraStack([&]() -> ErrorOr<int> {
Arena arena;
auto cursor = std::chrono::steady_clock::now();
@@ -8,7 +8,7 @@ package ExplorerTest impl;
fn Main() -> i32 {
var my_array : [i32; 1];
// CHECK:STDERR: RUNTIME ERROR: fail_print_uninitalized_array_element.carbon:[[@LINE+1]]: Printing uninitialized value
// CHECK:STDERR: COMPILATION ERROR: fail_print_uninitalized_array_element.carbon:[[@LINE+1]]: use of uninitialized variable my_array
Print("{0}", my_array[0]);
return 0;
}
@@ -3,13 +3,12 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// CHECK:STDOUT: 100
// CHECK:STDOUT: result: 0
package ExplorerTest api;
fn Main() -> i32 {
var my_array : [i32; 1];
// CHECK:STDERR: COMPILATION ERROR: fail_store_to_uninitialized_array.carbon:[[@LINE+1]]: use of uninitialized variable my_array
my_array[0] = 100;
Print("{0}", my_array[0]);
return 0;
@@ -0,0 +1,16 @@
// 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
package ExplorerTest api;
var my_array : [i32; 1];
fn Main() -> i32 {
// CHECK:STDERR: RUNTIME ERROR: fail_store_to_uninitialized_global_array.carbon:[[@LINE+1]]: undefined behavior: store to subobject of uninitialized value Uninit<Placeholder<my_array>>
my_array[0] = 100;
Print("{0}", my_array[0]);
return 0;
}
@@ -31,14 +31,14 @@ class MyOptional(T:! type){
}
fn get[self: Self] () -> T {
var y: T;
var x: MyOptionalElement(T) = self.element;
match(x){
case MyOptionalElement(T).Element( var x: T ) =>{
return x;
}
}
return y;
// TODO: Mark this as unreachable somehow.
return get();
}
var element: MyOptionalElement(T);
@@ -0,0 +1,23 @@
// 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
package ExplorerTest api;
class Point {
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var p: Point;
if (1 == 0) {
p = {.x = 0, .y = 0};
}
// CHECK:STDERR: RUNTIME ERROR: fail_store_to_uninitialized_class.carbon:[[@LINE+1]]: undefined behavior: store to subobject of uninitialized value Uninit<Placeholder<p>>
p.x = 1;
p.y = 2;
return p.x;
}
@@ -3,15 +3,11 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// CHECK:STDOUT: 100
// CHECK:STDOUT: result: 0
package ExplorerTest api;
var my_array : [i32; 1];
fn Main() -> i32 {
my_array[0] = 100;
Print("{0}", my_array[0]);
return 0;
var v: [i32; 2];
// CHECK:STDERR: COMPILATION ERROR: fail_array.carbon:[[@LINE+1]]: use of uninitialized variable v
return v[0];
}
+16
View File
@@ -0,0 +1,16 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var v: [i32; 2];
if (0 == 1) {
v = (1, 2);
}
// CHECK:STDERR: RUNTIME ERROR: fail_array_dynamic.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit<Placeholder<v>>
return v[0];
}
+19
View File
@@ -0,0 +1,19 @@
// 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
package ExplorerTest api;
base class B {
var n: i32;
}
class D extends B {}
fn Main() -> i32 {
var d: D;
// CHECK:STDERR: COMPILATION ERROR: fail_base_access.carbon:[[@LINE+1]]: use of uninitialized variable d
return d.n;
}
@@ -0,0 +1,17 @@
// 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
package ExplorerTest api;
class C {
var n: i32;
}
fn Main() -> i32 {
var c: C;
// CHECK:STDERR: COMPILATION ERROR: fail_compound_member_access.carbon:[[@LINE+1]]: use of uninitialized variable c
return c.(C.n);
}
@@ -0,0 +1,17 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var x: i32;
if (0 == 1) {
x = 0;
}
// Static analysis thinks `x` may be formed, defer the check to run-time.
// CHECK:STDERR: RUNTIME ERROR: fail_control_flow_defer_to_dynamic.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit<Placeholder<x>>
return x;
}
+13
View File
@@ -0,0 +1,13 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var v: bool;
// CHECK:STDERR: COMPILATION ERROR: fail_if_cond.carbon:[[@LINE+1]]: use of uninitialized variable v
return if v then 1 else 2;
}
+13
View File
@@ -0,0 +1,13 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var v: i32;
// CHECK:STDERR: COMPILATION ERROR: fail_if_else.carbon:[[@LINE+1]]: use of uninitialized variable v
return if false then 1 else v;
}
+13
View File
@@ -0,0 +1,13 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var v: i32;
// CHECK:STDERR: COMPILATION ERROR: fail_if_then.carbon:[[@LINE+1]]: use of uninitialized variable v
return if true then v else 2;
}
+19
View File
@@ -0,0 +1,19 @@
// 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
package ExplorerTest api;
class C {
fn F() -> i32 {
var n: i32;
// CHECK:STDERR: COMPILATION ERROR: fail_in_class.carbon:[[@LINE+1]]: use of uninitialized variable n
return n;
}
}
fn Main() -> i32 {
return C.F();
}
@@ -0,0 +1,17 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var pt: {.x: i32, .y: i32};
// TODO: Without this, we don't allow taking the address of `pt.x`.
// That's probably too restrictive.
if (1 == 0) { pt = {.x = 1, .y = 2}; }
var p: i32* = &pt.x;
// CHECK:STDERR: RUNTIME ERROR: fail_indirect_member_access.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit<Placeholder<pt>>
return *p;
}
+13
View File
@@ -0,0 +1,13 @@
// 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
package ExplorerTest api;
fn Main() -> i32 {
var v: i32;
// CHECK:STDERR: COMPILATION ERROR: fail_intrinsic.carbon:[[@LINE+1]]: use of uninitialized variable v
return if __intrinsic_int_eq(v, v) then 1 else 2;
}