1 /* 2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H 17 #define ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H 18 19 #include "checker/ETSchecker.h" 20 21 namespace ark::es2panda::checker { 22 class ETSChecker; 23 24 class AssignmentContext { 25 public: 26 // CC-OFFNXT(G.FUN.01-CPP) solid logic 27 AssignmentContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target, 28 const lexer::SourcePosition &pos, std::optional<util::DiagnosticWithParams> diag = std::nullopt, 29 TypeRelationFlag flags = TypeRelationFlag::NONE) 30 { 31 flags_ |= ((flags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; 32 flags_ |= ((flags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; 33 flags_ |= ((flags & TypeRelationFlag::NO_WIDENING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::WIDENING; 34 35 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 36 37 ES2PANDA_ASSERT(target != nullptr); 38 ES2PANDA_ASSERT(node != nullptr); 39 ES2PANDA_ASSERT(source != nullptr); 40 if (target->IsETSArrayType() && node->IsArrayExpression()) { 41 assignable_ = 42 ValidateArrayTypeInitializerByElement(relation, node->AsArrayExpression(), target->AsETSArrayType()); 43 relation->Result(assignable_); 44 return; 45 } 46 47 // CC-OFFNXT(G.FMT.02) project code style 48 flags_ |= flags; 49 relation->SetNode(node); 50 51 // NOTE (oeotvos) The narrowing flag will be applied here. It means, that the result of "let tmp: int = 1.5" 52 // will be 1, which could cause problems. 53 if (source->HasTypeFlag(TypeFlag::CONSTANT)) { 54 flags_ |= TypeRelationFlag::NARROWING; 55 } 56 57 relation->SetFlags(flags_); 58 59 if (!relation->IsAssignableTo(source, target)) { 60 if (relation->IsLegalBoxedPrimitiveConversion(target, source)) { 61 Type *sourceUnboxedType = etsChecker->MaybeUnboxType(source); 62 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetUnboxingFlag(sourceUnboxedType)); 63 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetBoxingFlag(target)); 64 } 65 if (((flags_ & TypeRelationFlag::UNBOXING) != 0) && !relation->IsTrue() && source->IsETSObjectType() && 66 !target->IsETSObjectType()) { 67 etsChecker->CheckUnboxedTypesAssignable(relation, source, target); 68 } 69 if (((flags_ & TypeRelationFlag::BOXING) != 0) && target->IsETSObjectType() && !relation->IsTrue()) { 70 etsChecker->CheckBoxedSourceTypeAssignable(relation, source, target); 71 } 72 } 73 74 if (!relation->IsTrue() && (flags_ & TypeRelationFlag::NO_THROW) == 0) { 75 relation->RaiseError(diag->kind, diag->params, pos); 76 } 77 78 relation->SetNode(nullptr); 79 relation->SetFlags(TypeRelationFlag::NONE); 80 assignable_ = relation->IsTrue(); 81 } 82 IsAssignable()83 bool IsAssignable() const 84 { 85 return assignable_; 86 } 87 88 bool ValidateArrayTypeInitializerByElement(TypeRelation *relation, ir::ArrayExpression *node, ETSArrayType *target); 89 90 private: 91 TypeRelationFlag flags_ = TypeRelationFlag::IN_ASSIGNMENT_CONTEXT; 92 bool assignable_ {false}; 93 }; 94 95 class InvocationContext { 96 public: 97 // CC-OFFNXT(G.FUN.01-CPP) solid logic 98 InvocationContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target, 99 const lexer::SourcePosition &pos, const std::optional<util::DiagnosticWithParams> &diag, 100 TypeRelationFlag initialFlags = TypeRelationFlag::NONE) 101 { 102 flags_ |= 103 ((initialFlags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; 104 flags_ |= 105 ((initialFlags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; 106 107 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 108 109 relation->SetNode(node); 110 relation->SetFlags(flags_ | initialFlags); 111 112 if (!relation->IsAssignableTo(source, target)) { 113 if (((flags_ & TypeRelationFlag::UNBOXING) != 0U) && !relation->IsTrue() && source->IsETSObjectType() && 114 !target->IsETSObjectType()) { 115 etsChecker->CheckUnboxedSourceTypeWithWideningAssignable(relation, source, target); 116 } 117 if (((flags_ & TypeRelationFlag::BOXING) != 0) && target->IsETSObjectType() && !relation->IsTrue()) { 118 etsChecker->CheckBoxedSourceTypeAssignable(relation, source, target); 119 } 120 } 121 122 relation->SetNode(nullptr); 123 relation->SetFlags(TypeRelationFlag::NONE); 124 125 if (!relation->IsTrue()) { 126 invocable_ = false; 127 if ((initialFlags & TypeRelationFlag::NO_THROW) == 0) { 128 relation->RaiseError(diag->kind, diag->params, pos); 129 } 130 return; 131 } 132 133 invocable_ = true; 134 } 135 IsInvocable()136 bool IsInvocable() const 137 { 138 return invocable_; 139 } 140 141 private: 142 TypeRelationFlag flags_ = TypeRelationFlag::NONE; 143 bool invocable_ {false}; 144 }; 145 146 class ConstraintCheckScope { 147 public: ConstraintCheckScope(ETSChecker * checker)148 explicit ConstraintCheckScope(ETSChecker *checker) : checker_(checker), isheld_(true) 149 { 150 size_t &counter = checker_->ConstraintCheckScopesCount(); 151 ES2PANDA_ASSERT(counter != 0 || checker_->PendingConstraintCheckRecords().empty()); 152 counter++; 153 } 154 ~ConstraintCheckScope()155 ~ConstraintCheckScope() 156 { 157 if (isheld_) { 158 Unlock(); 159 } 160 } 161 162 void TryCheckConstraints(); 163 164 NO_COPY_SEMANTIC(ConstraintCheckScope); 165 NO_MOVE_SEMANTIC(ConstraintCheckScope); 166 167 private: Unlock()168 bool Unlock() 169 { 170 ES2PANDA_ASSERT(isheld_); 171 isheld_ = false; 172 return --checker_->ConstraintCheckScopesCount() == 0; 173 } 174 175 ETSChecker *checker_; 176 bool isheld_ {}; 177 }; 178 179 class InstantiationContext { 180 public: InstantiationContext(ETSChecker * checker,ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs,const lexer::SourcePosition & pos)181 InstantiationContext(ETSChecker *checker, ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, 182 const lexer::SourcePosition &pos) 183 : checker_(checker) 184 { 185 if (ValidateTypeArguments(type, typeArgs, pos)) { 186 return; 187 } 188 InstantiateType(type, typeArgs); 189 } 190 InstantiationContext(ETSChecker * checker,ETSObjectType * type,ArenaVector<Type * > && typeArgs,const lexer::SourcePosition & pos)191 InstantiationContext(ETSChecker *checker, ETSObjectType *type, ArenaVector<Type *> &&typeArgs, 192 const lexer::SourcePosition &pos) 193 : checker_(checker) 194 { 195 if (type->HasObjectFlag(ETSObjectFlags::ENUM)) { 196 return; 197 } 198 InstantiateType(type, std::move(typeArgs), pos); 199 } 200 Result()201 Type *Result() 202 { 203 return result_; 204 } 205 206 private: 207 bool ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, 208 const lexer::SourcePosition &pos); 209 210 void InstantiateType(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs); 211 212 void InstantiateType(ETSObjectType *type, ArenaVector<Type *> &&typeArgTypes, const lexer::SourcePosition &pos); 213 util::StringView GetHashFromTypeArguments(ArenaVector<Type *> &typeArgTypes); 214 215 ETSChecker *checker_; 216 Type *result_ {}; 217 }; 218 219 } // namespace ark::es2panda::checker 220 221 #endif 222