| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- import .expression.CallArguments;
- import .expression.Expression;
- import .generic.TypeParameter;
- import .scope.TypeScope;
- import .type.BasicTypeID;
- import .type.GlobalTypeRegistry;
- import .type.ITypeID;
- import .type.member.LocalMemberCache;
-
- /**
- *
- * @author Hoofdgebruiker
- */
- public class FunctionHeader {
- static val NO_PARAMETERS as FunctionParameter[] = new FunctionParameter[](0);
- static val NO_TYPE_PARAMETERS as TypeParameter[] = new TypeParameter[](0);
-
- val typeParameters as TypeParameter[] : get;
- val returnType as ITypeID : get;
- val parameters as FunctionParameter[] : get;
- val thrownType as ITypeID : get;
-
- public this(returnType as ITypeID) {
- this.typeParameters = NO_TYPE_PARAMETERS;
- this.returnType = returnType;
- this.parameters = NO_PARAMETERS;
- this.thrownType = BasicTypeID.VOID;
- }
-
- public this(returnType as ITypeID, parameters... as ITypeID) {
- this.typeParameters = NO_TYPE_PARAMETERS;
- this.returnType = returnType;
- this.parameters = parameters.map(parameter => new FunctionParameter(parameter, null));
- this.thrownType = BasicTypeID.VOID;
- }
-
- public this(returnType as ITypeID, parameters... as FunctionParameter) {
- this.typeParameters = NO_TYPE_PARAMETERS;
- this.returnType = returnType;
- this.parameters = parameters;
- this.thrownType = BasicTypeID.VOID;
- }
-
- public this(typeParameters as TypeParameter[], returnType as ITypeID, thrownType as ITypeID, parameters... as FunctionParameter) {
- this.typeParameters = typeParameters;
- this.returnType = returnType;
- this.parameters = parameters;
- this.thrownType = thrownType;
- }
-
- public get numberOfTypeParameters as int
- => typeParameters.length;
-
- public get doesThrow as bool
- => thrownType != BasicTypeID.VOID;
-
- public instance(registry as GlobalTypeRegistry, mapping as ITypeID[TypeParameter]) as FunctionHeader {
- val genericParameters = this.typeParameters.map(parameter => parameter.withGenericArguments(registry, mapping));
- val returnType = this.returnType.withGenericArguments(registry, mapping);
- val parameters = this.parameters.map(parameter => parameter.withGenericArguments(registry, mapping));
- val thrownType = this.thrownType.withGenericArguments(registry, mapping);
- return new FunctionHeader(genericParameters, returnType, thrownType, parameters);
- }
-
- public inferTypes(cache as LocalMemberCache, arguments as CallArguments, resultHint as ITypeID[]) as ITypeID[]? {
- if arguments.arguments.length != this.parameters.length
- return null;
-
- val mapping = new ITypeID[TypeParameter];
- if !resultHint.empty {
- val temp = new ITypeID[TypeParameter];
- for hint in resultHint {
- if returnType.inferTypeParameters(cache, hint, temp) {
- mapping = temp;
- break;
- }
- }
- }
-
- // TODO: lambda header inference
- for i, parameter in parameters
- if !parameter.type.inferTypeParameters(cache, argument.arguments[i].type, mapping)
- return null;
-
- if mapping.size > typeParameters.length
- return null;
-
- val result = new ITypeID[](typeParameters.length);
- for i, typeParameter in typeParameters {
- if typeParameter !in mapping
- return null;
-
- result[i] = mapping[typeParameter];
- }
-
- return result;
- }
-
- public hasInferenceBlockingTypeParameters(parameters as TypeParameter[]) as bool
- => this.parameters
- .contains((i, parameter) => parameter.type.hasInferenceBlockingTypeParameters(parameters));
-
- public canCastTo(scope as TypeScope, header as FunctionHeader) as bool {
- if parameters.length != header.parameters.length
- return false; // TODO: check type argument compatibility
-
- if !scope.getTypeMembers(returnType).canCastImplicit(header.returnType)
- return false;
-
- return !parameters.contains((i, parameter)
- => !scope.getTypeMembers(header.parameters[i].type).canCastImplicit(parameter.type));
- }
-
- public accepts(scope as TypeScope, arguments... as Expression) as bool {
- if parameters.length != arguments.length
- return false;
-
- return !arguments.contains((i, argument) => !scope
- .getTypeMembers(argument.type)
- .canCastImplicit(parameters[i].type));
- }
-
- /**
- * Checks if two function headers are equivalent. Functions headers are
- * equivalent if their types are the same.
- *
- * @param other
- * @return
- */
- public isEquivalentTo(other as FunctionHeader) as bool {
- if parameters.length != other.parameters.length
- return false;
-
- return !parameters.contains((i, parameter) => parameter.type !== other.parameters[i].type);
- }
-
- /**
- * Checks if two function headers are similar. "similar" means that there
- * exists a set of parameters for which there is no way to determine which
- * one to call.
- *
- * Note that this does not mean that there is never confusion about which
- * method to call. There can be confusion due to implicit conversions. This
- * can be resolved by performing the conversions explicitly.
- *
- * It is illegal to have two similar methods with the same name.
- *
- * @param other
- * @return
- */
- public isSimilarTo(other as FunctionHeader) as bool {
- val common = int.min(parameters.length, other.parameters.length);
- for i in 0 .. common
- if parameters[i].type !== other.parameters[i].type
- return false;
-
- for i in common .. parameters.length
- if parameters[i].defaultValue == null
- return false;
-
- for i in common .. other.parameters.length
- if other.parameters[i].defaultValue == null
- return false;
-
- return true;
- }
-
- public withGenericArguments(registry as GlobalTypeRegistry, arguments as ITypeID[TypeParameter]) as FunctionHeader {
- val returnType = this.returnType.withGenericArguments(registry, arguments);
- val parameters = this.parameters.map(parameter => {
- val modified = parameter.type.withGenericArguments(registry, arguments);
- return modified == parameter.type ? parameter : new FunctionParameter(modified, parameter.name);
- });
- return new FunctionHeader(
- typeParameters,
- returnType,
- thrownType.withGenericArguments(registry, arguments),
- parameters);
- }
-
- public withGenericArguments(registry as GlobalTypeRegistry, arguments as ITypeID[]) as FunctionHeader {
- val typeArguments = new ITypeID[TypeParameter];
- for i, typeParameter in typeParameters
- typeArguments[typeParameter] = arguments[i];
-
- return withGenericArguments(registry, typeArguments);
- }
-
- public forTypeParameterInference() as FunctionHeader {
- return new FunctionHeader(BasicTypeID.UNDETERMINED, parameters);
- }
-
- public forLambda(lambdaHeader as FunctionHeader) as FunctionHeader {
- val parameters = lambdaHeader.parameters
- .map((i, parameter) => new FunctionParameter(this.parameters[i].type, parameter.name));
- return new FunctionHeader(typeParameters, returnType, thrownType, parameters);
- }
-
- public getVariadicParameter() as FunctionParameter?
- => !parameters.empty && parameters.last.variadic ? parameters.last : null;
-
- public explainWhyIncompatible(scope as TypeScope, arguments as CallArguments) as string {
- if this.parameters.length != arguments.arguments.length
- return parameters.length + " parameters expected but " + arguments.arguments.length + " given.";
-
- if numberOfTypeParameters != arguments.numberOfTypeArguments
- return numberOfTypeParameters + " type parameters expected but " + arguments.numberOfTypeArguments + " given.";
-
- for i, parameter in parameters
- if !scope.getTypeMembers(arguments.arguments[i].type).canCastImplicit(parameter.type)
- return "Parameter " + i + ": cannot cast " + arguments.arguments[i].type + " to " + parameter.type;
-
- return "Method should be compatible";
- }
-
- public as string {
- val result = new StringBuilder();
- if typeParameters.length > 0
- result << '<' << typeParameters.map(param => param as string).join(', ') << '>';
- result << '(' << parameters.map(parameter => parameter as string).join(', ') < ')';
- return result as string;
- }
- }
|