已合并
Incorrect cast in the numeric type #10565
fanglou创建于 4月27日
Incorrect cast in the numeric type #10565
已合并
共 3 个文件变更+208-36
| @@ -185,6 +185,69 @@ static void HandleFunctionParam(public_lib::Context *ctx, ir::ETSParameterExpres | |||
| 185 | varsMap->emplace(oldVar, newVar); | 185 | varsMap->emplace(oldVar, newVar); |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | +static ir::Expression *ConvertInitExpression(public_lib::Context *ctx, ir::Expression *init, checker::Type *targetType) | ||
| 189 | +{ | ||
| 190 | + auto *allocator = ctx->allocator; | ||
| 191 | + auto *parser = ctx->parser->AsETSParser(); | ||
| 192 | + auto *checker = ctx->GetChecker()->AsETSChecker(); | ||
| 193 | + auto *initType = init->TsType(); | ||
| 194 | + auto range = init->Range(); | ||
| 195 | + | ||
| 196 | + if (checker->IsTypeIdenticalTo(initType, targetType)) { | ||
| 197 | + return init; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + if (initType != nullptr && initType->IsBuiltinNumeric() && targetType->IsBuiltinNumeric()) { | ||
| 201 | + auto targetTypeStr = targetType->ToString(); | ||
| 202 | + if (!targetTypeStr.empty()) { | ||
| 203 | + std::string format = "@@E1.to" + targetTypeStr + "()"; | ||
| 204 | + auto *arg = parser->CreateFormattedExpression(format, init); | ||
| 205 | + arg->SetRange(range); | ||
| 206 | + return arg; | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + | ||
| 210 | + auto *arg = util::NodeAllocator::ForceSetParent<ir::TSAsExpression>( | ||
| 211 | + allocator, init, allocator->New<ir::OpaqueTypeNode>(targetType, allocator), false); | ||
| 212 | + arg->AsTSAsExpression()->TypeAnnotation()->SetRange(range); | ||
| 213 | + arg->SetRange(range); | ||
| 214 | + return arg; | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +static ir::VariableDeclarator *CreateBoxedDeclarator(ArenaAllocator *allocator, ir::VariableDeclarator *declarator, | ||
| 218 | + checker::Type *boxedType, ArenaVector<ir::Expression *> &&initArgs) | ||
| 219 | +{ | ||
| 220 | + auto *newInit = util::NodeAllocator::ForceSetParent<ir::ETSNewClassInstanceExpression>( | ||
| 221 | + allocator, allocator->New<ir::OpaqueTypeNode>(boxedType, allocator), std::move(initArgs)); | ||
| 222 | + auto *id = declarator->Id()->AsIdentifier(); | ||
| 223 | + auto *newDeclarator = util::NodeAllocator::ForceSetParent<ir::VariableDeclarator>( | ||
| 224 | + allocator, declarator->Flag(), allocator->New<ir::Identifier>(id->Name(), allocator), newInit); | ||
| 225 | + | ||
| 226 | + newDeclarator->SetParent(declarator->Parent()); | ||
| 227 | + newInit->GetTypeRef()->SetRange(declarator->Range()); | ||
| 228 | + newInit->SetRange(declarator->Range()); | ||
| 229 | + newDeclarator->Id()->SetRange(declarator->Range()); | ||
| 230 | + newDeclarator->SetRange(declarator->Range()); | ||
| 231 | + | ||
| 232 | + return newDeclarator; | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +static varbinder::LocalVariable *SetupNewVariable(ArenaAllocator *allocator, ir::VariableDeclarator *newDeclarator, | ||
| 236 | + varbinder::Variable *oldVar, varbinder::Scope *scope) | ||
| 237 | +{ | ||
| 238 | + auto *newDecl = allocator->New<varbinder::ConstDecl>(oldVar->Name(), newDeclarator); | ||
| 239 | + auto *newVar = allocator->New<varbinder::LocalVariable>(newDecl, oldVar->Flags()); | ||
| 240 | + | ||
| 241 | + newDeclarator->Id()->AsIdentifier()->SetVariable(newVar); | ||
| 242 | + newVar->AddFlag(varbinder::VariableFlags::INITIALIZED); | ||
| 243 | + newVar->SetScope(scope); | ||
| 244 | + | ||
| 245 | + scope->EraseBinding(oldVar->Name()); | ||
| 246 | + scope->InsertBinding(newVar->Name(), newVar); | ||
| 247 | + | ||
| 248 | + return newVar; | ||
| 249 | +} | ||
| 250 | + | ||
| 188 | static ir::AstNode *HandleVariableDeclarator(public_lib::Context *ctx, ir::VariableDeclarator *declarator, | 251 | static ir::AstNode *HandleVariableDeclarator(public_lib::Context *ctx, ir::VariableDeclarator *declarator, |
| 189 | ArenaMap<varbinder::Variable *, varbinder::Variable *> *varsMap) | 252 | ArenaMap<varbinder::Variable *, varbinder::Variable *> *varsMap) |
| 190 | { | 253 | { |
| @@ -197,6 +260,7 @@ static ir::AstNode *HandleVariableDeclarator(public_lib::Context *ctx, ir::Varia | |||
| 197 | auto *scope = oldVar->GetScope(); | 260 | auto *scope = oldVar->GetScope(); |
| 198 | auto *type = oldVar->TsType(); | 261 | auto *type = oldVar->TsType(); |
| 199 | auto *boxedType = checker->GlobalBuiltinBoxType(type); | 262 | auto *boxedType = checker->GlobalBuiltinBoxType(type); |
| 263 | + | ||
| 200 | bool inForInit = (declarator->Parent() != nullptr) && (declarator->Parent()->Parent() != nullptr) && | 264 | bool inForInit = (declarator->Parent() != nullptr) && (declarator->Parent()->Parent() != nullptr) && |
| 201 | declarator->Parent()->Parent()->IsForUpdateStatement(); | 265 | declarator->Parent()->Parent()->IsForUpdateStatement(); |
| 202 | if (inForInit && oldVar->HasFlag(varbinder::VariableFlags::PER_ITERATION)) { | 266 | if (inForInit && oldVar->HasFlag(varbinder::VariableFlags::PER_ITERATION)) { |
| @@ -205,44 +269,18 @@ static ir::AstNode *HandleVariableDeclarator(public_lib::Context *ctx, ir::Varia | |||
| 205 | 269 | ||
| 206 | auto initArgs = ArenaVector<ir::Expression *>(allocator->Adapter()); | 270 | auto initArgs = ArenaVector<ir::Expression *>(allocator->Adapter()); |
| 207 | if (declarator->Init() != nullptr) { | 271 | if (declarator->Init() != nullptr) { |
| 208 | - auto *arg = declarator->Init(); | 272 | + auto *arg = ConvertInitExpression(ctx, declarator->Init(), type); |
| 209 | - if (!checker->IsTypeIdenticalTo(arg->TsType(), type)) { | ||
| 210 | - arg = util::NodeAllocator::ForceSetParent<ir::TSAsExpression>( | ||
| 211 | - allocator, arg, allocator->New<ir::OpaqueTypeNode>(type, allocator), false); | ||
| 212 | - arg->AsTSAsExpression()->TypeAnnotation()->SetRange(declarator->Init()->Range()); | ||
| 213 | - arg->SetRange(declarator->Init()->Range()); | ||
| 214 | - } | ||
| 215 | initArgs.push_back(arg); | 273 | initArgs.push_back(arg); |
| 216 | } | 274 | } |
| 217 | - auto *newInit = util::NodeAllocator::ForceSetParent<ir::ETSNewClassInstanceExpression>( | ||
| 218 | - allocator, allocator->New<ir::OpaqueTypeNode>(boxedType, allocator), std::move(initArgs)); | ||
| 219 | - auto *newDeclarator = util::NodeAllocator::ForceSetParent<ir::VariableDeclarator>( | ||
| 220 | - allocator, declarator->Flag(), allocator->New<ir::Identifier>(id->Name(), allocator), newInit); | ||
| 221 | - ES2PANDA_ASSERT(newDeclarator != nullptr); | ||
| 222 | 275 | ||
| 223 | - newDeclarator->SetParent(declarator->Parent()); | 276 | + auto *newDeclarator = CreateBoxedDeclarator(allocator, declarator, boxedType, std::move(initArgs)); |
| 224 | - | 277 | + auto *newVar = SetupNewVariable(allocator, newDeclarator, oldVar, scope); |
| 225 | - newInit->GetTypeRef()->SetRange(declarator->Range()); | ||
| 226 | - newInit->SetRange(declarator->Range()); | ||
| 227 | - newDeclarator->Id()->SetRange(declarator->Range()); | ||
| 228 | - newDeclarator->SetRange(declarator->Range()); | ||
| 229 | - | ||
| 230 | - auto *newDecl = allocator->New<varbinder::ConstDecl>(oldVar->Name(), newDeclarator); | ||
| 231 | - auto *newVar = allocator->New<varbinder::LocalVariable>(newDecl, oldVar->Flags()); | ||
| 232 | - ES2PANDA_ASSERT(newVar != nullptr); | ||
| 233 | - newDeclarator->Id()->AsIdentifier()->SetVariable(newVar); | ||
| 234 | - newVar->AddFlag(varbinder::VariableFlags::INITIALIZED); | ||
| 235 | - newVar->SetScope(scope); | ||
| 236 | - | ||
| 237 | - scope->EraseBinding(oldVar->Name()); | ||
| 238 | - scope->InsertBinding(newVar->Name(), newVar); | ||
| 239 | 278 | ||
| 240 | auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope); | 279 | auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope); |
| 241 | auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); | 280 | auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); |
| 242 | auto scopeContext = checker::ScopeContext(checker, scope); | 281 | auto scopeContext = checker::ScopeContext(checker, scope); |
| 243 | 282 | ||
| 244 | newDeclarator->Check(checker); | 283 | newDeclarator->Check(checker); |
| 245 | - | ||
| 246 | varsMap->emplace(oldVar, newVar); | 284 | varsMap->emplace(oldVar, newVar); |
| 247 | 285 | ||
| 248 | return newDeclarator; | 286 | return newDeclarator; |
| @@ -305,7 +343,6 @@ static ir::AstNode *HandleReference(public_lib::Context *ctx, ir::Identifier *id | |||
| 305 | static ir::AstNode *HandleAssignment(public_lib::Context *ctx, ir::AssignmentExpression *ass, | 343 | static ir::AstNode *HandleAssignment(public_lib::Context *ctx, ir::AssignmentExpression *ass, |
| 306 | ArenaMap<varbinder::Variable *, varbinder::Variable *> const &varsMap) | 344 | ArenaMap<varbinder::Variable *, varbinder::Variable *> const &varsMap) |
| 307 | { | 345 | { |
| 308 | - // Should be true after opAssignment lowering | ||
| 309 | ES2PANDA_ASSERT(ass->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION); | 346 | ES2PANDA_ASSERT(ass->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION); |
| 310 | 347 | ||
| 311 | auto *parser = ctx->parser->AsETSParser(); | 348 | auto *parser = ctx->parser->AsETSParser(); |
| @@ -317,12 +354,24 @@ static ir::AstNode *HandleAssignment(public_lib::Context *ctx, ir::AssignmentExp | |||
| 317 | auto *scope = newVar->GetScope(); | 354 | auto *scope = newVar->GetScope(); |
| 318 | newVar->AddFlag(varbinder::VariableFlags::INITIALIZED); | 355 | newVar->AddFlag(varbinder::VariableFlags::INITIALIZED); |
| 319 | 356 | ||
| 357 | + auto *rightType = ass->Right()->TsType(); | ||
| 358 | + auto *targetType = oldVar->TsType(); | ||
| 359 | + auto *resultType = ass->TsType(); | ||
| 360 | + | ||
| 361 | + ir::Expression *res = nullptr; | ||
| 320 | // `as never` is prohibited. | 362 | // `as never` is prohibited. |
| 321 | - auto *res = ass->TsType()->IsETSNeverType() | 363 | + if (resultType->IsETSNeverType()) { |
| 322 | - ? parser->CreateFormattedExpression("@@I1.set(@@E2 as @@T3)", newVar->Name(), ass->Right(), | 364 | + res = parser->CreateFormattedExpression("@@I1.set(@@E2 as @@T3)", newVar->Name(), ass->Right(), targetType); |
| 323 | - oldVar->TsType()) | 365 | + } else if (rightType != nullptr && rightType->IsBuiltinNumeric() && targetType->IsBuiltinNumeric()) { |
| 324 | - : parser->CreateFormattedExpression("@@I1.set(@@E2 as @@T3) as @@T4", newVar->Name(), ass->Right(), | 366 | + // Use explicit conversion for numeric types instead of implicit 'as' |
| 325 | - oldVar->TsType(), ass->TsType()); | 367 | + auto targetTypeStr = targetType->ToString(); |
| 368 | + std::string format = "@@I1.set((@@E2).to" + targetTypeStr + "()) as @@T3"; | ||
| 369 | + res = parser->CreateFormattedExpression(format, newVar->Name(), ass->Right(), resultType); | ||
| 370 | + } else { | ||
| 371 | + // Non-numeric types: use 'as' expression | ||
| 372 | + res = parser->CreateFormattedExpression("@@I1.set(@@E2 as @@T3) as @@T4", newVar->Name(), ass->Right(), | ||
| 373 | + targetType, resultType); | ||
| 374 | + } | ||
| 326 | res->SetParent(ass->Parent()); | 375 | res->SetParent(ass->Parent()); |
| 327 | 376 | ||
| 328 | // NOTE(gogabr) -- The `get` and `set` properties remain without variable; this is OK for the current checker, but | 377 | // NOTE(gogabr) -- The `get` and `set` properties remain without variable; this is OK for the current checker, but |
| @@ -334,7 +383,7 @@ static ir::AstNode *HandleAssignment(public_lib::Context *ctx, ir::AssignmentExp | |||
| 334 | varBinder->ResolveReferencesForScopeWithContext(res, scope); | 383 | varBinder->ResolveReferencesForScopeWithContext(res, scope); |
| 335 | res->Check(checker); | 384 | res->Check(checker); |
| 336 | 385 | ||
| 337 | - ES2PANDA_ASSERT(ass->TsType()->IsETSNeverType() || res->TsType() == ass->TsType()); | 386 | + ES2PANDA_ASSERT(resultType->IsETSNeverType() || res->TsType() == ass->TsType()); |
| 338 | 387 | ||
| 339 | return res; | 388 | return res; |
| 340 | } | 389 | } |
| @@ -0,0 +1,60 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +function foo(): Int { | ||
| 17 | + return 42 | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +function test_capture_and_assign_number(): void { | ||
| 21 | + let v: number; | ||
| 22 | + let executed = false; | ||
| 23 | + let f = () => { | ||
| 24 | + v = foo() | ||
| 25 | + executed = true | ||
| 26 | + }; | ||
| 27 | + f(); | ||
| 28 | + arktest.assertTrue(executed) | ||
| 29 | + arktest.assertEQ(v, 42.0) | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +function test_capture_and_assign_int(): void { | ||
| 33 | + let v: Int; | ||
| 34 | + let executed = false; | ||
| 35 | + let f = () => { | ||
| 36 | + v = foo() | ||
| 37 | + executed = true | ||
| 38 | + }; | ||
| 39 | + f(); | ||
| 40 | + arktest.assertTrue(executed) | ||
| 41 | + arktest.assertEQ(v, 42) | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +function test_capture_and_assign_double(): void { | ||
| 45 | + let d: Double; | ||
| 46 | + let executed = false; | ||
| 47 | + let f = () => { | ||
| 48 | + d = 3.14 | ||
| 49 | + executed = true | ||
| 50 | + }; | ||
| 51 | + f(); | ||
| 52 | + arktest.assertTrue(executed) | ||
| 53 | + arktest.assertEQ(d, 3.14) | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +function main(): void { | ||
| 57 | + test_capture_and_assign_number() | ||
| 58 | + test_capture_and_assign_int() | ||
| 59 | + test_capture_and_assign_double() | ||
| 60 | +} | ||
| @@ -0,0 +1,63 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +function test_capture_and_read_number(): void { | ||
| 17 | + let v: number = 100.5; | ||
| 18 | + let result: number = 0.0; | ||
| 19 | + let f = () => { | ||
| 20 | + result = v | ||
| 21 | + }; | ||
| 22 | + f(); | ||
| 23 | + arktest.assertEQ(result, 100.5) | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +function test_capture_and_read_int(): void { | ||
| 27 | + let v: Int = 55; | ||
| 28 | + let result: Int = 0; | ||
| 29 | + let f = () => { | ||
| 30 | + result = v | ||
| 31 | + }; | ||
| 32 | + f(); | ||
| 33 | + arktest.assertEQ(result, 55) | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +function test_capture_and_read_double(): void { | ||
| 37 | + let v: Double = 2.718; | ||
| 38 | + let result: Double = 0.0; | ||
| 39 | + let f = () => { | ||
| 40 | + result = v | ||
| 41 | + }; | ||
| 42 | + f(); | ||
| 43 | + arktest.assertEQ(result, 2.718) | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +function test_capture_read_and_write(): void { | ||
| 47 | + let v: number = 10.0; | ||
| 48 | + let result: number = 0.0; | ||
| 49 | + let f = () => { | ||
| 50 | + result = v | ||
| 51 | + v = 20.0 | ||
| 52 | + }; | ||
| 53 | + f(); | ||
| 54 | + arktest.assertEQ(result, 10.0) | ||
| 55 | + arktest.assertEQ(v, 20.0) | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +function main(): void { | ||
| 59 | + test_capture_and_read_number() | ||
| 60 | + test_capture_and_read_int() | ||
| 61 | + test_capture_and_read_double() | ||
| 62 | + test_capture_read_and_write() | ||
| 63 | +} | ||