/// <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); }
/// <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); }