From f5c4541aec727c722d9bb4fba609fe0c5eca922a Mon Sep 17 00:00:00 2001 From: Peter Siket Date: Wed, 6 Mar 2024 18:14:28 +0100 Subject: [PATCH] Fixes the parameter boxing for generic alias types for simple cases. Issue: #I9OVB2 Internal issue: #15945 Change-Id: I19d6485b156efecbf73bc3537bd7dc13234a70bb Signed-off-by: Peter Siket --- ets2panda/checker/ets/helpers.cpp | 57 +++++++++++++-- ets2panda/test/runtime/ets/15945.ets | 24 ++++++ ets2panda/test/runtime/ets/AliasArray.ets | 73 +++++++++++++++++++ ets2panda/test/runtime/ets/AliasClass.ets | 57 +++++++++++++++ ets2panda/test/runtime/ets/AliasFunction.ets | 53 ++++++++++++++ ets2panda/test/runtime/ets/AliasPrimitive.ets | 38 ++++++++++ ets2panda/test/runtime/ets/AliasTuple.ets | 53 ++++++++++++++ ets2panda/test/runtime/ets/AliasUnion.ets | 46 ++++++++++++ 8 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 ets2panda/test/runtime/ets/15945.ets create mode 100644 ets2panda/test/runtime/ets/AliasArray.ets create mode 100644 ets2panda/test/runtime/ets/AliasClass.ets create mode 100644 ets2panda/test/runtime/ets/AliasFunction.ets create mode 100644 ets2panda/test/runtime/ets/AliasPrimitive.ets create mode 100644 ets2panda/test/runtime/ets/AliasTuple.ets create mode 100644 ets2panda/test/runtime/ets/AliasUnion.ets diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index a5ee1b3bb6..6357e44e9a 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -1050,6 +1050,41 @@ void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpre } } +static void CheckExpandedType(Type *expandedAliasType, std::set ¶metersNeedToBeBoxed, + bool needToBeBoxed) +{ + if (expandedAliasType->IsETSTypeParameter()) { + auto paramName = expandedAliasType->AsETSTypeParameter()->GetDeclNode()->Name()->Name(); + if (needToBeBoxed) { + parametersNeedToBeBoxed.insert(paramName); + } + } else if (expandedAliasType->IsETSObjectType()) { + auto objectType = expandedAliasType->AsETSObjectType(); + needToBeBoxed = + objectType->GetDeclNode()->IsClassDefinition() || objectType->GetDeclNode()->IsTSInterfaceDeclaration(); + for (const auto typeArgument : objectType->TypeArguments()) { + CheckExpandedType(typeArgument, parametersNeedToBeBoxed, needToBeBoxed); + } + } else if (expandedAliasType->IsETSTupleType()) { + auto tupleType = expandedAliasType->AsETSTupleType(); + needToBeBoxed = false; + for (auto type : tupleType->GetTupleTypesList()) { + CheckExpandedType(type, parametersNeedToBeBoxed, needToBeBoxed); + } + } else if (expandedAliasType->IsETSArrayType()) { + auto arrayType = expandedAliasType->AsETSArrayType(); + needToBeBoxed = false; + auto elementType = arrayType->ElementType(); + CheckExpandedType(elementType, parametersNeedToBeBoxed, needToBeBoxed); + } else if (expandedAliasType->IsETSUnionType()) { + auto unionType = expandedAliasType->AsETSUnionType(); + needToBeBoxed = false; + for (auto type : unionType->ConstituentTypes()) { + CheckExpandedType(type, parametersNeedToBeBoxed, needToBeBoxed); + } + } +} + Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams) { ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() && @@ -1078,17 +1113,29 @@ Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypePa // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) Type *const aliasType = GetReferencedTypeBase(name); - auto *const aliasSub = NewSubstitution(); - + auto *aliasSub = NewSubstitution(); if (typeAliasNode->TypeParams()->Params().size() != typeParams->Params().size()) { ThrowTypeError("Wrong number of type parameters for generic type alias", typeParams->Start()); } + std::set parametersNeedToBeBoxed; + auto expandedAliasType = aliasType->Substitute(Relation(), aliasSub); + CheckExpandedType(expandedAliasType, parametersNeedToBeBoxed, false); + for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { - auto *typeAliasType = typeAliasNode->TypeParams()->Params().at(idx)->Name()->Variable()->TsType(); - if (typeAliasType->IsETSTypeParameter()) { - aliasSub->insert({typeAliasType->AsETSTypeParameter(), typeParams->Params().at(idx)->TsType()}); + auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name(); + auto *typeAliasType = typeAliasTypeName->Variable()->TsType(); + if (!typeAliasType->IsETSTypeParameter()) { + continue; + } + auto paramType = typeParams->Params().at(idx)->TsType(); + if (parametersNeedToBeBoxed.find(typeAliasTypeName->Name()) != parametersNeedToBeBoxed.end()) { + auto boxedType = PrimitiveTypeAsETSBuiltinType(typeParams->Params().at(idx)->GetType(this)); + if (boxedType != nullptr) { + paramType = boxedType; + } } + aliasSub->insert({typeAliasType->AsETSTypeParameter(), paramType}); } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) diff --git a/ets2panda/test/runtime/ets/15945.ets b/ets2panda/test/runtime/ets/15945.ets new file mode 100644 index 0000000000..1c69108105 --- /dev/null +++ b/ets2panda/test/runtime/ets/15945.ets @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main() { + let m: Map = {"kek": 2} + m.set("kek", 1) + + let r: Record = {"kek": 2} + r.set("kek", 1) // <---------- FAIL HERE +} + + diff --git a/ets2panda/test/runtime/ets/AliasArray.ets b/ets2panda/test/runtime/ets/AliasArray.ets new file mode 100644 index 0000000000..5d9a988eae --- /dev/null +++ b/ets2panda/test/runtime/ets/AliasArray.ets @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +class C { + constructor (p: T) { this.v = p; } + v : T; +} + +type Alias1Array = T[]; +type Alias2Array = C[]; +type Alias3Array = C[][]; +type Alias4Array = ((p: T) => double)[]; +type Alias5Array = (p: T[]) => double; +type Alias6Array = (p: T[], q: T) => double; +type Alias7Array = (p: T1[], q: T2) => double; +type Alias8Array = [T][]; +type Alias9Array = [C][]; + +function main() +{ + let v1 : double[] = [1,2,3]; // double[] + v1[0] = new Int(4); + assert (v1[0] == 4); + assert (v1[1] == 2); + assert (v1[2] == 3); + + let v2 : Alias1Array = [5, 6, 7]; // double[] + v2[0] = new Int(8); + assert (v2[0] == 8); + assert (v2[1] == 6); + assert (v2[2] == 7); + + let v3 : Alias2Array = [new C(9)]; // C[] + assert(v3[0].v == 9); + v3[0].v = 10; + assert(v3[0].v == 10); + + let v4 : Alias3Array = [[new C(11)]]; // C[][] + assert(v4[0][0].v == 11); + v4[0][0].v = 12; + assert(v4[0][0].v == 12); + + let v5 : Alias4Array = [(p : double) : double => { return p; }]; // [(p: Double)=>Double] + assert(v5[0](13) == 13); + + let v6 : Alias5Array = (p : double[]) : double => { return p[0]; }; // (p: double[])=>Double + assert(v6([14.0]) == 14); + + // let v7 : Alias6Array = (p : double[], q: double) : double => { return p[0]; }; // (p: Double[], q: Double)=>Double + // assert(v7([15.0], 16) == 15); + + let v9 : Alias7Array = (p : double[], q: double) : double => { return p[0]; }; // (p: double[], q: Double)=>Double + assert(v9([17.0], 18) == 17); + + let v10 : Alias8Array = [[new Int(18)]]; // [double][] + assert(v10[0][0] == 18); + + let v11 : Alias9Array = [[new C(19)]]; // [C][] + assert(v11[0][0].v == 19); +} diff --git a/ets2panda/test/runtime/ets/AliasClass.ets b/ets2panda/test/runtime/ets/AliasClass.ets new file mode 100644 index 0000000000..e957f94cf0 --- /dev/null +++ b/ets2panda/test/runtime/ets/AliasClass.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C { + constructor (p: T) { this.v = p; } + v : T; +} + +class D { + constructor (p: T1, q: T2) { this.v = p; this.w = q; } + v : T1; + w : T2; +} + +type AliasPrimitive = T; +type AliasAlias = AliasPrimitive; + +type Alias1Class = C; +type Alias2Class = C; +type Alias3Class = D; +type Alias4Class = D; +type Alias5Class = D, AliasAlias[]> + +function main() { + let v1 : C = new C(1); // C + assert(v1.v == 1); + + let v2 : Alias1Class = new C(2); // C + assert(v2.v == 2); + + let v3 : Alias2Class = new C([3.0]); // C + assert(v3.v[0] == 3); + + let v4: Alias3Class = new D(4.0, 5); // D + assert(v4.v == 4.0); + assert(v4.w == 5); + + let v5: Alias4Class = new D(6, 7.0); // D + assert(v5.v == 6); + assert(v5.w == 7.0); + + let v6: Alias5Class = new D, double[]>(new C(8), [9.0]); // D, double[]> + assert(v6.v.v == 8); + assert(v6.w[0] == 9.0); +} diff --git a/ets2panda/test/runtime/ets/AliasFunction.ets b/ets2panda/test/runtime/ets/AliasFunction.ets new file mode 100644 index 0000000000..2644c39858 --- /dev/null +++ b/ets2panda/test/runtime/ets/AliasFunction.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C { + constructor (p: T) { this.v = p; } + v : T; +} + +type Alias1Function = (p : T) => double; +type Alias2Function = (p : double) => T; +type Alias3Function = (p : Int) => T[]; +type Alias4Function = (p : T[], q: T) => double; +type Alias5Function = (p : T1[], q: T2) => double; +type Alias6Function = (p : T1, q: T2) => R; +type Alias7Function = (p : (p1 : C) => double, q : double) => double; + +function main() { + let v1 : (p : double) => double = (p : double) : double => { return p; }; // (p:Double) => Double + assert(v1(1) == 1); + + let v2 : Alias1Function = (p : double) : double => { return p; }; // (p:Double) => Double + assert(v2(2) == 2); + + let v3 : Alias2Function = (p : double) : double => { return p; }; // (p:Double) => Double + assert(v3(3) == 3); + + let v4 : Alias3Function = (p : Int) : double[] => { return [p]; }; // (p:Double) => double[] + assert(v4(4)[0] == 4); + + // let v5 : Alias4Function = (p : double[], q: double) : double => { return p[0]; }; // (p: Double[], q: Double) => Double + // assert(v5([5.0], 6) == 5); + + let v6 : Alias5Function = (p : double[], q: double) : double => { return p[0]; }; // (p: double[], q: Double) => Double + assert(v6([7.0], 8) == 7.0); + + let v7 : Alias6Function = (p: double, q: double) : double => { return p + q; }; // (p: Double, q: Double) => Double + assert(v7(9, 10) == 19); + + let v9 : Alias7Function = (p : (p1 : C) => double, q: double) : double => { return p(new C(q)); }; // (p : (p1 : C Double, Double) : Double + assert(v9((p : C) : double => { return p.v;}, 20) == 20); +} diff --git a/ets2panda/test/runtime/ets/AliasPrimitive.ets b/ets2panda/test/runtime/ets/AliasPrimitive.ets new file mode 100644 index 0000000000..37a3102d96 --- /dev/null +++ b/ets2panda/test/runtime/ets/AliasPrimitive.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +type AliasPrimitive = T; +type AliasAlias = AliasPrimitive; + +function fn(p: double) +{ + assert(p == 42); +} + +function fn(p: Double) +{ + assert(false); +} + +function main() { + let v1 : double = new Int(42); // unboxing -> widening + fn(v1); + + let v2 : AliasPrimitive = new Int(42); // unboxing -> widening + fn(v2); + + let v3 : AliasAlias = new Int(42); // unboxing -> widening + fn(v3); +} diff --git a/ets2panda/test/runtime/ets/AliasTuple.ets b/ets2panda/test/runtime/ets/AliasTuple.ets new file mode 100644 index 0000000000..d5a179e38d --- /dev/null +++ b/ets2panda/test/runtime/ets/AliasTuple.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C { + constructor (p: T) { this.v = p; } + v: T; + } + +type Alias1Tuple = [T]; +type Alias2Tuple = [T, Int]; +type Alias3Tuple = [C, double]; +type Alias4Tuple = [T1[], T2]; +type Alias5Tuple = [C[], T2]; +type Alias6Tuple = [(p: T1) => double, T2]; + + +function main() { + let v1: [double] = [new Int(1)]; // [double] + let v2: [double, Int] = [new Double(2), new Int(3)]; // [Double, Int] + + let v3: Alias1Tuple = [new Int(4)]; // [double] + let v4: Alias2Tuple = [new Double(5), new Int(6)]; // [Double, Int] + + let v5: Alias3Tuple = [new C(7), 8]; // [C, Double] + assert(v5[0].v == 7); + assert(v5[1] == 8); + + let v6: Alias4Tuple = [[new Int(9), new Int(10), new Int(11)], 12]; // [double[], Double] + assert(v6[0][0] == 9); + assert(v6[0][1] == 10); + assert(v6[0][2] == 11); + assert(v6[1] == 12); + + let v7: Alias5Tuple = [[new C(13)], 14]; // [C[], Double] + assert(v7[0][0].v == 13); + assert(v7[1] == 14); + + let v9: Alias6Tuple = [(p: double) : double => { return p;}, 16]; // [(p: Double) => Double, Double] + assert(v9[0](15) == 15); + assert(v9[1] == 16); +} diff --git a/ets2panda/test/runtime/ets/AliasUnion.ets b/ets2panda/test/runtime/ets/AliasUnion.ets new file mode 100644 index 0000000000..17fa903ec5 --- /dev/null +++ b/ets2panda/test/runtime/ets/AliasUnion.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C { + constructor (p: T) { this.v = p; } + v: T; +} + +type Alias1Union = T1 | T2; +type Alias2Union = C | T2; +type Alias3Union = C[] | T2; +type Alias4Union = (p: T1) => double | T2; + +function main() { + let v1 : double | double = new Int(1); // primitive double + let v2 : double | string = 2; // boxed Double|String + + let v3 : Alias1Union = new Int(3); // primitive double + assert(v3 == 3); + let v4 : Alias1Union = 4; // boxed Double|String + assert(v4 == 4); + + let v5 : Alias2Union = new C(5); // C|String + let v6 : C = v5 as C; + assert(v6.v == 5); + + let v7 : Alias3Union = [new C(6)]; // C[]|String + let v9 : C[] = v7 as C[]; + assert (v9[0].v == 6); + + let v10 : Alias4Union = (p : double) : double => { return p; }; // (p: Double)=>Double |String + let v11 : (p : double) => double = v10 as (p : double) => double; + assert (v11(7) == 7); +} -- Gitee