// scopeAssignedValue scopes a named assigned value exported into the context.
func (sb *scopeBuilder) scopeAssignedValue(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// If the assigned value's name is _, then it is anonymous scope.
	if node.Get(parser.NodeNamedValueName) == ANONYMOUS_REFERENCE {
		return newScope().ForAnonymousScope(sb.sg.tdg).GetScope()
	}

	// If the assigned value is under a rejection, then it is always an error (but nullable, as it
	// may not be present always).
	if _, ok := node.TryGetIncomingNode(parser.NodeAssignedRejection); ok {
		return newScope().Valid().Assignable(sb.sg.tdg.ErrorTypeReference().AsNullable()).GetScope()
	}

	// Otherwise, the value is the assignment of the parent statement's expression.
	parentNode := node.GetIncomingNode(parser.NodeAssignedDestination)
	switch parentNode.Kind() {
	case parser.NodeTypeResolveStatement:
		// The assigned value exported by a resolve statement has the type of its expression.
		exprScope := sb.getScope(parentNode.GetNode(parser.NodeResolveStatementSource), context)
		if !exprScope.GetIsValid() {
			return newScope().Invalid().GetScope()
		}

		// If the parent node has a rejection, then the expression may be null.
		exprType := exprScope.ResolvedTypeRef(sb.sg.tdg)
		if _, ok := parentNode.TryGetNode(parser.NodeAssignedRejection); ok {
			exprType = exprType.AsNullable()
		}

		return newScope().Valid().Assignable(exprType).GetScope()

	default:
		panic(fmt.Sprintf("Unknown node exporting an assigned value: %v", parentNode.Kind()))
		return newScope().Invalid().GetScope()
	}
}
// scopeStructuralNewExpressionEntry scopes a single entry in a structural new expression.
func (sb *scopeBuilder) scopeStructuralNewExpressionEntry(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	parentNode := node.GetIncomingNode(parser.NodeStructuralNewExpressionChildEntry)
	parentExprScope := sb.getScope(parentNode.GetNode(parser.NodeStructuralNewTypeExpression), context)

	parentType := parentExprScope.StaticTypeRef(sb.sg.tdg)
	if parentExprScope.GetKind() == proto.ScopeKind_VALUE {
		parentType = parentExprScope.ResolvedTypeRef(sb.sg.tdg)
	}

	entryName := node.Get(parser.NodeStructuralNewEntryKey)

	// Get the scope for the value.
	valueScope := sb.getScope(node.GetNode(parser.NodeStructuralNewEntryValue), context)
	if !valueScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Lookup the member associated with the entry name.
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
	member, rerr := parentType.ResolveAccessibleMember(entryName, module, typegraph.MemberResolutionInstance)
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().GetScope()
	}

	// Ensure the member is assignable.
	if member.IsReadOnly() {
		sb.decorateWithError(node, "%v %v under type %v is read-only", member.Title(), member.Name(), parentType)
		return newScope().Invalid().GetScope()
	}

	// Get the member's assignable type, transformed under the parent type, and ensure it is assignable
	// from the type of the value.
	assignableType := member.AssignableType().TransformUnder(parentType)
	valueType := valueScope.ResolvedTypeRef(sb.sg.tdg)

	if aerr := valueType.CheckSubTypeOf(assignableType); aerr != nil {
		sb.decorateWithError(node, "Cannot assign value of type %v to %v %v: %v", valueType, member.Title(), member.Name(), aerr)
		return newScope().Invalid().GetScope()
	}

	return newScope().ForNamedScope(sb.getNamedScopeForMember(member), context).Valid().GetScope()
}
Exemple #3
0
// getPackageForImport returns the package information for the package imported by the given import
// package node.
func (g *SRG) getPackageForImport(importPackageNode compilergraph.GraphNode) importedPackage {
	importNode := importPackageNode.GetIncomingNode(parser.NodeImportPredicatePackageRef)

	// Note: There may not be a kind, in which case this will return empty string, which is the
	// default kind.
	packageKind, _ := importNode.TryGet(parser.NodeImportPredicateKind)
	packageLocation := importNode.Get(parser.NodeImportPredicateLocation)

	packageInfo, ok := g.packageMap.Get(packageKind, packageLocation)
	if !ok {
		source := importNode.Get(parser.NodeImportPredicateSource)
		subsource, _ := importPackageNode.TryGet(parser.NodeImportPredicateSubsource)
		panic(fmt.Sprintf("Missing package info for import %s %s (reference %v) (node %v)\nPackage Map: %v",
			source, subsource, packageLocation, importNode, g.packageMap))
	}

	return importedPackage{
		srg:          g,
		packageInfo:  packageInfo,
		importSource: compilercommon.InputSource(importPackageNode.Get(parser.NodePredicateSource)),
	}
}
// scopeNamedValue scopes a named value exported by a with or loop statement into context.
func (sb *scopeBuilder) scopeNamedValue(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// If the value's name is _, then it is anonymous scope.
	if node.Get(parser.NodeNamedValueName) == ANONYMOUS_REFERENCE {
		return newScope().ForAnonymousScope(sb.sg.tdg).GetScope()
	}

	// Find the parent node creating this named value.
	parentNode := node.GetIncomingNode(parser.NodeStatementNamedValue)

	switch parentNode.Kind() {
	case parser.NodeTypeWithStatement:
		// The named value exported by a with statement has the type of its expression.
		exprScope := sb.getScope(parentNode.GetNode(parser.NodeWithStatementExpression), context)
		if !exprScope.GetIsValid() {
			return newScope().Invalid().GetScope()
		}

		return newScope().Valid().AssignableResolvedTypeOf(exprScope).GetScope()

	case parser.NodeTypeMatchStatement:
		// The named value exported by a match statement has the type of its expression (although
		// this is then overridden with specific hints under each case).
		exprScope := sb.getScope(parentNode.GetNode(parser.NodeMatchStatementExpression), context)
		if !exprScope.GetIsValid() {
			return newScope().Invalid().GetScope()
		}

		return newScope().Valid().AssignableResolvedTypeOf(exprScope).GetScope()

	case parser.NodeTypeLoopExpression:
		fallthrough

	case parser.NodeTypeLoopStatement:
		// The named value exported by a loop statement or expression has the type of the generic of the
		// Stream<T> interface implemented.
		var predicate compilergraph.Predicate = parser.NodeLoopStatementExpression
		if parentNode.Kind() == parser.NodeTypeLoopExpression {
			predicate = parser.NodeLoopExpressionStreamExpression
		}

		exprScope := sb.getScope(parentNode.GetNode(predicate), context)
		if !exprScope.GetIsValid() {
			return newScope().Invalid().GetScope()
		}

		loopExprType := exprScope.ResolvedTypeRef(sb.sg.tdg)

		// Check for a Streamable.
		generics, lerr := loopExprType.CheckConcreteSubtypeOf(sb.sg.tdg.StreamableType())
		if lerr == nil {
			return newScope().Valid().WithLabel(proto.ScopeLabel_STREAMABLE_LOOP).Assignable(generics[0]).GetScope()
		} else {
			generics, serr := loopExprType.CheckConcreteSubtypeOf(sb.sg.tdg.StreamType())
			if serr != nil {
				sb.decorateWithError(parentNode, "Loop iterable expression must implement type 'stream' or 'streamable': %v", serr)
				return newScope().Invalid().GetScope()
			}

			return newScope().WithLabel(proto.ScopeLabel_STREAM_LOOP).Valid().Assignable(generics[0]).GetScope()
		}

	default:
		panic(fmt.Sprintf("Unknown node exporting a named value: %v", parentNode.Kind()))
		return newScope().Invalid().GetScope()
	}
}