diff --git a/src/builtins/base.tq b/src/builtins/base.tq index a60f84c676..abaad65832 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -992,6 +992,8 @@ extern class WasmDebugInfo extends Struct { extern class WasmExceptionTag extends Struct { index: Smi; } +const kTaggedSize: constexpr int31 generates 'kTaggedSize'; + const kSmiTagSize: constexpr int31 generates 'kSmiTagSize'; const V8_INFINITY: constexpr float64 generates 'V8_INFINITY'; diff --git a/src/builtins/torque-internal.tq b/src/builtins/torque-internal.tq index 99cc52f19f..93858668d9 100644 --- a/src/builtins/torque-internal.tq +++ b/src/builtins/torque-internal.tq @@ -3,10 +3,87 @@ // found in the LICENSE file. namespace torque_internal { + // TODO(gsps): Synthesize SizeOf in the compiler + + macro SizeOf(): constexpr int31; + SizeOf(): constexpr int31 { + return kTaggedSize; + } + + // Unsafe is a marker that we require to be passed when calling internal APIs + // // that might lead to unsoundness when used incorrectly. Unsafe markers + // should therefore not be instantiated anywhere outside of this namespace. + struct Unsafe {} struct Reference { const object: HeapObject; const offset: intptr; + unsafeMarker: Unsafe; + } + + macro UnsafeNewReference(object: HeapObject, offset: intptr):&T { + return Reference{ + object: object, + offset: offset, + unsafeMarker: Unsafe {} + }; + } + + struct Slice { + Access(index: intptr):&T labels OutOfBounds { + if (Convert(index) < Convert(this.length)) { + return UnsafeNewReference( + this.object, this.offset + index * SizeOf()); + } else { + goto OutOfBounds; + } + } + + Iterator(): SliceIterator { + const end = this.offset + this.length * SizeOf(); + return SliceIterator{ + object: this.object, + start: this.offset, + end: end, + unsafeMarker: Unsafe {} + }; + } + + const object: HeapObject; + const offset: intptr; + const length: intptr; + unsafeMarker: Unsafe; + } + + macro UnsafeNewSlice( + object: HeapObject, offset: intptr, length: intptr): Slice { + return Slice{ + object: object, + offset: offset, + length: length, + unsafeMarker: Unsafe {} + }; + } + + struct SliceIterator { + Empty(): bool { + return this.start == this.end; + } + + Next():&T labels NoMore { + if (this.Empty()) { + goto NoMore; + } else { + const result = UnsafeNewReference(this.object, this.start); + this.start += SizeOf(); + return result; + } + } + + object: HeapObject; + start: intptr; + end: intptr; + unsafeMarker: Unsafe; } } // namespace torque_internal diff --git a/src/torque/constants.h b/src/torque/constants.h index 948e74bc1a..eb89b429cf 100644 --- a/src/torque/constants.h +++ b/src/torque/constants.h @@ -51,6 +51,7 @@ static const char* const CONST_INT32_TYPE_STRING = "constexpr int32"; static const char* const CONST_FLOAT64_TYPE_STRING = "constexpr float64"; static const char* const TORQUE_INTERNAL_NAMESPACE_STRING = "torque_internal"; static const char* const REFERENCE_TYPE_STRING = "Reference"; +static const char* const STRUCT_NAMESPACE_STRING = "_struct"; inline bool IsConstexprName(const std::string& name) { return name.substr(0, std::strlen(CONSTEXPR_TYPE_PREFIX)) == diff --git a/src/torque/contextual.h b/src/torque/contextual.h index 92d2bdf3d7..d7764bfa68 100644 --- a/src/torque/contextual.h +++ b/src/torque/contextual.h @@ -15,7 +15,7 @@ namespace internal { namespace torque { template -V8_EXPORT_PRIVATE typename Variable::VariableType*& ContextualVariableTop(); +V8_EXPORT_PRIVATE typename Variable::Scope*& ContextualVariableTop(); // {ContextualVariable} provides a clean alternative to a global variable. // The contextual variable is mutable, and supports managing the value of @@ -30,8 +30,6 @@ V8_EXPORT_PRIVATE typename Variable::VariableType*& ContextualVariableTop(); template class ContextualVariable { public: - using VariableType = VarType; - // A {Scope} contains a new object of type {VarType} and gives // ContextualVariable::Get() access to it. Upon destruction, the contextual // variable is restored to the state before the {Scope} was created. Scopes @@ -41,18 +39,20 @@ class ContextualVariable { public: template explicit Scope(Args&&... args) - : current_(std::forward(args)...), previous_(Top()) { - Top() = ¤t_; + : value_(std::forward(args)...), previous_(Top()) { + Top() = this; } ~Scope() { // Ensure stack discipline. - DCHECK_EQ(¤t_, Top()); + DCHECK_EQ(this, Top()); Top() = previous_; } + VarType& Value() { return value_; } + private: - VarType current_; - VarType* previous_; + VarType value_; + Scope* previous_; static_assert(std::is_base_of::value, "Curiously Recurring Template Pattern"); @@ -65,13 +65,13 @@ class ContextualVariable { // for this contextual variable. static VarType& Get() { DCHECK_NOT_NULL(Top()); - return *Top(); + return Top()->Value(); } private: template - friend typename T::VariableType*& ContextualVariableTop(); - static VarType*& Top() { return ContextualVariableTop(); } + friend typename T::Scope*& ContextualVariableTop(); + static Scope*& Top() { return ContextualVariableTop(); } static bool HasScope() { return Top() != nullptr; } friend class MessageBuilder; @@ -82,11 +82,11 @@ class ContextualVariable { struct VarName \ : v8::internal::torque::ContextualVariable {} -#define DEFINE_CONTEXTUAL_VARIABLE(VarName) \ - template <> \ - V8_EXPORT_PRIVATE VarName::VariableType*& ContextualVariableTop() { \ - static thread_local VarName::VariableType* top = nullptr; \ - return top; \ +#define DEFINE_CONTEXTUAL_VARIABLE(VarName) \ + template <> \ + V8_EXPORT_PRIVATE VarName::Scope*& ContextualVariableTop() { \ + static thread_local VarName::Scope* top = nullptr; \ + return top; \ } // By inheriting from {ContextualClass} a class can become a contextual variable diff --git a/src/torque/declarable.cc b/src/torque/declarable.cc index e7bc42993b..5bcaff016d 100644 --- a/src/torque/declarable.cc +++ b/src/torque/declarable.cc @@ -75,6 +75,7 @@ TypeArgumentInference Generic::InferSpecializationTypes( std::vector explicit_parameters( parameters.begin() + implicit_count, parameters.end()); + CurrentScope::Scope generic_scope(ParentScope()); TypeArgumentInference inference(generic_parameters(), explicit_specialization_types, explicit_parameters, arguments); diff --git a/src/torque/type-inference.cc b/src/torque/type-inference.cc index 025888f888..abd875f4f6 100644 --- a/src/torque/type-inference.cc +++ b/src/torque/type-inference.cc @@ -20,6 +20,10 @@ TypeArgumentInference::TypeArgumentInference( Fail("more explicit type arguments than expected"); return; } + if (term_parameters.size() != term_argument_types.size()) { + Fail("number of term parameters does not match number of term arguments!"); + return; + } for (size_t i = 0; i < type_parameters.size(); i++) { type_parameter_from_name_[type_parameters[i]->value] = i; @@ -28,7 +32,6 @@ TypeArgumentInference::TypeArgumentInference( inferred_[i] = {explicit_type_arguments[i]}; } - DCHECK_EQ(term_parameters.size(), term_argument_types.size()); for (size_t i = 0; i < term_parameters.size(); i++) { Match(term_parameters[i], term_argument_types[i]); if (HasFailed()) return; diff --git a/src/torque/type-inference.h b/src/torque/type-inference.h index c59e1a033b..671d68cce5 100644 --- a/src/torque/type-inference.h +++ b/src/torque/type-inference.h @@ -48,6 +48,11 @@ namespace torque { // In the above case the inference simply ignores inconsistent constraints on // `T`. Similarly, we ignore all constraints arising from formal parameters // that are function- or union-typed. +// +// Finally, note that term parameters are passed as type expressions, since +// we have no way of expressing a reference to type parameter as a Type. These +// type expressions are resolved during matching, so TypeArgumentInference +// should be instantiated in the appropriate scope. class TypeArgumentInference { public: TypeArgumentInference(const NameVector& type_parameters, diff --git a/src/torque/type-oracle.cc b/src/torque/type-oracle.cc index ec2324ff40..c7e11c2165 100644 --- a/src/torque/type-oracle.cc +++ b/src/torque/type-oracle.cc @@ -39,17 +39,6 @@ const StructType* TypeOracle::GetGenericStructTypeInstance( return *specialization; } else { CurrentScope::Scope generic_scope(generic_struct->ParentScope()); - // Create a temporary fake-namespace just to temporarily declare the - // specialization aliases for the generic types to create a signature. - Namespace tmp_namespace("_tmp"); - CurrentScope::Scope tmp_namespace_scope(&tmp_namespace); - auto arg_types_iterator = arg_types.begin(); - for (auto param : params) { - TypeAlias* alias = Declarations::DeclareType(param, *arg_types_iterator); - alias->SetIsUserDefined(false); - arg_types_iterator++; - } - auto struct_type = TypeVisitor::ComputeType(generic_struct->declaration(), {{generic_struct, arg_types}}); specializations.Add(arg_types, struct_type); diff --git a/src/torque/type-oracle.h b/src/torque/type-oracle.h index a319a7e75e..861c78373a 100644 --- a/src/torque/type-oracle.h +++ b/src/torque/type-oracle.h @@ -31,11 +31,12 @@ class TypeOracle : public ContextualClass { } static StructType* GetStructType( - const std::string& basename, + const StructDeclaration* decl, StructType::MaybeSpecializationKey specialized_from) { - StructType* result = - new StructType(CurrentNamespace(), basename, specialized_from); + Namespace* nspace = new Namespace(STRUCT_NAMESPACE_STRING); + StructType* result = new StructType(nspace, decl, specialized_from); Get().aggregate_types_.push_back(std::unique_ptr(result)); + Get().struct_namespaces_.push_back(std::unique_ptr(nspace)); return result; } @@ -260,6 +261,7 @@ class TypeOracle : public ContextualClass { std::vector> nominal_types_; std::vector> aggregate_types_; std::vector> top_types_; + std::vector> struct_namespaces_; }; } // namespace torque diff --git a/src/torque/type-visitor.cc b/src/torque/type-visitor.cc index 7945abfa9f..e25d05733e 100644 --- a/src/torque/type-visitor.cc +++ b/src/torque/type-visitor.cc @@ -110,9 +110,20 @@ void DeclareMethods(AggregateType* container_type, const StructType* TypeVisitor::ComputeType( StructDeclaration* decl, StructType::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); - StructType* struct_type = - TypeOracle::GetStructType(decl->name->value, specialized_from); + + 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, *arg_types_iterator); + alias->SetIsUserDefined(false); + arg_types_iterator++; + } + } + size_t offset = 0; for (auto& field : decl->fields) { CurrentSourcePosition::Scope position_activator( @@ -132,7 +143,6 @@ const StructType* TypeVisitor::ComputeType( false}); offset += LoweredSlotCount(field_type); } - DeclareMethods(struct_type, decl->methods); return struct_type; } @@ -318,6 +328,11 @@ void TypeVisitor::VisitClassFieldsAndMethods( DeclareMethods(class_type, class_declaration->methods); } +void TypeVisitor::VisitStructMethods( + StructType* struct_type, const StructDeclaration* struct_declaration) { + DeclareMethods(struct_type, struct_declaration->methods); +} + const StructType* TypeVisitor::ComputeTypeForStructExpression( TypeExpression* type_expression, const std::vector& term_argument_types) { @@ -350,9 +365,11 @@ const StructType* TypeVisitor::ComputeTypeForStructExpression( term_parameters.push_back(field.name_and_type.type); } + CurrentScope::Scope generic_scope(generic_struct->ParentScope()); TypeArgumentInference inference( generic_struct->declaration()->generic_parameters, explicit_type_arguments, term_parameters, term_argument_types); + if (inference.HasFailed()) { ReportError("failed to infer type arguments for struct ", basic->name, " initialization: ", inference.GetFailureReason()); diff --git a/src/torque/type-visitor.h b/src/torque/type-visitor.h index d03c127791..cafd752cbf 100644 --- a/src/torque/type-visitor.h +++ b/src/torque/type-visitor.h @@ -27,6 +27,8 @@ class TypeVisitor { static const Type* ComputeType(TypeExpression* type_expression); static void VisitClassFieldsAndMethods( ClassType* class_type, const ClassDeclaration* class_declaration); + static void VisitStructMethods(StructType* struct_type, + const StructDeclaration* struct_declaration); static Signature MakeSignature(const CallableDeclaration* declaration); static const StructType* ComputeTypeForStructExpression( TypeExpression* type_expression, diff --git a/src/torque/types.cc b/src/torque/types.cc index a4d47fd294..26eaa4b88d 100644 --- a/src/torque/types.cc +++ b/src/torque/types.cc @@ -298,7 +298,7 @@ std::string StructType::MangledName() const { std::stringstream result; // TODO(gsps): Add 'ST' as a prefix once we can control the generated type // name from Torque code - result << basename_; + result << decl_->name->value; if (specialized_from_) { for (const Type* t : specialized_from_->specialized_types) { std::string arg_type_string = t->MangledName(); @@ -345,6 +345,17 @@ std::string StructType::ToExplicitString() const { return result.str(); } +void StructType::Finalize() const { + if (is_finalized_) return; + { + CurrentScope::Scope scope_activator(nspace()); + CurrentSourcePosition::Scope position_activator(decl_->pos); + TypeVisitor::VisitStructMethods(const_cast(this), decl_); + } + is_finalized_ = true; + CheckForDuplicateFields(); +} + constexpr ClassFlags ClassType::kInternalFlags; ClassType::ClassType(const Type* parent, Namespace* nspace, diff --git a/src/torque/types.h b/src/torque/types.h index e6c8c12642..d2198d50c3 100644 --- a/src/torque/types.h +++ b/src/torque/types.h @@ -486,22 +486,19 @@ class StructType final : public AggregateType { private: friend class TypeOracle; - StructType(Namespace* nspace, const std::string& basename, + StructType(Namespace* nspace, const StructDeclaration* decl, MaybeSpecializationKey specialized_from = base::nullopt) : AggregateType(Kind::kStructType, nullptr, nspace, - ComputeName(basename, specialized_from)), - basename_(basename), + ComputeName(decl->name->value, specialized_from)), + decl_(decl), specialized_from_(specialized_from) {} - void Finalize() const override { - is_finalized_ = true; - CheckForDuplicateFields(); - } + void Finalize() const override; static std::string ComputeName(const std::string& basename, MaybeSpecializationKey specialized_from); - std::string basename_; + const StructDeclaration* decl_; MaybeSpecializationKey specialized_from_; }; diff --git a/test/cctest/torque/test-torque.cc b/test/cctest/torque/test-torque.cc index 86bc2d8629..5feedee940 100644 --- a/test/cctest/torque/test-torque.cc +++ b/test/cctest/torque/test-torque.cc @@ -484,6 +484,20 @@ TEST(TestReferences) { ft.Call(); } +TEST(TestSlices) { + CcTest::InitializeVM(); + Isolate* isolate(CcTest::i_isolate()); + i::HandleScope scope(isolate); + CodeAssemblerTester asm_tester(isolate); + TestTorqueAssembler m(asm_tester.state()); + { + m.TestSlices(); + m.Return(m.UndefinedConstant()); + } + FunctionTester ft(asm_tester.GenerateCode(), 0); + ft.Call(); +} + TEST(TestStaticAssert) { CcTest::InitializeVM(); Isolate* isolate(CcTest::i_isolate()); diff --git a/test/torque/test-torque.tq b/test/torque/test-torque.tq index de5f57f9c1..6912d472cf 100644 --- a/test/torque/test-torque.tq +++ b/test/torque/test-torque.tq @@ -842,6 +842,31 @@ namespace test { check(array.b == 9); } + @export + macro TestSlices() { + const fixedArray: FixedArray = AllocateZeroedFixedArray(3); + // TODO(gsps): Directly reference fixedArray.objects once supported + let slice = torque_internal::UnsafeNewSlice( + fixedArray, + (& fixedArray.length).offset + torque_internal::SizeOf(), 3); + + for (let i: intptr = 0; i < slice.length; i++) { + let ref = slice.Access(i) otherwise unreachable; + check(* ref == 0); + * ref = Convert(i) + 7; + } + + let it = slice.Iterator(); + let count: Smi = 0; + while (true) { + let ref = it.Next() otherwise break; + check(* ref == count + 7); + count++; + } + check(count == 3); + check(it.Empty()); + } + @export macro TestStaticAssert() { StaticAssert(1 + 2 == 3);