Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: compilation error when nested specialization of generic types #145

Merged
merged 2 commits into from
Mar 12, 2024

Conversation

Shanks0224
Copy link
Contributor

No description provided.

@Shanks0224
Copy link
Contributor Author

I am preparing to write a detailed documentation and will submit it when completed.

@xujuntwt95329
Copy link
Contributor

I am preparing to write a detailed documentation and will submit it when completed.

That would be great! Looking forward to it!

@Shanks0224 Shanks0224 force-pushed the main branch 4 times, most recently from 0a67d7b to ec8d1bb Compare February 5, 2024 09:28
src/utils.ts Outdated
Comment on lines 754 to 768
const typeArgumentsSignature: Array<string> = [];
const _typeParameters = genericClassType.typeArguments;
if (_typeParameters) {
_typeParameters.forEach((type) => {
const index = typeParameters.findIndex((t) => {
return t.name === type.name;
});
if (index == -1) {
throw new UnimplementError(
`${type.name} not found in typeParameters`,
);
}
typeArgumentsSignature.push(`${typeArguments[index].kind}`);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that these codes are aimed to get the typeArgumentsSignature, and used in several functions, why not encapsulate these into a separate function(like getTypeSignatureByTypeArguments)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
Comment on lines 773 to 776
const typeSignature =
typeArgumentsSignature.length > 0
? '<' + typeArgumentsSignature.join(',') + '>'
: '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And typeArgumentsSignature is only used to get the typeSignature string, maybe we can put this operation in the separate function(getTypeSignatureByTypeArguments), and use the typeSignature as return value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
Comment on lines 777 to 732
const cache = TypeResolver.specializedTypeCache.get(genericOwner);
let found: Type | undefined;
if (cache) {
cache.forEach((v) => {
if (v.has(genericClassType.className + typeSignature)) {
found = v.get(genericClassType.className + typeSignature);
}
});
}
if (found) return found as TSClass;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This operation seems is used in several functions too,
Maybe we can set a separate function:

function findTypeInSpecialiezedTypeCache(typeName: string): Type {
    xxx
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
Comment on lines 805 to 807
const reverse = genericMangledName.split('|').reverse();
reverse[0] = specializedName;
newClassType.mangledName = reverse.reverse().join('|');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These operations seems a little complex, how about

newClassType.mangledName = genericMangledName.substring(0, genericMangledName.lastIndexOf("|") + 1) + specializedName;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
Comment on lines 818 to 827
// update specializedTypeCache
if (TypeResolver.specializedTypeCache.has(genericOwner)) {
const value = new Map();
value.set(specializedName, newClassType);
TypeResolver.specializedTypeCache.get(genericOwner)!.push(value);
} else {
const value = new Map();
value.set(specializedName, newClassType);
TypeResolver.specializedTypeCache.set(genericOwner, [value]);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the operation update specializedTypeCache can be put in a function too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
Comment on lines 565 to 546
// specialized function type need to reset belongedClass and belongedScope
newFuncType.belongedClass = undefined;
newFuncType.setBelongedScope(undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this function, the belongedClass and belongedScope are both be set to undefined, where are they set to new values?
What's more, for function, why does it have belongedClass?

Copy link
Contributor Author

@Shanks0224 Shanks0224 Mar 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. In processGenericType() , if it is processing a specialization of a generic, genericTypeSpecialization() will be called first to generate a new specialized type and its corresponding Scope will first be set to undefined, as marked by this comment. The createScopeBySpecializedType() will then be called to create a new Scope.
  2. For the default function, there is no belongedClass property, and the code newFuncType.belongedClass = undefined; is redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
return newFuncType;
}

export function genericClassMethodSpecialization(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is only slightly different from genericFunctionSpecialization. Is it possible to merge them into one function?

Copy link
Contributor Author

@Shanks0224 Shanks0224 Mar 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to merge them together. genericMethodSpecialization will have some logic for processing Class. I encapsulated them separately, just to distinguish method and function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, how about use a parameter to distinguish method and function, like isMethod, and set the default value to true or false, so that we can reuse the common logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
parent: Scope,
classType: TSClass,
newName?: string,
export function genericMethodSpecialization(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is aim to generate functionScope and set its type for class' methods, right?

The other genericXXSpecialization functions are aim to generate specialization types, which may be cause some misunderstandings, so this function can rename as other names, like methodSpecialize etc.

Copy link
Contributor Author

@Shanks0224 Shanks0224 Mar 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this function is designed for the following situation:

class A {
   a: number;
   echo<T>(param: T) {...};
}
const a = new A();
a.echo(1);
a.echo('hello');

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
* @param context the parser context
* @returns a new generic type
*/
export function genericTypeTransformation(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between genericTypeSpecialization and genericTypeTransformation?

When does genericTypeSpecialization need to be called, and when does genericTypeTransformation need to be called?

Copy link
Contributor Author

@Shanks0224 Shanks0224 Mar 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Generic specialization refers to using specific types (such as number, string) to specialize the generic T. In order to realize this feature, we designed the genericTypeSpecialization().
  2. Generics have chain relationships:
class A<T> {...}
class B<X, Y> extends A<Y> {...}

In this case, we need to generate a new generic type B_A<Y>, and its specialization operation will not affect its original type A<T>. genericTypeTransformation()handles this situation.

In the function processGenericType, we can decide whether to perform specialization of the generic type or create a new generic type by whether typeArguments is a generic types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think there is not much difference between genericTypeSpecialization and genericTypeTransformation. Their logic is exactly the same, and their inner logic, like genericFunctionSpecialization and genericFuncTransformation is close too.

In genericFunctionSpecialization:

  1. get the new type list
  2. find if there already exists the specialized type, if found, return
  3. generate new type
  4. do sepecialization
  5. return the new type

In genericFuncTransformation:

  1. get the new type list
  2. generate new type
  3. return the new type

We can use a parameter doSpecialization to choose which action we require, the codes existed now is much duplicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* @param context the parser context
* @returns a new specialized FunctionScope
*/
function createFunctionScope(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not big difference between createFunctionScope and createMethodScope, can we use a common function to represent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to merge them together. createMethodScope will have some logic for processing Class. I encapsulated them separately, just to distinguish method and function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and how about merge them, and use a parameter to distinguish them? since their inner logic is very close.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/type.ts Outdated
Comment on lines 2348 to 2353
baseClassType = genericClassTransformation(
baseClassType,
typeArguments,
baseClassType.typeArguments!,
this.parserCtx,
classType.className,
) as TSClass;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use processGenericType?
I see that genericClassTransformation is used in processGenericType, in this situation, if invoke processGenericType directly, can it go to genericClassTransformation path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should call processGenericType directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

res.setExprType(this.typeResolver.generateNodeType(node));
const newSuperExpression = new SuperExpression(args);
newSuperExpression.tsNode = (
expr as SuperExpression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the outer function visitNode, the tsNode will be recorded in the expression, so I think there is no need to record the tsNode here again.

visitNode(node: ts.Node): Expression {
const expr = this.visitNodeInternal(node);
expr.tsNode = node;
return expr;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

).propertyAccessExpr,
newPropertyIdentifier,
);
expr.tsNode = tsNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

);
res = classType.getMethod(newPropertyName);
expr.tsNode = tsNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

)
: exprType;
newSuperExpression.setExprType(newExprType);
newSuperExpression.tsNode = superExpression.tsNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/utils.ts Outdated
return newFuncType;
}

export function genericClassMethodSpecialization(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, how about use a parameter to distinguish method and function, like isMethod, and set the default value to true or false, so that we can reuse the common logic.

src/utils.ts Outdated
* @param context the parser context
* @returns a new generic type
*/
export function genericTypeTransformation(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think there is not much difference between genericTypeSpecialization and genericTypeTransformation. Their logic is exactly the same, and their inner logic, like genericFunctionSpecialization and genericFuncTransformation is close too.

In genericFunctionSpecialization:

  1. get the new type list
  2. find if there already exists the specialized type, if found, return
  3. generate new type
  4. do sepecialization
  5. return the new type

In genericFuncTransformation:

  1. get the new type list
  2. generate new type
  3. return the new type

We can use a parameter doSpecialization to choose which action we require, the codes existed now is much duplicated.

* @param context the parser context
* @returns a new specialized FunctionScope
*/
function createFunctionScope(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and how about merge them, and use a parameter to distinguish them? since their inner logic is very close.

Copy link
Contributor

@yviansu yviansu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@xujuntwt95329 xujuntwt95329 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@xujuntwt95329
Copy link
Contributor

Thanks @Shanks0224 and @yviansu

@xujuntwt95329 xujuntwt95329 merged commit 8688156 into web-devkits:main Mar 12, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants