blob: a706fc561dd8d265859be16eb00c8f713023ef51 [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/torque/type-visitor.h"
#include "src/common/globals.h"
#include "src/torque/declarable.h"
#include "src/torque/global-context.h"
#include "src/torque/server-data.h"
#include "src/torque/type-inference.h"
#include "src/torque/type-oracle.h"
namespace v8 {
namespace internal {
namespace torque {
const Type* TypeVisitor::ComputeType(TypeDeclaration* decl,
MaybeSpecializationKey specialized_from,
Scope* specialization_requester) {
SourcePosition requester_position = CurrentSourcePosition::Get();
CurrentSourcePosition::Scope scope(decl->pos);
Scope* current_scope = CurrentScope::Get();
if (specialized_from) {
current_scope = TypeOracle::CreateGenericTypeInstantiationNamespace();
current_scope->SetSpecializationRequester(
{requester_position, specialization_requester,
Type::ComputeName(decl->name->value, specialized_from)});
}
CurrentScope::Scope new_current_scope_scope(current_scope);
if (specialized_from) {
auto& params = specialized_from->generic->generic_parameters();
auto arg_types_iterator = specialized_from->specialized_types.begin();
for (auto param : params) {
TypeAlias* alias =
Declarations::DeclareType(param.name, *arg_types_iterator);
alias->SetIsUserDefined(false);
arg_types_iterator++;
}
}
switch (decl->kind) {
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
return ComputeType(name::cast(decl), specialized_from);
AST_TYPE_DECLARATION_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNIMPLEMENTED();
}
}
const Type* TypeVisitor::ComputeType(TypeAliasDeclaration* decl,
MaybeSpecializationKey specialized_from) {
const Type* type = ComputeType(decl->type);
type->AddAlias(decl->name->value);
return type;
}
namespace {
std::string ComputeGeneratesType(base::Optional<std::string> opt_gen,
bool enforce_tnode_type) {
if (!opt_gen) return "";
const std::string& generates = *opt_gen;
if (enforce_tnode_type) {
if (generates.length() < 7 || generates.substr(0, 6) != "TNode<" ||
generates.substr(generates.length() - 1, 1) != ">") {
ReportError("generated type \"", generates,
"\" should be of the form \"TNode<...>\"");
}
return generates.substr(6, generates.length() - 7);
}
return generates;
}
} // namespace
const AbstractType* TypeVisitor::ComputeType(
AbstractTypeDeclaration* decl, MaybeSpecializationKey specialized_from) {
std::string generates =
ComputeGeneratesType(decl->generates, !decl->IsConstexpr());
const Type* parent_type = nullptr;
if (decl->extends) {
parent_type = TypeVisitor::ComputeType(*decl->extends);
if (parent_type->IsUnionType()) {
// UnionType::IsSupertypeOf requires that types can only extend from non-
// union types in order to work correctly.
ReportError("type \"", decl->name->value,
"\" cannot extend a type union");
}
}
if (decl->IsConstexpr() && decl->IsTransient()) {
ReportError("cannot declare a transient type that is also constexpr");
}
const Type* non_constexpr_version = nullptr;
if (decl->IsConstexpr()) {
QualifiedName non_constexpr_name{GetNonConstexprName(decl->name->value)};
if (auto type = Declarations::TryLookupType(non_constexpr_name)) {
non_constexpr_version = *type;
}
}
return TypeOracle::GetAbstractType(parent_type, decl->name->value,
decl->flags, generates,
non_constexpr_version, specialized_from);
}
void DeclareMethods(AggregateType* container_type,
const std::vector<Declaration*>& methods) {
for (auto declaration : methods) {
CurrentSourcePosition::Scope pos_scope(declaration->pos);
TorqueMacroDeclaration* method =
TorqueMacroDeclaration::DynamicCast(declaration);
Signature signature = TypeVisitor::MakeSignature(method);
signature.parameter_names.insert(
signature.parameter_names.begin() + signature.implicit_count,
MakeNode<Identifier>(kThisParameterName));
Statement* body = *(method->body);
const std::string& method_name(method->name->value);
signature.parameter_types.types.insert(
signature.parameter_types.types.begin() + signature.implicit_count,
container_type);
Declarations::CreateMethod(container_type, method_name, signature, body);
}
}
const BitFieldStructType* TypeVisitor::ComputeType(
BitFieldStructDeclaration* decl, MaybeSpecializationKey specialized_from) {
CurrentSourcePosition::Scope position_scope(decl->pos);
if (specialized_from.has_value()) {
ReportError("Bitfield struct specialization is not supported");
}
const Type* parent = TypeVisitor::ComputeType(decl->parent);
if (!IsAnyUnsignedInteger(parent)) {
ReportError(
"Bitfield struct must extend from an unsigned integer type, not ",
parent->ToString());
}
auto opt_size = SizeOf(parent);
if (!opt_size.has_value()) {
ReportError("Cannot determine size of bitfield struct ", decl->name->value,
" because of unsized parent type ", parent->ToString());
}
const size_t size = 8 * std::get<0>(*opt_size); // Convert bytes to bits.
BitFieldStructType* type = TypeOracle::GetBitFieldStructType(parent, decl);
// Iterate through all of the declared fields, checking their validity and
// registering them on the newly-constructed BitFieldStructType instance.
int offset = 0;
for (const auto& field : decl->fields) {
CurrentSourcePosition::Scope field_position_scope(
field.name_and_type.type->pos);
const Type* field_type = TypeVisitor::ComputeType(field.name_and_type.type);
if (!IsAllowedAsBitField(field_type)) {
ReportError("Type not allowed as bitfield: ",
field.name_and_type.name->value);
}
// Compute the maximum number of bits that could be used for a field of this
// type. Booleans are a special case, not included in SizeOf, because their
// runtime size is 32 bits but they should only occupy 1 bit as a bitfield.
size_t field_type_size = 0;
if (field_type->IsSubtypeOf(TypeOracle::GetBoolType())) {
field_type_size = 1;
} else {
auto opt_field_type_size = SizeOf(field_type);
if (!opt_field_type_size.has_value()) {
ReportError("Size unknown for type ", field_type->ToString());
}
field_type_size = 8 * std::get<0>(*opt_field_type_size);
}
if (field.num_bits < 1 ||
static_cast<size_t>(field.num_bits) > field_type_size) {
ReportError("Invalid number of bits for ",
field.name_and_type.name->value);
}
type->RegisterField({field.name_and_type.name->pos,
{field.name_and_type.name->value, field_type},
offset,
field.num_bits});
offset += field.num_bits;
if (static_cast<size_t>(offset) > size) {
ReportError("Too many total bits in ", decl->name->value);
}
}
return type;
}
const StructType* TypeVisitor::ComputeType(
StructDeclaration* decl, MaybeSpecializationKey specialized_from) {
StructType* struct_type = TypeOracle::GetStructType(decl, specialized_from);
CurrentScope::Scope struct_namespace_scope(struct_type->nspace());
CurrentSourcePosition::Scope position_activator(decl->pos);
ResidueClass offset = 0;
for (auto& field : decl->fields) {
CurrentSourcePosition::Scope position_activator(
field.name_and_type.type->pos);
const Type* field_type = TypeVisitor::ComputeType(field.name_and_type.type);
if (field_type->IsConstexpr()) {
ReportError("struct field \"", field.name_and_type.name->value,
"\" carries constexpr type \"", *field_type, "\"");
}
Field f{field.name_and_type.name->pos,
struct_type,
base::nullopt,
{field.name_and_type.name->value, field_type},
offset.SingleValue(),
false,
field.const_qualified,
false};
auto optional_size = SizeOf(f.name_and_type.type);
struct_type->RegisterField(f);
// Offsets are assigned based on an assumption of no space between members.
// This might lead to invalid alignment in some cases, but most structs are
// never actually packed in memory together (they just represent a batch of
// CSA TNode values that should be passed around together). For any struct
// that is used as a class field, we verify its offsets when setting up the
// class type.
if (optional_size.has_value()) {
size_t field_size = 0;
std::tie(field_size, std::ignore) = *optional_size;
offset += field_size;
} else {
// Structs may contain fields that aren't representable in packed form. If
// so, the offset of subsequent fields are marked as invalid.
offset = ResidueClass::Unknown();
}
}
return struct_type;
}
const ClassType* TypeVisitor::ComputeType(
ClassDeclaration* decl, MaybeSpecializationKey specialized_from) {
// TODO(sigurds): Remove this hack by introducing a declarable for classes.
const TypeAlias* alias =
Declarations::LookupTypeAlias(QualifiedName(decl->name->value));
DCHECK_EQ(*alias->delayed_, decl);
ClassFlags flags = decl->flags;
bool is_shape = flags & ClassFlag::kIsShape;
std::string generates = decl->name->value;
const Type* super_type = TypeVisitor::ComputeType(decl->super);
if (is_shape) {
if (!(flags & ClassFlag::kExtern)) {
ReportError("Shapes must be extern, add \"extern\" to the declaration.");
}
if (flags & ClassFlag::kUndefinedLayout) {
ReportError("Shapes need to define their layout.");
}
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class ||
!super_class->IsSubtypeOf(TypeOracle::GetJSObjectType())) {
Error("Shapes need to extend a subclass of ",
*TypeOracle::GetJSObjectType())
.Throw();
}
// Shapes use their super class in CSA code since they have incomplete
// support for type-checks on the C++ side.
generates = super_class->name();
}
if (super_type != TypeOracle::GetStrongTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError(
"class \"", decl->name->value,
"\" must extend either StrongTagged or an already declared class");
}
if (super_class->HasUndefinedLayout() &&
!(flags & ClassFlag::kUndefinedLayout)) {
Error("Class \"", decl->name->value,
"\" defines its layout but extends a class which does not")
.Position(decl->pos);
}
if ((flags & ClassFlag::kExport) &&
!(super_class->ShouldExport() || super_class->IsExtern())) {
Error("cannot export class ", decl->name,
" because superclass is neither @export or extern");
}
}
if ((flags & ClassFlag::kGenerateBodyDescriptor ||
flags & ClassFlag::kExport) &&
flags & ClassFlag::kUndefinedLayout) {
Error("Class \"", decl->name->value,
"\" requires a layout but doesn't have one");
}
if (flags & ClassFlag::kCustomCppClass) {
if (!(flags & ClassFlag::kExport)) {
Error("Only exported classes can have a custom C++ class.");
}
if (flags & ClassFlag::kExtern) {
Error("No need to specify ", ANNOTATION_CUSTOM_CPP_CLASS,
", extern classes always have a custom C++ class.");
}
}
if (flags & ClassFlag::kExtern) {
if (decl->generates) {
bool enforce_tnode_type = true;
generates = ComputeGeneratesType(decl->generates, enforce_tnode_type);
}
if (flags & ClassFlag::kExport) {
Error("cannot export a class that is marked extern");
}
} else {
if (decl->generates) {
ReportError("Only extern classes can specify a generated type.");
}
if (super_type != TypeOracle::GetStrongTaggedType()) {
if (flags & ClassFlag::kUndefinedLayout) {
Error("non-external classes must have defined layouts");
}
}
flags = flags | ClassFlag::kGeneratePrint | ClassFlag::kGenerateVerify |
ClassFlag::kGenerateBodyDescriptor;
}
if (!(flags & ClassFlag::kExtern) &&
(flags & ClassFlag::kHasSameInstanceTypeAsParent)) {
Error("non-extern Torque-defined classes must have unique instance types");
}
if ((flags & ClassFlag::kHasSameInstanceTypeAsParent) &&
!(flags & ClassFlag::kDoNotGenerateCast || flags & ClassFlag::kIsShape)) {
Error(
"classes that inherit their instance type must be annotated with "
"@doNotGenerateCast");
}
return TypeOracle::GetClassType(super_type, decl->name->value, flags,
generates, decl, alias);
}
const Type* TypeVisitor::ComputeType(TypeExpression* type_expression) {
if (auto* basic = BasicTypeExpression::DynamicCast(type_expression)) {
QualifiedName qualified_name{basic->namespace_qualification, basic->name};
auto& args = basic->generic_arguments;
const Type* type;
SourcePosition pos = SourcePosition::Invalid();
if (args.empty()) {
auto* alias = Declarations::LookupTypeAlias(qualified_name);
type = alias->type();
pos = alias->GetDeclarationPosition();
} else {
auto* generic_type =
Declarations::LookupUniqueGenericType(qualified_name);
type = TypeOracle::GetGenericTypeInstance(generic_type,
ComputeTypeVector(args));
pos = generic_type->declaration()->name->pos;
}
if (GlobalContext::collect_language_server_data()) {
LanguageServerData::AddDefinition(type_expression->pos, pos);
}
return type;
} else if (auto* union_type =
UnionTypeExpression::DynamicCast(type_expression)) {
return TypeOracle::GetUnionType(ComputeType(union_type->a),
ComputeType(union_type->b));
} else if (auto* function_type_exp =
FunctionTypeExpression::DynamicCast(type_expression)) {
TypeVector argument_types;
for (TypeExpression* type_exp : function_type_exp->parameters) {
argument_types.push_back(ComputeType(type_exp));
}
return TypeOracle::GetBuiltinPointerType(
argument_types, ComputeType(function_type_exp->return_type));
} else {
auto* precomputed = PrecomputedTypeExpression::cast(type_expression);
return precomputed->type;
}
}
Signature TypeVisitor::MakeSignature(const CallableDeclaration* declaration) {
LabelDeclarationVector definition_vector;
for (const auto& label : declaration->labels) {
LabelDeclaration def = {label.name, ComputeTypeVector(label.types)};
definition_vector.push_back(def);
}
base::Optional<std::string> arguments_variable;
if (declaration->parameters.has_varargs)
arguments_variable = declaration->parameters.arguments_variable;
Signature result{declaration->parameters.names,
arguments_variable,
{ComputeTypeVector(declaration->parameters.types),
declaration->parameters.has_varargs},
declaration->parameters.implicit_count,
ComputeType(declaration->return_type),
definition_vector,
declaration->transitioning};
return result;
}
void TypeVisitor::VisitClassFieldsAndMethods(
ClassType* class_type, const ClassDeclaration* class_declaration) {
const ClassType* super_class = class_type->GetSuperClass();
ResidueClass class_offset = 0;
size_t header_size = 0;
if (super_class) {
class_offset = super_class->size();
header_size = super_class->header_size();
}
for (const ClassFieldExpression& field_expression :
class_declaration->fields) {
CurrentSourcePosition::Scope position_activator(
field_expression.name_and_type.type->pos);
const Type* field_type = ComputeType(field_expression.name_and_type.type);
if (class_type->IsShape()) {
if (!field_type->IsSubtypeOf(TypeOracle::GetObjectType())) {
ReportError(
"in-object properties only support subtypes of Object, but "
"found type ",
*field_type);
}
if (field_expression.weak) {
ReportError("in-object properties cannot be weak");
}
}
base::Optional<Expression*> array_length = field_expression.index;
const Field& field = class_type->RegisterField(
{field_expression.name_and_type.name->pos,
class_type,
array_length,
{field_expression.name_and_type.name->value, field_type},
class_offset.SingleValue(),
field_expression.weak,
field_expression.const_qualified,
field_expression.generate_verify});
ResidueClass field_size = std::get<0>(field.GetFieldSizeInformation());
if (field.index) {
// Validate that a value at any index in a packed array is aligned
// correctly, since it is possible to define a struct whose size is not a
// multiple of its alignment.
field.ValidateAlignment(class_offset +
field_size * ResidueClass::Unknown());
if (auto literal = NumberLiteralExpression::DynamicCast(*field.index)) {
size_t value = static_cast<size_t>(literal->number);
if (value != literal->number) {
Error("non-integral array length").Position(field.pos);
}
field_size *= value;
} else {
field_size *= ResidueClass::Unknown();
}
}
field.ValidateAlignment(class_offset);
class_offset += field_size;
// In-object properties are not considered part of the header.
if (class_offset.SingleValue() && !class_type->IsShape()) {
header_size = *class_offset.SingleValue();
}
if (!field.index && !class_offset.SingleValue()) {
Error("Indexed fields have to be at the end of the object")
.Position(field.pos);
}
}
DCHECK_GT(header_size, 0);
class_type->header_size_ = header_size;
class_type->size_ = class_offset;
class_type->GenerateAccessors();
DeclareMethods(class_type, class_declaration->methods);
}
void TypeVisitor::VisitStructMethods(
StructType* struct_type, const StructDeclaration* struct_declaration) {
DeclareMethods(struct_type, struct_declaration->methods);
}
const Type* TypeVisitor::ComputeTypeForStructExpression(
TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types) {
auto* basic = BasicTypeExpression::DynamicCast(type_expression);
if (!basic) {
ReportError("expected basic type expression referring to struct");
}
QualifiedName qualified_name{basic->namespace_qualification, basic->name};
base::Optional<GenericType*> maybe_generic_type =
Declarations::TryLookupGenericType(qualified_name);
StructDeclaration* decl =
maybe_generic_type
? StructDeclaration::DynamicCast((*maybe_generic_type)->declaration())
: nullptr;
// Compute types of non-generic structs as usual
if (!(maybe_generic_type && decl)) {
const Type* type = ComputeType(type_expression);
if (!type->IsStructType() && !type->IsBitFieldStructType()) {
ReportError(*type,
" is not a struct or bitfield struct, but used like one");
}
return type;
}
auto generic_type = *maybe_generic_type;
auto explicit_type_arguments = ComputeTypeVector(basic->generic_arguments);
std::vector<TypeExpression*> term_parameters;
auto& fields = decl->fields;
term_parameters.reserve(fields.size());
for (auto& field : fields) {
term_parameters.push_back(field.name_and_type.type);
}
CurrentScope::Scope generic_scope(generic_type->ParentScope());
TypeArgumentInference inference(
generic_type->generic_parameters(), explicit_type_arguments,
term_parameters,
TransformVector<base::Optional<const Type*>>(term_argument_types));
if (inference.HasFailed()) {
ReportError("failed to infer type arguments for struct ", basic->name,
" initialization: ", inference.GetFailureReason());
}
if (GlobalContext::collect_language_server_data()) {
LanguageServerData::AddDefinition(type_expression->pos,
generic_type->declaration()->name->pos);
}
return StructType::cast(
TypeOracle::GetGenericTypeInstance(generic_type, inference.GetResult()));
}
} // namespace torque
} // namespace internal
} // namespace v8