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