2 // Copyright (c) 2004, 2005, 2006, 2007 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 using Boo
.Lang
.Compiler
.Ast
;
30 using System
.Collections
.Generic
;
33 namespace Boo
.Lang
.Compiler
.TypeSystem
35 public class GenericsServices
: AbstractCompilerComponent
37 public GenericsServices(CompilerContext context
)
43 /// Constructs an entity from a generic definition and arguments, after ensuring the construction is valid.
45 /// <param name="definition">The generic definition entity.</param>
46 /// <param name="node">The node in which construction occurs.</param>
47 /// <param name="argumentNodes">The nodes of the arguments supplied for generic construction.</param>
48 /// <returns>The constructed entity.</returns>
49 public IEntity
ConstructEntity(IEntity definition
, Node constructionNode
, TypeReferenceCollection argumentNodes
)
51 // Ambiguous generic constructions are handled separately
52 if (definition
.EntityType
== EntityType
.Ambiguous
)
54 return ConstructAmbiguousEntity((Ambiguous
)definition
, constructionNode
, argumentNodes
);
57 // Check that the construction is valid
58 if (!CheckGenericConstruction(definition
, constructionNode
, argumentNodes
, Errors
))
60 return TypeSystemServices
.ErrorEntity
;
63 // Construct a type or a method according to the definition
64 IType
[] arguments
= Array
.ConvertAll
<TypeReference
, IType
>(
65 argumentNodes
.ToArray(),
66 delegate(TypeReference tr
) { return (IType)tr.Entity; }
);
68 if (IsGenericType(definition
))
70 return ((IType
)definition
).GenericInfo
.ConstructType(arguments
);
72 if (IsGenericMethod(definition
))
74 return ((IMethod
)definition
).GenericInfo
.ConstructMethod(arguments
);
77 // Should never be reached - if definition is neither a generic type nor a generic method,
78 // CheckGenericConstruction would've indicated this
79 return TypeSystemServices
.ErrorEntity
;
83 /// Constructs generic entities out of an ambiguous definition.
85 private IEntity
ConstructAmbiguousEntity(Ambiguous ambiguousDefinition
, Node constructionNode
, TypeReferenceCollection argumentNodes
)
87 GenericConstructionChecker checker
= new GenericConstructionChecker(
91 new CompilerErrorCollection());
93 List
<IEntity
> matches
= new List
<IEntity
>(ambiguousDefinition
.Entities
);
95 // Filter matches by genericness, generity and constraints
96 Predicate
<IEntity
>[] filters
= new Predicate
<IEntity
>[] {
97 checker
.NotGenericDefinition
,
98 checker
.IncorrectGenerity
,
99 checker
.ViolatesParameterConstraints
};
101 foreach (Predicate
<IEntity
> filter
in filters
)
103 checker
.Errors
.Clear();
104 matches
.RemoveAll(filter
);
106 // If no matches pass the filter, record the first error only
107 // (providing all the distinct errors that occured would be superfluous)
108 if (matches
.Count
== 0)
110 Errors
.Add(checker
.Errors
[0]);
111 return TypeSystemServices
.ErrorEntity
;
114 // If only one match passes the filter, continue construction normally
115 if (matches
.Count
== 1)
117 return ConstructEntity(matches
[0], constructionNode
, argumentNodes
);
121 // Several matches have passed the filter -
122 // construct all of them and return another Ambiguous entity
123 IEntity
[] constructed
= Array
.ConvertAll
<IEntity
, IEntity
>(
125 delegate(IEntity def
) { return ConstructEntity(def, constructionNode, argumentNodes); }
);
127 return new Ambiguous(constructed
);
131 /// Checks whether a given set of arguments can be used to construct a generic type or method from a specified definition.
133 public bool CheckGenericConstruction(IEntity definition
, Node node
, TypeReferenceCollection arguments
, CompilerErrorCollection errors
)
135 // Ensure definition is a valid entity
136 if (definition
== null || TypeSystemServices
.IsError(definition
))
141 // Ensure definition really is a generic definition
142 GenericConstructionChecker checker
= new GenericConstructionChecker(
143 TypeSystemServices
, node
, arguments
, Errors
);
146 checker
.NotGenericDefinition(definition
) ||
147 checker
.IncorrectGenerity(definition
) ||
148 checker
.ViolatesParameterConstraints(definition
));
151 public static bool IsGenericMethod(IEntity entity
)
153 IMethod method
= entity
as IMethod
;
154 return (method
!= null && method
.GenericInfo
!= null);
157 public static bool IsGenericType(IEntity entity
)
159 IType type
= entity
as IType
;
160 return (type
!= null && type
.GenericInfo
!= null);
164 /// Finds types constructed from the specified definition in the specified type's interfaces and base types.
166 /// <param name="type">The type in whose hierarchy to search for constructed types.</param>
167 /// <param name="definition">The generic type definition whose constructed versions to search for.</param>
168 /// <returns>Yields the matching types.</returns>
169 public static IEnumerable
<IType
> FindConstructedTypes(IType type
, IType definition
)
173 // Check if type itself is constructed from the definition
174 if (type
.ConstructedInfo
!= null &&
175 type
.ConstructedInfo
.GenericDefinition
== definition
)
180 // Look in type's immediate interfaces
181 IType
[] interfaces
= type
.GetInterfaces();
182 if (interfaces
!= null)
184 foreach (IType interfaceType
in interfaces
)
186 foreach (IType match
in FindConstructedTypes(interfaceType
, definition
))
193 // Move on to the type's base type
194 type
= type
.BaseType
;
199 /// Determines whether a specified type is an open generic type -
200 /// that is, if it contains generic parameters.
202 public static bool IsOpenGenericType(IType type
)
204 return (GetTypeGenerity(type
) != 0);
208 /// Gets the generic parameters associated with a generic type or generic method definition.
210 /// <returns>An array of IGenericParameter objects, or null if the specified entity isn't a generic definition.</returns>
211 public static IGenericParameter
[] GetGenericParameters(IEntity definition
)
213 if (IsGenericType(definition
))
215 return ((IType
)definition
).GenericInfo
.GenericParameters
;
217 if (IsGenericMethod(definition
))
219 return ((IMethod
)definition
).GenericInfo
.GenericParameters
;
225 /// Determines the number of open generic parameters in the specified type.
227 public static int GetTypeGenerity(IType type
)
229 // Dive into arrays and refs
230 if (type
.IsByRef
|| type
.IsArray
)
232 return GetTypeGenerity(type
.GetElementType());
235 // A generic parameter has a generity of one
236 if (type
is IGenericParameter
)
241 // Generic parameters and generic arguments both contribute
242 // to a types genrity
245 // A generic type gets a generity equals to the number its type parameters
246 if (type
.GenericInfo
!= null)
248 generity
+= type
.GenericInfo
.GenericParameters
.Length
;
251 // A constructed type gets the accumulated generity of its type arguments
252 if (type
.ConstructedInfo
!= null)
254 foreach (IType typeArg
in type
.ConstructedInfo
.GenericArguments
)
256 generity
+= GetTypeGenerity(typeArg
);
265 /// Checks a generic construction for several kinds of errors.
267 public class GenericConstructionChecker
269 Node _constructionNode
;
270 TypeReferenceCollection _argumentNodes
;
271 CompilerErrorCollection _errors
;
272 TypeSystemServices _tss
;
274 public GenericConstructionChecker(TypeSystemServices tss
, Node constructionNode
, TypeReferenceCollection argumentNodes
, CompilerErrorCollection errorCollection
)
277 _constructionNode
= constructionNode
;
278 _argumentNodes
= argumentNodes
;
279 _errors
= errorCollection
;
282 public TypeReferenceCollection ArgumentNodes
284 get { return _argumentNodes; }
287 public Node ConstructionNode
289 get { return _constructionNode; }
292 public CompilerErrorCollection Errors
294 get { return _errors; }
298 /// Checks if a specified entity is not a generic definition.
300 public bool NotGenericDefinition(IEntity entity
)
302 if (!(GenericsServices
.IsGenericType(entity
) || GenericsServices
.IsGenericMethod(entity
)))
304 Errors
.Add(CompilerErrorFactory
.NotAGenericDefinition(ConstructionNode
, entity
.FullName
));
311 /// Checks if the number of generic parameters on a specified definition
312 /// does not match the number of supplied type arguments.
314 public bool IncorrectGenerity(IEntity definition
)
316 int parametersCount
= GenericsServices
.GetGenericParameters(definition
).Length
;
317 if (parametersCount
!= ArgumentNodes
.Count
)
319 Errors
.Add(CompilerErrorFactory
.GenericDefinitionArgumentCount(ConstructionNode
, definition
.FullName
, parametersCount
));
326 /// Checks if the given type arguments violate any constraints
327 /// declared on the type parameters of a specified generic definition.
329 public bool ViolatesParameterConstraints(IEntity definition
)
331 IGenericParameter
[] parameters
= GenericsServices
.GetGenericParameters(definition
);
334 for (int i
= 0; i
< parameters
.Length
; i
++)
336 if (ViolatesParameterConstraints(parameters
[i
], ArgumentNodes
[i
]))
346 /// Checks if a specified type argument violates the constraints
347 /// declared on a specified type paramter.
349 public bool ViolatesParameterConstraints(IGenericParameter parameter
, TypeReference argumentNode
)
351 IType argument
= TypeSystemServices
.GetEntity(argumentNode
) as IType
;
353 // Ensure argument is a valid type
354 if (argument
== null || TypeSystemServices
.IsError(argument
))
361 // Check type semantics constraints
362 if (parameter
.IsClass
&& !argument
.IsClass
)
364 Errors
.Add(CompilerErrorFactory
.GenericArgumentMustBeReferenceType(ConstructionNode
, parameter
, argument
));
368 if (parameter
.IsValueType
&& !argument
.IsValueType
)
370 Errors
.Add(CompilerErrorFactory
.GenericArgumentMustBeValueType(argumentNode
, parameter
, argument
));
374 if (parameter
.MustHaveDefaultConstructor
&& !HasDefaultConstructor(argument
))
376 Errors
.Add(CompilerErrorFactory
.GenericArgumentMustHaveDefaultConstructor(argumentNode
, parameter
, argument
));
380 // Check base type constraints
381 IType
[] baseTypes
= parameter
.GetBaseTypeConstraints();
382 if (baseTypes
!= null)
384 foreach (IType baseType
in baseTypes
)
386 // Don't check for System.ValueType supertype constraint
387 // if parameter also has explicit value type constraint
388 if (baseType
== _tss
.ValueTypeType
&& parameter
.IsValueType
)
391 if (!baseType
.IsAssignableFrom(argument
))
393 Errors
.Add(CompilerErrorFactory
.GenericArgumentMustHaveBaseType(argumentNode
, parameter
, argument
, baseType
));
403 /// Checks whether a given type has a default (parameterless) consructor.
405 private static bool HasDefaultConstructor(IType argument
)
407 if (argument
.IsValueType
)
412 IConstructor
[] constructors
= argument
.GetConstructors();
413 foreach (IConstructor ctor
in constructors
)
415 if (ctor
.GetParameters().Length
== 0)