| /** | |
| * Semantic analysis for D types. | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typesem.d, _typesem.d) | |
| * Documentation: https://dlang.org/phobos/dmd_typesem.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typesem.d | |
| */ | |
| module dmd.typesem; | |
| import core.checkedint; | |
| import core.stdc.string; | |
| import core.stdc.stdio; | |
| import dmd.access; | |
| import dmd.aggregate; | |
| import dmd.aliasthis; | |
| import dmd.arrayop; | |
| import dmd.arraytypes; | |
| import dmd.astcodegen; | |
| import dmd.astenums; | |
| import dmd.dcast; | |
| import dmd.dclass; | |
| import dmd.declaration; | |
| import dmd.denum; | |
| import dmd.dimport; | |
| import dmd.dmangle; | |
| import dmd.dmodule; | |
| import dmd.dscope; | |
| import dmd.dstruct; | |
| import dmd.dsymbol; | |
| import dmd.dsymbolsem; | |
| import dmd.dtemplate; | |
| import dmd.errors; | |
| import dmd.expression; | |
| import dmd.expressionsem; | |
| import dmd.func; | |
| import dmd.globals; | |
| import dmd.hdrgen; | |
| import dmd.id; | |
| import dmd.identifier; | |
| import dmd.imphint; | |
| import dmd.importc; | |
| import dmd.init; | |
| import dmd.initsem; | |
| import dmd.visitor; | |
| import dmd.mtype; | |
| import dmd.objc; | |
| import dmd.opover; | |
| import dmd.parse; | |
| import dmd.root.complex; | |
| import dmd.root.ctfloat; | |
| import dmd.root.rmem; | |
| import dmd.common.outbuffer; | |
| import dmd.root.rootobject; | |
| import dmd.root.string; | |
| import dmd.root.stringtable; | |
| import dmd.safe; | |
| import dmd.semantic3; | |
| import dmd.sideeffect; | |
| import dmd.target; | |
| import dmd.tokens; | |
| /************************** | |
| * This evaluates exp while setting length to be the number | |
| * of elements in the tuple t. | |
| */ | |
| private Expression semanticLength(Scope* sc, Type t, Expression exp) | |
| { | |
| if (auto tt = t.isTypeTuple()) | |
| { | |
| ScopeDsymbol sym = new ArrayScopeSymbol(sc, tt); | |
| sym.parent = sc.scopesym; | |
| sc = sc.push(sym); | |
| sc = sc.startCTFE(); | |
| exp = exp.expressionSemantic(sc); | |
| exp = resolveProperties(sc, exp); | |
| sc = sc.endCTFE(); | |
| sc.pop(); | |
| } | |
| else | |
| { | |
| sc = sc.startCTFE(); | |
| exp = exp.expressionSemantic(sc); | |
| exp = resolveProperties(sc, exp); | |
| sc = sc.endCTFE(); | |
| } | |
| return exp; | |
| } | |
| private Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp) | |
| { | |
| ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup); | |
| sym.parent = sc.scopesym; | |
| sc = sc.push(sym); | |
| sc = sc.startCTFE(); | |
| exp = exp.expressionSemantic(sc); | |
| exp = resolveProperties(sc, exp); | |
| sc = sc.endCTFE(); | |
| sc.pop(); | |
| return exp; | |
| } | |
| /************************************* | |
| * Resolve a tuple index, `s[oindex]`, by figuring out what `s[oindex]` represents. | |
| * Setting one of pe/pt/ps. | |
| * Params: | |
| * loc = location for error messages | |
| * sc = context | |
| * s = symbol being indexed - could be a tuple, could be an expression | |
| * pe = set if s[oindex] is an Expression, otherwise null | |
| * pt = set if s[oindex] is a Type, otherwise null | |
| * ps = set if s[oindex] is a Dsymbol, otherwise null | |
| * oindex = index into s | |
| */ | |
| private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex) | |
| { | |
| auto tup = s.isTupleDeclaration(); | |
| auto eindex = isExpression(oindex); | |
| auto tindex = isType(oindex); | |
| auto sindex = isDsymbol(oindex); | |
| if (!tup) | |
| { | |
| // It's really an index expression | |
| if (tindex) | |
| eindex = new TypeExp(loc, tindex); | |
| else if (sindex) | |
| eindex = symbolToExp(sindex, loc, sc, false); | |
| Expression e = new IndexExp(loc, symbolToExp(s, loc, sc, false), eindex); | |
| e = e.expressionSemantic(sc); | |
| resolveExp(e, pt, pe, ps); | |
| return; | |
| } | |
| // Convert oindex to Expression, then try to resolve to constant. | |
| if (tindex) | |
| tindex.resolve(loc, sc, eindex, tindex, sindex); | |
| if (sindex) | |
| eindex = symbolToExp(sindex, loc, sc, false); | |
| if (!eindex) | |
| { | |
| .error(loc, "index `%s` is not an expression", oindex.toChars()); | |
| pt = Type.terror; | |
| return; | |
| } | |
| eindex = semanticLength(sc, tup, eindex); | |
| eindex = eindex.ctfeInterpret(); | |
| if (eindex.op == EXP.error) | |
| { | |
| pt = Type.terror; | |
| return; | |
| } | |
| const(uinteger_t) d = eindex.toUInteger(); | |
| if (d >= tup.objects.dim) | |
| { | |
| .error(loc, "tuple index `%llu` exceeds length %llu", d, cast(ulong)tup.objects.dim); | |
| pt = Type.terror; | |
| return; | |
| } | |
| RootObject o = (*tup.objects)[cast(size_t)d]; | |
| ps = isDsymbol(o); | |
| if (auto t = isType(o)) | |
| pt = t.typeSemantic(loc, sc); | |
| if (auto e = isExpression(o)) | |
| resolveExp(e, pt, pe, ps); | |
| } | |
| /************************************* | |
| * Takes an array of Identifiers and figures out if | |
| * it represents a Type, Expression, or Dsymbol. | |
| * Params: | |
| * mt = array of identifiers | |
| * loc = location for error messages | |
| * sc = context | |
| * s = symbol to start search at | |
| * scopesym = unused | |
| * pe = set if expression otherwise null | |
| * pt = set if type otherwise null | |
| * ps = set if symbol otherwise null | |
| * typeid = set if in TypeidExpression https://dlang.org/spec/expression.html#TypeidExpression | |
| */ | |
| private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, | |
| out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) | |
| { | |
| version (none) | |
| { | |
| printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, mt.toChars()); | |
| if (scopesym) | |
| printf("\tscopesym = '%s'\n", scopesym.toChars()); | |
| } | |
| if (!s) | |
| { | |
| /* Look for what user might have intended | |
| */ | |
| const p = mt.mutableOf().unSharedOf().toChars(); | |
| auto id = Identifier.idPool(p, cast(uint)strlen(p)); | |
| if (const n = importHint(id.toString())) | |
| error(loc, "`%s` is not defined, perhaps `import %.*s;` ?", p, cast(int)n.length, n.ptr); | |
| else if (auto s2 = sc.search_correct(id)) | |
| error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2.kind(), s2.toChars()); | |
| else if (const q = Scope.search_correct_C(id)) | |
| error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q); | |
| else if ((id == Id.This && sc.getStructClassScope()) || | |
| (id == Id._super && sc.getClassScope())) | |
| error(loc, "undefined identifier `%s`, did you mean `typeof(%s)`?", p, p); | |
| else | |
| error(loc, "undefined identifier `%s`", p); | |
| pt = Type.terror; | |
| return; | |
| } | |
| //printf("\t1: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); | |
| Declaration d = s.isDeclaration(); | |
| if (d && (d.storage_class & STC.templateparameter)) | |
| s = s.toAlias(); | |
| else | |
| { | |
| // check for deprecated or disabled aliases | |
| // functions are checked after overloading | |
| // templates are checked after matching constraints | |
| if (!s.isFuncDeclaration() && !s.isTemplateDeclaration()) | |
| s.checkDeprecated(loc, sc); | |
| if (d) | |
| d.checkDisabled(loc, sc, true); | |
| } | |
| s = s.toAlias(); | |
| //printf("\t2: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); | |
| for (size_t i = 0; i < mt.idents.dim; i++) | |
| { | |
| RootObject id = mt.idents[i]; | |
| if (id.dyncast() == DYNCAST.expression || | |
| id.dyncast() == DYNCAST.type) | |
| { | |
| Type tx; | |
| Expression ex; | |
| Dsymbol sx; | |
| resolveTupleIndex(loc, sc, s, ex, tx, sx, id); | |
| if (sx) | |
| { | |
| s = sx.toAlias(); | |
| continue; | |
| } | |
| if (tx) | |
| ex = new TypeExp(loc, tx); | |
| assert(ex); | |
| ex = typeToExpressionHelper(mt, ex, i + 1); | |
| ex = ex.expressionSemantic(sc); | |
| resolveExp(ex, pt, pe, ps); | |
| return; | |
| } | |
| Type t = s.getType(); // type symbol, type alias, or type tuple? | |
| uint errorsave = global.errors; | |
| int flags = t is null ? SearchLocalsOnly : IgnorePrivateImports; | |
| Dsymbol sm = s.searchX(loc, sc, id, flags); | |
| if (sm) | |
| { | |
| if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm)) | |
| { | |
| .error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); | |
| sm = null; | |
| } | |
| // Same check as in Expression.semanticY(DotIdExp) | |
| else if (sm.isPackage() && checkAccess(sc, sm.isPackage())) | |
| { | |
| // @@@DEPRECATED_2.106@@@ | |
| // Should be an error in 2.106. Just remove the deprecation call | |
| // and uncomment the null assignment | |
| deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", sm.kind(), sm.toPrettyChars(), sm.toPrettyChars()); | |
| //sm = null; | |
| } | |
| } | |
| if (global.errors != errorsave) | |
| { | |
| pt = Type.terror; | |
| return; | |
| } | |
| void helper3() | |
| { | |
| Expression e; | |
| VarDeclaration v = s.isVarDeclaration(); | |
| FuncDeclaration f = s.isFuncDeclaration(); | |
| if (intypeid || !v && !f) | |
| e = symbolToExp(s, loc, sc, true); | |
| else | |
| e = new VarExp(loc, s.isDeclaration(), true); | |
| e = typeToExpressionHelper(mt, e, i); | |
| e = e.expressionSemantic(sc); | |
| resolveExp(e, pt, pe, ps); | |
| } | |
| //printf("\t3: s = %p %s %s, sm = %p\n", s, s.kind(), s.toChars(), sm); | |
| if (intypeid && !t && sm && sm.needThis()) | |
| return helper3(); | |
| if (VarDeclaration v = s.isVarDeclaration()) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=19913 | |
| // v.type would be null if it is a forward referenced member. | |
| if (v.type is null) | |
| v.dsymbolSemantic(sc); | |
| if (v.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) || | |
| v.type.isConst() || v.type.isImmutable()) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=13087 | |
| // this.field is not constant always | |
| if (!v.isThisDeclaration()) | |
| return helper3(); | |
| } | |
| } | |
| if (!sm) | |
| { | |
| if (!t) | |
| { | |
| if (s.isDeclaration()) // var, func, or tuple declaration? | |
| { | |
| t = s.isDeclaration().type; | |
| if (!t && s.isTupleDeclaration()) // expression tuple? | |
| return helper3(); | |
| } | |
| else if (s.isTemplateInstance() || | |
| s.isImport() || s.isPackage() || s.isModule()) | |
| { | |
| return helper3(); | |
| } | |
| } | |
| if (t) | |
| { | |
| sm = t.toDsymbol(sc); | |
| if (sm && id.dyncast() == DYNCAST.identifier) | |
| { | |
| sm = sm.search(loc, cast(Identifier)id, IgnorePrivateImports); | |
| if (!sm) | |
| return helper3(); | |
| } | |
| else | |
| return helper3(); | |
| } | |
| else | |
| { | |
| if (id.dyncast() == DYNCAST.dsymbol) | |
| { | |
| // searchX already handles errors for template instances | |
| assert(global.errors); | |
| } | |
| else | |
| { | |
| assert(id.dyncast() == DYNCAST.identifier); | |
| sm = s.search_correct(cast(Identifier)id); | |
| if (sm) | |
| error(loc, "identifier `%s` of `%s` is not defined, did you mean %s `%s`?", id.toChars(), mt.toChars(), sm.kind(), sm.toChars()); | |
| else | |
| error(loc, "identifier `%s` of `%s` is not defined", id.toChars(), mt.toChars()); | |
| } | |
| pe = ErrorExp.get(); | |
| return; | |
| } | |
| } | |
| s = sm.toAlias(); | |
| } | |
| if (auto em = s.isEnumMember()) | |
| { | |
| // It's not a type, it's an expression | |
| pe = em.getVarExp(loc, sc); | |
| return; | |
| } | |
| if (auto v = s.isVarDeclaration()) | |
| { | |
| /* This is mostly same with DsymbolExp::semantic(), but we cannot use it | |
| * because some variables used in type context need to prevent lowering | |
| * to a literal or contextful expression. For example: | |
| * | |
| * enum a = 1; alias b = a; | |
| * template X(alias e){ alias v = e; } alias x = X!(1); | |
| * struct S { int v; alias w = v; } | |
| * // TypeIdentifier 'a', 'e', and 'v' should be EXP.variable, | |
| * // because getDsymbol() need to work in AliasDeclaration::semantic(). | |
| */ | |
| if (!v.type || | |
| !v.type.deco && v.inuse) | |
| { | |
| if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494 | |
| error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars()); | |
| else | |
| error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars()); | |
| pt = Type.terror; | |
| return; | |
| } | |
| if (v.type.ty == Terror) | |
| pt = Type.terror; | |
| else | |
| pe = new VarExp(loc, v); | |
| return; | |
| } | |
| if (auto fld = s.isFuncLiteralDeclaration()) | |
| { | |
| //printf("'%s' is a function literal\n", fld.toChars()); | |
| auto e = new FuncExp(loc, fld); | |
| pe = e.expressionSemantic(sc); | |
| return; | |
| } | |
| version (none) | |
| { | |
| if (FuncDeclaration fd = s.isFuncDeclaration()) | |
| { | |
| pe = new DsymbolExp(loc, fd); | |
| return; | |
| } | |
| } | |
| Type t; | |
| while (1) | |
| { | |
| t = s.getType(); | |
| if (t) | |
| break; | |
| ps = s; | |
| return; | |
| } | |
| if (auto ti = t.isTypeInstance()) | |
| if (ti != mt && !ti.deco) | |
| { | |
| if (!ti.tempinst.errors) | |
| error(loc, "forward reference to `%s`", ti.toChars()); | |
| pt = Type.terror; | |
| return; | |
| } | |
| if (t.ty == Ttuple) | |
| pt = t; | |
| else | |
| pt = t.merge(); | |
| } | |
| /************************************ | |
| * Transitively search a type for all function types. | |
| * If any function types with parameters are found that have parameter identifiers | |
| * or default arguments, remove those and create a new type stripped of those. | |
| * This is used to determine the "canonical" version of a type which is useful for | |
| * comparisons. | |
| * Params: | |
| * t = type to scan | |
| * Returns: | |
| * `t` if no parameter identifiers or default arguments found, otherwise a new type that is | |
| * the same as t but with no parameter identifiers or default arguments. | |
| */ | |
| private Type stripDefaultArgs(Type t) | |
| { | |
| static Parameters* stripParams(Parameters* parameters) | |
| { | |
| static Parameter stripParameter(Parameter p) | |
| { | |
| Type t = stripDefaultArgs(p.type); | |
| return (t != p.type || p.defaultArg || p.ident || p.userAttribDecl) | |
| ? new Parameter(p.storageClass, t, null, null, null) | |
| : null; | |
| } | |
| if (parameters) | |
| { | |
| foreach (i, p; *parameters) | |
| { | |
| Parameter ps = stripParameter(p); | |
| if (ps) | |
| { | |
| // Replace params with a copy we can modify | |
| Parameters* nparams = new Parameters(parameters.dim); | |
| foreach (j, ref np; *nparams) | |
| { | |
| Parameter pj = (*parameters)[j]; | |
| if (j < i) | |
| np = pj; | |
| else if (j == i) | |
| np = ps; | |
| else | |
| { | |
| Parameter nps = stripParameter(pj); | |
| np = nps ? nps : pj; | |
| } | |
| } | |
| return nparams; | |
| } | |
| } | |
| } | |
| return parameters; | |
| } | |
| if (t is null) | |
| return t; | |
| if (auto tf = t.isTypeFunction()) | |
| { | |
| Type tret = stripDefaultArgs(tf.next); | |
| Parameters* params = stripParams(tf.parameterList.parameters); | |
| if (tret == tf.next && params == tf.parameterList.parameters) | |
| return t; | |
| TypeFunction tr = tf.copy().isTypeFunction(); | |
| tr.parameterList.parameters = params; | |
| tr.next = tret; | |
| //printf("strip %s\n <- %s\n", tr.toChars(), t.toChars()); | |
| return tr; | |
| } | |
| else if (auto tt = t.isTypeTuple()) | |
| { | |
| Parameters* args = stripParams(tt.arguments); | |
| if (args == tt.arguments) | |
| return t; | |
| TypeTuple tr = t.copy().isTypeTuple(); | |
| tr.arguments = args; | |
| return tr; | |
| } | |
| else if (t.ty == Tenum) | |
| { | |
| // TypeEnum::nextOf() may be != NULL, but it's not necessary here. | |
| return t; | |
| } | |
| else | |
| { | |
| Type tn = t.nextOf(); | |
| Type n = stripDefaultArgs(tn); | |
| if (n == tn) | |
| return t; | |
| TypeNext tr = cast(TypeNext)t.copy(); | |
| tr.next = n; | |
| return tr; | |
| } | |
| } | |
| /****************************************** | |
| * We've mistakenly parsed `t` as a type. | |
| * Redo `t` as an Expression only if there are no type modifiers. | |
| * Params: | |
| * t = mistaken type | |
| * Returns: | |
| * t redone as Expression, null if cannot | |
| */ | |
| Expression typeToExpression(Type t) | |
| { | |
| static Expression visitSArray(TypeSArray t) | |
| { | |
| if (auto e = t.next.typeToExpression()) | |
| return new ArrayExp(t.dim.loc, e, t.dim); | |
| return null; | |
| } | |
| static Expression visitAArray(TypeAArray t) | |
| { | |
| if (auto e = t.next.typeToExpression()) | |
| { | |
| if (auto ei = t.index.typeToExpression()) | |
| return new ArrayExp(t.loc, e, ei); | |
| } | |
| return null; | |
| } | |
| static Expression visitIdentifier(TypeIdentifier t) | |
| { | |
| return typeToExpressionHelper(t, new IdentifierExp(t.loc, t.ident)); | |
| } | |
| static Expression visitInstance(TypeInstance t) | |
| { | |
| return typeToExpressionHelper(t, new ScopeExp(t.loc, t.tempinst)); | |
| } | |
| // easy way to enable 'auto v = new int[mixin("exp")];' in 2.088+ | |
| static Expression visitMixin(TypeMixin t) | |
| { | |
| return new TypeExp(t.loc, t); | |
| } | |
| if (t.mod) | |
| return null; | |
| switch (t.ty) | |
| { | |
| case Tsarray: return visitSArray(t.isTypeSArray()); | |
| case Taarray: return visitAArray(t.isTypeAArray()); | |
| case Tident: return visitIdentifier(t.isTypeIdentifier()); | |
| case Tinstance: return visitInstance(t.isTypeInstance()); | |
| case Tmixin: return visitMixin(t.isTypeMixin()); | |
| default: return null; | |
| } | |
| } | |
| /* Helper function for `typeToExpression`. Contains common code | |
| * for TypeQualified derived classes. | |
| */ | |
| Expression typeToExpressionHelper(TypeQualified t, Expression e, size_t i = 0) | |
| { | |
| //printf("toExpressionHelper(e = %s %s)\n", EXPtoString(e.op).ptr, e.toChars()); | |
| foreach (id; t.idents[i .. t.idents.dim]) | |
| { | |
| //printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars()); | |
| final switch (id.dyncast()) | |
| { | |
| // ... '. ident' | |
| case DYNCAST.identifier: | |
| e = new DotIdExp(e.loc, e, cast(Identifier)id); | |
| break; | |
| // ... '. name!(tiargs)' | |
| case DYNCAST.dsymbol: | |
| auto ti = (cast(Dsymbol)id).isTemplateInstance(); | |
| assert(ti); | |
| e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs); | |
| break; | |
| // ... '[type]' | |
| case DYNCAST.type: // https://issues.dlang.org/show_bug.cgi?id=1215 | |
| e = new ArrayExp(t.loc, e, new TypeExp(t.loc, cast(Type)id)); | |
| break; | |
| // ... '[expr]' | |
| case DYNCAST.expression: // https://issues.dlang.org/show_bug.cgi?id=1215 | |
| e = new ArrayExp(t.loc, e, cast(Expression)id); | |
| break; | |
| case DYNCAST.object: | |
| case DYNCAST.tuple: | |
| case DYNCAST.parameter: | |
| case DYNCAST.statement: | |
| case DYNCAST.condition: | |
| case DYNCAST.templateparameter: | |
| case DYNCAST.initializer: | |
| assert(0); | |
| } | |
| } | |
| return e; | |
| } | |
| /****************************************** | |
| * Perform semantic analysis on a type. | |
| * Params: | |
| * type = Type AST node | |
| * loc = the location of the type | |
| * sc = context | |
| * Returns: | |
| * `Type` with completed semantic analysis, `Terror` if errors | |
| * were encountered | |
| */ | |
| extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) | |
| { | |
| static Type error() | |
| { | |
| return Type.terror; | |
| } | |
| Type visitType(Type t) | |
| { | |
| // @@@DEPRECATED_2.110@@@ | |
| // Use of `cent` and `ucent` has always been an error. | |
| // Starting from 2.100, recommend core.int128 as a replace for the | |
| // lack of compiler support. | |
| if (t.ty == Tint128 || t.ty == Tuns128) | |
| { | |
| .error(loc, "`cent` and `ucent` types are obsolete, use `core.int128.Cent` instead"); | |
| return error(); | |
| } | |
| return t.merge(); | |
| } | |
| Type visitVector(TypeVector mtype) | |
| { | |
| const errors = global.errors; | |
| mtype.basetype = mtype.basetype.typeSemantic(loc, sc); | |
| if (errors != global.errors) | |
| return error(); | |
| mtype.basetype = mtype.basetype.toBasetype().mutableOf(); | |
| if (mtype.basetype.ty != Tsarray) | |
| { | |
| .error(loc, "T in __vector(T) must be a static array, not `%s`", mtype.basetype.toChars()); | |
| return error(); | |
| } | |
| TypeSArray t = mtype.basetype.isTypeSArray(); | |
| const sz = cast(int)t.size(loc); | |
| final switch (target.isVectorTypeSupported(sz, t.nextOf())) | |
| { | |
| case 0: | |
| // valid | |
| break; | |
| case 1: | |
| // no support at all | |
| .error(loc, "SIMD vector types not supported on this platform"); | |
| return error(); | |
| case 2: | |
| // invalid base type | |
| .error(loc, "vector type `%s` is not supported on this platform", mtype.toChars()); | |
| return error(); | |
| case 3: | |
| // invalid size | |
| .error(loc, "%d byte vector type `%s` is not supported on this platform", sz, mtype.toChars()); | |
| return error(); | |
| } | |
| return merge(mtype); | |
| } | |
| Type visitSArray(TypeSArray mtype) | |
| { | |
| //printf("TypeSArray::semantic() %s\n", toChars()); | |
| Type t; | |
| Expression e; | |
| Dsymbol s; | |
| mtype.next.resolve(loc, sc, e, t, s); | |
| if (auto tup = s ? s.isTupleDeclaration() : null) | |
| { | |
| mtype.dim = semanticLength(sc, tup, mtype.dim); | |
| mtype.dim = mtype.dim.ctfeInterpret(); | |
| if (mtype.dim.op == EXP.error) | |
| return error(); | |
| uinteger_t d = mtype.dim.toUInteger(); | |
| if (d >= tup.objects.dim) | |
| { | |
| .error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tup.objects.dim); | |
| return error(); | |
| } | |
| RootObject o = (*tup.objects)[cast(size_t)d]; | |
| if (o.dyncast() != DYNCAST.type) | |
| { | |
| .error(loc, "`%s` is not a type", mtype.toChars()); | |
| return error(); | |
| } | |
| return (cast(Type)o).addMod(mtype.mod); | |
| } | |
| if (t && t.ty == Terror) | |
| return error(); | |
| Type tn = mtype.next.typeSemantic(loc, sc); | |
| if (tn.ty == Terror) | |
| return error(); | |
| Type tbn = tn.toBasetype(); | |
| if (mtype.dim) | |
| { | |
| auto errors = global.errors; | |
| mtype.dim = semanticLength(sc, tbn, mtype.dim); | |
| if (errors != global.errors) | |
| return error(); | |
| mtype.dim = mtype.dim.optimize(WANTvalue); | |
| mtype.dim = mtype.dim.ctfeInterpret(); | |
| if (mtype.dim.op == EXP.error) | |
| return error(); | |
| errors = global.errors; | |
| dinteger_t d1 = mtype.dim.toInteger(); | |
| if (errors != global.errors) | |
| return error(); | |
| mtype.dim = mtype.dim.implicitCastTo(sc, Type.tsize_t); | |
| mtype.dim = mtype.dim.optimize(WANTvalue); | |
| if (mtype.dim.op == EXP.error) | |
| return error(); | |
| errors = global.errors; | |
| dinteger_t d2 = mtype.dim.toInteger(); | |
| if (errors != global.errors) | |
| return error(); | |
| if (mtype.dim.op == EXP.error) | |
| return error(); | |
| Type overflowError() | |
| { | |
| .error(loc, "`%s` size %llu * %llu exceeds 0x%llx size limit for static array", | |
| mtype.toChars(), cast(ulong)tbn.size(loc), cast(ulong)d1, target.maxStaticDataSize); | |
| return error(); | |
| } | |
| if (d1 != d2) | |
| return overflowError(); | |
| Type tbx = tbn.baseElemOf(); | |
| if (tbx.ty == Tstruct && !tbx.isTypeStruct().sym.members || | |
| tbx.ty == Tenum && !tbx.isTypeEnum().sym.members) | |
| { | |
| /* To avoid meaningless error message, skip the total size limit check | |
| * when the bottom of element type is opaque. | |
| */ | |
| } | |
| else if (tbn.isTypeBasic() || | |
| tbn.ty == Tpointer || | |
| tbn.ty == Tarray || | |
| tbn.ty == Tsarray || | |
| tbn.ty == Taarray || | |
| (tbn.ty == Tstruct && tbn.isTypeStruct().sym.sizeok == Sizeok.done) || | |
| tbn.ty == Tclass) | |
| { | |
| /* Only do this for types that don't need to have semantic() | |
| * run on them for the size, since they may be forward referenced. | |
| */ | |
| bool overflow = false; | |
| if (mulu(tbn.size(loc), d2, overflow) >= target.maxStaticDataSize || overflow) | |
| return overflowError(); | |
| } | |
| } | |
| switch (tbn.ty) | |
| { | |
| case Ttuple: | |
| { | |
| // Index the tuple to get the type | |
| assert(mtype.dim); | |
| TypeTuple tt = tbn.isTypeTuple(); | |
| uinteger_t d = mtype.dim.toUInteger(); | |
| if (d >= tt.arguments.dim) | |
| { | |
| .error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tt.arguments.dim); | |
| return error(); | |
| } | |
| Type telem = (*tt.arguments)[cast(size_t)d].type; | |
| return telem.addMod(mtype.mod); | |
| } | |
| case Tfunction: | |
| case Tnone: | |
| .error(loc, "cannot have array of `%s`", tbn.toChars()); | |
| return error(); | |
| default: | |
| break; | |
| } | |
| if (tbn.isscope()) | |
| { | |
| .error(loc, "cannot have array of scope `%s`", tbn.toChars()); | |
| return error(); | |
| } | |
| /* Ensure things like const(immutable(T)[3]) become immutable(T[3]) | |
| * and const(T)[3] become const(T[3]) | |
| */ | |
| mtype.next = tn; | |
| mtype.transitive(); | |
| return mtype.addMod(tn.mod).merge(); | |
| } | |
| Type visitDArray(TypeDArray mtype) | |
| { | |
| Type tn = mtype.next.typeSemantic(loc, sc); | |
| Type tbn = tn.toBasetype(); | |
| switch (tbn.ty) | |
| { | |
| case Ttuple: | |
| return tbn; | |
| case Tfunction: | |
| case Tnone: | |
| .error(loc, "cannot have array of `%s`", tbn.toChars()); | |
| return error(); | |
| case Terror: | |
| return error(); | |
| default: | |
| break; | |
| } | |
| if (tn.isscope()) | |
| { | |
| .error(loc, "cannot have array of scope `%s`", tn.toChars()); | |
| return error(); | |
| } | |
| mtype.next = tn; | |
| mtype.transitive(); | |
| return merge(mtype); | |
| } | |
| Type visitAArray(TypeAArray mtype) | |
| { | |
| //printf("TypeAArray::semantic() %s index.ty = %d\n", mtype.toChars(), mtype.index.ty); | |
| if (mtype.deco) | |
| { | |
| return mtype; | |
| } | |
| mtype.loc = loc; | |
| if (sc) | |
| sc.setNoFree(); | |
| // Deal with the case where we thought the index was a type, but | |
| // in reality it was an expression. | |
| if (mtype.index.ty == Tident || mtype.index.ty == Tinstance || mtype.index.ty == Tsarray || mtype.index.ty == Ttypeof || mtype.index.ty == Treturn || mtype.index.ty == Tmixin) | |
| { | |
| Expression e; | |
| Type t; | |
| Dsymbol s; | |
| mtype.index.resolve(loc, sc, e, t, s); | |
| // https://issues.dlang.org/show_bug.cgi?id=15478 | |
| if (s) | |
| e = symbolToExp(s, loc, sc, false); | |
| if (e) | |
| { | |
| // It was an expression - | |
| // Rewrite as a static array | |
| auto tsa = new TypeSArray(mtype.next, e); | |
| return tsa.typeSemantic(loc, sc); | |
| } | |
| else if (t) | |
| mtype.index = t.typeSemantic(loc, sc); | |
| else | |
| { | |
| .error(loc, "index is not a type or an expression"); | |
| return error(); | |
| } | |
| } | |
| else | |
| mtype.index = mtype.index.typeSemantic(loc, sc); | |
| mtype.index = mtype.index.merge2(); | |
| if (mtype.index.nextOf() && !mtype.index.nextOf().isImmutable()) | |
| { | |
| mtype.index = mtype.index.constOf().mutableOf(); | |
| version (none) | |
| { | |
| printf("index is %p %s\n", mtype.index, mtype.index.toChars()); | |
| mtype.index.check(); | |
| printf("index.mod = x%x\n", mtype.index.mod); | |
| printf("index.ito = x%p\n", mtype.index.getMcache().ito); | |
| if (mtype.index.getMcache().ito) | |
| { | |
| printf("index.ito.mod = x%x\n", mtype.index.getMcache().ito.mod); | |
| printf("index.ito.ito = x%p\n", mtype.index.getMcache().ito.getMcache().ito); | |
| } | |
| } | |
| } | |
| switch (mtype.index.toBasetype().ty) | |
| { | |
| case Tfunction: | |
| case Tvoid: | |
| case Tnone: | |
| case Ttuple: | |
| .error(loc, "cannot have associative array key of `%s`", mtype.index.toBasetype().toChars()); | |
| goto case Terror; | |
| case Terror: | |
| return error(); | |
| default: | |
| break; | |
| } | |
| Type tbase = mtype.index.baseElemOf(); | |
| while (tbase.ty == Tarray) | |
| tbase = tbase.nextOf().baseElemOf(); | |
| if (auto ts = tbase.isTypeStruct()) | |
| { | |
| /* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up. | |
| */ | |
| StructDeclaration sd = ts.sym; | |
| if (sd.semanticRun < PASS.semanticdone) | |
| sd.dsymbolSemantic(null); | |
| // duplicate a part of StructDeclaration::semanticTypeInfoMembers | |
| //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash); | |
| if (sd.xeq && sd.xeq.isGenerated() && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done) | |
| { | |
| uint errors = global.startGagging(); | |
| sd.xeq.semantic3(sd.xeq._scope); | |
| if (global.endGagging(errors)) | |
| sd.xeq = sd.xerreq; | |
| } | |
| //printf("AA = %s, key: xeq = %p, xhash = %p\n", toChars(), sd.xeq, sd.xhash); | |
| const(char)* s = (mtype.index.toBasetype().ty != Tstruct) ? "bottom of " : ""; | |
| if (!sd.xeq) | |
| { | |
| // If sd.xhash != NULL: | |
| // sd or its fields have user-defined toHash. | |
| // AA assumes that its result is consistent with bitwise equality. | |
| // else: | |
| // bitwise equality & hashing | |
| } | |
| else if (sd.xeq == sd.xerreq) | |
| { | |
| if (search_function(sd, Id.eq)) | |
| { | |
| .error(loc, "%sAA key type `%s` does not have `bool opEquals(ref const %s) const`", s, sd.toChars(), sd.toChars()); | |
| } | |
| else | |
| { | |
| .error(loc, "%sAA key type `%s` does not support const equality", s, sd.toChars()); | |
| } | |
| return error(); | |
| } | |
| else if (!sd.xhash) | |
| { | |
| if (search_function(sd, Id.eq)) | |
| { | |
| .error(loc, "%sAA key type `%s` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined", s, sd.toChars()); | |
| } | |
| else | |
| { | |
| .error(loc, "%sAA key type `%s` supports const equality but doesn't support const hashing", s, sd.toChars()); | |
| } | |
| return error(); | |
| } | |
| else | |
| { | |
| // defined equality & hashing | |
| assert(sd.xeq && sd.xhash); | |
| /* xeq and xhash may be implicitly defined by compiler. For example: | |
| * struct S { int[] arr; } | |
| * With 'arr' field equality and hashing, compiler will implicitly | |
| * generate functions for xopEquals and xtoHash in TypeInfo_Struct. | |
| */ | |
| } | |
| } | |
| else if (tbase.ty == Tclass && !tbase.isTypeClass().sym.isInterfaceDeclaration()) | |
| { | |
| ClassDeclaration cd = tbase.isTypeClass().sym; | |
| if (cd.semanticRun < PASS.semanticdone) | |
| cd.dsymbolSemantic(null); | |
| if (!ClassDeclaration.object) | |
| { | |
| .error(Loc.initial, "missing or corrupt object.d"); | |
| fatal(); | |
| } | |
| __gshared FuncDeclaration feq = null; | |
| __gshared FuncDeclaration fcmp = null; | |
| __gshared FuncDeclaration fhash = null; | |
| if (!feq) | |
| feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration(); | |
| if (!fcmp) | |
| fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration(); | |
| if (!fhash) | |
| fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration(); | |
| assert(fcmp && feq && fhash); | |
| if (feq.vtblIndex < cd.vtbl.dim && cd.vtbl[feq.vtblIndex] == feq) | |
| { | |
| version (all) | |
| { | |
| if (fcmp.vtblIndex < cd.vtbl.dim && cd.vtbl[fcmp.vtblIndex] != fcmp) | |
| { | |
| const(char)* s = (mtype.index.toBasetype().ty != Tclass) ? "bottom of " : ""; | |
| .error(loc, "%sAA key type `%s` now requires equality rather than comparison", s, cd.toChars()); | |
| errorSupplemental(loc, "Please override `Object.opEquals` and `Object.toHash`."); | |
| } | |
| } | |
| } | |
| } | |
| mtype.next = mtype.next.typeSemantic(loc, sc).merge2(); | |
| mtype.transitive(); | |
| switch (mtype.next.toBasetype().ty) | |
| { | |
| case Tfunction: | |
| case Tvoid: | |
| case Tnone: | |
| case Ttuple: | |
| .error(loc, "cannot have associative array of `%s`", mtype.next.toChars()); | |
| goto case Terror; | |
| case Terror: | |
| return error(); | |
| default: | |
| break; | |
| } | |
| if (mtype.next.isscope()) | |
| { | |
| .error(loc, "cannot have array of scope `%s`", mtype.next.toChars()); | |
| return error(); | |
| } | |
| return merge(mtype); | |
| } | |
| Type visitPointer(TypePointer mtype) | |
| { | |
| //printf("TypePointer::semantic() %s\n", toChars()); | |
| if (mtype.deco) | |
| { | |
| return mtype; | |
| } | |
| Type n = mtype.next.typeSemantic(loc, sc); | |
| switch (n.toBasetype().ty) | |
| { | |
| case Ttuple: | |
| .error(loc, "cannot have pointer to `%s`", n.toChars()); | |
| goto case Terror; | |
| case Terror: | |
| return error(); | |
| default: | |
| break; | |
| } | |
| if (n != mtype.next) | |
| { | |
| mtype.deco = null; | |
| } | |
| mtype.next = n; | |
| if (mtype.next.ty != Tfunction) | |
| { | |
| mtype.transitive(); | |
| return merge(mtype); | |
| } | |
| version (none) | |
| { | |
| return merge(mtype); | |
| } | |
| else | |
| { | |
| mtype.deco = merge(mtype).deco; | |
| /* Don't return merge(), because arg identifiers and default args | |
| * can be different | |
| * even though the types match | |
| */ | |
| return mtype; | |
| } | |
| } | |
| Type visitReference(TypeReference mtype) | |
| { | |
| //printf("TypeReference::semantic()\n"); | |
| Type n = mtype.next.typeSemantic(loc, sc); | |
| if (n != mtype.next) | |
| mtype.deco = null; | |
| mtype.next = n; | |
| mtype.transitive(); | |
| return merge(mtype); | |
| } | |
| Type visitFunction(TypeFunction mtype) | |
| { | |
| if (mtype.deco) // if semantic() already run | |
| { | |
| //printf("already done\n"); | |
| return mtype; | |
| } | |
| //printf("TypeFunction::semantic() this = %p\n", this); | |
| //printf("TypeFunction::semantic() %s, sc.stc = %llx\n", mtype.toChars(), sc.stc); | |
| bool errors = false; | |
| if (mtype.inuse > global.recursionLimit) | |
| { | |
| mtype.inuse = 0; | |
| .error(loc, "recursive type"); | |
| return error(); | |
| } | |
| /* Copy in order to not mess up original. | |
| * This can produce redundant copies if inferring return type, | |
| * as semantic() will get called again on this. | |
| */ | |
| TypeFunction tf = mtype.copy().toTypeFunction(); | |
| if (mtype.parameterList.parameters) | |
| { | |
| tf.parameterList.parameters = mtype.parameterList.parameters.copy(); | |
| for (size_t i = 0; i < mtype.parameterList.parameters.dim; i++) | |
| { | |
| Parameter p = cast(Parameter)mem.xmalloc(__traits(classInstanceSize, Parameter)); | |
| memcpy(cast(void*)p, cast(void*)(*mtype.parameterList.parameters)[i], __traits(classInstanceSize, Parameter)); | |
| (*tf.parameterList.parameters)[i] = p; | |
| } | |
| } | |
| if (sc.stc & STC.pure_) | |
| tf.purity = PURE.fwdref; | |
| if (sc.stc & STC.nothrow_) | |
| tf.isnothrow = true; | |
| if (sc.stc & STC.nogc) | |
| tf.isnogc = true; | |
| if (sc.stc & STC.ref_) | |
| tf.isref = true; | |
| if (sc.stc & STC.return_) | |
| tf.isreturn = true; | |
| if (sc.stc & STC.returnScope) | |
| tf.isreturnscope = true; | |
| if (sc.stc & STC.returninferred) | |
| tf.isreturninferred = true; | |
| if (sc.stc & STC.scope_) | |
| tf.isScopeQual = true; | |
| if (sc.stc & STC.scopeinferred) | |
| tf.isscopeinferred = true; | |
| // if (tf.isreturn && !tf.isref) | |
| // tf.isScopeQual = true; // return by itself means 'return scope' | |
| if (tf.trust == TRUST.default_) | |
| { | |
| if (sc.stc & STC.safe) | |
| tf.trust = TRUST.safe; | |
| else if (sc.stc & STC.system) | |
| tf.trust = TRUST.system; | |
| else if (sc.stc & STC.trusted) | |
| tf.trust = TRUST.trusted; | |
| } | |
| if (sc.stc & STC.property) | |
| tf.isproperty = true; | |
| if (sc.stc & STC.live) | |
| tf.islive = true; | |
| tf.linkage = sc.linkage; | |
| if (tf.linkage == LINK.system) | |
| tf.linkage = target.systemLinkage(); | |
| version (none) | |
| { | |
| /* If the parent is @safe, then this function defaults to safe | |
| * too. | |
| * If the parent's @safe-ty is inferred, then this function's @safe-ty needs | |
| * to be inferred first. | |
| */ | |
| if (tf.trust == TRUST.default_) | |
| for (Dsymbol p = sc.func; p; p = p.toParent2()) | |
| { | |
| FuncDeclaration fd = p.isFuncDeclaration(); | |
| if (fd) | |
| { | |
| if (fd.isSafeBypassingInference()) | |
| tf.trust = TRUST.safe; // default to @safe | |
| break; | |
| } | |
| } | |
| } | |
| bool wildreturn = false; | |
| if (tf.next) | |
| { | |
| sc = sc.push(); | |
| sc.stc &= ~(STC.TYPECTOR | STC.FUNCATTR); | |
| tf.next = tf.next.typeSemantic(loc, sc); | |
| sc = sc.pop(); | |
| errors |= tf.checkRetType(loc); | |
| if (tf.next.isscope() && !tf.isctor) | |
| { | |
| .error(loc, "functions cannot return `scope %s`", tf.next.toChars()); | |
| errors = true; | |
| } | |
| if (tf.next.hasWild()) | |
| wildreturn = true; | |
| if (tf.isreturn && !tf.isref && !tf.next.hasPointers()) | |
| { | |
| tf.isreturn = false; | |
| } | |
| } | |
| /// Perform semantic on the default argument to a parameter | |
| /// Modify the `defaultArg` field of `fparam`, which must not be `null` | |
| /// Returns `false` whether an error was encountered. | |
| static bool defaultArgSemantic (ref Parameter fparam, Scope* sc) | |
| { | |
| Expression e = fparam.defaultArg; | |
| const isRefOrOut = fparam.isReference(); | |
| const isAuto = fparam.storageClass & (STC.auto_ | STC.autoref); | |
| if (isRefOrOut && !isAuto) | |
| { | |
| e = e.expressionSemantic(sc); | |
| e = resolveProperties(sc, e); | |
| } | |
| else | |
| { | |
| e = inferType(e, fparam.type); | |
| Initializer iz = new ExpInitializer(e.loc, e); | |
| iz = iz.initializerSemantic(sc, fparam.type, INITnointerpret); | |
| e = iz.initializerToExpression(); | |
| } | |
| if (e.op == EXP.function_) // https://issues.dlang.org/show_bug.cgi?id=4820 | |
| { | |
| FuncExp fe = e.isFuncExp(); | |
| // Replace function literal with a function symbol, | |
| // since default arg expression must be copied when used | |
| // and copying the literal itself is wrong. | |
| e = new VarExp(e.loc, fe.fd, false); | |
| e = new AddrExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| } | |
| if (isRefOrOut && (!isAuto || e.isLvalue()) | |
| && !MODimplicitConv(e.type.mod, fparam.type.mod)) | |
| { | |
| const(char)* errTxt = fparam.storageClass & STC.ref_ ? "ref" : "out"; | |
| .error(e.loc, "expression `%s` of type `%s` is not implicitly convertible to type `%s %s` of parameter `%s`", | |
| e.toChars(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars()); | |
| } | |
| e = e.implicitCastTo(sc, fparam.type); | |
| // default arg must be an lvalue | |
| if (isRefOrOut && !isAuto && | |
| !(global.params.previewIn && (fparam.storageClass & STC.in_)) && | |
| global.params.rvalueRefParam != FeatureState.enabled) | |
| e = e.toLvalue(sc, e); | |
| fparam.defaultArg = e; | |
| return (e.op != EXP.error); | |
| } | |
| ubyte wildparams = 0; | |
| if (tf.parameterList.parameters) | |
| { | |
| /* Create a scope for evaluating the default arguments for the parameters | |
| */ | |
| Scope* argsc = sc.push(); | |
| argsc.stc = 0; // don't inherit storage class | |
| argsc.visibility = Visibility(Visibility.Kind.public_); | |
| argsc.func = null; | |
| size_t dim = tf.parameterList.length; | |
| for (size_t i = 0; i < dim; i++) | |
| { | |
| Parameter fparam = tf.parameterList[i]; | |
| fparam.storageClass |= STC.parameter; | |
| mtype.inuse++; | |
| fparam.type = fparam.type.typeSemantic(loc, argsc); | |
| mtype.inuse--; | |
| if (fparam.type.ty == Terror) | |
| { | |
| errors = true; | |
| continue; | |
| } | |
| fparam.type = fparam.type.addStorageClass(fparam.storageClass); | |
| if (fparam.storageClass & (STC.auto_ | STC.alias_ | STC.static_)) | |
| { | |
| if (!fparam.type) | |
| continue; | |
| } | |
| fparam.type = fparam.type.cAdjustParamType(sc); // adjust C array and function parameter types | |
| Type t = fparam.type.toBasetype(); | |
| /* If fparam after semantic() turns out to be a tuple, the number of parameters may | |
| * change. | |
| */ | |
| if (auto tt = t.isTypeTuple()) | |
| { | |
| /* TypeFunction::parameter also is used as the storage of | |
| * Parameter objects for FuncDeclaration. So we should copy | |
| * the elements of TypeTuple::arguments to avoid unintended | |
| * sharing of Parameter object among other functions. | |
| */ | |
| if (tt.arguments && tt.arguments.dim) | |
| { | |
| /* Propagate additional storage class from tuple parameters to their | |
| * element-parameters. | |
| * Make a copy, as original may be referenced elsewhere. | |
| */ | |
| size_t tdim = tt.arguments.dim; | |
| auto newparams = new Parameters(tdim); | |
| for (size_t j = 0; j < tdim; j++) | |
| { | |
| Parameter narg = (*tt.arguments)[j]; | |
| // https://issues.dlang.org/show_bug.cgi?id=12744 | |
| // If the storage classes of narg | |
| // conflict with the ones in fparam, it's ignored. | |
| StorageClass stc = fparam.storageClass | narg.storageClass; | |
| StorageClass stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); | |
| StorageClass stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); | |
| if (stc1 && stc2 && stc1 != stc2) | |
| { | |
| OutBuffer buf1; stcToBuffer(&buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : 0)); | |
| OutBuffer buf2; stcToBuffer(&buf2, stc2); | |
| .error(loc, "incompatible parameter storage classes `%s` and `%s`", | |
| buf1.peekChars(), buf2.peekChars()); | |
| errors = true; | |
| stc = stc1 | (stc & ~(STC.ref_ | STC.out_ | STC.lazy_)); | |
| } | |
| (*newparams)[j] = new Parameter( | |
| stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl); | |
| } | |
| fparam.type = new TypeTuple(newparams); | |
| fparam.type = fparam.type.typeSemantic(loc, argsc); | |
| } | |
| fparam.storageClass = STC.parameter; | |
| /* Reset number of parameters, and back up one to do this fparam again, | |
| * now that it is a tuple | |
| */ | |
| dim = tf.parameterList.length; | |
| i--; | |
| continue; | |
| } | |
| if (t.ty == Tfunction) | |
| { | |
| .error(loc, "cannot have parameter of function type `%s`", fparam.type.toChars()); | |
| errors = true; | |
| } | |
| else if (!fparam.isReference() && | |
| (t.ty == Tstruct || t.ty == Tsarray || t.ty == Tenum)) | |
| { | |
| Type tb2 = t.baseElemOf(); | |
| if (tb2.ty == Tstruct && !tb2.isTypeStruct().sym.members || | |
| tb2.ty == Tenum && !tb2.isTypeEnum().sym.memtype) | |
| { | |
| if (global.params.previewIn && (fparam.storageClass & STC.in_)) | |
| { | |
| .error(loc, "cannot infer `ref` for `in` parameter `%s` of opaque type `%s`", | |
| fparam.toChars(), fparam.type.toChars()); | |
| } | |
| else | |
| .error(loc, "cannot have parameter of opaque type `%s` by value", | |
| fparam.type.toChars()); | |
| errors = true; | |
| } | |
| } | |
| else if (!(fparam.storageClass & STC.lazy_) && t.ty == Tvoid) | |
| { | |
| .error(loc, "cannot have parameter of type `%s`", fparam.type.toChars()); | |
| errors = true; | |
| } | |
| if (fparam.storageClass & STC.return_) | |
| { | |
| if (fparam.isReference()) | |
| { | |
| // Disabled for the moment awaiting improvement to allow return by ref | |
| // to be transformed into return by scope. | |
| if (0 && !tf.isref) | |
| { | |
| auto stc = fparam.storageClass & (STC.ref_ | STC.out_); | |
| .error(loc, "parameter `%s` is `return %s` but function does not return by `ref`", | |
| fparam.ident ? fparam.ident.toChars() : "", | |
| stcToString(stc).ptr); | |
| errors = true; | |
| } | |
| } | |
| else | |
| { | |
| if (!(fparam.storageClass & STC.scope_)) | |
| fparam.storageClass |= STC.scope_ | STC.scopeinferred; // 'return' implies 'scope' | |
| if (tf.isref) | |
| { | |
| } | |
| else if (tf.next && !tf.next.hasPointers() && tf.next.toBasetype().ty != Tvoid) | |
| { | |
| fparam.storageClass &= ~STC.return_; // https://issues.dlang.org/show_bug.cgi?id=18963 | |
| } | |
| } | |
| if (i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe && | |
| (t.isTypeDArray() || t.isTypeClass())) | |
| { | |
| /* This is because they can be constructed on the stack | |
| * https://dlang.org/spec/function.html#typesafe_variadic_functions | |
| */ | |
| .error(loc, "typesafe variadic function parameter `%s` of type `%s` cannot be marked `return`", | |
| fparam.ident ? fparam.ident.toChars() : "", t.toChars()); | |
| errors = true; | |
| } | |
| } | |
| if (fparam.storageClass & STC.out_) | |
| { | |
| if (ubyte m = fparam.type.mod & (MODFlags.immutable_ | MODFlags.const_ | MODFlags.wild)) | |
| { | |
| .error(loc, "cannot have `%s out` parameter of type `%s`", MODtoChars(m), t.toChars()); | |
| errors = true; | |
| } | |
| else | |
| { | |
| Type tv = t.baseElemOf(); | |
| if (tv.ty == Tstruct && tv.isTypeStruct().sym.noDefaultCtor) | |
| { | |
| .error(loc, "cannot have `out` parameter of type `%s` because the default construction is disabled", fparam.type.toChars()); | |
| errors = true; | |
| } | |
| } | |
| } | |
| if (t.hasWild()) | |
| { | |
| wildparams |= 1; | |
| //if (tf.next && !wildreturn) | |
| // error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)"); | |
| } | |
| /* Scope attribute is not necessary if the parameter type does not have pointers | |
| */ | |
| const sr = buildScopeRef(fparam.storageClass); | |
| switch (sr) | |
| { | |
| case ScopeRef.Scope: | |
| case ScopeRef.RefScope: | |
| case ScopeRef.ReturnRef_Scope: | |
| if (!fparam.type.hasPointers()) | |
| fparam.storageClass &= ~STC.scope_; | |
| break; | |
| case ScopeRef.ReturnScope: | |
| case ScopeRef.Ref_ReturnScope: | |
| if (!fparam.type.hasPointers()) | |
| fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope); | |
| break; | |
| default: | |
| break; | |
| } | |
| // Remove redundant storage classes for type, they are already applied | |
| fparam.storageClass &= ~(STC.TYPECTOR); | |
| // -preview=in: add `ref` storage class to suited `in` params | |
| if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) | |
| { | |
| auto ts = t.baseElemOf().isTypeStruct(); | |
| const isPOD = !ts || ts.sym.isPOD(); | |
| if (!isPOD || target.preferPassByRef(t)) | |
| fparam.storageClass |= STC.ref_; | |
| } | |
| } | |
| // Now that we completed semantic for the argument types, | |
| // run semantic on their default values, | |
| // bearing in mind tuples have been expanded. | |
| // We need to keep a pair of [oidx, eidx] (original index, | |
| // extended index), as we need to run semantic when `oidx` changes. | |
| size_t tupleOrigIdx = size_t.max; | |
| size_t tupleExtIdx = size_t.max; | |
| foreach (oidx, oparam, eidx, eparam; tf.parameterList) | |
| { | |
| // oparam (original param) will always have the default arg | |
| // if there's one, but `eparam` will not if it's an expanded | |
| // tuple. When we see an expanded tuple, we need to save its | |
| // position to get the offset in it later on. | |
| if (oparam.defaultArg) | |
| { | |
| // Get the obvious case out of the way | |
| if (oparam is eparam) | |
| errors |= !defaultArgSemantic(eparam, argsc); | |
| // We're seeing a new tuple | |
| else if (tupleOrigIdx == size_t.max || tupleOrigIdx < oidx) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=18572 | |
| * | |
| * If a tuple parameter has a default argument, when expanding the parameter | |
| * tuple the default argument tuple must also be expanded. | |
| */ | |
| tupleOrigIdx = oidx; | |
| tupleExtIdx = eidx; | |
| errors |= !defaultArgSemantic(oparam, argsc); | |
| TupleExp te = oparam.defaultArg.isTupleExp(); | |
| if (te && te.exps && te.exps.length) | |
| eparam.defaultArg = (*te.exps)[0]; | |
| } | |
| // Processing an already-seen tuple | |
| else | |
| { | |
| TupleExp te = oparam.defaultArg.isTupleExp(); | |
| if (te && te.exps && te.exps.length) | |
| eparam.defaultArg = (*te.exps)[eidx - tupleExtIdx]; | |
| } | |
| } | |
| // We need to know the default argument to resolve `auto ref`, | |
| // hence why this has to take place as the very last step. | |
| /* Resolve "auto ref" storage class to be either ref or value, | |
| * based on the argument matching the parameter | |
| */ | |
| if (eparam.storageClass & STC.auto_) | |
| { | |
| Expression farg = mtype.fargs && eidx < mtype.fargs.dim ? | |
| (*mtype.fargs)[eidx] : eparam.defaultArg; | |
| if (farg && (eparam.storageClass & STC.ref_)) | |
| { | |
| if (!farg.isLvalue()) | |
| eparam.storageClass &= ~STC.ref_; // value parameter | |
| eparam.storageClass &= ~STC.auto_; // https://issues.dlang.org/show_bug.cgi?id=14656 | |
| eparam.storageClass |= STC.autoref; | |
| } | |
| else if (mtype.incomplete && (eparam.storageClass & STC.ref_)) | |
| { | |
| // the default argument may have been temporarily removed, | |
| // see usage of `TypeFunction.incomplete`. | |
| // https://issues.dlang.org/show_bug.cgi?id=19891 | |
| eparam.storageClass &= ~STC.auto_; | |
| eparam.storageClass |= STC.autoref; | |
| } | |
| else | |
| { | |
| .error(loc, "`auto` can only be used as part of `auto ref` for template function parameters"); | |
| errors = true; | |
| } | |
| } | |
| } | |
| argsc.pop(); | |
| } | |
| if (tf.isWild()) | |
| wildparams |= 2; | |
| if (wildreturn && !wildparams) | |
| { | |
| .error(loc, "`inout` on `return` means `inout` must be on a parameter as well for `%s`", mtype.toChars()); | |
| errors = true; | |
| } | |
| tf.isInOutParam = (wildparams & 1) != 0; | |
| tf.isInOutQual = (wildparams & 2) != 0; | |
| if (tf.isproperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2)) | |
| { | |
| .error(loc, "properties can only have zero, one, or two parameter"); | |
| errors = true; | |
| } | |
| if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 && | |
| !(sc.flags & SCOPE.Cfile)) | |
| { | |
| .error(loc, "variadic functions with non-D linkage must have at least one parameter"); | |
| errors = true; | |
| } | |
| if (errors) | |
| return error(); | |
| if (tf.next) | |
| tf.deco = tf.merge().deco; | |
| /* Don't return merge(), because arg identifiers and default args | |
| * can be different | |
| * even though the types match | |
| */ | |
| return tf; | |
| } | |
| Type visitDelegate(TypeDelegate mtype) | |
| { | |
| //printf("TypeDelegate::semantic() %s\n", mtype.toChars()); | |
| if (mtype.deco) // if semantic() already run | |
| { | |
| //printf("already done\n"); | |
| return mtype; | |
| } | |
| mtype.next = mtype.next.typeSemantic(loc, sc); | |
| if (mtype.next.ty != Tfunction) | |
| return error(); | |
| /* In order to deal with https://issues.dlang.org/show_bug.cgi?id=4028 | |
| * perhaps default arguments should | |
| * be removed from next before the merge. | |
| */ | |
| version (none) | |
| { | |
| return mtype.merge(); | |
| } | |
| else | |
| { | |
| /* Don't return merge(), because arg identifiers and default args | |
| * can be different | |
| * even though the types match | |
| */ | |
| mtype.deco = mtype.merge().deco; | |
| return mtype; | |
| } | |
| } | |
| Type visitIdentifier(TypeIdentifier mtype) | |
| { | |
| Type t; | |
| Expression e; | |
| Dsymbol s; | |
| //printf("TypeIdentifier::semantic(%s)\n", mtype.toChars()); | |
| mtype.resolve(loc, sc, e, t, s); | |
| if (t) | |
| { | |
| //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco); | |
| return t.addMod(mtype.mod); | |
| } | |
| else | |
| { | |
| if (s) | |
| { | |
| auto td = s.isTemplateDeclaration; | |
| if (td && td.onemember && td.onemember.isAggregateDeclaration) | |
| .error(loc, "template %s `%s` is used as a type without instantiation" | |
| ~ "; to instantiate it use `%s!(arguments)`", | |
| s.kind, s.toPrettyChars, s.ident.toChars); | |
| else | |
| .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); | |
| //assert(0); | |
| } | |
| else if (e.op == EXP.variable) // special case: variable is used as a type | |
| { | |
| /* | |
| N.B. This branch currently triggers for the following code | |
| template test(x* x) | |
| { | |
| } | |
| i.e. the compiler prints "variable x is used as a type" | |
| which isn't a particularly good error message (x is a variable?). | |
| */ | |
| Dsymbol varDecl = mtype.toDsymbol(sc); | |
| const(Loc) varDeclLoc = varDecl.getLoc(); | |
| Module varDeclModule = varDecl.getModule(); //This can be null | |
| .error(loc, "variable `%s` is used as a type", mtype.toChars()); | |
| //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 | |
| if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported | |
| { | |
| const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc(); | |
| .errorSupplemental( | |
| varDeclModuleImportLoc, | |
| "variable `%s` is imported here from: `%s`", | |
| varDecl.toChars, | |
| varDeclModule.toPrettyChars, | |
| ); | |
| } | |
| .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars); | |
| } | |
| else | |
| .error(loc, "`%s` is used as a type", mtype.toChars()); | |
| return error(); | |
| } | |
| } | |
| Type visitInstance(TypeInstance mtype) | |
| { | |
| Type t; | |
| Expression e; | |
| Dsymbol s; | |
| //printf("TypeInstance::semantic(%p, %s)\n", this, toChars()); | |
| { | |
| const errors = global.errors; | |
| mtype.resolve(loc, sc, e, t, s); | |
| // if we had an error evaluating the symbol, suppress further errors | |
| if (!t && errors != global.errors) | |
| return error(); | |
| } | |
| if (!t) | |
| { | |
| if (!e && s && s.errors) | |
| { | |
| // if there was an error evaluating the symbol, it might actually | |
| // be a type. Avoid misleading error messages. | |
| .error(loc, "`%s` had previous errors", mtype.toChars()); | |
| } | |
| else | |
| .error(loc, "`%s` is used as a type", mtype.toChars()); | |
| return error(); | |
| } | |
| return t; | |
| } | |
| Type visitTypeof(TypeTypeof mtype) | |
| { | |
| //printf("TypeTypeof::semantic() %s\n", mtype.toChars()); | |
| Expression e; | |
| Type t; | |
| Dsymbol s; | |
| mtype.resolve(loc, sc, e, t, s); | |
| if (s && (t = s.getType()) !is null) | |
| t = t.addMod(mtype.mod); | |
| if (!t) | |
| { | |
| .error(loc, "`%s` is used as a type", mtype.toChars()); | |
| return error(); | |
| } | |
| return t; | |
| } | |
| Type visitTraits(TypeTraits mtype) | |
| { | |
| if (mtype.ty == Terror) | |
| return mtype; | |
| const inAlias = (sc.flags & SCOPE.alias_) != 0; | |
| if (mtype.exp.ident != Id.allMembers && | |
| mtype.exp.ident != Id.derivedMembers && | |
| mtype.exp.ident != Id.getMember && | |
| mtype.exp.ident != Id.parent && | |
| mtype.exp.ident != Id.parameters && | |
| mtype.exp.ident != Id.child && | |
| mtype.exp.ident != Id.toType && | |
| mtype.exp.ident != Id.getOverloads && | |
| mtype.exp.ident != Id.getVirtualFunctions && | |
| mtype.exp.ident != Id.getVirtualMethods && | |
| mtype.exp.ident != Id.getAttributes && | |
| mtype.exp.ident != Id.getUnitTests && | |
| mtype.exp.ident != Id.getAliasThis) | |
| { | |
| static immutable (const(char)*)[2] ctxt = ["as type", "in alias"]; | |
| .error(mtype.loc, "trait `%s` is either invalid or not supported %s", | |
| mtype.exp.ident.toChars, ctxt[inAlias]); | |
| mtype.ty = Terror; | |
| return mtype; | |
| } | |
| import dmd.traits : semanticTraits; | |
| Type result; | |
| if (Expression e = semanticTraits(mtype.exp, sc)) | |
| { | |
| switch (e.op) | |
| { | |
| case EXP.dotVariable: | |
| mtype.sym = e.isDotVarExp().var; | |
| break; | |
| case EXP.variable: | |
| mtype.sym = e.isVarExp().var; | |
| break; | |
| case EXP.function_: | |
| auto fe = e.isFuncExp(); | |
| mtype.sym = fe.td ? fe.td : fe.fd; | |
| break; | |
| case EXP.dotTemplateDeclaration: | |
| mtype.sym = e.isDotTemplateExp().td; | |
| break; | |
| case EXP.dSymbol: | |
| mtype.sym = e.isDsymbolExp().s; | |
| break; | |
| case EXP.template_: | |
| mtype.sym = e.isTemplateExp().td; | |
| break; | |
| case EXP.scope_: | |
| mtype.sym = e.isScopeExp().sds; | |
| break; | |
| case EXP.tuple: | |
| TupleExp te = e.isTupleExp(); | |
| Objects* elems = new Objects(te.exps.dim); | |
| foreach (i; 0 .. elems.dim) | |
| { | |
| auto src = (*te.exps)[i]; | |
| switch (src.op) | |
| { | |
| case EXP.type: | |
| (*elems)[i] = src.isTypeExp().type; | |
| break; | |
| case EXP.dotType: | |
| (*elems)[i] = src.isDotTypeExp().sym.isType(); | |
| break; | |
| case EXP.overloadSet: | |
| (*elems)[i] = src.isOverExp().type; | |
| break; | |
| default: | |
| if (auto sym = isDsymbol(src)) | |
| (*elems)[i] = sym; | |
| else | |
| (*elems)[i] = src; | |
| } | |
| } | |
| TupleDeclaration td = new TupleDeclaration(e.loc, Identifier.generateId("__aliastup"), elems); | |
| mtype.sym = td; | |
| break; | |
| case EXP.dotType: | |
| result = e.isDotTypeExp().sym.isType(); | |
| break; | |
| case EXP.type: | |
| result = e.isTypeExp().type; | |
| break; | |
| case EXP.overloadSet: | |
| result = e.isOverExp().type; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (result) | |
| result = result.addMod(mtype.mod); | |
| if (!inAlias && !result) | |
| { | |
| if (!global.errors) | |
| .error(mtype.loc, "`%s` does not give a valid type", mtype.toChars); | |
| return error(); | |
| } | |
| return result; | |
| } | |
| Type visitReturn(TypeReturn mtype) | |
| { | |
| //printf("TypeReturn::semantic() %s\n", toChars()); | |
| Expression e; | |
| Type t; | |
| Dsymbol s; | |
| mtype.resolve(loc, sc, e, t, s); | |
| if (s && (t = s.getType()) !is null) | |
| t = t.addMod(mtype.mod); | |
| if (!t) | |
| { | |
| .error(loc, "`%s` is used as a type", mtype.toChars()); | |
| return error(); | |
| } | |
| return t; | |
| } | |
| Type visitStruct(TypeStruct mtype) | |
| { | |
| //printf("TypeStruct::semantic('%s')\n", mtype.toChars()); | |
| if (mtype.deco) | |
| return mtype; | |
| /* Don't semantic for sym because it should be deferred until | |
| * sizeof needed or its members accessed. | |
| */ | |
| // instead, parent should be set correctly | |
| assert(mtype.sym.parent); | |
| if (mtype.sym.type.ty == Terror) | |
| return error(); | |
| return merge(mtype); | |
| } | |
| Type visitEnum(TypeEnum mtype) | |
| { | |
| //printf("TypeEnum::semantic() %s\n", toChars()); | |
| return mtype.deco ? mtype : merge(mtype); | |
| } | |
| Type visitClass(TypeClass mtype) | |
| { | |
| //printf("TypeClass::semantic(%s)\n", mtype.toChars()); | |
| if (mtype.deco) | |
| return mtype; | |
| /* Don't semantic for sym because it should be deferred until | |
| * sizeof needed or its members accessed. | |
| */ | |
| // instead, parent should be set correctly | |
| assert(mtype.sym.parent); | |
| if (mtype.sym.type.ty == Terror) | |
| return error(); | |
| return merge(mtype); | |
| } | |
| Type visitTuple(TypeTuple mtype) | |
| { | |
| //printf("TypeTuple::semantic(this = %p)\n", this); | |
| //printf("TypeTuple::semantic() %p, %s\n", this, toChars()); | |
| if (!mtype.deco) | |
| mtype.deco = merge(mtype).deco; | |
| /* Don't return merge(), because a tuple with one type has the | |
| * same deco as that type. | |
| */ | |
| return mtype; | |
| } | |
| Type visitSlice(TypeSlice mtype) | |
| { | |
| //printf("TypeSlice::semantic() %s\n", toChars()); | |
| Type tn = mtype.next.typeSemantic(loc, sc); | |
| //printf("next: %s\n", tn.toChars()); | |
| Type tbn = tn.toBasetype(); | |
| if (tbn.ty != Ttuple) | |
| { | |
| .error(loc, "can only slice tuple types, not `%s`", tbn.toChars()); | |
| return error(); | |
| } | |
| TypeTuple tt = cast(TypeTuple)tbn; | |
| mtype.lwr = semanticLength(sc, tbn, mtype.lwr); | |
| mtype.upr = semanticLength(sc, tbn, mtype.upr); | |
| mtype.lwr = mtype.lwr.ctfeInterpret(); | |
| mtype.upr = mtype.upr.ctfeInterpret(); | |
| if (mtype.lwr.op == EXP.error || mtype.upr.op == EXP.error) | |
| return error(); | |
| uinteger_t i1 = mtype.lwr.toUInteger(); | |
| uinteger_t i2 = mtype.upr.toUInteger(); | |
| if (!(i1 <= i2 && i2 <= tt.arguments.dim)) | |
| { | |
| .error(loc, "slice `[%llu..%llu]` is out of range of `[0..%llu]`", | |
| cast(ulong)i1, cast(ulong)i2, cast(ulong)tt.arguments.dim); | |
| return error(); | |
| } | |
| mtype.next = tn; | |
| mtype.transitive(); | |
| auto args = new Parameters(); | |
| args.reserve(cast(size_t)(i2 - i1)); | |
| foreach (arg; (*tt.arguments)[cast(size_t)i1 .. cast(size_t)i2]) | |
| { | |
| args.push(arg); | |
| } | |
| Type t = new TypeTuple(args); | |
| return t.typeSemantic(loc, sc); | |
| } | |
| Type visitMixin(TypeMixin mtype) | |
| { | |
| //printf("TypeMixin::semantic() %s\n", toChars()); | |
| Expression e; | |
| Type t; | |
| Dsymbol s; | |
| mtype.resolve(loc, sc, e, t, s); | |
| if (t && t.ty != Terror) | |
| return t; | |
| .error(mtype.loc, "`mixin(%s)` does not give a valid type", mtype.obj.toChars); | |
| return error(); | |
| } | |
| Type visitTag(TypeTag mtype) | |
| { | |
| //printf("TypeTag.semantic() %s\n", mtype.toChars()); | |
| if (mtype.resolved) | |
| { | |
| /* struct S s, *p; | |
| */ | |
| //printf("already resolved\n"); | |
| return mtype.resolved; | |
| } | |
| /* Find the current scope by skipping tag scopes. | |
| * In C, tag scopes aren't considered scopes. | |
| */ | |
| Scope* sc2 = sc; | |
| while (1) | |
| { | |
| sc2 = sc2.inner(); | |
| auto scopesym = sc2.scopesym; | |
| if (scopesym.isStructDeclaration()) | |
| { | |
| sc2 = sc2.enclosing; | |
| continue; | |
| } | |
| break; | |
| } | |
| /* Declare mtype as a struct/union/enum declaration | |
| */ | |
| void declareTag() | |
| { | |
| void declare(ScopeDsymbol sd) | |
| { | |
| sd.members = mtype.members; | |
| auto scopesym = sc2.inner().scopesym; | |
| if (scopesym.members) | |
| scopesym.members.push(sd); | |
| if (scopesym.symtab && !scopesym.symtabInsert(sd)) | |
| { | |
| Dsymbol s2 = scopesym.symtabLookup(sd, mtype.id); | |
| handleTagSymbols(*sc2, sd, s2, scopesym); | |
| } | |
| sd.parent = sc2.parent; | |
| sd.dsymbolSemantic(sc2); | |
| } | |
| switch (mtype.tok) | |
| { | |
| case TOK.enum_: | |
| auto ed = new EnumDeclaration(mtype.loc, mtype.id, mtype.base); | |
| declare(ed); | |
| mtype.resolved = visitEnum(new TypeEnum(ed)); | |
| break; | |
| case TOK.struct_: | |
| auto sd = new StructDeclaration(mtype.loc, mtype.id, false); | |
| declare(sd); | |
| mtype.resolved = visitStruct(new TypeStruct(sd)); | |
| break; | |
| case TOK.union_: | |
| auto ud = new UnionDeclaration(mtype.loc, mtype.id); | |
| declare(ud); | |
| mtype.resolved = visitStruct(new TypeStruct(ud)); | |
| break; | |
| default: | |
| assert(0); | |
| } | |
| } | |
| /* If it doesn't have a tag by now, supply one. | |
| * It'll be unique, and therefore introducing. | |
| * Declare it, and done. | |
| */ | |
| if (!mtype.id) | |
| { | |
| mtype.id = Identifier.generateId("__tag"[]); | |
| declareTag(); | |
| return mtype.resolved; | |
| } | |
| /* look for pre-existing declaration | |
| */ | |
| Dsymbol scopesym; | |
| auto s = sc2.search(mtype.loc, mtype.id, &scopesym, IgnoreErrors | TagNameSpace); | |
| if (!s || s.isModule()) | |
| { | |
| // no pre-existing declaration, so declare it | |
| if (mtype.tok == TOK.enum_ && !mtype.members) | |
| .error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3 | |
| declareTag(); | |
| return mtype.resolved; | |
| } | |
| /* A redeclaration only happens if both declarations are in | |
| * the same scope | |
| */ | |
| const bool redeclar = (scopesym == sc2.inner().scopesym); | |
| if (redeclar) | |
| { | |
| if (mtype.tok == TOK.enum_ && s.isEnumDeclaration()) | |
| { | |
| auto ed = s.isEnumDeclaration(); | |
| if (mtype.members && ed.members) | |
| .error(mtype.loc, "`%s` already has members", mtype.id.toChars()); | |
| else if (!ed.members) | |
| { | |
| ed.members = mtype.members; | |
| } | |
| else | |
| { | |
| } | |
| mtype.resolved = ed.type; | |
| } | |
| else if (mtype.tok == TOK.union_ && s.isUnionDeclaration() || | |
| mtype.tok == TOK.struct_ && s.isStructDeclaration()) | |
| { | |
| // Add members to original declaration | |
| auto sd = s.isStructDeclaration(); | |
| if (mtype.members && sd.members) | |
| { | |
| /* struct S { int b; }; | |
| * struct S { int a; } *s; | |
| */ | |
| .error(mtype.loc, "`%s` already has members", mtype.id.toChars()); | |
| } | |
| else if (!sd.members) | |
| { | |
| /* struct S; | |
| * struct S { int a; } *s; | |
| */ | |
| sd.members = mtype.members; | |
| if (sd.semanticRun == PASS.semanticdone) | |
| { | |
| /* The first semantic pass marked `sd` as an opaque struct. | |
| * Re-run semantic so that all newly assigned members are | |
| * picked up and added to the symtab. | |
| */ | |
| sd.semanticRun = PASS.semantic; | |
| sd.dsymbolSemantic(sc2); | |
| } | |
| } | |
| else | |
| { | |
| /* struct S { int a; }; | |
| * struct S *s; | |
| */ | |
| } | |
| mtype.resolved = sd.type; | |
| } | |
| else | |
| { | |
| /* int S; | |
| * struct S { int a; } *s; | |
| */ | |
| .error(mtype.loc, "redeclaration of `%s`", mtype.id.toChars()); | |
| mtype.resolved = error(); | |
| } | |
| } | |
| else if (mtype.members) | |
| { | |
| /* struct S; | |
| * { struct S { int a; } *s; } | |
| */ | |
| declareTag(); | |
| } | |
| else | |
| { | |
| if (mtype.tok == TOK.enum_ && s.isEnumDeclaration()) | |
| { | |
| mtype.resolved = s.isEnumDeclaration().type; | |
| } | |
| else if (mtype.tok == TOK.union_ && s.isUnionDeclaration() || | |
| mtype.tok == TOK.struct_ && s.isStructDeclaration()) | |
| { | |
| /* struct S; | |
| * { struct S *s; } | |
| */ | |
| mtype.resolved = s.isStructDeclaration().type; | |
| } | |
| else | |
| { | |
| /* union S; | |
| * { struct S *s; } | |
| */ | |
| .error(mtype.loc, "redeclaring `%s %s` as `%s %s`", | |
| s.kind(), s.toChars(), Token.toChars(mtype.tok), mtype.id.toChars()); | |
| declareTag(); | |
| } | |
| } | |
| return mtype.resolved; | |
| } | |
| switch (type.ty) | |
| { | |
| default: return visitType(type); | |
| case Tvector: return visitVector(type.isTypeVector()); | |
| case Tsarray: return visitSArray(type.isTypeSArray()); | |
| case Tarray: return visitDArray(type.isTypeDArray()); | |
| case Taarray: return visitAArray(type.isTypeAArray()); | |
| case Tpointer: return visitPointer(type.isTypePointer()); | |
| case Treference: return visitReference(type.isTypeReference()); | |
| case Tfunction: return visitFunction(type.isTypeFunction()); | |
| case Tdelegate: return visitDelegate(type.isTypeDelegate()); | |
| case Tident: return visitIdentifier(type.isTypeIdentifier()); | |
| case Tinstance: return visitInstance(type.isTypeInstance()); | |
| case Ttypeof: return visitTypeof(type.isTypeTypeof()); | |
| case Ttraits: return visitTraits(type.isTypeTraits()); | |
| case Treturn: return visitReturn(type.isTypeReturn()); | |
| case Tstruct: return visitStruct(type.isTypeStruct()); | |
| case Tenum: return visitEnum(type.isTypeEnum()); | |
| case Tclass: return visitClass(type.isTypeClass()); | |
| case Ttuple: return visitTuple(type.isTypeTuple()); | |
| case Tslice: return visitSlice(type.isTypeSlice()); | |
| case Tmixin: return visitMixin(type.isTypeMixin()); | |
| case Ttag: return visitTag(type.isTypeTag()); | |
| } | |
| } | |
| /****************************************** | |
| * Compile the MixinType, returning the type or expression AST. | |
| * | |
| * Doesn't run semantic() on the returned object. | |
| * Params: | |
| * tm = mixin to compile as a type or expression | |
| * loc = location for error messages | |
| * sc = context | |
| * Return: | |
| * null if error, else RootObject AST as parsed | |
| */ | |
| RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) | |
| { | |
| OutBuffer buf; | |
| if (expressionsToString(buf, sc, tm.exps)) | |
| return null; | |
| const errors = global.errors; | |
| const len = buf.length; | |
| buf.writeByte(0); | |
| const str = buf.extractSlice()[0 .. len]; | |
| scope p = new Parser!ASTCodegen(loc, sc._module, str, false); | |
| p.nextToken(); | |
| //printf("p.loc.linnum = %d\n", p.loc.linnum); | |
| auto o = p.parseTypeOrAssignExp(TOK.endOfFile); | |
| if (errors != global.errors) | |
| { | |
| assert(global.errors != errors); // should have caught all these cases | |
| return null; | |
| } | |
| if (p.token.value != TOK.endOfFile) | |
| { | |
| .error(loc, "incomplete mixin type `%s`", str.ptr); | |
| return null; | |
| } | |
| return o; | |
| } | |
| /************************************ | |
| * If an identical type to `type` is in `type.stringtable`, return | |
| * the latter one. Otherwise, add it to `type.stringtable`. | |
| * Some types don't get merged and are returned as-is. | |
| * Params: | |
| * type = Type to check against existing types | |
| * Returns: | |
| * the type that was merged | |
| */ | |
| extern (C++) Type merge(Type type) | |
| { | |
| switch (type.ty) | |
| { | |
| case Terror: | |
| case Ttypeof: | |
| case Tident: | |
| case Tinstance: | |
| case Tmixin: | |
| case Ttag: | |
| return type; // don't merge placeholder types | |
| case Tsarray: | |
| // prevents generating the mangle if the array dim is not yet known | |
| if (!type.isTypeSArray().dim.isIntegerExp()) | |
| return type; | |
| goto default; | |
| case Tenum: | |
| break; | |
| case Taarray: | |
| if (!type.isTypeAArray().index.merge().deco) | |
| return type; | |
| goto default; | |
| default: | |
| if (type.nextOf() && !type.nextOf().deco) | |
| return type; | |
| break; | |
| } | |
| //printf("merge(%s)\n", toChars()); | |
| if (!type.deco) | |
| { | |
| OutBuffer buf; | |
| buf.reserve(32); | |
| mangleToBuffer(type, &buf); | |
| auto sv = type.stringtable.update(buf[]); | |
| if (sv.value) | |
| { | |
| Type t = sv.value; | |
| debug | |
| { | |
| import core.stdc.stdio; | |
| if (!t.deco) | |
| printf("t = %s\n", t.toChars()); | |
| } | |
| assert(t.deco); | |
| //printf("old value, deco = '%s' %p\n", t.deco, t.deco); | |
| return t; | |
| } | |
| else | |
| { | |
| Type t = stripDefaultArgs(type); | |
| sv.value = t; | |
| type.deco = t.deco = cast(char*)sv.toDchars(); | |
| //printf("new value, deco = '%s' %p\n", t.deco, t.deco); | |
| return t; | |
| } | |
| } | |
| return type; | |
| } | |
| /*************************************** | |
| * Calculate built-in properties which just the type is necessary. | |
| * | |
| * Params: | |
| * t = the type for which the property is calculated | |
| * scope_ = the scope from which the property is being accessed. Used for visibility checks only. | |
| * loc = the location where the property is encountered | |
| * ident = the identifier of the property | |
| * flag = if flag & 1, don't report "not a property" error and just return NULL. | |
| * Returns: | |
| * expression representing the property, or null if not a property and (flag & 1) | |
| */ | |
| Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag) | |
| { | |
| Expression visitType(Type mt) | |
| { | |
| Expression e; | |
| static if (LOGDOTEXP) | |
| { | |
| printf("Type::getProperty(type = '%s', ident = '%s')\n", mt.toChars(), ident.toChars()); | |
| } | |
| if (ident == Id.__sizeof) | |
| { | |
| const sz = mt.size(loc); | |
| if (sz == SIZE_INVALID) | |
| return ErrorExp.get(); | |
| e = new IntegerExp(loc, sz, Type.tsize_t); | |
| } | |
| else if (ident == Id.__xalignof) | |
| { | |
| const explicitAlignment = mt.alignment(); | |
| const naturalAlignment = mt.alignsize(); | |
| const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get()); | |
| e = new IntegerExp(loc, actualAlignment, Type.tsize_t); | |
| } | |
| else if (ident == Id._init) | |
| { | |
| Type tb = mt.toBasetype(); | |
| e = mt.defaultInitLiteral(loc); | |
| if (tb.ty == Tstruct && tb.needsNested()) | |
| { | |
| e.isStructLiteralExp().useStaticInit = true; | |
| } | |
| } | |
| else if (ident == Id._mangleof) | |
| { | |
| if (!mt.deco) | |
| { | |
| error(loc, "forward reference of type `%s.mangleof`", mt.toChars()); | |
| e = ErrorExp.get(); | |
| } | |
| else | |
| { | |
| e = new StringExp(loc, mt.deco.toDString()); | |
| Scope sc; | |
| e = e.expressionSemantic(&sc); | |
| } | |
| } | |
| else if (ident == Id.stringof) | |
| { | |
| const s = mt.toChars(); | |
| e = new StringExp(loc, s.toDString()); | |
| Scope sc; | |
| e = e.expressionSemantic(&sc); | |
| } | |
| else if (flag && mt != Type.terror) | |
| { | |
| return null; | |
| } | |
| else | |
| { | |
| Dsymbol s = null; | |
| if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) | |
| s = mt.toDsymbol(null); | |
| if (s) | |
| s = s.search_correct(ident); | |
| if (s && !symbolIsVisible(scope_, s)) | |
| s = null; | |
| if (mt != Type.terror) | |
| { | |
| if (s) | |
| error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); | |
| else if (ident == Id.call && mt.ty == Tclass) | |
| error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); | |
| else if (const n = importHint(ident.toString())) | |
| error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); | |
| else | |
| { | |
| error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); | |
| if (auto dsym = mt.toDsymbol(scope_)) | |
| if (auto sym = dsym.isAggregateDeclaration()) | |
| { | |
| if (auto fd = search_function(sym, Id.opDispatch)) | |
| errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message"); | |
| else if (!sym.members) | |
| errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); | |
| } | |
| } | |
| } | |
| e = ErrorExp.get(); | |
| } | |
| return e; | |
| } | |
| Expression visitError(TypeError) | |
| { | |
| return ErrorExp.get(); | |
| } | |
| Expression visitBasic(TypeBasic mt) | |
| { | |
| Expression integerValue(dinteger_t i) | |
| { | |
| return new IntegerExp(loc, i, mt); | |
| } | |
| Expression intValue(dinteger_t i) | |
| { | |
| return new IntegerExp(loc, i, Type.tint32); | |
| } | |
| Expression floatValue(real_t r) | |
| { | |
| if (mt.isreal() || mt.isimaginary()) | |
| return new RealExp(loc, r, mt); | |
| else | |
| { | |
| return new ComplexExp(loc, complex_t(r, r), mt); | |
| } | |
| } | |
| //printf("TypeBasic::getProperty('%s')\n", ident.toChars()); | |
| if (ident == Id.max) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tint8: return integerValue(byte.max); | |
| case Tuns8: return integerValue(ubyte.max); | |
| case Tint16: return integerValue(short.max); | |
| case Tuns16: return integerValue(ushort.max); | |
| case Tint32: return integerValue(int.max); | |
| case Tuns32: return integerValue(uint.max); | |
| case Tint64: return integerValue(long.max); | |
| case Tuns64: return integerValue(ulong.max); | |
| case Tbool: return integerValue(bool.max); | |
| case Tchar: return integerValue(char.max); | |
| case Twchar: return integerValue(wchar.max); | |
| case Tdchar: return integerValue(dchar.max); | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return floatValue(target.FloatProperties.max); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return floatValue(target.DoubleProperties.max); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return floatValue(target.RealProperties.max); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.min) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tint8: return integerValue(byte.min); | |
| case Tuns8: | |
| case Tuns16: | |
| case Tuns32: | |
| case Tuns64: | |
| case Tbool: | |
| case Tchar: | |
| case Twchar: | |
| case Tdchar: return integerValue(0); | |
| case Tint16: return integerValue(short.min); | |
| case Tint32: return integerValue(int.min); | |
| case Tint64: return integerValue(long.min); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.min_normal) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return floatValue(target.FloatProperties.min_normal); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return floatValue(target.DoubleProperties.min_normal); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return floatValue(target.RealProperties.min_normal); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.nan) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Tcomplex64: | |
| case Tcomplex80: | |
| case Timaginary32: | |
| case Timaginary64: | |
| case Timaginary80: | |
| case Tfloat32: | |
| case Tfloat64: | |
| case Tfloat80: return floatValue(target.RealProperties.nan); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.infinity) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Tcomplex64: | |
| case Tcomplex80: | |
| case Timaginary32: | |
| case Timaginary64: | |
| case Timaginary80: | |
| case Tfloat32: | |
| case Tfloat64: | |
| case Tfloat80: return floatValue(target.RealProperties.infinity); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.dig) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return intValue(target.FloatProperties.dig); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return intValue(target.DoubleProperties.dig); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return intValue(target.RealProperties.dig); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.epsilon) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return floatValue(target.FloatProperties.epsilon); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return floatValue(target.DoubleProperties.epsilon); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return floatValue(target.RealProperties.epsilon); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.mant_dig) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return intValue(target.FloatProperties.mant_dig); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return intValue(target.DoubleProperties.mant_dig); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return intValue(target.RealProperties.mant_dig); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.max_10_exp) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return intValue(target.FloatProperties.max_10_exp); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return intValue(target.DoubleProperties.max_10_exp); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return intValue(target.RealProperties.max_10_exp); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.max_exp) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return intValue(target.FloatProperties.max_exp); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return intValue(target.DoubleProperties.max_exp); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return intValue(target.RealProperties.max_exp); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.min_10_exp) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return intValue(target.FloatProperties.min_10_exp); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return intValue(target.DoubleProperties.min_10_exp); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return intValue(target.RealProperties.min_10_exp); | |
| default: break; | |
| } | |
| } | |
| else if (ident == Id.min_exp) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| case Timaginary32: | |
| case Tfloat32: return intValue(target.FloatProperties.min_exp); | |
| case Tcomplex64: | |
| case Timaginary64: | |
| case Tfloat64: return intValue(target.DoubleProperties.min_exp); | |
| case Tcomplex80: | |
| case Timaginary80: | |
| case Tfloat80: return intValue(target.RealProperties.min_exp); | |
| default: break; | |
| } | |
| } | |
| return visitType(mt); | |
| } | |
| Expression visitVector(TypeVector mt) | |
| { | |
| return visitType(mt); | |
| } | |
| Expression visitEnum(TypeEnum mt) | |
| { | |
| Expression e; | |
| if (ident == Id.max || ident == Id.min) | |
| { | |
| return mt.sym.getMaxMinValue(loc, ident); | |
| } | |
| else if (ident == Id._init) | |
| { | |
| e = mt.defaultInitLiteral(loc); | |
| } | |
| else if (ident == Id.stringof) | |
| { | |
| e = new StringExp(loc, mt.toString()); | |
| Scope sc; | |
| e = e.expressionSemantic(&sc); | |
| } | |
| else if (ident == Id._mangleof) | |
| { | |
| e = visitType(mt); | |
| } | |
| else | |
| { | |
| e = mt.toBasetype().getProperty(scope_, loc, ident, flag); | |
| } | |
| return e; | |
| } | |
| Expression visitTuple(TypeTuple mt) | |
| { | |
| Expression e; | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", mt.toChars(), ident.toChars()); | |
| } | |
| if (ident == Id.length) | |
| { | |
| e = new IntegerExp(loc, mt.arguments.dim, Type.tsize_t); | |
| } | |
| else if (ident == Id._init) | |
| { | |
| e = mt.defaultInitLiteral(loc); | |
| } | |
| else if (flag) | |
| { | |
| e = null; | |
| } | |
| else | |
| { | |
| error(loc, "no property `%s` for tuple `%s`", ident.toChars(), mt.toChars()); | |
| e = ErrorExp.get(); | |
| } | |
| return e; | |
| } | |
| switch (t.ty) | |
| { | |
| default: return t.isTypeBasic() ? | |
| visitBasic(cast(TypeBasic)t) : | |
| visitType(t); | |
| case Terror: return visitError (t.isTypeError()); | |
| case Tvector: return visitVector(t.isTypeVector()); | |
| case Tenum: return visitEnum (t.isTypeEnum()); | |
| case Ttuple: return visitTuple (t.isTypeTuple()); | |
| } | |
| } | |
| /*************************************** | |
| * Determine if Expression `exp` should instead be a Type, a Dsymbol, or remain an Expression. | |
| * Params: | |
| * exp = Expression to look at | |
| * t = if exp should be a Type, set t to that Type else null | |
| * s = if exp should be a Dsymbol, set s to that Dsymbol else null | |
| * e = if exp should remain an Expression, set e to that Expression else null | |
| * | |
| */ | |
| private void resolveExp(Expression exp, out Type t, out Expression e, out Dsymbol s) | |
| { | |
| if (exp.isTypeExp()) | |
| t = exp.type; | |
| else if (auto ve = exp.isVarExp()) | |
| { | |
| if (auto v = ve.var.isVarDeclaration()) | |
| e = exp; | |
| else | |
| s = ve.var; | |
| } | |
| else if (auto te = exp.isTemplateExp()) | |
| s = te.td; | |
| else if (auto se = exp.isScopeExp()) | |
| s = se.sds; | |
| else if (exp.isFuncExp()) | |
| s = getDsymbol(exp); | |
| else if (auto dte = exp.isDotTemplateExp()) | |
| s = dte.td; | |
| else if (exp.isErrorExp()) | |
| t = Type.terror; | |
| else | |
| e = exp; | |
| } | |
| /************************************ | |
| * Resolve type 'mt' to either type, symbol, or expression. | |
| * If errors happened, resolved to Type.terror. | |
| * | |
| * Params: | |
| * mt = type to be resolved | |
| * loc = the location where the type is encountered | |
| * sc = the scope of the type | |
| * pe = is set if t is an expression | |
| * pt = is set if t is a type | |
| * ps = is set if t is a symbol | |
| * intypeid = true if in type id | |
| */ | |
| void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) | |
| { | |
| void returnExp(Expression e) | |
| { | |
| pe = e; | |
| pt = null; | |
| ps = null; | |
| } | |
| void returnType(Type t) | |
| { | |
| pe = null; | |
| pt = t; | |
| ps = null; | |
| } | |
| void returnSymbol(Dsymbol s) | |
| { | |
| pe = null; | |
| pt = null; | |
| ps = s; | |
| } | |
| void returnError() | |
| { | |
| returnType(Type.terror); | |
| } | |
| void visitType(Type mt) | |
| { | |
| //printf("Type::resolve() %s, %d\n", mt.toChars(), mt.ty); | |
| Type t = typeSemantic(mt, loc, sc); | |
| assert(t); | |
| returnType(t); | |
| } | |
| void visitSArray(TypeSArray mt) | |
| { | |
| //printf("TypeSArray::resolve() %s\n", mt.toChars()); | |
| mt.next.resolve(loc, sc, pe, pt, ps, intypeid); | |
| //printf("s = %p, e = %p, t = %p\n", ps, pe, pt); | |
| if (pe) | |
| { | |
| // It's really an index expression | |
| if (Dsymbol s = getDsymbol(pe)) | |
| pe = new DsymbolExp(loc, s); | |
| returnExp(new ArrayExp(loc, pe, mt.dim)); | |
| } | |
| else if (ps) | |
| { | |
| Dsymbol s = ps; | |
| if (auto tup = s.isTupleDeclaration()) | |
| { | |
| mt.dim = semanticLength(sc, tup, mt.dim); | |
| mt.dim = mt.dim.ctfeInterpret(); | |
| if (mt.dim.op == EXP.error) | |
| return returnError(); | |
| const d = mt.dim.toUInteger(); | |
| if (d >= tup.objects.dim) | |
| { | |
| error(loc, "tuple index `%llu` exceeds length %llu", d, cast(ulong) tup.objects.dim); | |
| return returnError(); | |
| } | |
| RootObject o = (*tup.objects)[cast(size_t)d]; | |
| if (o.dyncast() == DYNCAST.dsymbol) | |
| { | |
| return returnSymbol(cast(Dsymbol)o); | |
| } | |
| if (o.dyncast() == DYNCAST.expression) | |
| { | |
| Expression e = cast(Expression)o; | |
| if (e.op == EXP.dSymbol) | |
| return returnSymbol(e.isDsymbolExp().s); | |
| else | |
| return returnExp(e); | |
| } | |
| if (o.dyncast() == DYNCAST.type) | |
| { | |
| return returnType((cast(Type)o).addMod(mt.mod)); | |
| } | |
| /* Create a new TupleDeclaration which | |
| * is a slice [d..d+1] out of the old one. | |
| * Do it this way because TemplateInstance::semanticTiargs() | |
| * can handle unresolved Objects this way. | |
| */ | |
| auto objects = new Objects(1); | |
| (*objects)[0] = o; | |
| return returnSymbol(new TupleDeclaration(loc, tup.ident, objects)); | |
| } | |
| else | |
| return visitType(mt); | |
| } | |
| else | |
| { | |
| if (pt.ty != Terror) | |
| mt.next = pt; // prevent re-running semantic() on 'next' | |
| visitType(mt); | |
| } | |
| } | |
| void visitDArray(TypeDArray mt) | |
| { | |
| //printf("TypeDArray::resolve() %s\n", mt.toChars()); | |
| mt.next.resolve(loc, sc, pe, pt, ps, intypeid); | |
| //printf("s = %p, e = %p, t = %p\n", ps, pe, pt); | |
| if (pe) | |
| { | |
| // It's really a slice expression | |
| if (Dsymbol s = getDsymbol(pe)) | |
| pe = new DsymbolExp(loc, s); | |
| returnExp(new ArrayExp(loc, pe)); | |
| } | |
| else if (ps) | |
| { | |
| if (auto tup = ps.isTupleDeclaration()) | |
| { | |
| // keep ps | |
| } | |
| else | |
| visitType(mt); | |
| } | |
| else | |
| { | |
| if (pt.ty != Terror) | |
| mt.next = pt; // prevent re-running semantic() on 'next' | |
| visitType(mt); | |
| } | |
| } | |
| void visitAArray(TypeAArray mt) | |
| { | |
| //printf("TypeAArray::resolve() %s\n", mt.toChars()); | |
| // Deal with the case where we thought the index was a type, but | |
| // in reality it was an expression. | |
| if (mt.index.ty == Tident || mt.index.ty == Tinstance || mt.index.ty == Tsarray) | |
| { | |
| Expression e; | |
| Type t; | |
| Dsymbol s; | |
| mt.index.resolve(loc, sc, e, t, s, intypeid); | |
| if (e) | |
| { | |
| // It was an expression - | |
| // Rewrite as a static array | |
| auto tsa = new TypeSArray(mt.next, e); | |
| tsa.mod = mt.mod; // just copy mod field so tsa's semantic is not yet done | |
| return tsa.resolve(loc, sc, pe, pt, ps, intypeid); | |
| } | |
| else if (t) | |
| mt.index = t; | |
| else | |
| .error(loc, "index is not a type or an expression"); | |
| } | |
| visitType(mt); | |
| } | |
| /************************************* | |
| * Takes an array of Identifiers and figures out if | |
| * it represents a Type or an Expression. | |
| * Output: | |
| * if expression, pe is set | |
| * if type, pt is set | |
| */ | |
| void visitIdentifier(TypeIdentifier mt) | |
| { | |
| //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars()); | |
| if (mt.ident == Id.ctfe) | |
| { | |
| error(loc, "variable `__ctfe` cannot be read at compile time"); | |
| return returnError(); | |
| } | |
| if (mt.ident == Id.builtin_va_list) // gcc has __builtin_va_xxxx for stdarg.h | |
| { | |
| /* Since we don't support __builtin_va_start, -arg, -end, we don't | |
| * have to actually care what -list is. A void* will do. | |
| * If we ever do care, import core.stdc.stdarg and pull | |
| * the definition out of that, similarly to how std.math is handled for PowExp | |
| */ | |
| pt = target.va_listType(loc, sc); | |
| return; | |
| } | |
| Dsymbol scopesym; | |
| Dsymbol s = sc.search(loc, mt.ident, &scopesym); | |
| /* | |
| * https://issues.dlang.org/show_bug.cgi?id=1170 | |
| * https://issues.dlang.org/show_bug.cgi?id=10739 | |
| * | |
| * If a symbol is not found, it might be declared in | |
| * a mixin-ed string or a mixin-ed template, so before | |
| * issuing an error semantically analyze all string/template | |
| * mixins that are members of the current ScopeDsymbol. | |
| */ | |
| if (!s && sc.enclosing) | |
| { | |
| ScopeDsymbol sds = sc.enclosing.scopesym; | |
| if (sds && sds.members) | |
| { | |
| void semanticOnMixin(Dsymbol member) | |
| { | |
| if (auto compileDecl = member.isCompileDeclaration()) | |
| compileDecl.dsymbolSemantic(sc); | |
| else if (auto mixinTempl = member.isTemplateMixin()) | |
| mixinTempl.dsymbolSemantic(sc); | |
| } | |
| sds.members.foreachDsymbol( s => semanticOnMixin(s) ); | |
| s = sc.search(loc, mt.ident, &scopesym); | |
| } | |
| } | |
| if (s) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=16042 | |
| // If `f` is really a function template, then replace `f` | |
| // with the function template declaration. | |
| if (auto f = s.isFuncDeclaration()) | |
| { | |
| if (auto td = getFuncTemplateDecl(f)) | |
| { | |
| // If not at the beginning of the overloaded list of | |
| // `TemplateDeclaration`s, then get the beginning | |
| if (td.overroot) | |
| td = td.overroot; | |
| s = td; | |
| } | |
| } | |
| } | |
| mt.resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid); | |
| if (pt) | |
| pt = pt.addMod(mt.mod); | |
| } | |
| void visitInstance(TypeInstance mt) | |
| { | |
| // Note close similarity to TypeIdentifier::resolve() | |
| //printf("TypeInstance::resolve(sc = %p, tempinst = '%s')\n", sc, mt.tempinst.toChars()); | |
| mt.tempinst.dsymbolSemantic(sc); | |
| if (!global.gag && mt.tempinst.errors) | |
| return returnError(); | |
| mt.resolveHelper(loc, sc, mt.tempinst, null, pe, pt, ps, intypeid); | |
| if (pt) | |
| pt = pt.addMod(mt.mod); | |
| //if (pt) printf("pt = %d '%s'\n", pt.ty, pt.toChars()); | |
| } | |
| void visitTypeof(TypeTypeof mt) | |
| { | |
| //printf("TypeTypeof::resolve(this = %p, sc = %p, idents = '%s')\n", mt, sc, mt.toChars()); | |
| //static int nest; if (++nest == 50) *(char*)0=0; | |
| if (sc is null) | |
| { | |
| error(loc, "invalid scope"); | |
| return returnError(); | |
| } | |
| if (mt.inuse) | |
| { | |
| mt.inuse = 2; | |
| error(loc, "circular `typeof` definition"); | |
| Lerr: | |
| mt.inuse--; | |
| return returnError(); | |
| } | |
| mt.inuse++; | |
| /* Currently we cannot evaluate 'exp' in speculative context, because | |
| * the type implementation may leak to the final execution. Consider: | |
| * | |
| * struct S(T) { | |
| * string toString() const { return "x"; } | |
| * } | |
| * void main() { | |
| * alias X = typeof(S!int()); | |
| * assert(typeid(X).toString() == "x"); | |
| * } | |
| */ | |
| Scope* sc2 = sc.push(); | |
| if (!mt.exp.isTypeidExp()) | |
| /* Treat typeof(typeid(exp)) as needing | |
| * the full semantic analysis of the typeid. | |
| * https://issues.dlang.org/show_bug.cgi?id=20958 | |
| */ | |
| sc2.intypeof = 1; | |
| auto exp2 = mt.exp.expressionSemantic(sc2); | |
| exp2 = resolvePropertiesOnly(sc2, exp2); | |
| sc2.pop(); | |
| if (exp2.op == EXP.error) | |
| { | |
| if (!global.gag) | |
| mt.exp = exp2; | |
| goto Lerr; | |
| } | |
| mt.exp = exp2; | |
| if (mt.exp.op == EXP.type || | |
| mt.exp.op == EXP.scope_) | |
| { | |
| if (mt.exp.checkType()) | |
| goto Lerr; | |
| /* Today, 'typeof(func)' returns void if func is a | |
| * function template (TemplateExp), or | |
| * template lambda (FuncExp). | |
| * It's actually used in Phobos as an idiom, to branch code for | |
| * template functions. | |
| */ | |
| } | |
| if (auto f = mt.exp.op == EXP.variable ? mt.exp.isVarExp().var.isFuncDeclaration() | |
| : mt.exp.op == EXP.dotVariable ? mt.exp.isDotVarExp().var.isFuncDeclaration() : null) | |
| { | |
| // f might be a unittest declaration which is incomplete when compiled | |
| // without -unittest. That causes a segfault in checkForwardRef, see | |
| // https://issues.dlang.org/show_bug.cgi?id=20626 | |
| if ((!f.isUnitTestDeclaration() || global.params.useUnitTests) && f.checkForwardRef(loc)) | |
| goto Lerr; | |
| } | |
| if (auto f = isFuncAddress(mt.exp)) | |
| { | |
| if (f.checkForwardRef(loc)) | |
| goto Lerr; | |
| } | |
| Type t = mt.exp.type; | |
| if (!t) | |
| { | |
| error(loc, "expression `%s` has no type", mt.exp.toChars()); | |
| goto Lerr; | |
| } | |
| if (t.ty == Ttypeof) | |
| { | |
| error(loc, "forward reference to `%s`", mt.toChars()); | |
| goto Lerr; | |
| } | |
| if (mt.idents.dim == 0) | |
| { | |
| returnType(t.addMod(mt.mod)); | |
| } | |
| else | |
| { | |
| if (Dsymbol s = t.toDsymbol(sc)) | |
| mt.resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid); | |
| else | |
| { | |
| auto e = typeToExpressionHelper(mt, new TypeExp(loc, t)); | |
| e = e.expressionSemantic(sc); | |
| resolveExp(e, pt, pe, ps); | |
| } | |
| if (pt) | |
| pt = pt.addMod(mt.mod); | |
| } | |
| mt.inuse--; | |
| } | |
| void visitReturn(TypeReturn mt) | |
| { | |
| //printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars()); | |
| Type t; | |
| { | |
| FuncDeclaration func = sc.func; | |
| if (!func) | |
| { | |
| error(loc, "`typeof(return)` must be inside function"); | |
| return returnError(); | |
| } | |
| if (func.fes) | |
| func = func.fes.func; | |
| t = func.type.nextOf(); | |
| if (!t) | |
| { | |
| error(loc, "cannot use `typeof(return)` inside function `%s` with inferred return type", sc.func.toChars()); | |
| return returnError(); | |
| } | |
| } | |
| if (mt.idents.dim == 0) | |
| { | |
| return returnType(t.addMod(mt.mod)); | |
| } | |
| else | |
| { | |
| if (Dsymbol s = t.toDsymbol(sc)) | |
| mt.resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid); | |
| else | |
| { | |
| auto e = typeToExpressionHelper(mt, new TypeExp(loc, t)); | |
| e = e.expressionSemantic(sc); | |
| resolveExp(e, pt, pe, ps); | |
| } | |
| if (pt) | |
| pt = pt.addMod(mt.mod); | |
| } | |
| } | |
| void visitSlice(TypeSlice mt) | |
| { | |
| mt.next.resolve(loc, sc, pe, pt, ps, intypeid); | |
| if (pe) | |
| { | |
| // It's really a slice expression | |
| if (Dsymbol s = getDsymbol(pe)) | |
| pe = new DsymbolExp(loc, s); | |
| return returnExp(new ArrayExp(loc, pe, new IntervalExp(loc, mt.lwr, mt.upr))); | |
| } | |
| else if (ps) | |
| { | |
| Dsymbol s = ps; | |
| TupleDeclaration td = s.isTupleDeclaration(); | |
| if (td) | |
| { | |
| /* It's a slice of a TupleDeclaration | |
| */ | |
| ScopeDsymbol sym = new ArrayScopeSymbol(sc, td); | |
| sym.parent = sc.scopesym; | |
| sc = sc.push(sym); | |
| sc = sc.startCTFE(); | |
| mt.lwr = mt.lwr.expressionSemantic(sc); | |
| mt.upr = mt.upr.expressionSemantic(sc); | |
| sc = sc.endCTFE(); | |
| sc = sc.pop(); | |
| mt.lwr = mt.lwr.ctfeInterpret(); | |
| mt.upr = mt.upr.ctfeInterpret(); | |
| const i1 = mt.lwr.toUInteger(); | |
| const i2 = mt.upr.toUInteger(); | |
| if (!(i1 <= i2 && i2 <= td.objects.dim)) | |
| { | |
| error(loc, "slice `[%llu..%llu]` is out of range of [0..%llu]", i1, i2, cast(ulong) td.objects.dim); | |
| return returnError(); | |
| } | |
| if (i1 == 0 && i2 == td.objects.dim) | |
| { | |
| return returnSymbol(td); | |
| } | |
| /* Create a new TupleDeclaration which | |
| * is a slice [i1..i2] out of the old one. | |
| */ | |
| auto objects = new Objects(cast(size_t)(i2 - i1)); | |
| for (size_t i = 0; i < objects.dim; i++) | |
| { | |
| (*objects)[i] = (*td.objects)[cast(size_t)i1 + i]; | |
| } | |
| return returnSymbol(new TupleDeclaration(loc, td.ident, objects)); | |
| } | |
| else | |
| visitType(mt); | |
| } | |
| else | |
| { | |
| if (pt.ty != Terror) | |
| mt.next = pt; // prevent re-running semantic() on 'next' | |
| visitType(mt); | |
| } | |
| } | |
| void visitMixin(TypeMixin mt) | |
| { | |
| RootObject o = mt.obj; | |
| // if already resolved just set pe/pt/ps and return. | |
| if (o) | |
| { | |
| pe = o.isExpression(); | |
| pt = o.isType(); | |
| ps = o.isDsymbol(); | |
| return; | |
| } | |
| o = mt.compileTypeMixin(loc, sc); | |
| if (auto t = o.isType()) | |
| { | |
| resolve(t, loc, sc, pe, pt, ps, intypeid); | |
| if (pt) | |
| pt = pt.addMod(mt.mod); | |
| } | |
| else if (auto e = o.isExpression()) | |
| { | |
| e = e.expressionSemantic(sc); | |
| if (auto et = e.isTypeExp()) | |
| returnType(et.type.addMod(mt.mod)); | |
| else | |
| returnExp(e); | |
| } | |
| else | |
| returnError(); | |
| // save the result | |
| mt.obj = pe ? pe : (pt ? pt : ps); | |
| } | |
| void visitTraits(TypeTraits tt) | |
| { | |
| if (Type t = typeSemantic(tt, loc, sc)) | |
| returnType(t); | |
| else if (tt.sym) | |
| returnSymbol(tt.sym); | |
| else | |
| return returnError(); | |
| } | |
| switch (mt.ty) | |
| { | |
| default: visitType (mt); break; | |
| case Tsarray: visitSArray (mt.isTypeSArray()); break; | |
| case Tarray: visitDArray (mt.isTypeDArray()); break; | |
| case Taarray: visitAArray (mt.isTypeAArray()); break; | |
| case Tident: visitIdentifier(mt.isTypeIdentifier()); break; | |
| case Tinstance: visitInstance (mt.isTypeInstance()); break; | |
| case Ttypeof: visitTypeof (mt.isTypeTypeof()); break; | |
| case Treturn: visitReturn (mt.isTypeReturn()); break; | |
| case Tslice: visitSlice (mt.isTypeSlice()); break; | |
| case Tmixin: visitMixin (mt.isTypeMixin()); break; | |
| case Ttraits: visitTraits (mt.isTypeTraits()); break; | |
| } | |
| } | |
| /************************ | |
| * Access the members of the object e. This type is same as e.type. | |
| * Params: | |
| * mt = type for which the dot expression is used | |
| * sc = instantiating scope | |
| * e = expression to convert | |
| * ident = identifier being used | |
| * flag = DotExpFlag bit flags | |
| * | |
| * Returns: | |
| * resulting expression with e.ident resolved | |
| */ | |
| Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) | |
| { | |
| Expression visitType(Type mt) | |
| { | |
| VarDeclaration v = null; | |
| static if (LOGDOTEXP) | |
| { | |
| printf("Type::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| Expression ex = e.lastComma(); | |
| if (ex.op == EXP.dotVariable) | |
| { | |
| DotVarExp dv = cast(DotVarExp)ex; | |
| v = dv.var.isVarDeclaration(); | |
| } | |
| else if (ex.op == EXP.variable) | |
| { | |
| VarExp ve = cast(VarExp)ex; | |
| v = ve.var.isVarDeclaration(); | |
| } | |
| if (v) | |
| { | |
| if (ident == Id.offsetof) | |
| { | |
| v.dsymbolSemantic(null); | |
| if (v.isField()) | |
| { | |
| auto ad = v.toParent().isAggregateDeclaration(); | |
| objc.checkOffsetof(e, ad); | |
| ad.size(e.loc); | |
| if (ad.sizeok != Sizeok.done) | |
| return ErrorExp.get(); | |
| return new IntegerExp(e.loc, v.offset, Type.tsize_t); | |
| } | |
| } | |
| else if (ident == Id._init) | |
| { | |
| Type tb = mt.toBasetype(); | |
| e = mt.defaultInitLiteral(e.loc); | |
| if (tb.ty == Tstruct && tb.needsNested()) | |
| { | |
| e.isStructLiteralExp().useStaticInit = true; | |
| } | |
| goto Lreturn; | |
| } | |
| } | |
| if (ident == Id.stringof) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=3796 | |
| * this should demangle e.type.deco rather than | |
| * pretty-printing the type. | |
| */ | |
| e = new StringExp(e.loc, e.toString()); | |
| } | |
| else | |
| e = mt.getProperty(sc, e.loc, ident, flag & DotExpFlag.gag); | |
| Lreturn: | |
| if (e) | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| Expression visitError(TypeError) | |
| { | |
| return ErrorExp.get(); | |
| } | |
| Expression visitBasic(TypeBasic mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| Type t; | |
| if (ident == Id.re) | |
| { | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| t = mt.tfloat32; | |
| goto L1; | |
| case Tcomplex64: | |
| t = mt.tfloat64; | |
| goto L1; | |
| case Tcomplex80: | |
| t = mt.tfloat80; | |
| goto L1; | |
| L1: | |
| e = e.castTo(sc, t); | |
| break; | |
| case Tfloat32: | |
| case Tfloat64: | |
| case Tfloat80: | |
| break; | |
| case Timaginary32: | |
| t = mt.tfloat32; | |
| goto L2; | |
| case Timaginary64: | |
| t = mt.tfloat64; | |
| goto L2; | |
| case Timaginary80: | |
| t = mt.tfloat80; | |
| goto L2; | |
| L2: | |
| e = new RealExp(e.loc, CTFloat.zero, t); | |
| break; | |
| default: | |
| e = mt.Type.getProperty(sc, e.loc, ident, flag); | |
| break; | |
| } | |
| } | |
| else if (ident == Id.im) | |
| { | |
| Type t2; | |
| switch (mt.ty) | |
| { | |
| case Tcomplex32: | |
| t = mt.timaginary32; | |
| t2 = mt.tfloat32; | |
| goto L3; | |
| case Tcomplex64: | |
| t = mt.timaginary64; | |
| t2 = mt.tfloat64; | |
| goto L3; | |
| case Tcomplex80: | |
| t = mt.timaginary80; | |
| t2 = mt.tfloat80; | |
| goto L3; | |
| L3: | |
| e = e.castTo(sc, t); | |
| e.type = t2; | |
| break; | |
| case Timaginary32: | |
| t = mt.tfloat32; | |
| goto L4; | |
| case Timaginary64: | |
| t = mt.tfloat64; | |
| goto L4; | |
| case Timaginary80: | |
| t = mt.tfloat80; | |
| goto L4; | |
| L4: | |
| e = e.copy(); | |
| e.type = t; | |
| break; | |
| case Tfloat32: | |
| case Tfloat64: | |
| case Tfloat80: | |
| e = new RealExp(e.loc, CTFloat.zero, mt); | |
| break; | |
| default: | |
| e = mt.Type.getProperty(sc, e.loc, ident, flag); | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| return visitType(mt); | |
| } | |
| if (!(flag & 1) || e) | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| Expression visitVector(TypeVector mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| if (ident == Id.ptr && e.op == EXP.call) | |
| { | |
| /* The trouble with EXP.call is the return ABI for float[4] is different from | |
| * __vector(float[4]), and a type paint won't do. | |
| */ | |
| e = new AddrExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| return e.castTo(sc, mt.basetype.nextOf().pointerTo()); | |
| } | |
| if (ident == Id.array) | |
| { | |
| //e = e.castTo(sc, basetype); | |
| // Keep lvalue-ness | |
| e = new VectorArrayExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (ident == Id._init || ident == Id.offsetof || ident == Id.stringof || ident == Id.__xalignof) | |
| { | |
| // init should return a new VectorExp | |
| // https://issues.dlang.org/show_bug.cgi?id=12776 | |
| // offsetof does not work on a cast expression, so use e directly | |
| // stringof should not add a cast to the output | |
| return visitType(mt); | |
| } | |
| // Properties based on the vector element type and are values of the element type | |
| if (ident == Id.max || ident == Id.min || ident == Id.min_normal || | |
| ident == Id.nan || ident == Id.infinity || ident == Id.epsilon) | |
| { | |
| auto vet = mt.basetype.isTypeSArray().next; // vector element type | |
| if (auto ev = getProperty(vet, sc, e.loc, ident, DotExpFlag.gag)) | |
| return ev.castTo(sc, mt); // 'broadcast' ev to the vector elements | |
| } | |
| return mt.basetype.dotExp(sc, e.castTo(sc, mt.basetype), ident, flag); | |
| } | |
| Expression visitArray(TypeArray mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| e = visitType(mt); | |
| if (!(flag & 1) || e) | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| Expression visitSArray(TypeSArray mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| if (ident == Id.length) | |
| { | |
| Loc oldLoc = e.loc; | |
| e = mt.dim.copy(); | |
| e.loc = oldLoc; | |
| } | |
| else if (ident == Id.ptr) | |
| { | |
| if (e.op == EXP.type) | |
| { | |
| e.error("`%s` is not an expression", e.toChars()); | |
| return ErrorExp.get(); | |
| } | |
| else if (mt.dim.toUInteger() < 1 && checkUnsafeDotExp(sc, e, ident, flag)) | |
| { | |
| // .ptr on static array is @safe unless size is 0 | |
| // https://issues.dlang.org/show_bug.cgi?id=20853 | |
| return ErrorExp.get(); | |
| } | |
| e = e.castTo(sc, e.type.nextOf().pointerTo()); | |
| } | |
| else if (ident == Id._tupleof) | |
| { | |
| if (e.isTypeExp()) | |
| { | |
| e.error("`.tupleof` cannot be used on type `%s`", mt.toChars); | |
| return ErrorExp.get(); | |
| } | |
| else | |
| { | |
| const length = cast(size_t)mt.dim.toUInteger(); | |
| auto exps = new Expressions(); | |
| exps.reserve(length); | |
| foreach (i; 0 .. length) | |
| exps.push(new IndexExp(e.loc, e, new IntegerExp(e.loc, i, Type.tsize_t))); | |
| e = new TupleExp(e.loc, exps); | |
| } | |
| } | |
| else | |
| { | |
| e = visitArray(mt); | |
| } | |
| if (!(flag & 1) || e) | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| Expression visitDArray(TypeDArray mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| if (e.op == EXP.type && (ident == Id.length || ident == Id.ptr)) | |
| { | |
| e.error("`%s` is not an expression", e.toChars()); | |
| return ErrorExp.get(); | |
| } | |
| if (ident == Id.length) | |
| { | |
| if (e.op == EXP.string_) | |
| { | |
| StringExp se = cast(StringExp)e; | |
| return new IntegerExp(se.loc, se.len, Type.tsize_t); | |
| } | |
| if (e.op == EXP.null_) | |
| { | |
| return new IntegerExp(e.loc, 0, Type.tsize_t); | |
| } | |
| if (checkNonAssignmentArrayOp(e)) | |
| { | |
| return ErrorExp.get(); | |
| } | |
| e = new ArrayLengthExp(e.loc, e); | |
| e.type = Type.tsize_t; | |
| return e; | |
| } | |
| else if (ident == Id.ptr) | |
| { | |
| if (checkUnsafeDotExp(sc, e, ident, flag)) | |
| return ErrorExp.get(); | |
| return e.castTo(sc, mt.next.pointerTo()); | |
| } | |
| else | |
| { | |
| return visitArray(mt); | |
| } | |
| } | |
| Expression visitAArray(TypeAArray mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| if (ident == Id.length) | |
| { | |
| __gshared FuncDeclaration fd_aaLen = null; | |
| if (fd_aaLen is null) | |
| { | |
| auto fparams = new Parameters(); | |
| fparams.push(new Parameter(STC.const_ | STC.scope_, mt, null, null, null)); | |
| fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); | |
| TypeFunction tf = fd_aaLen.type.toTypeFunction(); | |
| tf.purity = PURE.const_; | |
| tf.isnothrow = true; | |
| tf.isnogc = false; | |
| } | |
| Expression ev = new VarExp(e.loc, fd_aaLen, false); | |
| e = new CallExp(e.loc, ev, e); | |
| e.type = fd_aaLen.type.toTypeFunction().next; | |
| return e; | |
| } | |
| else | |
| { | |
| return visitType(mt); | |
| } | |
| } | |
| Expression visitReference(TypeReference mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| // References just forward things along | |
| return mt.next.dotExp(sc, e, ident, flag); | |
| } | |
| Expression visitDelegate(TypeDelegate mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| if (ident == Id.ptr) | |
| { | |
| e = new DelegatePtrExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| } | |
| else if (ident == Id.funcptr) | |
| { | |
| if (checkUnsafeDotExp(sc, e, ident, flag)) | |
| { | |
| return ErrorExp.get(); | |
| } | |
| e = new DelegateFuncptrExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| } | |
| else | |
| { | |
| return visitType(mt); | |
| } | |
| return e; | |
| } | |
| /*************************************** | |
| * `ident` was not found as a member of `mt`. | |
| * Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`. | |
| * If that fails, forward to visitType(). | |
| * Params: | |
| * mt = class or struct | |
| * sc = context | |
| * e = `this` for `ident` | |
| * ident = name of member | |
| * flag = flag & 1, don't report "not a property" error and just return NULL. | |
| * flag & DotExpFlag.noAliasThis, don't do 'alias this' resolution. | |
| * Returns: | |
| * resolved expression if found, otherwise null | |
| */ | |
| Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag) | |
| { | |
| //printf("Type.noMember(e: %s ident: %s flag: %d)\n", e.toChars(), ident.toChars(), flag); | |
| bool gagError = flag & 1; | |
| __gshared int nest; // https://issues.dlang.org/show_bug.cgi?id=17380 | |
| static Expression returnExp(Expression e) | |
| { | |
| --nest; | |
| return e; | |
| } | |
| if (++nest > global.recursionLimit) | |
| { | |
| .error(e.loc, "cannot resolve identifier `%s`", ident.toChars()); | |
| return returnExp(gagError ? null : ErrorExp.get()); | |
| } | |
| assert(mt.ty == Tstruct || mt.ty == Tclass); | |
| auto sym = mt.toDsymbol(sc).isAggregateDeclaration(); | |
| assert(sym); | |
| if (// https://issues.dlang.org/show_bug.cgi?id=22054 | |
| // if a class or struct does not have a body | |
| // there is no point in searching for its members | |
| sym.members && | |
| ident != Id.__sizeof && | |
| ident != Id.__xalignof && | |
| ident != Id._init && | |
| ident != Id._mangleof && | |
| ident != Id.stringof && | |
| ident != Id.offsetof && | |
| // https://issues.dlang.org/show_bug.cgi?id=15045 | |
| // Don't forward special built-in member functions. | |
| ident != Id.ctor && | |
| ident != Id.dtor && | |
| ident != Id.__xdtor && | |
| ident != Id.postblit && | |
| ident != Id.__xpostblit) | |
| { | |
| /* Look for overloaded opDot() to see if we should forward request | |
| * to it. | |
| */ | |
| if (auto fd = search_function(sym, Id.opDot)) | |
| { | |
| /* Rewrite e.ident as: | |
| * e.opDot().ident | |
| */ | |
| e = build_overload(e.loc, sc, e, null, fd); | |
| // @@@DEPRECATED_2.110@@@. | |
| // Deprecated in 2.082, made an error in 2.100. | |
| e.error("`opDot` is obsolete. Use `alias this`"); | |
| return ErrorExp.get(); | |
| } | |
| /* Look for overloaded opDispatch to see if we should forward request | |
| * to it. | |
| */ | |
| if (auto fd = search_function(sym, Id.opDispatch)) | |
| { | |
| /* Rewrite e.ident as: | |
| * e.opDispatch!("ident") | |
| */ | |
| TemplateDeclaration td = fd.isTemplateDeclaration(); | |
| if (!td) | |
| { | |
| fd.error("must be a template `opDispatch(string s)`, not a %s", fd.kind()); | |
| return returnExp(ErrorExp.get()); | |
| } | |
| auto se = new StringExp(e.loc, ident.toString()); | |
| auto tiargs = new Objects(); | |
| tiargs.push(se); | |
| auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs); | |
| dti.ti.tempdecl = td; | |
| /* opDispatch, which doesn't need IFTI, may occur instantiate error. | |
| * e.g. | |
| * template opDispatch(name) if (isValid!name) { ... } | |
| */ | |
| uint errors = gagError ? global.startGagging() : 0; | |
| e = dti.semanticY(sc, 0); | |
| if (gagError && global.endGagging(errors)) | |
| e = null; | |
| return returnExp(e); | |
| } | |
| /* See if we should forward to the alias this. | |
| */ | |
| auto alias_e = flag & DotExpFlag.noAliasThis ? null | |
| : resolveAliasThis(sc, e, gagError); | |
| if (alias_e && alias_e != e) | |
| { | |
| /* Rewrite e.ident as: | |
| * e.aliasthis.ident | |
| */ | |
| auto die = new DotIdExp(e.loc, alias_e, ident); | |
| auto errors = gagError ? 0 : global.startGagging(); | |
| auto exp = die.semanticY(sc, gagError); | |
| if (!gagError) | |
| { | |
| global.endGagging(errors); | |
| if (exp && exp.op == EXP.error) | |
| exp = null; | |
| } | |
| if (exp && gagError) | |
| // now that we know that the alias this leads somewhere useful, | |
| // go back and print deprecations/warnings that we skipped earlier due to the gag | |
| resolveAliasThis(sc, e, false); | |
| return returnExp(exp); | |
| } | |
| } | |
| return returnExp(visitType(mt)); | |
| } | |
| Expression visitStruct(TypeStruct mt) | |
| { | |
| Dsymbol s; | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| assert(e.op != EXP.dot); | |
| // https://issues.dlang.org/show_bug.cgi?id=14010 | |
| if (!(sc.flags & SCOPE.Cfile) && ident == Id._mangleof) | |
| { | |
| return mt.getProperty(sc, e.loc, ident, flag & 1); | |
| } | |
| /* If e.tupleof | |
| */ | |
| if (ident == Id._tupleof) | |
| { | |
| /* Create a TupleExp out of the fields of the struct e: | |
| * (e.field0, e.field1, e.field2, ...) | |
| */ | |
| e = e.expressionSemantic(sc); // do this before turning on noaccesscheck | |
| if (!mt.sym.determineFields()) | |
| { | |
| error(e.loc, "unable to determine fields of `%s` because of forward references", mt.toChars()); | |
| } | |
| Expression e0; | |
| Expression ev = e.op == EXP.type ? null : e; | |
| if (ev) | |
| ev = extractSideEffect(sc, "__tup", e0, ev); | |
| auto exps = new Expressions(); | |
| exps.reserve(mt.sym.fields.dim); | |
| for (size_t i = 0; i < mt.sym.fields.dim; i++) | |
| { | |
| VarDeclaration v = mt.sym.fields[i]; | |
| Expression ex; | |
| if (ev) | |
| ex = new DotVarExp(e.loc, ev, v); | |
| else | |
| { | |
| ex = new VarExp(e.loc, v); | |
| ex.type = ex.type.addMod(e.type.mod); | |
| } | |
| exps.push(ex); | |
| } | |
| e = new TupleExp(e.loc, e0, exps); | |
| Scope* sc2 = sc.push(); | |
| sc2.flags |= SCOPE.noaccesscheck; | |
| e = e.expressionSemantic(sc2); | |
| sc2.pop(); | |
| return e; | |
| } | |
| immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0; | |
| s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports); | |
| L1: | |
| if (!s) | |
| { | |
| return noMember(mt, sc, e, ident, flag); | |
| } | |
| if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) | |
| { | |
| return noMember(mt, sc, e, ident, flag); | |
| } | |
| s = s.toAlias(); | |
| if (auto em = s.isEnumMember()) | |
| { | |
| return em.getVarExp(e.loc, sc); | |
| } | |
| if (auto v = s.isVarDeclaration()) | |
| { | |
| v.checkDeprecated(e.loc, sc); | |
| v.checkDisabled(e.loc, sc); | |
| if (!v.type || | |
| !v.type.deco && v.inuse) | |
| { | |
| if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494 | |
| e.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars()); | |
| else | |
| e.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars()); | |
| return ErrorExp.get(); | |
| } | |
| if (v.type.ty == Terror) | |
| { | |
| return ErrorExp.get(); | |
| } | |
| if ((v.storage_class & STC.manifest) && v._init) | |
| { | |
| if (v.inuse) | |
| { | |
| e.error("circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); | |
| return ErrorExp.get(); | |
| } | |
| checkAccess(e.loc, sc, null, v); | |
| Expression ve = new VarExp(e.loc, v); | |
| if (!isTrivialExp(e)) | |
| { | |
| ve = new CommaExp(e.loc, e, ve); | |
| } | |
| return ve.expressionSemantic(sc); | |
| } | |
| } | |
| if (auto t = s.getType()) | |
| { | |
| return (new TypeExp(e.loc, t)).expressionSemantic(sc); | |
| } | |
| TemplateMixin tm = s.isTemplateMixin(); | |
| if (tm) | |
| { | |
| return new DotExp(e.loc, e, new ScopeExp(e.loc, tm)).expressionSemantic(sc); | |
| } | |
| TemplateDeclaration td = s.isTemplateDeclaration(); | |
| if (td) | |
| { | |
| if (e.op == EXP.type) | |
| e = new TemplateExp(e.loc, td); | |
| else | |
| e = new DotTemplateExp(e.loc, e, td); | |
| return e.expressionSemantic(sc); | |
| } | |
| TemplateInstance ti = s.isTemplateInstance(); | |
| if (ti) | |
| { | |
| if (!ti.semanticRun) | |
| { | |
| ti.dsymbolSemantic(sc); | |
| if (!ti.inst || ti.errors) // if template failed to expand | |
| { | |
| return ErrorExp.get(); | |
| } | |
| } | |
| s = ti.inst.toAlias(); | |
| if (!s.isTemplateInstance()) | |
| goto L1; | |
| if (e.op == EXP.type) | |
| e = new ScopeExp(e.loc, ti); | |
| else | |
| e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti)); | |
| return e.expressionSemantic(sc); | |
| } | |
| if (s.isImport() || s.isModule() || s.isPackage()) | |
| { | |
| return symbolToExp(s, e.loc, sc, false); | |
| } | |
| OverloadSet o = s.isOverloadSet(); | |
| if (o) | |
| { | |
| auto oe = new OverExp(e.loc, o); | |
| if (e.op == EXP.type) | |
| { | |
| return oe; | |
| } | |
| return new DotExp(e.loc, e, oe); | |
| } | |
| Declaration d = s.isDeclaration(); | |
| if (!d) | |
| { | |
| e.error("`%s.%s` is not a declaration", e.toChars(), ident.toChars()); | |
| return ErrorExp.get(); | |
| } | |
| if (e.op == EXP.type) | |
| { | |
| /* It's: | |
| * Struct.d | |
| */ | |
| if (TupleDeclaration tup = d.isTupleDeclaration()) | |
| { | |
| e = new TupleExp(e.loc, tup); | |
| return e.expressionSemantic(sc); | |
| } | |
| if (d.needThis() && sc.intypeof != 1) | |
| { | |
| /* Rewrite as: | |
| * this.d | |
| */ | |
| if (hasThis(sc)) | |
| { | |
| e = new DotVarExp(e.loc, new ThisExp(e.loc), d); | |
| return e.expressionSemantic(sc); | |
| } | |
| } | |
| if (d.semanticRun == PASS.initial) | |
| d.dsymbolSemantic(null); | |
| checkAccess(e.loc, sc, e, d); | |
| auto ve = new VarExp(e.loc, d); | |
| if (d.isVarDeclaration() && d.needThis()) | |
| ve.type = d.type.addMod(e.type.mod); | |
| return ve; | |
| } | |
| bool unreal = e.op == EXP.variable && (cast(VarExp)e).var.isField(); | |
| if (d.isDataseg() || unreal && d.isField()) | |
| { | |
| // (e, d) | |
| checkAccess(e.loc, sc, e, d); | |
| Expression ve = new VarExp(e.loc, d); | |
| e = unreal ? ve : new CommaExp(e.loc, e, ve); | |
| return e.expressionSemantic(sc); | |
| } | |
| e = new DotVarExp(e.loc, e, d); | |
| return e.expressionSemantic(sc); | |
| } | |
| Expression visitEnum(TypeEnum mt) | |
| { | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e.toChars(), ident.toChars(), mt.toChars()); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=14010 | |
| if (ident == Id._mangleof) | |
| { | |
| return mt.getProperty(sc, e.loc, ident, flag & 1); | |
| } | |
| if (mt.sym.semanticRun < PASS.semanticdone) | |
| mt.sym.dsymbolSemantic(null); | |
| Dsymbol s = mt.sym.search(e.loc, ident); | |
| if (!s) | |
| { | |
| if (ident == Id.max || ident == Id.min || ident == Id._init) | |
| { | |
| return mt.getProperty(sc, e.loc, ident, flag & 1); | |
| } | |
| Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1); | |
| if (!(flag & 1) && !res) | |
| { | |
| if (auto ns = mt.sym.search_correct(ident)) | |
| e.error("no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toChars(), mt.toChars(), mt.toChars(), | |
| ns.toChars()); | |
| else | |
| e.error("no property `%s` for type `%s`", ident.toChars(), | |
| mt.toChars()); | |
| return ErrorExp.get(); | |
| } | |
| return res; | |
| } | |
| EnumMember m = s.isEnumMember(); | |
| return m.getVarExp(e.loc, sc); | |
| } | |
| Expression visitClass(TypeClass mt) | |
| { | |
| Dsymbol s; | |
| static if (LOGDOTEXP) | |
| { | |
| printf("TypeClass::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); | |
| } | |
| assert(e.op != EXP.dot); | |
| // https://issues.dlang.org/show_bug.cgi?id=12543 | |
| if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof) | |
| { | |
| return mt.Type.getProperty(sc, e.loc, ident, 0); | |
| } | |
| /* If e.tupleof | |
| */ | |
| if (ident == Id._tupleof) | |
| { | |
| objc.checkTupleof(e, mt); | |
| /* Create a TupleExp | |
| */ | |
| e = e.expressionSemantic(sc); // do this before turning on noaccesscheck | |
| mt.sym.size(e.loc); // do semantic of type | |
| Expression e0; | |
| Expression ev = e.op == EXP.type ? null : e; | |
| if (ev) | |
| ev = extractSideEffect(sc, "__tup", e0, ev); | |
| auto exps = new Expressions(); | |
| exps.reserve(mt.sym.fields.dim); | |
| for (size_t i = 0; i < mt.sym.fields.dim; i++) | |
| { | |
| VarDeclaration v = mt.sym.fields[i]; | |
| // Don't include hidden 'this' pointer | |
| if (v.isThisDeclaration()) | |
| continue; | |
| Expression ex; | |
| if (ev) | |
| ex = new DotVarExp(e.loc, ev, v); | |
| else | |
| { | |
| ex = new VarExp(e.loc, v); | |
| ex.type = ex.type.addMod(e.type.mod); | |
| } | |
| exps.push(ex); | |
| } | |
| e = new TupleExp(e.loc, e0, exps); | |
| Scope* sc2 = sc.push(); | |
| sc2.flags |= SCOPE.noaccesscheck; | |
| e = e.expressionSemantic(sc2); | |
| sc2.pop(); | |
| return e; | |
| } | |
| int flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0; | |
| s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports); | |
| L1: | |
| if (!s) | |
| { | |
| // See if it's a 'this' class or a base class | |
| if (mt.sym.ident == ident) | |
| { | |
| if (e.op == EXP.type) | |
| { | |
| return mt.Type.getProperty(sc, e.loc, ident, 0); | |
| } | |
| e = new DotTypeExp(e.loc, e, mt.sym); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (auto cbase = mt.sym.searchBase(ident)) | |
| { | |
| if (e.op == EXP.type) | |
| { | |
| return mt.Type.getProperty(sc, e.loc, ident, 0); | |
| } | |
| if (auto ifbase = cbase.isInterfaceDeclaration()) | |
| e = new CastExp(e.loc, e, ifbase.type); | |
| else | |
| e = new DotTypeExp(e.loc, e, cbase); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (ident == Id.classinfo) | |
| { | |
| if (!Type.typeinfoclass) | |
| { | |
| error(e.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used"); | |
| return ErrorExp.get(); | |
| } | |
| Type t = Type.typeinfoclass.type; | |
| if (e.op == EXP.type || e.op == EXP.dotType) | |
| { | |
| /* For type.classinfo, we know the classinfo | |
| * at compile time. | |
| */ | |
| if (!mt.sym.vclassinfo) | |
| mt.sym.vclassinfo = new TypeInfoClassDeclaration(mt.sym.type); | |
| e = new VarExp(e.loc, mt.sym.vclassinfo); | |
| e = e.addressOf(); | |
| e.type = t; // do this so we don't get redundant dereference | |
| } | |
| else | |
| { | |
| /* For class objects, the classinfo reference is the first | |
| * entry in the vtbl[] | |
| */ | |
| e = new PtrExp(e.loc, e); | |
| e.type = t.pointerTo(); | |
| if (mt.sym.isInterfaceDeclaration()) | |
| { | |
| if (mt.sym.isCPPinterface()) | |
| { | |
| /* C++ interface vtbl[]s are different in that the | |
| * first entry is always pointer to the first virtual | |
| * function, not classinfo. | |
| * We can't get a .classinfo for it. | |
| */ | |
| error(e.loc, "no `.classinfo` for C++ interface objects"); | |
| } | |
| /* For an interface, the first entry in the vtbl[] | |
| * is actually a pointer to an instance of struct Interface. | |
| * The first member of Interface is the .classinfo, | |
| * so add an extra pointer indirection. | |
| */ | |
| e.type = e.type.pointerTo(); | |
| e = new PtrExp(e.loc, e); | |
| e.type = t.pointerTo(); | |
| } | |
| e = new PtrExp(e.loc, e, t); | |
| } | |
| return e; | |
| } | |
| if (ident == Id.__vptr) | |
| { | |
| /* The pointer to the vtbl[] | |
| * *cast(immutable(void*)**)e | |
| */ | |
| e = e.castTo(sc, mt.tvoidptr.immutableOf().pointerTo().pointerTo()); | |
| e = new PtrExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (ident == Id.__monitor && mt.sym.hasMonitor()) | |
| { | |
| /* The handle to the monitor (call it a void*) | |
| * *(cast(void**)e + 1) | |
| */ | |
| e = e.castTo(sc, mt.tvoidptr.pointerTo()); | |
| e = new AddExp(e.loc, e, IntegerExp.literal!1); | |
| e = new PtrExp(e.loc, e); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (ident == Id.outer && mt.sym.vthis) | |
| { | |
| if (mt.sym.vthis.semanticRun == PASS.initial) | |
| mt.sym.vthis.dsymbolSemantic(null); | |
| if (auto cdp = mt.sym.toParentLocal().isClassDeclaration()) | |
| { | |
| auto dve = new DotVarExp(e.loc, e, mt.sym.vthis); | |
| dve.type = cdp.type.addMod(e.type.mod); | |
| return dve; | |
| } | |
| /* https://issues.dlang.org/show_bug.cgi?id=15839 | |
| * Find closest parent class through nested functions. | |
| */ | |
| for (auto p = mt.sym.toParentLocal(); p; p = p.toParentLocal()) | |
| { | |
| auto fd = p.isFuncDeclaration(); | |
| if (!fd) | |
| break; | |
| auto ad = fd.isThis(); | |
| if (!ad && fd.isNested()) | |
| continue; | |
| if (!ad) | |
| break; | |
| if (auto cdp = ad.isClassDeclaration()) | |
| { | |
| auto ve = new ThisExp(e.loc); | |
| ve.var = fd.vthis; | |
| const nestedError = fd.vthis.checkNestedReference(sc, e.loc); | |
| assert(!nestedError); | |
| ve.type = cdp.type.addMod(fd.vthis.type.mod).addMod(e.type.mod); | |
| return ve; | |
| } | |
| break; | |
| } | |
| // Continue to show enclosing function's frame (stack or closure). | |
| auto dve = new DotVarExp(e.loc, e, mt.sym.vthis); | |
| dve.type = mt.sym.vthis.type.addMod(e.type.mod); | |
| return dve; | |
| } | |
| return noMember(mt, sc, e, ident, flag & 1); | |
| } | |
| if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) | |
| { | |
| return noMember(mt, sc, e, ident, flag); | |
| } | |
| if (!s.isFuncDeclaration()) // because of overloading | |
| { | |
| s.checkDeprecated(e.loc, sc); | |
| if (auto d = s.isDeclaration()) | |
| d.checkDisabled(e.loc, sc); | |
| } | |
| s = s.toAlias(); | |
| if (auto em = s.isEnumMember()) | |
| { | |
| return em.getVarExp(e.loc, sc); | |
| } | |
| if (auto v = s.isVarDeclaration()) | |
| { | |
| if (!v.type || | |
| !v.type.deco && v.inuse) | |
| { | |
| if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494 | |
| e.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars()); | |
| else | |
| e.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars()); | |
| return ErrorExp.get(); | |
| } | |
| if (v.type.ty == Terror) | |
| { | |
| return ErrorExp.get(); | |
| } | |
| if ((v.storage_class & STC.manifest) && v._init) | |
| { | |
| if (v.inuse) | |
| { | |
| e.error("circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); | |
| return ErrorExp.get(); | |
| } | |
| checkAccess(e.loc, sc, null, v); | |
| Expression ve = new VarExp(e.loc, v); | |
| ve = ve.expressionSemantic(sc); | |
| return ve; | |
| } | |
| } | |
| if (auto t = s.getType()) | |
| { | |
| return (new TypeExp(e.loc, t)).expressionSemantic(sc); | |
| } | |
| TemplateMixin tm = s.isTemplateMixin(); | |
| if (tm) | |
| { | |
| return new DotExp(e.loc, e, new ScopeExp(e.loc, tm)).expressionSemantic(sc); | |
| } | |
| TemplateDeclaration td = s.isTemplateDeclaration(); | |
| Expression toTemplateExp(TemplateDeclaration td) | |
| { | |
| if (e.op == EXP.type) | |
| e = new TemplateExp(e.loc, td); | |
| else | |
| e = new DotTemplateExp(e.loc, e, td); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (td) | |
| { | |
| return toTemplateExp(td); | |
| } | |
| TemplateInstance ti = s.isTemplateInstance(); | |
| if (ti) | |
| { | |
| if (!ti.semanticRun) | |
| { | |
| ti.dsymbolSemantic(sc); | |
| if (!ti.inst || ti.errors) // if template failed to expand | |
| { | |
| return ErrorExp.get(); | |
| } | |
| } | |
| s = ti.inst.toAlias(); | |
| if (!s.isTemplateInstance()) | |
| goto L1; | |
| if (e.op == EXP.type) | |
| e = new ScopeExp(e.loc, ti); | |
| else | |
| e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti)); | |
| return e.expressionSemantic(sc); | |
| } | |
| if (s.isImport() || s.isModule() || s.isPackage()) | |
| { | |
| e = symbolToExp(s, e.loc, sc, false); | |
| return e; | |
| } | |
| OverloadSet o = s.isOverloadSet(); | |
| if (o) | |
| { | |
| auto oe = new OverExp(e.loc, o); | |
| if (e.op == EXP.type) | |
| { | |
| return oe; | |
| } | |
| return new DotExp(e.loc, e, oe); | |
| } | |
| Declaration d = s.isDeclaration(); | |
| if (!d) | |
| { | |
| e.error("`%s.%s` is not a declaration", e.toChars(), ident.toChars()); | |
| return ErrorExp.get(); | |
| } | |
| if (e.op == EXP.type) | |
| { | |
| /* It's: | |
| * Class.d | |
| */ | |
| if (TupleDeclaration tup = d.isTupleDeclaration()) | |
| { | |
| e = new TupleExp(e.loc, tup); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (mt.sym.classKind == ClassKind.objc | |
| && d.isFuncDeclaration() | |
| && d.isFuncDeclaration().isStatic | |
| && d.isFuncDeclaration().objc.selector) | |
| { | |
| auto classRef = new ObjcClassReferenceExp(e.loc, mt.sym); | |
| return new DotVarExp(e.loc, classRef, d).expressionSemantic(sc); | |
| } | |
| else if (d.needThis() && sc.intypeof != 1) | |
| { | |
| /* Rewrite as: | |
| * this.d | |
| */ | |
| AggregateDeclaration ad = d.isMemberLocal(); | |
| if (auto f = hasThis(sc)) | |
| { | |
| // This is almost same as getRightThis() in expressionsem.d | |
| Expression e1; | |
| Type t; | |
| /* returns: true to continue, false to return */ | |
| if (f.hasDualContext()) | |
| { | |
| if (f.followInstantiationContext(ad)) | |
| { | |
| e1 = new VarExp(e.loc, f.vthis); | |
| e1 = new PtrExp(e1.loc, e1); | |
| e1 = new IndexExp(e1.loc, e1, IntegerExp.literal!1); | |
| auto pd = f.toParent2().isDeclaration(); | |
| assert(pd); | |
| t = pd.type.toBasetype(); | |
| e1 = getThisSkipNestedFuncs(e1.loc, sc, f.toParent2(), ad, e1, t, d, true); | |
| if (!e1) | |
| { | |
| e = new VarExp(e.loc, d); | |
| return e; | |
| } | |
| goto L2; | |
| } | |
| } | |
| e1 = new ThisExp(e.loc); | |
| e1 = e1.expressionSemantic(sc); | |
| L2: | |
| t = e1.type.toBasetype(); | |
| ClassDeclaration cd = e.type.isClassHandle(); | |
| ClassDeclaration tcd = t.isClassHandle(); | |
| if (cd && tcd && (tcd == cd || cd.isBaseOf(tcd, null))) | |
| { | |
| e = new DotTypeExp(e1.loc, e1, cd); | |
| e = new DotVarExp(e.loc, e, d); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| if (tcd && tcd.isNested()) | |
| { | |
| /* e1 is the 'this' pointer for an inner class: tcd. | |
| * Rewrite it as the 'this' pointer for the outer class. | |
| */ | |
| auto vthis = tcd.followInstantiationContext(ad) ? tcd.vthis2 : tcd.vthis; | |
| e1 = new DotVarExp(e.loc, e1, vthis); | |
| e1.type = vthis.type; | |
| e1.type = e1.type.addMod(t.mod); | |
| // Do not call ensureStaticLinkTo() | |
| //e1 = e1.expressionSemantic(sc); | |
| // Skip up over nested functions, and get the enclosing | |
| // class type. | |
| e1 = getThisSkipNestedFuncs(e1.loc, sc, tcd.toParentP(ad), ad, e1, t, d, true); | |
| if (!e1) | |
| { | |
| e = new VarExp(e.loc, d); | |
| return e; | |
| } | |
| goto L2; | |
| } | |
| } | |
| } | |
| //printf("e = %s, d = %s\n", e.toChars(), d.toChars()); | |
| if (d.semanticRun == PASS.initial) | |
| d.dsymbolSemantic(null); | |
| // If static function, get the most visible overload. | |
| // Later on the call is checked for correctness. | |
| // https://issues.dlang.org/show_bug.cgi?id=12511 | |
| Dsymbol d2 = d; | |
| if (auto fd = d.isFuncDeclaration()) | |
| { | |
| import dmd.access : mostVisibleOverload; | |
| d2 = mostVisibleOverload(fd, sc._module); | |
| } | |
| checkAccess(e.loc, sc, e, d2); | |
| if (d2.isDeclaration()) | |
| { | |
| d = cast(Declaration)d2; | |
| auto ve = new VarExp(e.loc, d); | |
| if (d.isVarDeclaration() && d.needThis()) | |
| ve.type = d.type.addMod(e.type.mod); | |
| return ve; | |
| } | |
| else if (d2.isTemplateDeclaration()) | |
| { | |
| return toTemplateExp(cast(TemplateDeclaration)d2); | |
| } | |
| else | |
| assert(0); | |
| } | |
| bool unreal = e.op == EXP.variable && (cast(VarExp)e).var.isField(); | |
| if (d.isDataseg() || unreal && d.isField()) | |
| { | |
| // (e, d) | |
| checkAccess(e.loc, sc, e, d); | |
| Expression ve = new VarExp(e.loc, d); | |
| e = unreal ? ve : new CommaExp(e.loc, e, ve); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| e = new DotVarExp(e.loc, e, d); | |
| e = e.expressionSemantic(sc); | |
| return e; | |
| } | |
| switch (mt.ty) | |
| { | |
| case Tvector: return visitVector (mt.isTypeVector()); | |
| case Tsarray: return visitSArray (mt.isTypeSArray()); | |
| case Tstruct: return visitStruct (mt.isTypeStruct()); | |
| case Tenum: return visitEnum (mt.isTypeEnum()); | |
| case Terror: return visitError (mt.isTypeError()); | |
| case Tarray: return visitDArray (mt.isTypeDArray()); | |
| case Taarray: return visitAArray (mt.isTypeAArray()); | |
| case Treference: return visitReference(mt.isTypeReference()); | |
| case Tdelegate: return visitDelegate (mt.isTypeDelegate()); | |
| case Tclass: return visitClass (mt.isTypeClass()); | |
| default: return mt.isTypeBasic() | |
| ? visitBasic(cast(TypeBasic)mt) | |
| : visitType(mt); | |
| } | |
| } | |
| /************************ | |
| * Get the the default initialization expression for a type. | |
| * Params: | |
| * mt = the type for which the init expression is returned | |
| * loc = the location where the expression needs to be evaluated | |
| * isCfile = default initializers are different with C | |
| * | |
| * Returns: | |
| * The initialization expression for the type. | |
| */ | |
| extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfile = false) | |
| { | |
| Expression visitBasic(TypeBasic mt) | |
| { | |
| static if (LOGDEFAULTINIT) | |
| { | |
| printf("TypeBasic::defaultInit() '%s' isCfile: %d\n", mt.toChars(), isCfile); | |
| } | |
| dinteger_t value = 0; | |
| switch (mt.ty) | |
| { | |
| case Tchar: | |
| value = isCfile ? 0 : 0xFF; | |
| break; | |
| case Twchar: | |
| case Tdchar: | |
| value = isCfile ? 0 : 0xFFFF; | |
| break; | |
| case Timaginary32: | |
| case Timaginary64: | |
| case Timaginary80: | |
| case Tfloat32: | |
| case Tfloat64: | |
| case Tfloat80: | |
| return new RealExp(loc, isCfile ? CTFloat.zero : target.RealProperties.nan, mt); | |
| case Tcomplex32: | |
| case Tcomplex64: | |
| case Tcomplex80: | |
| { | |
| // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN). | |
| const cvalue = isCfile ? complex_t(CTFloat.zero, CTFloat.zero) | |
| : complex_t(target.RealProperties.nan, target.RealProperties.nan); | |
| return new ComplexExp(loc, cvalue, mt); | |
| } | |
| case Tvoid: | |
| error(loc, "`void` does not have a default initializer"); | |
| return ErrorExp.get(); | |
| default: | |
| break; | |
| } | |
| return new IntegerExp(loc, value, mt); | |
| } | |
| Expression visitVector(TypeVector mt) | |
| { | |
| //printf("TypeVector::defaultInit()\n"); | |
| assert(mt.basetype.ty == Tsarray); | |
| Expression e = mt.basetype.defaultInit(loc, isCfile); | |
| auto ve = new VectorExp(loc, e, mt); | |
| ve.type = mt; | |
| ve.dim = cast(int)(mt.basetype.size(loc) / mt.elementType().size(loc)); | |
| return ve; | |
| } | |
| Expression visitSArray(TypeSArray mt) | |
| { | |
| static if (LOGDEFAULTINIT) | |
| { | |
| printf("TypeSArray::defaultInit() '%s' isCfile %d\n", mt.toChars(), isCfile); | |
| } | |
| if (mt.next.ty == Tvoid) | |
| return mt.tuns8.defaultInit(loc, isCfile); | |
| else | |
| return mt.next.defaultInit(loc, isCfile); | |
| } | |
| Expression visitFunction(TypeFunction mt) | |
| { | |
| error(loc, "`function` does not have a default initializer"); | |
| return ErrorExp.get(); | |
| } | |
| Expression visitStruct(TypeStruct mt) | |
| { | |
| static if (LOGDEFAULTINIT) | |
| { | |
| printf("TypeStruct::defaultInit() '%s'\n", mt.toChars()); | |
| } | |
| Declaration d = new SymbolDeclaration(mt.sym.loc, mt.sym); | |
| assert(d); | |
| d.type = mt; | |
| d.storage_class |= STC.rvalue; // https://issues.dlang.org/show_bug.cgi?id=14398 | |
| return new VarExp(mt.sym.loc, d); | |
| } | |
| Expression visitEnum(TypeEnum mt) | |
| { | |
| static if (LOGDEFAULTINIT) | |
| { | |
| printf("TypeEnum::defaultInit() '%s'\n", mt.toChars()); | |
| } | |
| // Initialize to first member of enum | |
| Expression e = mt.sym.getDefaultValue(loc); | |
| e = e.copy(); | |
| e.loc = loc; | |
| e.type = mt; // to deal with const, immutable, etc., variants | |
| return e; | |
| } | |
| Expression visitTuple(TypeTuple mt) | |
| { | |
| static if (LOGDEFAULTINIT) | |
| { | |
| printf("TypeTuple::defaultInit() '%s'\n", mt.toChars()); | |
| } | |
| auto exps = new Expressions(mt.arguments.dim); | |
| for (size_t i = 0; i < mt.arguments.dim; i++) | |
| { | |
| Parameter p = (*mt.arguments)[i]; | |
| assert(p.type); | |
| Expression e = p.type.defaultInitLiteral(loc); | |
| if (e.op == EXP.error) | |
| { | |
| return e; | |
| } | |
| (*exps)[i] = e; | |
| } | |
| return new TupleExp(loc, exps); | |
| } | |
| Expression visitNoreturn(TypeNoreturn mt) | |
| { | |
| static if (LOGDEFAULTINIT) | |
| { | |
| printf("TypeNoreturn::defaultInit() '%s'\n", mt.toChars()); | |
| } | |
| auto cond = IntegerExp.createBool(false); | |
| auto msg = new StringExp(loc, "Accessed expression of type `noreturn`"); | |
| msg.type = Type.tstring; | |
| auto ae = new AssertExp(loc, cond, msg); | |
| ae.type = mt; | |
| return ae; | |
| } | |
| switch (mt.ty) | |
| { | |
| case Tvector: return visitVector (mt.isTypeVector()); | |
| case Tsarray: return visitSArray (mt.isTypeSArray()); | |
| case Tfunction: return visitFunction(mt.isTypeFunction()); | |
| case Tstruct: return visitStruct (mt.isTypeStruct()); | |
| case Tenum: return visitEnum (mt.isTypeEnum()); | |
| case Ttuple: return visitTuple (mt.isTypeTuple()); | |
| case Tnull: return new NullExp(Loc.initial, Type.tnull); | |
| case Terror: return ErrorExp.get(); | |
| case Tarray: | |
| case Taarray: | |
| case Tpointer: | |
| case Treference: | |
| case Tdelegate: | |
| case Tclass: return new NullExp(loc, mt); | |
| case Tnoreturn: return visitNoreturn(mt.isTypeNoreturn()); | |
| default: return mt.isTypeBasic() ? | |
| visitBasic(cast(TypeBasic)mt) : | |
| null; | |
| } | |
| } | |
| /****************************** | |
| * Get the value of the .max/.min property of `ed` as an Expression. | |
| * Lazily computes the value and caches it in maxval/minval. | |
| * Reports any errors. | |
| * Params: | |
| * ed = the EnumDeclaration being examined | |
| * loc = location to use for error messages | |
| * id = Id::max or Id::min | |
| * Returns: | |
| * corresponding value of .max/.min | |
| */ | |
| private Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) | |
| { | |
| //printf("EnumDeclaration::getMaxValue()\n"); | |
| static Expression pvalToResult(Expression e, const ref Loc loc) | |
| { | |
| if (e.op != EXP.error) | |
| { | |
| e = e.copy(); | |
| e.loc = loc; | |
| } | |
| return e; | |
| } | |
| Expression* pval = (id == Id.max) ? &ed.maxval : &ed.minval; | |
| Expression errorReturn() | |
| { | |
| *pval = ErrorExp.get(); | |
| return *pval; | |
| } | |
| if (ed.inuse) | |
| { | |
| ed.error(loc, "recursive definition of `.%s` property", id.toChars()); | |
| return errorReturn(); | |
| } | |
| if (*pval) | |
| return pvalToResult(*pval, loc); | |
| if (ed._scope) | |
| dsymbolSemantic(ed, ed._scope); | |
| if (ed.errors) | |
| return errorReturn(); | |
| if (!ed.members) | |
| { | |
| if (ed.isSpecial()) | |
| { | |
| /* Allow these special enums to not need a member list | |
| */ | |
| return ed.memtype.getProperty(ed._scope, loc, id, 0); | |
| } | |
| ed.error(loc, "is opaque and has no `.%s`", id.toChars()); | |
| return errorReturn(); | |
| } | |
| if (!(ed.memtype && ed.memtype.isintegral())) | |
| { | |
| ed.error(loc, "has no `.%s` property because base type `%s` is not an integral type", | |
| id.toChars(), ed.memtype ? ed.memtype.toChars() : ""); | |
| return errorReturn(); | |
| } | |
| bool first = true; | |
| for (size_t i = 0; i < ed.members.dim; i++) | |
| { | |
| EnumMember em = (*ed.members)[i].isEnumMember(); | |
| if (!em) | |
| continue; | |
| if (em.errors) | |
| { | |
| ed.errors = true; | |
| continue; | |
| } | |
| if (em.semanticRun < PASS.semanticdone) | |
| { | |
| em.error("is forward referenced looking for `.%s`", id.toChars()); | |
| ed.errors = true; | |
| continue; | |
| } | |
| if (first) | |
| { | |
| *pval = em.value; | |
| first = false; | |
| } | |
| else | |
| { | |
| /* In order to work successfully with UDTs, | |
| * build expressions to do the comparisons, | |
| * and let the semantic analyzer and constant | |
| * folder give us the result. | |
| */ | |
| /* Compute: | |
| * if (e > maxval) | |
| * maxval = e; | |
| */ | |
| Expression e = em.value; | |
| Expression ec = new CmpExp(id == Id.max ? EXP.greaterThan : EXP.lessThan, em.loc, e, *pval); | |
| ed.inuse++; | |
| ec = ec.expressionSemantic(em._scope); | |
| ed.inuse--; | |
| ec = ec.ctfeInterpret(); | |
| if (ec.op == EXP.error) | |
| { | |
| ed.errors = true; | |
| continue; | |
| } | |
| if (ec.toInteger()) | |
| *pval = e; | |
| } | |
| } | |
| return ed.errors ? errorReturn() : pvalToResult(*pval, loc); | |
| } |