Make type contexts expect a value of type Type. (#2357)

In particular, this means that a type can implement `ImplicitAs(Type)` and have values of that type behave like types.

This implies that `()` and `{}` are no longer types. They are now values whose type is the result of converting `()` or `{}` to type `Type`, as has been discussed recently and seems to be the supported direction. This fixes various cases where these types were previously mishandled.
This commit is contained in:
Richard Smith
2022-10-28 08:20:49 -07:00
committed by GitHub
parent f46eaf49a2
commit 0219218b01
40 changed files with 674 additions and 319 deletions
+6 -4
View File
@@ -51,7 +51,11 @@ abstract class Expression : AstNode;
class BoolTypeLiteral : Expression;
class BoolLiteral : Expression;
class CallExpression : Expression;
class FunctionTypeLiteral : Expression;
abstract class ConstantValueLiteral : Expression;
class FunctionTypeLiteral : ConstantValueLiteral;
class StructTypeLiteral : ConstantValueLiteral;
class ArrayTypeLiteral : ConstantValueLiteral;
class ValueLiteral : ConstantValueLiteral;
abstract class MemberAccessExpression : Expression;
class SimpleMemberAccessExpression : MemberAccessExpression;
class CompoundMemberAccessExpression : MemberAccessExpression;
@@ -64,16 +68,14 @@ abstract class Expression : AstNode;
class StringTypeLiteral : Expression;
class TupleLiteral : Expression;
class StructLiteral : Expression;
class StructTypeLiteral : Expression;
class TypeTypeLiteral : Expression;
class ValueLiteral : Expression;
class IdentifierExpression : Expression;
class DotSelfExpression : Expression;
class IntrinsicExpression : Expression;
class IfExpression : Expression;
class WhereExpression : Expression;
class BuiltinConvertExpression : Expression;
class UnimplementedExpression : Expression;
class ArrayTypeLiteral : Expression;
abstract class WhereClause : AstNode;
class IsWhereClause : WhereClause;
class EqualsWhereClause : WhereClause;
+7
View File
@@ -257,6 +257,12 @@ void Expression::Print(llvm::raw_ostream& out) const {
}
break;
}
case ExpressionKind::BuiltinConvertExpression: {
// These don't represent source syntax, so just print the original
// expression.
out << *cast<BuiltinConvertExpression>(this)->source_expression();
break;
}
case ExpressionKind::UnimplementedExpression: {
const auto& unimplemented = cast<UnimplementedExpression>(*this);
out << "UnimplementedExpression<" << unimplemented.label() << ">(";
@@ -332,6 +338,7 @@ void Expression::PrintID(llvm::raw_ostream& out) const {
case ExpressionKind::CompoundMemberAccessExpression:
case ExpressionKind::IfExpression:
case ExpressionKind::WhereExpression:
case ExpressionKind::BuiltinConvertExpression:
case ExpressionKind::TupleLiteral:
case ExpressionKind::StructLiteral:
case ExpressionKind::StructTypeLiteral:
+77 -28
View File
@@ -504,21 +504,15 @@ class TupleLiteral : public Expression {
std::vector<Nonnull<Expression*>> fields_;
};
// A non-empty literal value of a struct type.
//
// It can't be empty because the syntax `{}` is a struct type literal as well
// as a literal value of that type, so for consistency we always represent it
// as a StructTypeLiteral rather than let it oscillate unpredictably between
// the two.
// A literal value of a struct type.
class StructLiteral : public Expression {
public:
explicit StructLiteral(SourceLocation loc) : StructLiteral(loc, {}) {}
explicit StructLiteral(SourceLocation loc,
std::vector<FieldInitializer> fields)
: Expression(AstNodeKind::StructLiteral, loc),
fields_(std::move(fields)) {
CARBON_CHECK(!fields_.empty())
<< "`{}` is represented as a StructTypeLiteral, not a StructLiteral.";
}
fields_(std::move(fields)) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromStructLiteral(node->kind());
@@ -531,18 +525,48 @@ class StructLiteral : public Expression {
std::vector<FieldInitializer> fields_;
};
// A base class for literals with a constant value determined by type-checking.
class ConstantValueLiteral : public Expression {
public:
explicit ConstantValueLiteral(
AstNodeKind kind, SourceLocation source_loc,
std::optional<Nonnull<const Value*>> constant_value = std::nullopt)
: Expression(kind, source_loc), constant_value_(constant_value) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromConstantValueLiteral(node->kind());
}
// Returns the constant value of this expression.
auto constant_value() const -> const Value& {
CARBON_CHECK(constant_value_);
return **constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CARBON_CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::optional<Nonnull<const Value*>> constant_value_;
};
// A literal representing a struct type.
//
// Code that handles this type may sometimes need to have special-case handling
// for `{}`, which is a struct value in addition to being a struct type.
class StructTypeLiteral : public Expression {
// Note that a struct type literal can't be empty because `{}` is a struct
// value. However, that value implicitly converts to a type.
class StructTypeLiteral : public ConstantValueLiteral {
public:
explicit StructTypeLiteral(SourceLocation loc) : StructTypeLiteral(loc, {}) {}
explicit StructTypeLiteral(SourceLocation loc,
std::vector<FieldInitializer> fields)
: Expression(AstNodeKind::StructTypeLiteral, loc),
fields_(std::move(fields)) {}
: ConstantValueLiteral(AstNodeKind::StructTypeLiteral, loc),
fields_(std::move(fields)) {
CARBON_CHECK(!fields_.empty())
<< "`{}` is represented as a StructLiteral, not a StructTypeLiteral.";
}
static auto classof(const AstNode* node) -> bool {
return InheritsFromStructTypeLiteral(node->kind());
@@ -623,12 +647,12 @@ class CallExpression : public Expression {
Bindings bindings_;
};
class FunctionTypeLiteral : public Expression {
class FunctionTypeLiteral : public ConstantValueLiteral {
public:
explicit FunctionTypeLiteral(SourceLocation source_loc,
Nonnull<TupleLiteral*> parameter,
Nonnull<Expression*> return_type)
: Expression(AstNodeKind::FunctionTypeLiteral, source_loc),
: ConstantValueLiteral(AstNodeKind::FunctionTypeLiteral, source_loc),
parameter_(parameter),
return_type_(return_type) {}
@@ -688,13 +712,13 @@ class TypeTypeLiteral : public Expression {
// A literal value. This is used in desugaring, and can't be expressed in
// source syntax.
class ValueLiteral : public Expression {
class ValueLiteral : public ConstantValueLiteral {
public:
// Value literals are created by type-checking, and so are created with their
// type and value category already known.
ValueLiteral(SourceLocation source_loc, Nonnull<const Value*> value,
Nonnull<const Value*> type, ValueCategory value_category)
: Expression(AstNodeKind::ValueLiteral, source_loc), value_(value) {
: ConstantValueLiteral(AstNodeKind::ValueLiteral, source_loc, value) {
set_static_type(type);
set_value_category(value_category);
}
@@ -702,11 +726,6 @@ class ValueLiteral : public Expression {
static auto classof(const AstNode* node) -> bool {
return InheritsFromValueLiteral(node->kind());
}
auto value() const -> const Value& { return *value_; }
private:
Nonnull<const Value*> value_;
};
class IntrinsicExpression : public Expression {
@@ -936,6 +955,36 @@ class WhereExpression : public RewritableMixin<Expression> {
std::optional<Nonnull<const GenericBinding*>> enclosing_dot_self_;
};
// A builtin conversion to a type determined by type-checking. These are
// created by type-checking when a type conversion is found to be necessary but
// that conversion is implemented directly rather than by an `ImplicitAs`
// implementation.
class BuiltinConvertExpression : public Expression {
public:
BuiltinConvertExpression(Nonnull<Expression*> source_expression,
Nonnull<const Value*> destination_type)
: Expression(AstNodeKind::BuiltinConvertExpression,
source_expression->source_loc()),
source_expression_(source_expression) {
set_static_type(destination_type);
set_value_category(ValueCategory::Let);
}
static auto classof(const AstNode* node) -> bool {
return InheritsFromBuiltinConvertExpression(node->kind());
}
auto source_expression() -> Nonnull<Expression*> {
return source_expression_;
}
auto source_expression() const -> Nonnull<const Expression*> {
return source_expression_;
}
private:
Nonnull<Expression*> source_expression_;
};
// An expression whose semantics have not been implemented. This can be used
// as a placeholder during development, in order to implement and test parsing
// of a new expression syntax without having to implement its semantics.
@@ -976,14 +1025,14 @@ class UnimplementedExpression : public Expression {
};
// A literal representing a statically-sized array type.
class ArrayTypeLiteral : public Expression {
class ArrayTypeLiteral : public ConstantValueLiteral {
public:
// Constructs an array type literal which uses the given expressions to
// represent the element type and size.
ArrayTypeLiteral(SourceLocation source_loc,
Nonnull<Expression*> element_type_expression,
Nonnull<Expression*> size_expression)
: Expression(AstNodeKind::ArrayTypeLiteral, source_loc),
: ConstantValueLiteral(AstNodeKind::ArrayTypeLiteral, source_loc),
element_type_expression_(element_type_expression),
size_expression_(size_expression) {}
+6
View File
@@ -115,6 +115,12 @@ static auto ExpressionToProto(const Expression& expression)
break;
}
case ExpressionKind::BuiltinConvertExpression: {
expression_proto = ExpressionToProto(
*cast<BuiltinConvertExpression>(expression).source_expression());
break;
}
case ExpressionKind::CallExpression: {
const auto& call = cast<CallExpression>(expression);
auto* call_proto = expression_proto.mutable_call();
+64 -60
View File
@@ -289,11 +289,13 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
generic_args[&var_type.binding()] = v;
return true;
}
case Value::Kind::TupleType:
case Value::Kind::TupleValue:
switch (v->kind()) {
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
const auto& p_tup = cast<TupleValue>(*p);
const auto& v_tup = cast<TupleValue>(*v);
const auto& p_tup = cast<TupleValueBase>(*p);
const auto& v_tup = cast<TupleValueBase>(*v);
CARBON_CHECK(p_tup.elements().size() == v_tup.elements().size());
for (size_t i = 0; i < p_tup.elements().size(); ++i) {
if (!PatternMatch(p_tup.elements()[i], v_tup.elements()[i],
@@ -305,7 +307,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
return true;
}
case Value::Kind::UninitializedValue: {
const auto& p_tup = cast<TupleValue>(*p);
const auto& p_tup = cast<TupleValueBase>(*p);
for (const auto& ele : p_tup.elements()) {
if (!PatternMatch(ele, arena->New<UninitializedValue>(ele),
source_loc, bindings, generic_args, trace_stream,
@@ -501,6 +503,7 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
case ExpressionKind::WhereExpression:
case ExpressionKind::DotSelfExpression:
case ExpressionKind::ArrayTypeLiteral:
case ExpressionKind::BuiltinConvertExpression:
CARBON_FATAL() << "Can't treat expression as lvalue: " << exp;
case ExpressionKind::UnimplementedExpression:
CARBON_FATAL() << "Unimplemented: " << exp;
@@ -669,6 +672,8 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
case Value::Kind::TypeType:
case Value::Kind::FunctionType:
case Value::Kind::PointerType:
case Value::Kind::TupleType:
case Value::Kind::StructType:
case Value::Kind::AutoType:
case Value::Kind::NominalClassType:
case Value::Kind::MixinPseudoType:
@@ -722,6 +727,13 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
InstantiateType(destination_type, source_loc));
return arena_->New<NominalClassValue>(inst_dest, value);
}
case Value::Kind::TypeType:
case Value::Kind::ConstraintType:
case Value::Kind::InterfaceType: {
CARBON_CHECK(struct_val.elements().empty())
<< "only empty structs convert to Type";
return arena_->New<StructType>();
}
default: {
CARBON_CHECK(IsValueKindDependent(destination_type) ||
isa<TypeType, ConstraintType>(destination_type))
@@ -731,26 +743,13 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
}
}
}
case Value::Kind::StructType: {
// The value `{}` has kind `StructType` not `StructValue`. This value can
// be converted to an empty class type.
if (const auto* destination_class_type =
dyn_cast<NominalClassType>(destination_type)) {
CARBON_CHECK(cast<StructType>(*value).fields().empty())
<< "only an empty struct type value converts to class type";
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_dest,
InstantiateType(destination_type, source_loc));
return arena_->New<NominalClassValue>(inst_dest, value);
}
return value;
}
case Value::Kind::TupleValue: {
const auto& tuple = cast<TupleValue>(value);
const auto* tuple = cast<TupleValue>(value);
std::vector<Nonnull<const Value*>> destination_element_types;
switch (destination_type->kind()) {
case Value::Kind::TupleValue:
case Value::Kind::TupleType:
destination_element_types =
cast<TupleValue>(destination_type)->elements();
cast<TupleType>(destination_type)->elements();
break;
case Value::Kind::StaticArrayType: {
const auto& array_type = cast<StaticArrayType>(*destination_type);
@@ -758,6 +757,18 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
&array_type.element_type());
break;
}
case Value::Kind::TypeType:
case Value::Kind::ConstraintType:
case Value::Kind::InterfaceType: {
std::vector<Nonnull<const Value*>> new_elements;
Nonnull<const Value*> type_type = arena_->New<TypeType>();
for (Nonnull<const Value*> value : tuple->elements()) {
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value_as_type,
Convert(value, type_type, source_loc));
new_elements.push_back(value_as_type);
}
return arena_->New<TupleType>(std::move(new_elements));
}
default: {
CARBON_CHECK(IsValueKindDependent(destination_type) ||
isa<TypeType, ConstraintType>(destination_type))
@@ -782,7 +793,16 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> value,
EvalAssociatedConstant(cast<AssociatedConstant>(value), source_loc));
if (isa<AssociatedConstant>(value)) {
if (auto* new_const = dyn_cast<AssociatedConstant>(value)) {
// TODO: Detect whether conversions are required in type-checking.
if (isa<TypeType, ConstraintType, InterfaceType>(destination_type) &&
isa<TypeType, ConstraintType, InterfaceType>(
new_const->constant().static_type())) {
// No further conversions are required.
return value;
}
// We need to convert this, and we don't know how because we don't have
// the value yet.
return ProgramError(source_loc)
<< "value of associated constant " << *value << " is not known";
}
@@ -989,19 +1009,6 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
CreateStruct(literal.fields(), act.results()));
}
}
case ExpressionKind::StructTypeLiteral: {
const auto& struct_type = cast<StructTypeLiteral>(exp);
if (act.pos() < static_cast<int>(struct_type.fields().size())) {
return todo_.Spawn(std::make_unique<ExpressionAction>(
&struct_type.fields()[act.pos()].expression()));
} else {
std::vector<NamedValue> fields;
for (size_t i = 0; i < struct_type.fields().size(); ++i) {
fields.push_back({struct_type.fields()[i].name(), act.results()[i]});
}
return todo_.FinishAction(arena_->New<StructType>(std::move(fields)));
}
}
case ExpressionKind::SimpleMemberAccessExpression: {
const auto& access = cast<SimpleMemberAccessExpression>(exp);
bool forming_member_name = isa<TypeOfMemberName>(&access.static_type());
@@ -1407,22 +1414,6 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
CARBON_CHECK(act.pos() == 0);
return todo_.FinishAction(arena_->New<TypeType>());
}
case ExpressionKind::FunctionTypeLiteral: {
if (act.pos() == 0) {
return todo_.Spawn(std::make_unique<ExpressionAction>(
&cast<FunctionTypeLiteral>(exp).parameter()));
} else if (act.pos() == 1) {
// { { pt :: fn [] -> e :: C, E, F} :: S, H}
// -> { { e :: fn pt -> []) :: C, E, F} :: S, H}
return todo_.Spawn(std::make_unique<ExpressionAction>(
&cast<FunctionTypeLiteral>(exp).return_type()));
} else {
// { { rt :: fn pt -> [] :: C, E, F} :: S, H}
// -> { fn pt -> rt :: {C, E, F} :: S, H}
return todo_.FinishAction(
arena_->New<FunctionType>(act.results()[0], act.results()[1]));
}
}
case ExpressionKind::ContinuationTypeLiteral: {
CARBON_CHECK(act.pos() == 0);
return todo_.FinishAction(arena_->New<ContinuationType>());
@@ -1436,9 +1427,18 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
CARBON_CHECK(act.pos() == 0);
return todo_.FinishAction(arena_->New<StringType>());
}
case ExpressionKind::FunctionTypeLiteral:
case ExpressionKind::StructTypeLiteral:
case ExpressionKind::ArrayTypeLiteral:
case ExpressionKind::ValueLiteral: {
CARBON_CHECK(act.pos() == 0);
return todo_.FinishAction(&cast<ValueLiteral>(exp).value());
auto* value = &cast<ConstantValueLiteral>(exp).constant_value();
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> destination,
InstantiateType(&exp.static_type(), exp.source_loc()));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> result,
Convert(value, destination, exp.source_loc()));
return todo_.FinishAction(result);
}
case ExpressionKind::IfExpression: {
const auto& if_expr = cast<IfExpression>(exp);
@@ -1460,21 +1460,25 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
CARBON_CHECK(rewrite) << "where expression should be rewritten";
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(*rewrite));
}
case ExpressionKind::UnimplementedExpression:
CARBON_FATAL() << "Unimplemented: " << exp;
case ExpressionKind::ArrayTypeLiteral: {
const auto& array_literal = cast<ArrayTypeLiteral>(exp);
case ExpressionKind::BuiltinConvertExpression: {
const auto& convert_expr = cast<BuiltinConvertExpression>(exp);
if (act.pos() == 0) {
return todo_.Spawn(std::make_unique<ExpressionAction>(
&array_literal.element_type_expression()));
} else if (act.pos() == 1) {
return todo_.Spawn(std::make_unique<ExpressionAction>(
&array_literal.size_expression()));
convert_expr.source_expression()));
} else {
return todo_.FinishAction(arena_->New<StaticArrayType>(
act.results()[0], cast<IntValue>(act.results()[1])->value()));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> destination,
InstantiateType(&convert_expr.static_type(),
convert_expr.source_loc()));
// TODO: Remove all calls to Convert other than this one. We shouldn't
// need them any more.
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> result,
Convert(act.results()[0], destination, convert_expr.source_loc()));
return todo_.FinishAction(result);
}
}
case ExpressionKind::UnimplementedExpression:
CARBON_FATAL() << "Unimplemented: " << exp;
} // switch (exp->kind)
}
+2 -2
View File
@@ -71,7 +71,7 @@ void AbstractPattern::AppendElementsTo(
}
} else if (const auto* value = value_.dyn_cast<const Value*>()) {
if (const auto* tuple = dyn_cast<TupleValue>(value)) {
const auto* tuple_type = cast<TupleValue>(type_);
const auto* tuple_type = cast<TupleType>(type_);
CARBON_CHECK(tuple->elements().size() == tuple_type->elements().size());
for (size_t i = 0; i != tuple->elements().size(); ++i) {
out.push_back(
@@ -180,7 +180,7 @@ auto PatternMatrix::FirstColumnDiscriminators() const -> DiscriminatorSet {
continue;
case AbstractPattern::Compound: {
const Value& type = row[0].type();
if (const auto* tuple = dyn_cast<TupleValue>(&type)) {
if (const auto* tuple = dyn_cast<TupleType>(&type)) {
// If we find a tuple match, we've found all constructors (there's
// only one!) and none were missing.
return {
+3 -1
View File
@@ -271,8 +271,10 @@ static auto ResolveNames(Expression& expression,
case ExpressionKind::StringLiteral:
case ExpressionKind::StringTypeLiteral:
case ExpressionKind::TypeTypeLiteral:
case ExpressionKind::ValueLiteral:
break;
case ExpressionKind::ValueLiteral:
case ExpressionKind::BuiltinConvertExpression:
CARBON_FATAL() << "should not exist before type checking";
case ExpressionKind::UnimplementedExpression:
return ProgramError(expression.source_loc()) << "Unimplemented";
}
@@ -121,6 +121,11 @@ static auto ResolveUnformed(Nonnull<const Expression*> expression,
&cast<SimpleMemberAccessExpression>(*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::DotSelfExpression:
case ExpressionKind::IntLiteral:
case ExpressionKind::BoolLiteral:
+308 -154
View File
@@ -28,6 +28,7 @@
#include "explorer/interpreter/pattern_analysis.h"
#include "explorer/interpreter/value.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/TinyPtrVector.h"
@@ -95,6 +96,7 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::TupleValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
@@ -110,8 +112,6 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
case Value::Kind::ConstraintImplWitness:
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
// These are values, not types.
return false;
case Value::Kind::IntType:
@@ -125,7 +125,10 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
case Value::Kind::ContinuationType:
case Value::Kind::StringType:
case Value::Kind::StaticArrayType:
case Value::Kind::TupleValue:
case Value::Kind::TupleType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
// These are types whose values are not types.
return false;
case Value::Kind::AutoType:
@@ -136,16 +139,15 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
case Value::Kind::TypeType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
case Value::Kind::TypeOfMixinPseudoType:
// A value of one of these types is itself always a type.
return true;
}
}
// Returns whether the value is a valid result from a type expression,
// as opposed to a non-type value.
// `auto` is not considered a type by the function if `concrete` is false.
static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
// Returns whether the value is a type value, such as might be a valid type for
// a syntactic pattern. This includes types involving `auto`. Use
// `TypeContainsAuto` to determine if a type involves `auto`.
static auto IsType(Nonnull<const Value*> value) -> bool {
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
@@ -154,6 +156,7 @@ static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::TupleValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
@@ -170,16 +173,13 @@ static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
return false;
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
// Names aren't first-class values, and their types aren't first-class
// types.
return false;
case Value::Kind::IntType:
case Value::Kind::BoolType:
case Value::Kind::TypeType:
case Value::Kind::PointerType:
case Value::Kind::FunctionType:
case Value::Kind::StructType:
case Value::Kind::TupleType:
case Value::Kind::NominalClassType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
@@ -188,21 +188,13 @@ static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
case Value::Kind::VariableType:
case Value::Kind::StringType:
case Value::Kind::StaticArrayType:
return true;
case Value::Kind::AutoType:
// `auto` isn't a concrete type, it's a pattern that matches types.
return !concrete;
case Value::Kind::TupleValue: {
for (Nonnull<const Value*> field : cast<TupleValue>(*value).elements()) {
if (!IsType(field, concrete)) {
return false;
}
}
return true;
}
case Value::Kind::PointerType: {
return IsType(&cast<PointerType>(*value).type(), concrete);
}
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
case Value::Kind::TypeOfMixinPseudoType:
// These aren't first-class types, but they are still types.
return true;
case Value::Kind::AssociatedConstant: {
// An associated type is an associated constant whose type is a
// type-of-type.
@@ -213,27 +205,17 @@ static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
return IsTypeOfType(&assoc.constant().static_type());
}
case Value::Kind::MixinPseudoType:
case Value::Kind::TypeOfMixinPseudoType:
// Mixin type is a second-class type that cannot be used
// within a type annotation expression.
return false;
}
}
static auto ExpectIsType(SourceLocation source_loc, Nonnull<const Value*> value)
-> ErrorOr<Success> {
if (!IsType(value)) {
return ProgramError(source_loc) << "Expected a type, but got " << *value;
} else {
return Success();
}
}
// Expect that a type is complete. Issue a diagnostic if not.
static auto ExpectCompleteType(SourceLocation source_loc,
std::string_view context,
Nonnull<const Value*> type) -> ErrorOr<Success> {
CARBON_RETURN_IF_ERROR(ExpectIsType(source_loc, type));
CARBON_CHECK(IsType(type));
switch (type->kind()) {
case Value::Kind::IntValue:
@@ -244,6 +226,7 @@ static auto ExpectCompleteType(SourceLocation source_loc,
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::StructValue:
case Value::Kind::TupleValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::BindingPlaceholderValue:
@@ -258,10 +241,7 @@ static auto ExpectCompleteType(SourceLocation source_loc,
case Value::Kind::ConstraintImplWitness:
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
case Value::Kind::MixinPseudoType:
case Value::Kind::TypeOfMixinPseudoType:
CARBON_FATAL() << "should not see non-type values";
case Value::Kind::IntType:
@@ -274,7 +254,10 @@ static auto ExpectCompleteType(SourceLocation source_loc,
case Value::Kind::ConstraintType:
case Value::Kind::ContinuationType:
case Value::Kind::VariableType:
case Value::Kind::AssociatedConstant: {
case Value::Kind::AssociatedConstant:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
case Value::Kind::TypeOfMixinPseudoType: {
// These types are always complete.
return Success();
}
@@ -284,7 +267,7 @@ static auto ExpectCompleteType(SourceLocation source_loc,
// complete.
return Success();
case Value::Kind::TupleValue: {
case Value::Kind::TupleType: {
// TODO: Tuple types should be complete only if all element types are
// complete.
return Success();
@@ -323,18 +306,83 @@ static auto ExpectCompleteType(SourceLocation source_loc,
// Returns whether *value represents the type of a Carbon value, as
// opposed to a type pattern or a non-type value.
static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
return IsType(value, /*concrete=*/true);
static auto TypeContainsAuto(Nonnull<const Value*> type) -> bool {
CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type;
switch (type->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::TupleValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AddrValue:
case Value::Kind::AlternativeConstructorValue:
case Value::Kind::ContinuationValue:
case Value::Kind::StringValue:
case Value::Kind::UninitializedValue:
case Value::Kind::ImplWitness:
case Value::Kind::BindingWitness:
case Value::Kind::ConstraintWitness:
case Value::Kind::ConstraintImplWitness:
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
case Value::Kind::MixinPseudoType:
CARBON_FATAL() << "non-type value";
case Value::Kind::IntType:
case Value::Kind::BoolType:
case Value::Kind::TypeType:
case Value::Kind::VariableType:
case Value::Kind::StringType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
// These types do not contain other types.
return false;
case Value::Kind::FunctionType:
case Value::Kind::NominalClassType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::AssociatedConstant:
// These types can contain other types, but those types can't involve
// `auto`.
return false;
case Value::Kind::AutoType:
return true;
case Value::Kind::StructType:
return llvm::any_of(
llvm::map_range(cast<StructType>(type)->fields(),
[](const NamedValue& v) { return v.value; }),
TypeContainsAuto);
case Value::Kind::TupleType:
return llvm::any_of(cast<TupleType>(type)->elements(), TypeContainsAuto);
case Value::Kind::PointerType:
return TypeContainsAuto(&cast<PointerType>(type)->type());
case Value::Kind::StaticArrayType:
return TypeContainsAuto(&cast<StaticArrayType>(type)->element_type());
}
}
auto TypeChecker::ExpectIsConcreteType(SourceLocation source_loc,
Nonnull<const Value*> value)
-> ErrorOr<Success> {
if (!IsConcreteType(value)) {
return ProgramError(source_loc) << "Expected a type, but got " << *value;
} else {
return Success();
}
// Returns whether `type` is a placeholder type, which is a second-class type
// that cannot be the type of a binding but can be the type of an expression.
static auto IsPlaceholderType(Nonnull<const Value*> type) -> bool {
CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type;
return isa<TypeOfParameterizedEntityName, TypeOfMemberName,
TypeOfMixinPseudoType>(type);
}
// Returns whether `value` is a concrete type, which would be valid as the
// static type of an expression. This is currently any type other than `auto`.
static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
return IsType(value) && !TypeContainsAuto(value);
}
// Returns the named field, or None if not found.
@@ -444,15 +492,23 @@ auto TypeChecker::IsImplicitlyConvertible(
return true;
}
break;
case Value::Kind::TypeType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
// A value of empty struct type implicitly converts to a type.
if (cast<StructType>(*source).fields().empty()) {
return true;
}
break;
default:
break;
}
break;
case Value::Kind::TupleValue: {
const auto& source_tuple = cast<TupleValue>(*source);
case Value::Kind::TupleType: {
const auto& source_tuple = cast<TupleType>(*source);
switch (destination->kind()) {
case Value::Kind::TupleValue: {
const auto& destination_tuple = cast<TupleValue>(*destination);
case Value::Kind::TupleType: {
const auto& destination_tuple = cast<TupleType>(*destination);
if (source_tuple.elements().size() !=
destination_tuple.elements().size()) {
break;
@@ -490,7 +546,10 @@ auto TypeChecker::IsImplicitlyConvertible(
}
break;
}
case Value::Kind::TypeType: {
case Value::Kind::TypeType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType: {
// A tuple value converts to a type if all of its fields do.
bool all_types = true;
for (Nonnull<const Value*> source_element : source_tuple.elements()) {
if (!IsImplicitlyConvertible(
@@ -541,38 +600,59 @@ auto TypeChecker::ImplicitlyConvert(std::string_view context,
-> ErrorOr<Nonnull<Expression*>> {
Nonnull<const Value*> source_type = &source->static_type();
// A type implicitly converts to a constraint if there is an impl of that
// constraint for that type in scope.
if (isa<InterfaceType, ConstraintType>(destination)) {
CARBON_ASSIGN_OR_RETURN(
Nonnull<const ConstraintType*> destination_constraint,
ConvertToConstraintType(source->source_loc(), "implicit conversion",
destination));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> source_value,
InterpExp(source, arena_, trace_stream_));
if (trace_stream_) {
**trace_stream_ << "converting type " << *source_value
<< " to constraint " << *destination_constraint << " for "
<< context << " in scope " << impl_scope << "\n";
}
// Note, we discard the witness. We don't actually need it in order to
// perform the conversion, but we do want to know it exists.
CARBON_RETURN_IF_ERROR(impl_scope.Resolve(
destination_constraint, source_value, source->source_loc(), *this));
// This conversion is a no-op at runtime.
// TODO: Should we record the change in type in the AST?
CARBON_RETURN_IF_ERROR(
ExpectNonPlaceholderType(source->source_loc(), &source->static_type()));
if (TypeEqual(&source->static_type(), destination, std::nullopt)) {
// No conversions are required.
return source;
}
// TODO: If a builtin conversion works, for now we don't create any
// expression to do the conversion and rely on the interpreter to know how to
// do it.
// TODO: This doesn't work for cases of combined built-in and user-defined
// conversion, such as converting a struct element via an `ImplicitAs` impl.
if (IsImplicitlyConvertible(source_type, destination, impl_scope,
/*allow_user_defined_conversions=*/false)) {
return source;
// A type only implicitly converts to a constraint if there is an impl of
// that constraint for that type in scope.
if (isa<InterfaceType, ConstraintType>(destination)) {
// First convert the source expression to type `Type`.
CARBON_ASSIGN_OR_RETURN(Nonnull<Expression*> source_as_type,
ImplicitlyConvert(context, impl_scope, source,
arena_->New<TypeType>()));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
InterpExp(source_as_type, arena_, trace_stream_));
CARBON_ASSIGN_OR_RETURN(
Nonnull<const ConstraintType*> destination_constraint,
ConvertToConstraintType(source->source_loc(), "implicit conversion",
destination));
destination = destination_constraint;
if (trace_stream_) {
**trace_stream_ << "converting type " << *converted_value
<< " to constraint " << *destination_constraint
<< " for " << context << " in scope " << impl_scope
<< "\n";
}
// Note, we discard the witness. We don't actually need it in order to
// perform the conversion, but we do want to know it exists.
// TODO: A value of constraint type should carry both the type and the
// witness.
CARBON_RETURN_IF_ERROR(impl_scope.Resolve(destination_constraint,
converted_value,
source->source_loc(), *this));
return arena_->New<ValueLiteral>(source->source_loc(), converted_value,
destination_constraint,
ValueCategory::Let);
}
if (IsTypeOfType(source_type) && IsTypeOfType(destination)) {
// No conversion is required.
return source;
}
// Perform the builtin conversion.
return arena_->New<BuiltinConvertExpression>(source, destination);
}
ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
impl_scope, source,
BuiltinInterfaceName{Builtins::ImplicitAs, destination},
@@ -650,6 +730,29 @@ auto TypeChecker::BuildBuiltinMethodCall(const ImplScope& impl_scope,
return {call};
}
// Checks that the given type is not a placeholder type. Diagnoses otherwise.
auto TypeChecker::ExpectNonPlaceholderType(SourceLocation source_loc,
Nonnull<const Value*> type)
-> ErrorOr<Success> {
if (!IsPlaceholderType(type)) {
return Success();
}
if (auto* member_name = dyn_cast<TypeOfMemberName>(type)) {
return ProgramError(source_loc)
<< *member_name << " can only be used in a member access or alias";
}
if (auto* param_entity = dyn_cast<TypeOfParameterizedEntityName>(type)) {
return ProgramError(source_loc)
<< "'" << param_entity->name() << "' must be given an argument list";
}
if (auto* mixin_type = dyn_cast<TypeOfMixinPseudoType>(type)) {
return ProgramError(source_loc)
<< "invalid use of mixin "
<< mixin_type->mixin_type().declaration().name();
}
CARBON_FATAL() << "unknown kind of placeholder type " << *type;
}
auto TypeChecker::ExpectType(SourceLocation source_loc,
std::string_view context,
Nonnull<const Value*> expected,
@@ -786,12 +889,12 @@ auto TypeChecker::ArgumentDeduction::Deduce(Nonnull<const Value*> param,
}
return Success();
}
case Value::Kind::TupleValue: {
if (arg->kind() != Value::Kind::TupleValue) {
case Value::Kind::TupleType: {
if (arg->kind() != Value::Kind::TupleType) {
return handle_non_deduced_type();
}
const auto& param_tup = cast<TupleValue>(*param);
const auto& arg_tup = cast<TupleValue>(*arg);
const auto& param_tup = cast<TupleType>(*param);
const auto& arg_tup = cast<TupleType>(*arg);
if (param_tup.elements().size() != arg_tup.elements().size()) {
return ProgramError(source_loc_)
<< "mismatch in tuple sizes, expected "
@@ -951,6 +1054,7 @@ auto TypeChecker::ArgumentDeduction::Deduce(Nonnull<const Value*> param,
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::StructValue:
case Value::Kind::TupleValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::BindingPlaceholderValue:
@@ -1042,11 +1146,9 @@ auto TypeChecker::ArgumentDeduction::Finish(TypeChecker& type_checker,
const Value* binding_type = &binding->static_type();
const Value* substituted_type =
type_checker.Substitute(bindings, binding_type);
if (!IsTypeOfType(substituted_type)) {
CARBON_ASSIGN_OR_RETURN(
arg, type_checker.ImplicitlyConvert(context_, impl_scope, arg,
substituted_type));
}
CARBON_ASSIGN_OR_RETURN(
arg, type_checker.ImplicitlyConvert(context_, impl_scope, arg,
substituted_type));
// Evaluate the argument to get the value.
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value,
@@ -1715,12 +1817,17 @@ auto TypeChecker::SubstituteImpl(const Bindings& bindings,
return arena_->New<AssociatedConstant>(base, interface, &assoc.constant(),
witness);
}
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
std::vector<Nonnull<const Value*>> elts;
for (const auto& elt : cast<TupleValue>(*type).elements()) {
for (const auto& elt : cast<TupleValueBase>(*type).elements()) {
elts.push_back(SubstituteImpl(bindings, elt));
}
return arena_->New<TupleValue>(elts);
if (isa<TupleType>(type)) {
return arena_->New<TupleType>(std::move(elts));
} else {
return arena_->New<TupleValue>(std::move(elts));
}
}
case Value::Kind::StructType: {
std::vector<NamedValue> fields;
@@ -1853,14 +1960,17 @@ auto TypeChecker::SubstituteImpl(const Bindings& bindings,
witness.index());
}
case Value::Kind::StaticArrayType:
case Value::Kind::ChoiceType:
case Value::Kind::MixinPseudoType:
// TODO: These can contain bindings. We should substitute into them.
return type;
case Value::Kind::AutoType:
case Value::Kind::IntType:
case Value::Kind::BoolType:
case Value::Kind::TypeType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::StringType:
case Value::Kind::MixinPseudoType:
// These types cannot contain bindings or witnesses.
return type;
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfParameterizedEntityName:
@@ -1870,13 +1980,9 @@ auto TypeChecker::SubstituteImpl(const Bindings& bindings,
return type;
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
@@ -1884,12 +1990,18 @@ auto TypeChecker::SubstituteImpl(const Bindings& bindings,
case Value::Kind::AddrValue:
case Value::Kind::AlternativeConstructorValue:
case Value::Kind::ContinuationValue:
case Value::Kind::StringValue:
case Value::Kind::UninitializedValue:
// This can happen when substituting into the arguments of a class or
// interface.
// TODO: Implement substitution for these cases.
return type;
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::StringValue:
case Value::Kind::UninitializedValue:
// These values cannot contain bindings or witnesses.
return type;
}
}
@@ -2058,7 +2170,7 @@ auto TypeChecker::DeduceCallBindings(
llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
const ImplScope& impl_scope) -> ErrorOr<Success> {
llvm::ArrayRef<Nonnull<const Value*>> params =
cast<TupleValue>(*params_type).elements();
cast<TupleType>(*params_type).elements();
llvm::ArrayRef<Nonnull<Expression*>> args =
cast<TupleLiteral>(call.argument()).fields();
if (params.size() != args.size()) {
@@ -2303,6 +2415,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
switch (e->kind()) {
case ExpressionKind::ValueLiteral:
case ExpressionKind::BuiltinConvertExpression:
CARBON_FATAL() << "attempting to type check node " << *e
<< " generated during type checking";
case ExpressionKind::IndexExpression: {
@@ -2311,8 +2424,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
CARBON_RETURN_IF_ERROR(TypeCheckExp(&index.offset(), impl_scope));
const Value& object_type = index.object().static_type();
switch (object_type.kind()) {
case Value::Kind::TupleValue: {
const auto& tuple_type = cast<TupleValue>(object_type);
case Value::Kind::TupleType: {
const auto& tuple_type = cast<TupleType>(object_type);
CARBON_RETURN_IF_ERROR(
ExpectExactType(index.offset().source_loc(), "tuple index",
arena_->New<IntType>(),
@@ -2341,7 +2454,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
return Success();
}
default:
return ProgramError(e->source_loc()) << "expected a tuple";
return ProgramError(e->source_loc())
<< "only arrays and tuples can be indexed, found "
<< object_type;
}
}
case ExpressionKind::TupleLiteral: {
@@ -2349,10 +2464,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
for (auto* arg : cast<TupleLiteral>(*e).fields()) {
CARBON_RETURN_IF_ERROR(TypeCheckExp(arg, impl_scope));
CARBON_RETURN_IF_ERROR(
ExpectIsConcreteType(arg->source_loc(), &arg->static_type()));
ExpectNonPlaceholderType(arg->source_loc(), &arg->static_type()));
arg_types.push_back(&arg->static_type());
}
e->set_static_type(arena_->New<TupleValue>(std::move(arg_types)));
e->set_static_type(arena_->New<TupleType>(std::move(arg_types)));
e->set_value_category(ValueCategory::Let);
return Success();
}
@@ -2360,7 +2475,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
std::vector<NamedValue> arg_types;
for (auto& arg : cast<StructLiteral>(*e).fields()) {
CARBON_RETURN_IF_ERROR(TypeCheckExp(&arg.expression(), impl_scope));
CARBON_RETURN_IF_ERROR(ExpectIsConcreteType(
CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
arg.expression().source_loc(), &arg.expression().static_type()));
arg_types.push_back({arg.name(), &arg.expression().static_type()});
}
@@ -2370,19 +2485,17 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
case ExpressionKind::StructTypeLiteral: {
auto& struct_type = cast<StructTypeLiteral>(*e);
std::vector<NamedValue> fields;
for (auto& arg : struct_type.fields()) {
CARBON_RETURN_IF_ERROR(TypeCheckTypeExp(&arg.expression(), impl_scope));
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> type,
TypeCheckTypeExp(&arg.expression(), impl_scope));
fields.push_back({.name = arg.name(), .value = type});
}
if (struct_type.fields().empty()) {
// `{}` is the type of `{}`, just as `()` is the type of `()`.
// This applies only if there are no fields, because (unlike with
// tuples) non-empty struct types are syntactically disjoint
// from non-empty struct values.
struct_type.set_static_type(arena_->New<StructType>());
} else {
struct_type.set_static_type(arena_->New<TypeType>());
}
e->set_value_category(ValueCategory::Let);
struct_type.set_static_type(arena_->New<TypeType>());
struct_type.set_value_category(ValueCategory::Let);
struct_type.set_constant_value(
arena_->New<StructType>(std::move(fields)));
return Success();
}
case ExpressionKind::SimpleMemberAccessExpression: {
@@ -3040,9 +3153,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
case Operator::As: {
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> type,
InterpExp(op.arguments()[1], arena_, trace_stream_));
CARBON_RETURN_IF_ERROR(
ExpectIsConcreteType(op.arguments()[1]->source_loc(), type));
TypeCheckTypeExp(op.arguments()[1], impl_scope));
ErrorOr<Nonnull<Expression*>> converted =
BuildBuiltinMethodCall(impl_scope, op.arguments()[0],
BuiltinInterfaceName{Builtins::As, type},
@@ -3129,10 +3240,13 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
case ExpressionKind::FunctionTypeLiteral: {
auto& fn = cast<FunctionTypeLiteral>(*e);
CARBON_RETURN_IF_ERROR(TypeCheckTypeExp(&fn.parameter(), impl_scope));
CARBON_RETURN_IF_ERROR(TypeCheckTypeExp(&fn.return_type(), impl_scope));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> param,
TypeCheckTypeExp(&fn.parameter(), impl_scope));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> ret,
TypeCheckTypeExp(&fn.return_type(), impl_scope));
fn.set_static_type(arena_->New<TypeType>());
fn.set_value_category(ValueCategory::Let);
fn.set_constant_value(arena_->New<FunctionType>(param, ret));
return Success();
}
case ExpressionKind::StringLiteral:
@@ -3160,7 +3274,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
e->source_loc(), "Print argument 1", arena_->New<IntType>(),
&args[1]->static_type(), impl_scope));
}
e->set_static_type(TupleValue::Empty());
e->set_static_type(TupleType::Empty());
e->set_value_category(ValueCategory::Let);
return Success();
case IntrinsicExpression::Intrinsic::Assert: {
@@ -3174,7 +3288,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
CARBON_RETURN_IF_ERROR(ExpectType(
e->source_loc(), "__intrinsic_assert argument 1",
arena_->New<StringType>(), &args[1]->static_type(), impl_scope));
e->set_static_type(TupleValue::Empty());
e->set_static_type(TupleType::Empty());
e->set_value_category(ValueCategory::Let);
return Success();
}
@@ -3196,7 +3310,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
const auto* arg_type = &args[0]->static_type();
CARBON_RETURN_IF_ERROR(
ExpectPointerType(e->source_loc(), "*", arg_type));
e->set_static_type(TupleValue::Empty());
e->set_static_type(TupleType::Empty());
e->set_value_category(ValueCategory::Let);
return Success();
}
@@ -3496,9 +3610,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
CARBON_FATAL() << "Unimplemented: " << *e;
case ExpressionKind::ArrayTypeLiteral: {
auto& array_literal = cast<ArrayTypeLiteral>(*e);
CARBON_RETURN_IF_ERROR(TypeCheckTypeExp(
&array_literal.element_type_expression(), impl_scope));
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> element_type,
TypeCheckTypeExp(&array_literal.element_type_expression(),
impl_scope));
CARBON_RETURN_IF_ERROR(
TypeCheckExp(&array_literal.size_expression(), impl_scope));
CARBON_RETURN_IF_ERROR(ExpectExactType(
@@ -3514,6 +3629,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
array_literal.set_static_type(arena_->New<TypeType>());
array_literal.set_value_category(ValueCategory::Let);
array_literal.set_constant_value(arena_->New<StaticArrayType>(
element_type, cast<IntValue>(size_value)->value()));
return Success();
}
}
@@ -3571,11 +3688,24 @@ auto TypeChecker::TypeCheckTypeExp(Nonnull<Expression*> type_expression,
const ImplScope& impl_scope, bool concrete)
-> ErrorOr<Nonnull<const Value*>> {
CARBON_RETURN_IF_ERROR(TypeCheckExp(type_expression, impl_scope));
CARBON_ASSIGN_OR_RETURN(
type_expression,
ImplicitlyConvert("type expression", impl_scope, type_expression,
arena_->New<TypeType>()));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
InterpExp(type_expression, arena_, trace_stream_));
CARBON_RETURN_IF_ERROR(
concrete ? ExpectIsConcreteType(type_expression->source_loc(), type)
: ExpectIsType(type_expression->source_loc(), type));
CARBON_CHECK(IsType(type))
<< "type expression did not produce a type, got " << *type;
if (concrete) {
if (TypeContainsAuto(type)) {
return ProgramError(type_expression->source_loc())
<< "`auto` is not permitted in this context";
}
CARBON_CHECK(IsConcreteType(type))
<< "unknown kind of non-concrete type " << *type;
}
CARBON_CHECK(!IsPlaceholderType(type))
<< "should be no way to write a placeholder type";
return type;
}
@@ -3646,14 +3776,30 @@ auto TypeChecker::TypeCheckPattern(
return !isa<BindingPattern>(pattern);
})) {
return ProgramError(binding.type().source_loc())
<< "The type of a binding pattern cannot contain bindings.";
<< "the type of a binding pattern cannot contain bindings";
}
CARBON_RETURN_IF_ERROR(TypeCheckPattern(
&binding.type(), std::nullopt, impl_scope, enclosing_value_category));
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> type,
InterpPattern(&binding.type(), arena_, trace_stream_));
CARBON_RETURN_IF_ERROR(ExpectIsType(binding.source_loc(), type));
// Convert to a type.
// TODO: Convert the pattern before interpreting it rather than doing
// this as a separate step.
if (!isa<TypeType>(binding.type().static_type())) {
auto* literal = arena_->New<ValueLiteral>(binding.source_loc(), type,
&binding.type().static_type(),
ValueCategory::Let);
CARBON_ASSIGN_OR_RETURN(
auto* converted,
ImplicitlyConvert("type of name binding", impl_scope, literal,
arena_->New<TypeType>()));
CARBON_ASSIGN_OR_RETURN(type,
InterpExp(converted, arena_, trace_stream_));
}
CARBON_CHECK(IsType(type))
<< "conversion to type succeeded but didn't produce a type, got "
<< *type;
if (expected) {
if (IsConcreteType(type)) {
CARBON_RETURN_IF_ERROR(ExpectType(p->source_loc(), "name binding",
@@ -3664,13 +3810,19 @@ auto TypeChecker::TypeCheckPattern(
std::nullopt, generic_args, trace_stream_,
this->arena_)) {
return ProgramError(binding.type().source_loc())
<< "Type pattern '" << *type
<< "type pattern '" << *type
<< "' does not match actual type '" << **expected << "'";
}
type = *expected;
}
} else if (TypeContainsAuto(type)) {
return ProgramError(binding.source_loc())
<< "cannot deduce `auto` type for " << binding;
}
CARBON_RETURN_IF_ERROR(ExpectIsConcreteType(binding.source_loc(), type));
CARBON_CHECK(IsConcreteType(type)) << "did not resolve " << binding
<< " to concrete type, got " << *type;
CARBON_CHECK(!IsPlaceholderType(type))
<< "should be no way to write a placeholder type";
binding.set_static_type(type);
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> binding_value,
InterpPattern(&binding, arena_, trace_stream_));
@@ -3685,8 +3837,8 @@ auto TypeChecker::TypeCheckPattern(
auto& binding = cast<GenericBinding>(*p);
if (expected) {
return ProgramError(binding.type().source_loc())
<< "Generic binding may not occur in pattern with expected "
"type: "
<< "generic binding may not occur in pattern with expected "
"type "
<< binding;
}
@@ -3695,18 +3847,18 @@ auto TypeChecker::TypeCheckPattern(
case PatternKind::TuplePattern: {
auto& tuple = cast<TuplePattern>(*p);
std::vector<Nonnull<const Value*>> field_types;
if (expected && (*expected)->kind() != Value::Kind::TupleValue) {
if (expected && (*expected)->kind() != Value::Kind::TupleType) {
return ProgramError(p->source_loc()) << "didn't expect a tuple";
}
if (expected && tuple.fields().size() !=
cast<TupleValue>(**expected).elements().size()) {
cast<TupleType>(**expected).elements().size()) {
return ProgramError(tuple.source_loc()) << "tuples of different length";
}
for (size_t i = 0; i < tuple.fields().size(); ++i) {
Nonnull<Pattern*> field = tuple.fields()[i];
std::optional<Nonnull<const Value*>> expected_field_type;
if (expected) {
expected_field_type = cast<TupleValue>(**expected).elements()[i];
expected_field_type = cast<TupleType>(**expected).elements()[i];
}
CARBON_RETURN_IF_ERROR(TypeCheckPattern(
field, expected_field_type, impl_scope, enclosing_value_category));
@@ -3716,7 +3868,7 @@ auto TypeChecker::TypeCheckPattern(
}
field_types.push_back(&field->static_type());
}
tuple.set_static_type(arena_->New<TupleValue>(std::move(field_types)));
tuple.set_static_type(arena_->New<TupleType>(std::move(field_types)));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> tuple_value,
InterpPattern(&tuple, arena_, trace_stream_));
SetValue(&tuple, tuple_value);
@@ -3873,6 +4025,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
case StatementKind::Match: {
auto& match = cast<Match>(*s);
CARBON_RETURN_IF_ERROR(TypeCheckExp(&match.expression(), impl_scope));
CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
match.expression().source_loc(), &match.expression().static_type()));
std::vector<Match::Clause> new_clauses;
std::optional<Nonnull<const Value*>> expected_type;
PatternMatrix patterns;
@@ -3976,6 +4130,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
// so we can use its type to deduce parts of the type of the binding.
if (var.has_init()) {
CARBON_RETURN_IF_ERROR(TypeCheckExp(&var.init(), impl_scope));
CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
var.init().source_loc(), &var.init().static_type()));
init_type = &var.init().static_type();
}
CARBON_RETURN_IF_ERROR(TypeCheckPattern(&var.pattern(), init_type,
@@ -4051,6 +4207,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
CARBON_RETURN_IF_ERROR(TypeCheckExp(&ret.expression(), impl_scope));
ReturnTerm& return_term = ret.function().return_term();
if (return_term.is_auto()) {
CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
ret.source_loc(), &ret.expression().static_type()));
return_term.set_static_type(&ret.expression().static_type());
} else {
CARBON_ASSIGN_OR_RETURN(
@@ -4205,15 +4363,15 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
if (std::optional<Nonnull<Expression*>> return_expression =
f->return_term().type_expression();
return_expression.has_value()) {
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> ret_type,
TypeCheckTypeExp(*return_expression, function_scope,
/*concrete=*/false));
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> ret_type,
TypeCheckTypeExp(*return_expression, function_scope));
// TODO: This is setting the constant value of the return type. It would
// make more sense if this were called `set_constant_value` rather than
// `set_static_type`.
f->return_term().set_static_type(ret_type);
} else if (f->return_term().is_omitted()) {
f->return_term().set_static_type(TupleValue::Empty());
f->return_term().set_static_type(TupleType::Empty());
} else {
// We have to type-check the body in order to determine the return type.
if (!f->body().has_value()) {
@@ -4226,9 +4384,8 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
ExpectReturnOnAllPaths(f->body(), f->source_loc()));
}
}
CARBON_CHECK(IsConcreteType(&f->return_term().static_type()));
CARBON_RETURN_IF_ERROR(
ExpectIsConcreteType(f->source_loc(), &f->return_term().static_type()));
f->set_static_type(arena_->New<FunctionType>(
&f->param_pattern().static_type(), std::move(generic_parameters),
&f->return_term().static_type(), std::move(deduced_bindings),
@@ -5111,6 +5268,7 @@ static auto IsValidTypeForAliasTarget(Nonnull<const Value*> type) -> bool {
case Value::Kind::PointerType:
case Value::Kind::StaticArrayType:
case Value::Kind::StructType:
case Value::Kind::TupleType:
case Value::Kind::NominalClassType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
@@ -5311,16 +5469,12 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
return ProgramError(var.binding().type().source_loc())
<< "Expected expression for variable type";
}
Expression& type =
cast<ExpressionPattern>(var.binding().type()).expression();
CARBON_RETURN_IF_ERROR(TypeCheckPattern(&var.binding(), std::nullopt,
*scope_info.innermost_scope,
var.value_category()));
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> declared_type,
InterpExp(&type, arena_, trace_stream_));
CARBON_RETURN_IF_ERROR(ExpectCompleteType(
var.source_loc(), "type of variable", declared_type));
var.set_static_type(declared_type);
var.source_loc(), "type of variable", &var.binding().static_type()));
var.set_static_type(&var.binding().static_type());
break;
}
+4 -5
View File
@@ -333,11 +333,6 @@ class TypeChecker {
auto ExpectReturnOnAllPaths(std::optional<Nonnull<Statement*>> opt_stmt,
SourceLocation source_loc) -> ErrorOr<Success>;
// Verifies that *value represents a concrete type, as opposed to a
// type pattern or a non-type value.
auto ExpectIsConcreteType(SourceLocation source_loc,
Nonnull<const Value*> value) -> ErrorOr<Success>;
// Returns the field names of the class together with their types.
auto FieldTypes(const NominalClassType& class_type) const
-> std::vector<NamedValue>;
@@ -370,6 +365,10 @@ class TypeChecker {
Nonnull<const Value*> destination)
-> ErrorOr<Nonnull<Expression*>>;
// Checks that the given type is not a placeholder type. Diagnoses otherwise.
auto ExpectNonPlaceholderType(SourceLocation source_loc,
Nonnull<const Value*> type) -> ErrorOr<Success>;
// Determine whether `type1` and `type2` are considered to be the same type
// in the given scope. This is true if they're structurally identical or if
// there is an equality relation in scope that specifies that they are the
+19 -14
View File
@@ -21,6 +21,7 @@ namespace Carbon {
using llvm::cast;
using llvm::dyn_cast;
using llvm::dyn_cast_or_null;
using llvm::isa;
auto StructValue::FindField(std::string_view name) const
-> std::optional<Nonnull<const Value*>> {
@@ -87,13 +88,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
case Value::Kind::NominalClassValue: {
const auto& object = cast<NominalClassValue>(*v);
// Look for a field.
// Note that the value representation of an empty class is a
// `StructType`, not a `StructValue`.
std::optional<Nonnull<const Value*>> field;
if (const auto* struct_value = dyn_cast<StructValue>(&object.inits())) {
field = struct_value->FindField(f);
}
if (field.has_value()) {
if (std::optional<Nonnull<const Value*>> field =
cast<StructValue>(object.inits()).FindField(f)) {
return *field;
} else {
// Look for a method in the object's class
@@ -185,9 +181,10 @@ static auto SetFieldImpl(
path_end, field_value, source_loc));
return arena->New<NominalClassValue>(&object.type(), inits);
}
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
std::vector<Nonnull<const Value*>> elements =
cast<TupleValue>(*value).elements();
cast<TupleValueBase>(*value).elements();
// TODO(geoffromer): update FieldPath to hold integers as well as strings.
int index = std::stoi(std::string((*path_begin).name()));
if (index < 0 || static_cast<size_t>(index) >= elements.size()) {
@@ -197,7 +194,11 @@ static auto SetFieldImpl(
CARBON_ASSIGN_OR_RETURN(
elements[index], SetFieldImpl(arena, elements[index], path_begin + 1,
path_end, field_value, source_loc));
return arena->New<TupleValue>(elements);
if (isa<TupleType>(value)) {
return arena->New<TupleType>(elements);
} else {
return arena->New<TupleValue>(elements);
}
}
default:
CARBON_FATAL() << "field access not allowed for value " << *value;
@@ -272,10 +273,12 @@ void Value::Print(llvm::raw_ostream& out) const {
out << cast<NominalClassType>(s.type()).declaration().name() << s.inits();
break;
}
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
out << "(";
llvm::ListSeparator sep;
for (Nonnull<const Value*> element : cast<TupleValue>(*this).elements()) {
for (Nonnull<const Value*> element :
cast<TupleValueBase>(*this).elements()) {
out << sep << *element;
}
out << ")";
@@ -705,9 +708,10 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
}
case Value::Kind::ChoiceType:
return cast<ChoiceType>(*t1).name() == cast<ChoiceType>(*t2).name();
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
const auto& tup1 = cast<TupleValue>(*t1);
const auto& tup2 = cast<TupleValue>(*t2);
const auto& tup1 = cast<TupleValueBase>(*t1);
const auto& tup2 = cast<TupleValueBase>(*t2);
if (tup1.elements().size() != tup2.elements().size()) {
return false;
}
@@ -806,11 +810,12 @@ auto ValueStructurallyEqual(
body1.has_value() == body2.has_value() &&
(!body1.has_value() || *body1 == *body2);
}
case Value::Kind::TupleType:
case Value::Kind::TupleValue: {
const std::vector<Nonnull<const Value*>>& elements1 =
cast<TupleValue>(*v1).elements();
cast<TupleValueBase>(*v1).elements();
const std::vector<Nonnull<const Value*>>& elements2 =
cast<TupleValue>(*v2).elements();
cast<TupleValueBase>(*v2).elements();
if (elements1.size() != elements2.size()) {
return false;
}
+47 -22
View File
@@ -61,6 +61,7 @@ class Value {
AutoType,
StructType,
NominalClassType,
TupleType,
MixinPseudoType,
InterfaceType,
ConstraintType,
@@ -298,20 +299,13 @@ class BoolValue : public Value {
bool value_;
};
// A non-empty value of a struct type.
//
// It can't be empty because `{}` is a struct type as well as a value of that
// type, so for consistency we always represent it as a StructType rather than
// let it oscillate unpredictably between the two. However, this means code
// that handles StructValue instances may also need to be able to handle
// StructType instances.
// A value of a struct type. Note that the expression `{}` is a value of type
// `{} as Type`; the former is a `StructValue` and the latter is a
// `StructType`.
class StructValue : public Value {
public:
explicit StructValue(std::vector<NamedValue> elements)
: Value(Kind::StructValue), elements_(std::move(elements)) {
CARBON_CHECK(!elements_.empty())
<< "`{}` is represented as a StructType, not a StructValue.";
}
: Value(Kind::StructValue), elements_(std::move(elements)) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::StructValue;
@@ -391,10 +385,32 @@ class AlternativeValue : public Value {
Nonnull<const Value*> argument_;
};
// A tuple value.
class TupleValue : public Value {
// Base class for tuple types and tuple values. These are the same other than
// their type-of-type, but we separate them to make it easier to tell types and
// values apart.
class TupleValueBase : public Value {
public:
// An empty tuple, also known as the unit type.
explicit TupleValueBase(Value::Kind kind,
std::vector<Nonnull<const Value*>> elements)
: Value(kind), elements_(std::move(elements)) {}
auto elements() const -> llvm::ArrayRef<Nonnull<const Value*>> {
return elements_;
}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::TupleValue ||
value->kind() == Kind::TupleType;
}
private:
std::vector<Nonnull<const Value*>> elements_;
};
// A tuple value.
class TupleValue : public TupleValueBase {
public:
// An empty tuple.
static auto Empty() -> Nonnull<const TupleValue*> {
static const TupleValue empty =
TupleValue(std::vector<Nonnull<const Value*>>());
@@ -402,18 +418,30 @@ class TupleValue : public Value {
}
explicit TupleValue(std::vector<Nonnull<const Value*>> elements)
: Value(Kind::TupleValue), elements_(std::move(elements)) {}
: TupleValueBase(Kind::TupleValue, std::move(elements)) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::TupleValue;
}
};
auto elements() const -> llvm::ArrayRef<Nonnull<const Value*>> {
return elements_;
// A tuple type. This is the result of converting a tuple value containing
// only types to type Type.
class TupleType : public TupleValueBase {
public:
// The unit type.
static auto Empty() -> Nonnull<const TupleType*> {
static const TupleType empty =
TupleType(std::vector<Nonnull<const Value*>>());
return static_cast<Nonnull<const TupleType*>>(&empty);
}
private:
std::vector<Nonnull<const Value*>> elements_;
explicit TupleType(std::vector<Nonnull<const Value*>> elements)
: TupleValueBase(Kind::TupleType, std::move(elements)) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::TupleType;
}
};
// A binding placeholder value.
@@ -587,9 +615,6 @@ class AutoType : public Value {
};
// A struct type.
//
// Code that handles this type may sometimes need to have special-case handling
// for `{}`, which is a struct value in addition to being a struct type.
class StructType : public Value {
public:
StructType() : StructType(std::vector<NamedValue>{}) {}
+4 -4
View File
@@ -753,7 +753,9 @@ paren_expression_contents:
;
struct_literal:
LEFT_CURLY_BRACE struct_literal_contents RIGHT_CURLY_BRACE
LEFT_CURLY_BRACE RIGHT_CURLY_BRACE
{ $$ = arena->New<StructLiteral>(context.source_loc()); }
| LEFT_CURLY_BRACE struct_literal_contents RIGHT_CURLY_BRACE
{ $$ = arena->New<StructLiteral>(context.source_loc(), $2); }
| LEFT_CURLY_BRACE struct_literal_contents COMMA RIGHT_CURLY_BRACE
{ $$ = arena->New<StructLiteral>(context.source_loc(), $2); }
@@ -769,9 +771,7 @@ struct_literal_contents:
;
struct_type_literal:
LEFT_CURLY_BRACE RIGHT_CURLY_BRACE
{ $$ = arena->New<StructTypeLiteral>(context.source_loc()); }
| LEFT_CURLY_BRACE struct_type_literal_contents RIGHT_CURLY_BRACE
LEFT_CURLY_BRACE struct_type_literal_contents RIGHT_CURLY_BRACE
{ $$ = arena->New<StructTypeLiteral>(context.source_loc(), $2); }
| LEFT_CURLY_BRACE struct_type_literal_contents COMMA RIGHT_CURLY_BRACE
{ $$ = arena->New<StructTypeLiteral>(context.source_loc(), $2); }
@@ -0,0 +1,22 @@
// 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: result: 3
package ExplorerTest api;
class TypeLike {
impl as ImplicitAs(Type) {
fn Convert[me: Self]() -> Type { return i32; }
}
fn Make() -> Self { return {}; }
}
fn Main() -> TypeLike.Make() {
var v: [TypeLike.Make(); 2] = (1, 2);
return v[0] + v[1];
}
+1 -1
View File
@@ -9,6 +9,6 @@
package ExplorerTest api;
fn Main() -> i32 {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/as/fail_destination_not_type.carbon:[[@LINE+1]]: Expected a type, but got 7
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/as/fail_destination_not_type.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'Type'
return 4 as 7;
}
@@ -8,7 +8,7 @@
package ExplorerTest api;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_alternative_not_type.carbon:[[@LINE+1]]: Expected a type, but got (42)
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_alternative_not_type.carbon:[[@LINE+1]]: type error in type expression: '(i32)' is not implicitly convertible to 'Type'
choice C { X(42) }
fn Main() -> i32 {
+1 -1
View File
@@ -9,7 +9,7 @@
package ExplorerTest api;
fn Main() -> i32 {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_nested_binding.carbon:[[@LINE+1]]: The type of a binding pattern cannot contain bindings.
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_nested_binding.carbon:[[@LINE+1]]: the type of a binding pattern cannot contain bindings
var x: (T: Type) = 1;
return 1;
}
+1 -1
View File
@@ -11,7 +11,7 @@ package ExplorerTest api;
fn Main () -> i32
{
// 42 cannot be used as the type of a variable.
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_type.carbon:[[@LINE+1]]: Expected a type, but got 42
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_type.carbon:[[@LINE+1]]: type error in type of name binding: 'i32' is not implicitly convertible to 'Type'
var x: 42 = 0;
return x;
}
+1 -1
View File
@@ -8,7 +8,7 @@
package ExplorerTest api;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_extends_non_class.carbon:[[@LINE+1]]: Expected a type, but got 3
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_extends_non_class.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'Type'
class C extends 3 {
var x: i32;
var y: i32;
+1 -1
View File
@@ -17,7 +17,7 @@ fn H[T:! Type](x: T) {}
fn Main() -> i32 {
H(C.F);
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_method_deduced.carbon:[[@LINE+1]]: Expected a type, but got member name G
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_method_deduced.carbon:[[@LINE+1]]: member name G can only be used in a member access or alias
H(C.G);
return 0;
}
+1 -1
View File
@@ -15,7 +15,7 @@ class C {
fn Main() -> i32 {
var f: auto = C.F;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_method_in_var.carbon:[[@LINE+1]]: Expected a type, but got member name G
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_method_in_var.carbon:[[@LINE+1]]: member name G can only be used in a member access or alias
var g: auto = C.G;
return 0;
+1 -1
View File
@@ -14,7 +14,7 @@ class C {
}
fn ReturnF() -> auto { return C.F; }
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_return_method.carbon:[[@LINE+1]]: Expected a type, but got member name G
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_return_method.carbon:[[@LINE+1]]: member name G can only be used in a member access or alias
fn ReturnG() -> auto { return C.G; }
fn Main() -> i32 {
@@ -3,13 +3,12 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// RUN: %{not} %{explorer-run}
// RUN: %{not} %{explorer-run-trace}
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 0
package ExplorerTest api;
// TODO: This should work
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/comparison/fail_empty_struct.carbon:[[@LINE+1]]: type error in call: '({})' is not implicitly convertible to '(Type)'
external impl {} as EqWith({}) {
fn Equal[me: Self](other: Self) -> bool {
return true;
+1 -1
View File
@@ -10,7 +10,7 @@ package ExplorerTest api;
interface A {}
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_where_non_type_is.carbon:[[@LINE+1]]: Expected a type, but got 4
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_where_non_type_is.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'Type'
alias B = A where 4 is A;
fn Main() -> i32 { return 0; }
+1 -1
View File
@@ -9,7 +9,7 @@
package ExplorerTest api;
// 42 cannot be used as the type of a parameter.
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_parameter_type.carbon:[[@LINE+1]]: Expected a type, but got 42
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_parameter_type.carbon:[[@LINE+1]]: type error in type of name binding: 'i32' is not implicitly convertible to 'Type'
fn f(x: 42) -> i32 {
return x - 1;
}
@@ -9,7 +9,7 @@
package EmptyIdentifier impl;
fn apply[T:! Type, U:! Type](f: T, EmptyIdentifier: U) {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_return_call_has_invalid_body.carbon:[[@LINE+1]]: expected a tuple
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_return_call_has_invalid_body.carbon:[[@LINE+1]]: only arrays and tuples can be indexed, found bool
match (true[true]) {}
}
+1 -1
View File
@@ -13,7 +13,7 @@ fn test() -> i32 {
}
fn Main() -> i32 {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_var_type_is_call.carbon:[[@LINE+1]]: Expected a type, but got 1
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_var_type_is_call.carbon:[[@LINE+1]]: type error in type of name binding: 'i32' is not implicitly convertible to 'Type'
var x: test() = 1;
return 0;
}
@@ -0,0 +1,21 @@
// 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: result: 0
package ExplorerTest api;
class TypeLike {
impl as ImplicitAs(Type) {
fn Convert[me: Self]() -> Type { return i32; }
}
fn Make() -> Self { return {}; }
}
fn Main() -> TypeLike.Make() {
return 0;
}
@@ -9,6 +9,7 @@
package ExplorerTest api;
class Point(T:! i32) {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_bad_parameter_type.carbon:[[@LINE+1]]: type error in type of name binding: 'i32' is not implicitly convertible to 'Type'
fn Origin(zero: T) -> Point(T) {
return {.x = zero, .y = zero};
}
@@ -22,7 +23,6 @@ class Point(T:! i32) {
}
fn Main() -> i32 {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_bad_parameter_type.carbon:[[@LINE+1]]: type error in call: 'Type' is not implicitly convertible to 'i32'
var p: Point(i32) = Point(i32).Origin(0);
return p.GetX();
}
@@ -11,7 +11,7 @@ package ExplorerTest api;
fn Main() -> i32 {
var t: auto = 5;
match (t) {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_generic_in_pattern.carbon:[[@LINE+1]]: Generic binding may not occur in pattern with expected type: T:! i32
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_generic_in_pattern.carbon:[[@LINE+1]]: generic binding may not occur in pattern with expected type T:! i32
case T:! i32 => { return 0; }
default => { return 1; }
}
+1 -1
View File
@@ -14,7 +14,7 @@ class Point(T:! Type) {
}
// Error: wrote `Point` instead of `Point(T)`, `Point` by itself is not a type.
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_no_args.carbon:[[@LINE+1]]: Expected a type, but got Point
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_no_args.carbon:[[@LINE+1]]: 'Point' must be given an argument list
fn GetX[me: Point]() -> T {
return me.x;
}
@@ -10,7 +10,7 @@ package ExplorerTest api;
class Point(T:! Type) {
// The return type should be Point(T). Point by itself is not a type.
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_return_type_is_type.carbon:[[@LINE+1]]: Expected a type, but got Point
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_return_type_is_type.carbon:[[@LINE+1]]: 'Point' must be given an argument list
fn Create(x: T, y: T) -> Point {
return {.x = x, .y = y};
}
+1 -1
View File
@@ -8,7 +8,7 @@
package ExplorerTest api;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_not_type.carbon:[[@LINE+1]]: Expected a type, but got 42
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_not_type.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'Type'
fn F[a:! 42]();
fn Main() -> i32 {
+1 -1
View File
@@ -11,7 +11,7 @@ package ExplorerTest api;
interface Vector(Scalar:! Type) {
}
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/impl/fail_impl_as_parameterized.carbon:[[@LINE+1]]: Expected a type, but got Vector
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/impl/fail_impl_as_parameterized.carbon:[[@LINE+1]]: 'Vector' must be given an argument list
external impl i32 as Vector {}
fn Main() -> i32 {
+1 -1
View File
@@ -11,7 +11,7 @@ package ExplorerTest api;
interface A {}
interface B {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_impl_as_not_type.carbon:[[@LINE+1]]: Expected a type, but got 5
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_impl_as_not_type.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'Type'
impl 5 as A;
}
+1 -1
View File
@@ -11,7 +11,7 @@ package ExplorerTest api;
interface Vector {
fn Zero() -> i32;
}
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_impl_not_type.carbon:[[@LINE+1]]: Expected a type, but got "hello"
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_impl_not_type.carbon:[[@LINE+1]]: type error in type expression: 'String' is not implicitly convertible to 'Type'
impl "hello" as Vector {
fn Zero() -> i32 { return 0; }
}
+1 -1
View File
@@ -11,7 +11,7 @@ package ExplorerTest api;
__mixin Operations {}
fn Main() -> i32 {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail_mix_as_type_expr.carbon:[[@LINE+1]]: Expected a type, but got mixin Operations
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail_mix_as_type_expr.carbon:[[@LINE+1]]: invalid use of mixin Operations
var a: Operations;
return 0;
}
@@ -0,0 +1,22 @@
// 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: result: 3
package ExplorerTest api;
class TypeLike {
impl as ImplicitAs(Type) {
fn Convert[me: Self]() -> Type { return i32; }
}
fn Make() -> Self { return {}; }
}
fn Main() -> TypeLike.Make() {
var x: {.a: TypeLike.Make(), .b: TypeLike.Make()} = {.a = 1, .b = 2};
return x.a + x.b;
}
@@ -0,0 +1,14 @@
// 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;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/tuple/fail_nontype_tuple_as_type.carbon:[[@LINE+1]]: type error in type of name binding: '(i32, i32)' is not implicitly convertible to 'Type'
fn F[T:! (i32, i32)](x: T);
fn Main() -> i32;
+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
// RUN: %{not} %{explorer-run}
// RUN: %{not} %{explorer-run-trace}
package ExplorerTest api;
fn F[T:! ((), ())](x: T) -> () {
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/tuple/fail_type_tuple_as_type.carbon:[[@LINE+1]]: only arrays and tuples can be indexed, found T
return x[0];
}
fn Main() -> i32 {
F(((), ()));
return 0;
}