| /* | |
| * Copyright (C) 2017 Apple Inc. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| "use strict"; | |
| // This only resolves names. After this phase runs, all names will be resolved. This means that all | |
| // XYZRef objects now know who they resolve to. This does not yet allow type analysis, because there | |
| // are still TypeDefs. This throws type errors for failed name resolutions and for things that are | |
| // more convenient to check here than in the Checker. This usually involves things that need to be | |
| // checked before TypeRefToTypeDefSkipper. | |
| class NameResolver extends Visitor { | |
| constructor(nameContext) | |
| { | |
| super(); | |
| this._nameContext = nameContext; | |
| } | |
| doStatement(statement) | |
| { | |
| this._nameContext.doStatement(statement, () => statement.visit(this)); | |
| } | |
| _visitTypeParametersAndBuildNameContext(node) | |
| { | |
| let nameContext = new NameContext(this._nameContext); | |
| for (let typeParameter of node.typeParameters) { | |
| nameContext.add(typeParameter); | |
| typeParameter.visit(this); | |
| } | |
| return nameContext; | |
| } | |
| visitFunc(node) | |
| { | |
| let checker = new NameResolver(this._visitTypeParametersAndBuildNameContext(node)); | |
| node.returnType.visit(checker); | |
| for (let parameter of node.parameters) | |
| parameter.visit(checker); | |
| } | |
| visitFuncDef(node) | |
| { | |
| let contextWithTypeParameters = this._visitTypeParametersAndBuildNameContext(node); | |
| let checkerWithTypeParameters = new NameResolver(contextWithTypeParameters); | |
| node.returnType.visit(checkerWithTypeParameters); | |
| let contextWithParameters = new NameContext(contextWithTypeParameters); | |
| for (let parameter of node.parameters) { | |
| parameter.visit(checkerWithTypeParameters); | |
| contextWithParameters.add(parameter); | |
| } | |
| let checkerWithParameters = new NameResolver(contextWithParameters); | |
| node.body.visit(checkerWithParameters); | |
| } | |
| visitBlock(node) | |
| { | |
| let checker = new NameResolver(new NameContext(this._nameContext)); | |
| for (let statement of node.statements) | |
| statement.visit(checker); | |
| } | |
| visitIfStatement(node) | |
| { | |
| node.conditional.visit(this); | |
| // The bodies might not be Blocks, so we need to explicitly give them a new context. | |
| node.body.visit(new NameResolver(new NameContext(this._nameContext))); | |
| if (node.elseBody) | |
| node.elseBody.visit(new NameResolver(new NameContext(this._nameContext))); | |
| } | |
| visitWhileLoop(node) | |
| { | |
| node.conditional.visit(this); | |
| // The bodies might not be Blocks, so we need to explicitly give them a new context. | |
| node.body.visit(new NameResolver(new NameContext(this._nameContext))); | |
| } | |
| visitDoWhileLoop(node) | |
| { | |
| // The bodies might not be Blocks, so we need to explicitly give them a new context. | |
| node.body.visit(new NameResolver(new NameContext(this._nameContext))); | |
| node.conditional.visit(this); | |
| } | |
| visitForLoop(node) | |
| { | |
| let newResolver = new NameResolver(new NameContext(this._nameContext)) | |
| if (node.initialization) | |
| node.initialization.visit(newResolver); | |
| if (node.condition) | |
| node.condition.visit(newResolver); | |
| if (node.increment) | |
| node.increment.visit(newResolver); | |
| node.body.visit(newResolver); | |
| } | |
| visitProtocolDecl(node) | |
| { | |
| for (let parent of node.extends) | |
| parent.visit(this); | |
| let nameContext = new NameContext(this._nameContext); | |
| nameContext.add(node.typeVariable); | |
| let checker = new NameResolver(nameContext); | |
| for (let signature of node.signatures) | |
| signature.visit(checker); | |
| } | |
| visitProtocolRef(node) | |
| { | |
| let result = this._nameContext.get(Protocol, node.name); | |
| if (!result) | |
| throw new WTypeError(node.origin.originString, "Could not find protocol named " + node.name); | |
| node.protocolDecl = result; | |
| } | |
| visitProtocolFuncDecl(node) | |
| { | |
| this.visitFunc(node); | |
| let funcs = this._nameContext.get(Func, node.name); | |
| if (!funcs) | |
| throw new WTypeError(node.origin.originString, "Cannot find any functions named " + node.name); | |
| node.possibleOverloads = funcs; | |
| } | |
| visitTypeDef(node) | |
| { | |
| let nameContext = new NameContext(this._nameContext); | |
| for (let typeParameter of node.typeParameters) { | |
| typeParameter.visit(this); | |
| nameContext.add(typeParameter); | |
| } | |
| let checker = new NameResolver(nameContext); | |
| node.type.visit(checker); | |
| } | |
| visitStructType(node) | |
| { | |
| let nameContext = new NameContext(this._nameContext); | |
| for (let typeParameter of node.typeParameters) { | |
| typeParameter.visit(this); | |
| nameContext.add(typeParameter); | |
| } | |
| let checker = new NameResolver(nameContext); | |
| for (let field of node.fields) | |
| field.visit(checker); | |
| } | |
| _resolveTypeArguments(typeArguments) | |
| { | |
| for (let i = 0; i < typeArguments.length; ++i) { | |
| let typeArgument = typeArguments[i]; | |
| if (typeArgument instanceof TypeOrVariableRef) { | |
| let thing = this._nameContext.get(Anything, typeArgument.name); | |
| if (!thing) | |
| new WTypeError(typeArgument.origin.originString, "Could not find type or variable named " + typeArgument.name); | |
| if (thing instanceof Value) | |
| typeArguments[i] = new VariableRef(typeArgument.origin, typeArgument.name); | |
| else if (thing instanceof Type) | |
| typeArguments[i] = new TypeRef(typeArgument.origin, typeArgument.name, []); | |
| else | |
| throw new WTypeError(typeArgument.origin.originString, "Type argument resolved to wrong kind of thing: " + thing.kind); | |
| } | |
| if (typeArgument[i] instanceof Value | |
| && !typeArgument[i].isConstexpr) | |
| throw new WTypeError(typeArgument[i].origin.originString, "Expected constexpr"); | |
| } | |
| } | |
| visitTypeRef(node) | |
| { | |
| this._resolveTypeArguments(node.typeArguments); | |
| let type = node.type; | |
| if (!type) { | |
| type = this._nameContext.get(Type, node.name); | |
| if (!type) | |
| throw new WTypeError(node.origin.originString, "Could not find type named " + node.name); | |
| node.type = type; | |
| } | |
| if (type.typeParameters.length != node.typeArguments.length) | |
| throw new WTypeError(node.origin.originString, "Wrong number of type arguments (passed " + node.typeArguments.length + ", expected " + type.typeParameters.length + ")"); | |
| for (let i = 0; i < type.typeParameters.length; ++i) { | |
| let parameterIsType = type.typeParameters[i] instanceof TypeVariable; | |
| let argumentIsType = node.typeArguments[i] instanceof Type; | |
| node.typeArguments[i].visit(this); | |
| if (parameterIsType && !argumentIsType) | |
| throw new WTypeError(node.origin.originString, "Expected type, but got value at argument #" + i); | |
| if (!parameterIsType && argumentIsType) | |
| throw new WTypeError(node.origin.originString, "Expected value, but got type at argument #" + i); | |
| } | |
| } | |
| visitReferenceType(node) | |
| { | |
| let nameContext = new NameContext(this._nameContext); | |
| node.elementType.visit(new NameResolver(nameContext)); | |
| } | |
| visitVariableDecl(node) | |
| { | |
| this._nameContext.add(node); | |
| node.type.visit(this); | |
| if (node.initializer) | |
| node.initializer.visit(this); | |
| } | |
| visitVariableRef(node) | |
| { | |
| if (node.variable) | |
| return; | |
| let result = this._nameContext.get(Value, node.name); | |
| if (!result) | |
| throw new WTypeError(node.origin.originString, "Could not find variable named " + node.name); | |
| node.variable = result; | |
| } | |
| visitReturn(node) | |
| { | |
| node.func = this._nameContext.currentStatement; | |
| super.visitReturn(node); | |
| } | |
| _handlePropertyAccess(node) | |
| { | |
| node.possibleGetOverloads = this._nameContext.get(Func, node.getFuncName); | |
| node.possibleSetOverloads = this._nameContext.get(Func, node.setFuncName); | |
| node.possibleAndOverloads = this._nameContext.get(Func, node.andFuncName); | |
| if (!node.possibleGetOverloads && !node.possibleAndOverloads) | |
| throw new WTypeError(node.origin.originString, "Cannot find either " + node.getFuncName + " or " + node.andFuncName); | |
| } | |
| visitDotExpression(node) | |
| { | |
| // This could be a reference to an enum. Let's resolve that now. | |
| if (node.struct instanceof VariableRef) { | |
| let enumType = this._nameContext.get(Type, node.struct.name); | |
| if (enumType && enumType instanceof EnumType) { | |
| let enumMember = enumType.memberByName(node.fieldName); | |
| if (!enumMember) | |
| throw new WTypeError(node.origin.originString, "Enum " + enumType.name + " does not have a member named " + node.fieldName); | |
| node.become(new EnumLiteral(node.origin, enumMember)); | |
| return; | |
| } | |
| } | |
| this._handlePropertyAccess(node); | |
| super.visitDotExpression(node); | |
| } | |
| visitIndexExpression(node) | |
| { | |
| this._handlePropertyAccess(node); | |
| super.visitIndexExpression(node); | |
| } | |
| visitCallExpression(node) | |
| { | |
| this._resolveTypeArguments(node.typeArguments); | |
| let funcs = this._nameContext.get(Func, node.name); | |
| if (funcs) | |
| node.possibleOverloads = funcs; | |
| else { | |
| let type = this._nameContext.get(Type, node.name); | |
| if (!type) | |
| throw new WTypeError(node.origin.originString, "Cannot find any function or type named \"" + node.name + "\""); | |
| node.becomeCast(type); | |
| node.possibleOverloads = this._nameContext.get(Func, "operator cast"); | |
| if (!node.possibleOverloads) | |
| throw new WTypeError(node.origin.originString, "Cannot find any operator cast implementations in cast to " + type); | |
| } | |
| super.visitCallExpression(node); | |
| } | |
| } | |