static AbstractType HandleClassLikeMatch(DClassLike dc, ResolutionContext ctxt, object typeBase, bool canResolveBase) { AbstractType ret; UserDefinedType udt = null; var invisibleTypeParams = GetInvisibleTypeParameters(dc, ctxt); switch (dc.ClassType) { case DTokens.Struct: ret = new StructType(dc, typeBase as ISyntaxRegion, invisibleTypeParams); break; case DTokens.Union: ret = new UnionType(dc, typeBase as ISyntaxRegion, invisibleTypeParams); break; case DTokens.Class: udt = new ClassType(dc, typeBase as ISyntaxRegion, null, null, invisibleTypeParams); ret = null; break; case DTokens.Interface: udt = new InterfaceType(dc, typeBase as ISyntaxRegion, null, invisibleTypeParams); ret = null; break; case DTokens.Template: if (dc.ContainsAttribute(DTokens.Mixin)) { ret = new MixinTemplateType(dc, typeBase as ISyntaxRegion, invisibleTypeParams); } else { ret = new TemplateType(dc, typeBase as ISyntaxRegion, invisibleTypeParams); } break; default: ret = null; ctxt.LogError(new ResolutionError(dc, "Unknown type (" + DTokens.GetTokenString(dc.ClassType) + ")")); break; } if (dc.ClassType == DTokens.Class || dc.ClassType == DTokens.Interface) { ret = canResolveBase ? DResolver.ResolveBaseClasses(udt, ctxt) : udt; } return(ret); }
public static AbstractType Resolve(ArrayDecl ad, ResolutionContext ctxt) { var valueTypes = Resolve(ad.ValueType, ctxt); ctxt.CheckForSingleResult(valueTypes, ad); AbstractType valueType = null; AbstractType keyType = null; int fixedArrayLength = -1; if (valueTypes != null && valueTypes.Length != 0) { valueType = valueTypes[0]; } ISymbolValue val; keyType = ResolveKey(ad, out fixedArrayLength, out val, ctxt); if (keyType == null || (keyType is PrimitiveType && ((PrimitiveType)keyType).TypeToken == DTokens.Int)) { if (fixedArrayLength >= 0) { // D Magic: One might access tuple items directly in the pseudo array declaration - so stuff like Tup[0] i; becomes e.g. int i; var dtup = DResolver.StripMemberSymbols(valueType) as DTuple; if (dtup == null) { return(new ArrayType(valueType, fixedArrayLength, ad)); } if (fixedArrayLength < dtup.Items.Length) { return(AbstractType.Get(dtup.Items [fixedArrayLength])); } else { ctxt.LogError(ad, "TypeTuple only consists of " + dtup.Items.Length + " items. Can't access item at index " + fixedArrayLength); return(null); } } return(new ArrayType(valueType, ad)); } return(new AssocArrayType(valueType, keyType, ad)); }
public static AbstractType Resolve(TypeOfDeclaration typeOf, ResolutionContext ctxt) { // typeof(return) if (typeOf.Expression is TokenExpression && (typeOf.Expression as TokenExpression).Token == DTokens.Return) { var m = HandleNodeMatch(ctxt.ScopedBlock, ctxt, null, typeOf); if (m != null) { return(m); } } // typeOf(myInt) => int else if (typeOf.Expression != null) { var wantedTypes = Evaluation.EvaluateType(typeOf.Expression, ctxt); return(DResolver.StripMemberSymbols(wantedTypes)); } return(null); }
private static bool DeduceParam(ResolutionContext ctxt, DSymbol overload, DeducedTypeDictionary deducedTypes, IEnumerator <ISemantic> argEnum, TemplateParameter expectedParam) { if (expectedParam is TemplateThisParameter && overload.Base != null) { var ttp = (TemplateThisParameter)expectedParam; // Get the type of the type of 'this' - so of the result that is the overload's base var t = DResolver.StripMemberSymbols(overload.Base); if (t == null || t.DeclarationOrExpressionBase == null) { return(false); } //TODO: Still not sure if it's ok to pass a type result to it // - looking at things like typeof(T) that shall return e.g. const(A) instead of A only. if (!CheckAndDeduceTypeAgainstTplParameter(ttp, t, deducedTypes, ctxt)) { return(false); } return(true); } // Used when no argument but default arg given bool useDefaultType = false; if (argEnum.MoveNext() || (useDefaultType = HasDefaultType(expectedParam))) { // On tuples, take all following arguments and pass them to the check function if (expectedParam is TemplateTupleParameter) { var tupleItems = new List <ISemantic>(); // A tuple must at least contain one item! tupleItems.Add(argEnum.Current); while (argEnum.MoveNext()) { tupleItems.Add(argEnum.Current); } if (!CheckAndDeduceTypeTuple((TemplateTupleParameter)expectedParam, tupleItems, deducedTypes, ctxt)) { return(false); } } else if (argEnum.Current != null) { if (!CheckAndDeduceTypeAgainstTplParameter(expectedParam, argEnum.Current, deducedTypes, ctxt)) { return(false); } } else if (useDefaultType && CheckAndDeduceTypeAgainstTplParameter(expectedParam, null, deducedTypes, ctxt)) { // It's legit - just do nothing } else { return(false); } } else if (expectedParam is TemplateTupleParameter) { if (!CheckAndDeduceTypeTuple(expectedParam as TemplateTupleParameter, null, deducedTypes, ctxt)) { return(false); } } // There might be too few args - but that doesn't mean that it's not correct - it's only required that all parameters got satisfied with a type else if (!deducedTypes.AllParamatersSatisfied) { return(false); } return(true); }
/// <summary> /// Takes the class passed via the tr, and resolves its base class and/or implemented interfaces. /// Also usable for enums. /// /// Never returns null. Instead, the original 'tr' object will be returned if no base class was resolved. /// Will clone 'tr', whereas the new object will contain the base class. /// </summary> public static UserDefinedType ResolveBaseClasses(UserDefinedType tr, ResolutionContext ctxt, bool ResolveFirstBaseIdOnly = false) { if (bcStack > 8) { bcStack--; return(tr); } if (tr is EnumType) { var et = tr as EnumType; AbstractType bt = null; if (et.Definition.Type == null) { bt = new PrimitiveType(DTokens.Int); } else { if (tr.Definition.Parent is IBlockNode) { ctxt.PushNewScope((IBlockNode)tr.Definition.Parent); } var bts = TypeDeclarationResolver.Resolve(et.Definition.Type, ctxt); if (tr.Definition.Parent is IBlockNode) { ctxt.Pop(); } ctxt.CheckForSingleResult(bts, et.Definition.Type); if (bts != null && bts.Length != 0) { bt = bts[0]; } } return(new EnumType(et.Definition, bt, et.DeclarationOrExpressionBase)); } var dc = tr.Definition as DClassLike; // Return immediately if searching base classes of the Object class if (dc == null || ((dc.BaseClasses == null || dc.BaseClasses.Count < 1) && dc.Name == "Object")) { return(tr); } // If no base class(es) specified, and if it's no interface that is handled, return the global Object reference // -- and do not throw any error message, it's ok if (dc.BaseClasses == null || dc.BaseClasses.Count < 1) { if (tr is ClassType) // Only Classes can inherit from non-interfaces { return(new ClassType(dc, tr.DeclarationOrExpressionBase, ctxt.ParseCache.ObjectClassResult)); } return(tr); } #region Base class & interface resolution TemplateIntermediateType baseClass = null; var interfaces = new List <InterfaceType>(); if (!(tr is ClassType || tr is InterfaceType)) { if (dc.BaseClasses.Count != 0) { ctxt.LogError(dc, "Only classes and interfaces may inherit from other classes/interfaces"); } return(tr); } for (int i = 0; i < (ResolveFirstBaseIdOnly ? 1 : dc.BaseClasses.Count); i++) { var type = dc.BaseClasses[i]; // If there's an explicit 'Object' inheritance, also return the pre-resolved object class if (type is IdentifierDeclaration && ((IdentifierDeclaration)type).Id == "Object") { if (baseClass != null) { ctxt.LogError(new ResolutionError(dc, "Class must not have two base classes")); continue; } else if (i != 0) { ctxt.LogError(new ResolutionError(dc, "The base class name must preceed base interfaces")); continue; } baseClass = ctxt.ParseCache.ObjectClassResult; continue; } if (type == null || type.ToString(false) == dc.Name || dc.NodeRoot == dc) { ctxt.LogError(new ResolutionError(dc, "A class cannot inherit from itself")); continue; } ctxt.PushNewScope(dc.Parent as IBlockNode); bcStack++; var res = DResolver.StripAliasSymbols(TypeDeclarationResolver.Resolve(type, ctxt)); ctxt.CheckForSingleResult(res, type); if (res != null && res.Length != 0) { var r = res[0]; if (r is ClassType || r is TemplateType) { if (tr is InterfaceType) { ctxt.LogError(new ResolutionError(type, "An interface cannot inherit from non-interfaces")); } else if (i == 0) { baseClass = (TemplateIntermediateType)r; } else { ctxt.LogError(new ResolutionError(dc, "The base " + (r is ClassType ? "class" : "template") + " name must preceed base interfaces")); } } else if (r is InterfaceType) { interfaces.Add((InterfaceType)r); } else { ctxt.LogError(new ResolutionError(type, "Resolved class is neither a class nor an interface")); continue; } } bcStack--; ctxt.Pop(); } #endregion if (baseClass == null && interfaces.Count == 0) { return(tr); } if (tr is ClassType) { return(new ClassType(dc, tr.DeclarationOrExpressionBase, baseClass, interfaces.Count == 0 ? null : interfaces.ToArray(), tr.DeducedTypes)); } else if (tr is InterfaceType) { return(new InterfaceType(dc, tr.DeclarationOrExpressionBase, interfaces.Count == 0 ? null : interfaces.ToArray(), tr.DeducedTypes)); } // Method should end here return(tr); }
public static AbstractType GetMethodReturnType(DMethod method, ResolutionContext ctxt) { if ((ctxt.Options & ResolutionOptions.DontResolveBaseTypes) == ResolutionOptions.DontResolveBaseTypes) { return(null); } /* * If a method's type equals null, assume that it's an 'auto' function.. * 1) Search for a return statement * 2) Resolve the returned expression * 3) Use that one as the method's type */ bool pushMethodScope = ctxt.ScopedBlock != method; if (method.Type != null) { if (pushMethodScope) { ctxt.PushNewScope(method); } //FIXME: Is it legal to explicitly return a nested type? var returnType = TypeDeclarationResolver.Resolve(method.Type, ctxt); if (pushMethodScope) { ctxt.Pop(); } ctxt.CheckForSingleResult(returnType, method.Type); if (returnType != null && returnType.Length > 0) { return(returnType[0]); } } else if (method.Body != null) { ReturnStatement returnStmt = null; var list = new List <IStatement> { method.Body }; var list2 = new List <IStatement>(); bool foundMatch = false; while (!foundMatch && list.Count > 0) { foreach (var stmt in list) { if (stmt is ReturnStatement) { returnStmt = stmt as ReturnStatement; var te = returnStmt.ReturnExpression as TokenExpression; if (te == null || te.Token != DTokens.Null) { foundMatch = true; break; } } var statementContainingStatement = stmt as StatementContainingStatement; if (statementContainingStatement != null) { list2.AddRange(statementContainingStatement.SubStatements); } } list = list2; list2 = new List <IStatement>(); } if (returnStmt != null && returnStmt.ReturnExpression != null) { if (pushMethodScope) { var dedTypes = ctxt.CurrentContext.DeducedTemplateParameters; ctxt.PushNewScope(method, returnStmt); if (dedTypes.Count != 0) { foreach (var kv in dedTypes) { ctxt.CurrentContext.DeducedTemplateParameters[kv.Key] = kv.Value; } } } var t = DResolver.StripMemberSymbols(Evaluation.EvaluateType(returnStmt.ReturnExpression, ctxt)); if (pushMethodScope) { ctxt.Pop(); } return(t); } return(new PrimitiveType(DTokens.Void)); } return(null); }
/// <summary> /// The variable's or method's base type will be resolved (if auto type, the intializer's type will be taken). /// A class' base class will be searched. /// etc.. /// </summary> public static AbstractType HandleNodeMatch( INode m, ResolutionContext ctxt, AbstractType resultBase = null, object typeBase = null) { AbstractType ret = null; // See https://github.com/aBothe/Mono-D/issues/161 int stkC; if (stackCalls == null) { stackCalls = new Dictionary <INode, int>(); stackCalls[m] = stkC = 1; } else if (stackCalls.TryGetValue(m, out stkC)) { stackCalls[m] = ++stkC; } else { stackCalls[m] = stkC = 1; } /* * Pushing a new scope is only required if current scope cannot be found in the handled node's hierarchy. * Edit: No, it is required nearly every time because of nested type declarations - then, we do need the * current block scope. */ bool popAfterwards; { var newScope = m is IBlockNode ? (IBlockNode)m : m.Parent as IBlockNode; popAfterwards = ctxt.ScopedBlock != newScope && newScope != null; if (popAfterwards) { var options = ctxt.CurrentContext.ContextDependentOptions; var applyOptions = ctxt.ScopedBlockIsInNodeHierarchy(m); ctxt.PushNewScope(newScope); if (applyOptions) { ctxt.CurrentContext.ContextDependentOptions = options; } } } var canResolveBase = ((ctxt.Options & ResolutionOptions.DontResolveBaseTypes) != ResolutionOptions.DontResolveBaseTypes) && stkC < 10 && (m.Type == null || m.Type.ToString(false) != m.Name); // To support resolving type parameters to concrete types if the context allows this, introduce all deduced parameters to the current context if (resultBase is DSymbol) { ctxt.CurrentContext.IntroduceTemplateParameterTypes((DSymbol)resultBase); } var importSymbolNode = m as ImportSymbolNode; var variable = m as DVariable; // Only import symbol aliases are allowed to search in the parse cache if (importSymbolNode != null) { ret = HandleImportSymbolMatch(importSymbolNode, ctxt); } else if (variable != null) { AbstractType bt = null; if (!(variable is EponymousTemplate)) { if (canResolveBase) { var bts = TypeDeclarationResolver.Resolve(variable.Type, ctxt); ctxt.CheckForSingleResult(bts, variable.Type); if (bts != null && bts.Length != 0) { bt = bts [0]; } // For auto variables, use the initializer to get its type else if (variable.Initializer != null) { bt = DResolver.StripMemberSymbols(Evaluation.EvaluateType(variable.Initializer, ctxt)); } // Check if inside an foreach statement header if (bt == null && ctxt.ScopedStatement != null) { bt = GetForeachIteratorType(variable, ctxt); } } // Note: Also works for aliases! In this case, we simply try to resolve the aliased type, otherwise the variable's base type ret = variable.IsAlias ? new AliasedType(variable, bt, typeBase as ISyntaxRegion) as MemberSymbol : new MemberSymbol(variable, bt, typeBase as ISyntaxRegion); } else { ret = new EponymousTemplateType(variable as EponymousTemplate, GetInvisibleTypeParameters(variable, ctxt).AsReadOnly(), typeBase as ISyntaxRegion); } } else if (m is DMethod) { ret = new MemberSymbol(m as DNode, canResolveBase ? GetMethodReturnType(m as DMethod, ctxt) : null, typeBase as ISyntaxRegion); } else if (m is DClassLike) { ret = HandleClassLikeMatch(m as DClassLike, ctxt, typeBase, canResolveBase); } else if (m is DModule) { var mod = (DModule)m; if (typeBase != null && typeBase.ToString() != mod.ModuleName) { var pack = ctxt.ParseCache.LookupPackage(typeBase.ToString()).FirstOrDefault(); if (pack != null) { ret = new PackageSymbol(pack, typeBase as ISyntaxRegion); } } else { ret = new ModuleSymbol(m as DModule, typeBase as ISyntaxRegion); } } else if (m is DEnum) { ret = new EnumType((DEnum)m, typeBase as ISyntaxRegion); } else if (m is TemplateParameter.Node) { //ResolveResult[] templateParameterType = null; //TODO: Resolve the specialization type //var templateParameterType = TemplateInstanceHandler.ResolveTypeSpecialization(tmp, ctxt); ret = new TemplateParameterSymbol((m as TemplateParameter.Node).TemplateParameter, null, typeBase as ISyntaxRegion); } else if (m is NamedTemplateMixinNode) { var tmxNode = m as NamedTemplateMixinNode; ret = new MemberSymbol(tmxNode, canResolveBase ? ResolveSingle(tmxNode.Type, ctxt) : null, typeBase as ISyntaxRegion); } if (popAfterwards) { ctxt.Pop(); } else if (resultBase is DSymbol) { ctxt.CurrentContext.RemoveParamTypesFromPreferredLocals((DSymbol)resultBase); } if (stkC == 1) { stackCalls.Remove(m); } else { stackCalls[m] = stkC - 1; } return(ret); }
public static AbstractType ResolveKey(ArrayDecl ad, out int fixedArrayLength, out ISymbolValue keyVal, ResolutionContext ctxt) { keyVal = null; fixedArrayLength = -1; AbstractType keyType = null; if (ad.KeyExpression != null) { //TODO: Template instance expressions? var id_x = ad.KeyExpression as IdentifierExpression; if (id_x != null && id_x.IsIdentifier) { var id = new IdentifierDeclaration((string)id_x.Value) { Location = id_x.Location, EndLocation = id_x.EndLocation }; keyType = TypeDeclarationResolver.ResolveSingle(id, ctxt); if (keyType != null) { var tt = DResolver.StripAliasSymbol(keyType) as MemberSymbol; if (tt == null || !(tt.Definition is DVariable) || ((DVariable)tt.Definition).Initializer == null) { return(keyType); } } } try { keyVal = Evaluation.EvaluateValue(ad.KeyExpression, ctxt); if (keyVal != null) { // Take the value's type as array key type keyType = keyVal.RepresentedType; // It should be mostly a number only that points out how large the final array should be var pv = Evaluation.GetVariableContents(keyVal, new StandardValueProvider(ctxt)) as PrimitiveValue; if (pv != null) { fixedArrayLength = System.Convert.ToInt32(pv.Value); if (fixedArrayLength < 0) { ctxt.LogError(ad, "Invalid array size: Length value must be greater than 0"); } } //TODO Is there any other type of value allowed? } } catch { } } else { var t = Resolve(ad.KeyType, ctxt); ctxt.CheckForSingleResult(t, ad.KeyType); if (t != null && t.Length != 0) { return(t[0]); } } return(keyType); }
/// <summary> /// Used for searching further identifier list parts. /// /// a.b -- nextIdentifier would be 'b' whereas <param name="resultBases">resultBases</param> contained the resolution result for 'a' /// </summary> public static AbstractType[] ResolveFurtherTypeIdentifier(int nextIdentifierHash, IEnumerable <AbstractType> resultBases, ResolutionContext ctxt, object typeIdObject = null) { MemberSymbol statProp; if ((resultBases = DResolver.StripMemberSymbols(resultBases)) == null) { return(null); } var r = new List <AbstractType>(); foreach (var b in resultBases) { if (b is UserDefinedType) { var udt = b as UserDefinedType; var bn = udt.Definition as IBlockNode; bool pop = !(b is MixinTemplateType); if (!pop) { ctxt.PushNewScope(bn); } ctxt.CurrentContext.IntroduceTemplateParameterTypes(udt); r.AddRange(SingleNodeNameScan.SearchChildrenAndResolve(ctxt, bn, nextIdentifierHash, typeIdObject)); List <TemplateParameterSymbol> dedTypes = null; foreach (var t in r) { var ds = t as DSymbol; if (ds != null && ds.DeducedTypes == null) { if (dedTypes == null) { dedTypes = ctxt.DeducedTypesInHierarchy; } ds.DeducedTypes = new System.Collections.ObjectModel.ReadOnlyCollection <TemplateParameterSymbol>(dedTypes); } } statProp = StaticProperties.TryEvalPropertyType(ctxt, b, nextIdentifierHash); if (statProp != null) { r.Add(statProp); } ctxt.CurrentContext.RemoveParamTypesFromPreferredLocals(udt); if (!pop) { ctxt.Pop(); } } else if (b is PackageSymbol) { var pack = (b as PackageSymbol).Package; var accessedModule = pack.GetModule(nextIdentifierHash); if (accessedModule != null) { r.Add(new ModuleSymbol(accessedModule as DModule, typeIdObject as ISyntaxRegion, b as PackageSymbol)); } else if ((pack = pack.GetPackage(nextIdentifierHash)) != null) { r.Add(new PackageSymbol(pack, typeIdObject as ISyntaxRegion)); } } else if (b is ModuleSymbol) { r.AddRange(SingleNodeNameScan.SearchChildrenAndResolve(ctxt, (b as ModuleSymbol).Definition, nextIdentifierHash, typeIdObject)); } else { statProp = StaticProperties.TryEvalPropertyType(ctxt, b, nextIdentifierHash); if (statProp != null) { r.Add(statProp); } } // TODO: Search for UFCS symbols } return(r.Count == 0 ? null : r.ToArray()); }
/// <summary> /// string[] s; /// /// foreach(i;s) /// { /// // i is of type 'string' /// writeln(i); /// } /// </summary> public static AbstractType GetForeachIteratorType(DVariable i, ResolutionContext ctxt) { var r = new List <AbstractType>(); var curStmt = ctxt.ScopedStatement; bool init = true; // Walk up statement hierarchy -- note that foreach loops can be nested while (curStmt != null) { if (init) { init = false; } else { curStmt = curStmt.Parent; } if (curStmt is ForeachStatement) { var fe = (ForeachStatement)curStmt; if (fe.ForeachTypeList == null) { continue; } // If the searched variable is declared in the header int iteratorIndex = -1; for (int j = 0; j < fe.ForeachTypeList.Length; j++) { if (fe.ForeachTypeList[j] == i) { iteratorIndex = j; break; } } if (iteratorIndex == -1) { continue; } bool keyIsSearched = iteratorIndex == 0 && fe.ForeachTypeList.Length > 1; // foreach(var k, var v; 0 .. 9) if (keyIsSearched && fe.IsRangeStatement) { // -- it's static type int, of course(?) return(new PrimitiveType(DTokens.Int)); } var aggregateType = Evaluation.EvaluateType(fe.Aggregate, ctxt); aggregateType = DResolver.StripMemberSymbols(aggregateType); if (aggregateType == null) { return(null); } // The most common way to do a foreach if (aggregateType is AssocArrayType) { var ar = (AssocArrayType)aggregateType; return(keyIsSearched ? ar.KeyType : ar.ValueType); } else if (aggregateType is UserDefinedType) { var tr = (UserDefinedType)aggregateType; if (keyIsSearched || !(tr.Definition is IBlockNode)) { continue; } bool foundIterPropertyMatch = false; #region Foreach over Structs and Classes with Ranges // Enlist all 'back'/'front' members var t_l = new List <AbstractType>(); foreach (var n in (IBlockNode)tr.Definition) { if (fe.IsReverse ? n.Name == "back" : n.Name == "front") { t_l.Add(HandleNodeMatch(n, ctxt)); } } // Remove aliases var iterPropertyTypes = DResolver.StripAliasSymbols(t_l); foreach (var iterPropType in iterPropertyTypes) { if (iterPropType is MemberSymbol) { foundIterPropertyMatch = true; var itp = (MemberSymbol)iterPropType; // Only take non-parameterized methods if (itp.Definition is DMethod && ((DMethod)itp.Definition).Parameters.Count != 0) { continue; } // Handle its base type [return type] as iterator type if (itp.Base != null) { r.Add(itp.Base); } foundIterPropertyMatch = true; } } if (foundIterPropertyMatch) { continue; } #endregion #region Foreach over Structs and Classes with opApply t_l.Clear(); r.Clear(); foreach (var n in (IBlockNode)tr.Definition) { if (n is DMethod && (fe.IsReverse ? n.Name == "opApplyReverse" : n.Name == "opApply")) { t_l.Add(HandleNodeMatch(n, ctxt)); } } iterPropertyTypes = DResolver.StripAliasSymbols(t_l); foreach (var iterPropertyType in iterPropertyTypes) { if (iterPropertyType is MemberSymbol) { var mr = (MemberSymbol)iterPropertyType; var dm = mr.Definition as DMethod; if (dm == null || dm.Parameters.Count != 1) { continue; } var dg = dm.Parameters[0].Type as DelegateDeclaration; if (dg == null || dg.Parameters.Count != fe.ForeachTypeList.Length) { continue; } var paramType = Resolve(dg.Parameters[iteratorIndex].Type, ctxt); if (paramType != null && paramType.Length > 0) { r.Add(paramType[0]); } } } #endregion } if (r.Count > 1) { ctxt.LogError(new ResolutionError(curStmt, "Ambigous iterator type")); } return(r.Count != 0 ? r[0] : null); } } return(null); }