mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
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:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>{}) {}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
-4
@@ -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;
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user