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