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